Merge branch 'x86/asm' into x86/urgent
authorIngo Molnar <mingo@elte.hu>
Tue, 15 Dec 2009 19:33:22 +0000 (20:33 +0100)
committerIngo Molnar <mingo@elte.hu>
Tue, 15 Dec 2009 19:33:28 +0000 (20:33 +0100)
Merge reason: it's stable so lets push it upstream.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
1769 files changed:
Documentation/ABI/stable/sysfs-driver-qla2xxx [new file with mode: 0644]
Documentation/ABI/testing/procfs-diskstats
Documentation/ABI/testing/sysfs-block
Documentation/DMA-mapping.txt
Documentation/DocBook/device-drivers.tmpl
Documentation/DocBook/dvb/dvbapi.xml
Documentation/DocBook/dvb/dvbproperty.xml [moved from Documentation/DocBook/dvb/isdbt.xml with 99% similarity]
Documentation/DocBook/dvb/frontend.h.xml [new file with mode: 0644]
Documentation/DocBook/dvb/frontend.xml
Documentation/DocBook/genericirq.tmpl
Documentation/DocBook/kernel-hacking.tmpl
Documentation/DocBook/media-entities.tmpl
Documentation/DocBook/mtdnand.tmpl
Documentation/DocBook/v4l/controls.xml
Documentation/DocBook/v4l/pixfmt.xml
Documentation/DocBook/v4l/videodev2.h.xml
Documentation/DocBook/v4l/vidioc-g-fbuf.xml
Documentation/DocBook/writing-an-alsa-driver.tmpl
Documentation/arm/OMAP/DSS [new file with mode: 0644]
Documentation/dvb/README.dvb-usb
Documentation/edac.txt
Documentation/feature-removal-schedule.txt
Documentation/filesystems/00-INDEX
Documentation/filesystems/exofs.txt
Documentation/filesystems/ext4.txt
Documentation/filesystems/proc.txt
Documentation/gpio.txt
Documentation/hwmon/adt7473
Documentation/hwmon/adt7475
Documentation/hwmon/f71882fg
Documentation/hwmon/it87
Documentation/hwmon/mc13783-adc [new file with mode: 0644]
Documentation/hwmon/sysfs-interface
Documentation/hwmon/w83627hf
Documentation/lguest/lguest.c
Documentation/misc-devices/c2port.txt [moved from Documentation/c2port.txt with 100% similarity]
Documentation/misc-devices/ics932s401 [moved from Documentation/ics932s401 with 100% similarity]
Documentation/oops-tracing.txt
Documentation/power/regulator/consumer.txt
Documentation/scsi/ChangeLog.megaraid_sas
Documentation/sound/alsa/HD-Audio-Models.txt
Documentation/sound/alsa/HD-Audio.txt
Documentation/spi/spi-summary
Documentation/sysctl/kernel.txt
Documentation/sysctl/vm.txt
Documentation/timers/hpet.txt
Documentation/video4linux/CARDLIST.cx23885
Documentation/video4linux/CARDLIST.cx88
Documentation/video4linux/CARDLIST.em28xx
Documentation/video4linux/CARDLIST.saa7134
Documentation/video4linux/gspca.txt
Documentation/video4linux/si4713.txt
Documentation/video4linux/zr364xx.txt
Documentation/vm/page-types.c
Documentation/vm/slub.txt
MAINTAINERS
arch/alpha/mm/numa.c
arch/arm/common/scoop.c
arch/arm/configs/da830_omapl137_defconfig [deleted file]
arch/arm/configs/da8xx_omapl_defconfig [moved from arch/arm/configs/da850_omapl138_defconfig with 85% similarity]
arch/arm/configs/davinci_all_defconfig
arch/arm/configs/omap_3430sdp_defconfig
arch/arm/mach-at91/at91sam9g45_devices.c
arch/arm/mach-at91/at91sam9rl_devices.c
arch/arm/mach-at91/board-sam9m10g45ek.c
arch/arm/mach-at91/board-sam9rlek.c
arch/arm/mach-at91/include/mach/board.h
arch/arm/mach-bcmring/include/csp/reg.h
arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h
arch/arm/mach-bcmring/include/mach/csp/mm_addr.h
arch/arm/mach-bcmring/include/mach/dma.h
arch/arm/mach-davinci/Kconfig
arch/arm/mach-davinci/Makefile
arch/arm/mach-davinci/board-da830-evm.c
arch/arm/mach-davinci/board-da850-evm.c
arch/arm/mach-davinci/board-dm355-evm.c
arch/arm/mach-davinci/board-dm355-leopard.c
arch/arm/mach-davinci/board-dm365-evm.c
arch/arm/mach-davinci/board-dm644x-evm.c
arch/arm/mach-davinci/board-dm646x-evm.c
arch/arm/mach-davinci/board-neuros-osd2.c [new file with mode: 0644]
arch/arm/mach-davinci/board-sffsdr.c
arch/arm/mach-davinci/clock.c
arch/arm/mach-davinci/clock.h
arch/arm/mach-davinci/common.c
arch/arm/mach-davinci/cp_intc.c
arch/arm/mach-davinci/cpufreq.c [new file with mode: 0644]
arch/arm/mach-davinci/cpuidle.c [new file with mode: 0644]
arch/arm/mach-davinci/da830.c
arch/arm/mach-davinci/da850.c
arch/arm/mach-davinci/devices-da8xx.c
arch/arm/mach-davinci/devices.c
arch/arm/mach-davinci/dm355.c
arch/arm/mach-davinci/dm365.c
arch/arm/mach-davinci/dm644x.c
arch/arm/mach-davinci/dm646x.c
arch/arm/mach-davinci/dma.c
arch/arm/mach-davinci/gpio.c
arch/arm/mach-davinci/include/mach/asp.h
arch/arm/mach-davinci/include/mach/common.h
arch/arm/mach-davinci/include/mach/cpufreq.h [new file with mode: 0644]
arch/arm/mach-davinci/include/mach/cpuidle.h [new file with mode: 0644]
arch/arm/mach-davinci/include/mach/da8xx.h
arch/arm/mach-davinci/include/mach/dm365.h
arch/arm/mach-davinci/include/mach/dm644x.h
arch/arm/mach-davinci/include/mach/irqs.h
arch/arm/mach-davinci/include/mach/keyscan.h [new file with mode: 0644]
arch/arm/mach-davinci/include/mach/mux.h
arch/arm/mach-davinci/include/mach/system.h
arch/arm/mach-davinci/include/mach/usb.h [new file with mode: 0644]
arch/arm/mach-davinci/mux.c
arch/arm/mach-davinci/psc.c
arch/arm/mach-davinci/serial.c
arch/arm/mach-davinci/sram.c
arch/arm/mach-davinci/time.c
arch/arm/mach-davinci/usb.c
arch/arm/mach-lh7a40x/include/mach/hardware.h
arch/arm/mach-omap1/board-nokia770.c
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/clock24xx.c
arch/arm/mach-omap2/clock34xx.c
arch/arm/mach-omap2/io.c
arch/arm/mach-omap2/sdrc.c
arch/arm/mach-orion5x/pci.c
arch/arm/mach-pxa/include/mach/palmld.h
arch/arm/mach-pxa/include/mach/palmt5.h
arch/arm/mach-pxa/include/mach/palmtc.h
arch/arm/mach-pxa/include/mach/palmte2.h
arch/arm/mach-pxa/include/mach/palmtx.h
arch/arm/mach-pxa/include/mach/palmz72.h
arch/arm/mach-s3c2400/Kconfig
arch/arm/mach-s3c2410/Kconfig
arch/arm/mach-s3c2412/Kconfig
arch/arm/mach-s3c2440/Kconfig
arch/arm/mach-s3c2442/Kconfig
arch/arm/mach-s3c2443/Kconfig
arch/arm/mach-s3c6400/Kconfig
arch/arm/mach-s3c6400/setup-sdhci.c
arch/arm/mach-s3c6410/Kconfig
arch/arm/mach-s3c6410/setup-sdhci.c
arch/arm/mach-s5pc100/Kconfig
arch/arm/mach-sa1100/dma.c
arch/arm/mach-u300/include/mach/u300-regs.h
arch/arm/plat-mxc/include/mach/iomux-mx3.h
arch/arm/plat-mxc/include/mach/iomux-mxc91231.h
arch/arm/plat-mxc/pwm.c
arch/arm/plat-omap/dma.c
arch/arm/plat-omap/fb.c
arch/arm/plat-omap/include/plat/display.h [new file with mode: 0644]
arch/arm/plat-omap/include/plat/omap16xx.h
arch/arm/plat-omap/include/plat/sdrc.h
arch/arm/plat-omap/include/plat/vram.h [new file with mode: 0644]
arch/arm/plat-omap/include/plat/vrfb.h [new file with mode: 0644]
arch/arm/plat-omap/sram.c
arch/arm/plat-s3c/Kconfig
arch/arm/plat-s3c/pwm.c
arch/arm/plat-s3c24xx/Kconfig
arch/arm/plat-s3c24xx/adc.c
arch/arm/plat-s3c24xx/dma.c
arch/arm/plat-s3c24xx/include/plat/map.h
arch/arm/plat-s3c24xx/irq.c
arch/arm/plat-s3c64xx/Kconfig
arch/arm/plat-s5pc1xx/Kconfig
arch/avr32/boards/hammerhead/Kconfig
arch/blackfin/kernel/traps.c
arch/blackfin/mach-bf518/include/mach/defBF51x_base.h
arch/blackfin/mach-bf527/include/mach/defBF52x_base.h
arch/blackfin/mach-bf537/include/mach/defBF534.h
arch/blackfin/mach-bf548/include/mach/defBF544.h
arch/blackfin/mach-bf548/include/mach/defBF547.h
arch/blackfin/mach-bf548/include/mach/defBF548.h
arch/blackfin/mach-bf548/include/mach/defBF549.h
arch/cris/arch-v10/mm/fault.c
arch/cris/arch-v10/mm/tlb.c
arch/cris/mm/fault.c
arch/ia64/hp/common/sba_iommu.c
arch/ia64/ia32/ia32_entry.S
arch/ia64/include/asm/perfmon_default_smpl.h
arch/ia64/include/asm/sn/shubio.h
arch/ia64/include/asm/unistd.h
arch/ia64/kernel/esi.c
arch/ia64/kernel/perfmon.c
arch/ia64/sn/kernel/io_acpi_init.c
arch/m68k/ifpsp060/src/fpsp.S
arch/m68k/ifpsp060/src/pfpsp.S
arch/m68k/include/asm/bootinfo.h
arch/m68k/include/asm/processor.h
arch/m68k/include/asm/ptrace.h
arch/m68k/include/asm/thread_info_no.h
arch/m68knommu/Kconfig
arch/m68knommu/kernel/setup.c
arch/m68knommu/kernel/vmlinux.lds.S
arch/m68knommu/platform/523x/gpio.c
arch/m68knommu/platform/527x/gpio.c
arch/m68knommu/platform/528x/gpio.c
arch/m68knommu/platform/coldfire/clk.c
arch/m68knommu/platform/coldfire/head.S
arch/microblaze/lib/memcpy.c
arch/microblaze/lib/memmove.c
arch/microblaze/lib/memset.c
arch/mips/include/asm/mach-pnx833x/gpio.h
arch/mips/include/asm/sgi/ioc.h
arch/mips/include/asm/sibyte/sb1250_mac.h
arch/mips/include/asm/sn/sn0/hubio.h
arch/mips/kernel/smtc.c
arch/mips/math-emu/dp_sub.c
arch/mips/txx9/generic/smsc_fdc37m81x.c
arch/parisc/kernel/perf.c
arch/powerpc/include/asm/reg_fsl_emb.h
arch/powerpc/kernel/kgdb.c
arch/powerpc/kernel/tau_6xx.c
arch/powerpc/kernel/vdso.c
arch/powerpc/oprofile/op_model_cell.c
arch/powerpc/platforms/52xx/mpc52xx_pci.c
arch/powerpc/platforms/powermac/pci.c
arch/powerpc/sysdev/dart_iommu.c
arch/s390/Kconfig
arch/s390/defconfig
arch/s390/include/asm/atomic.h
arch/s390/include/asm/ccwdev.h
arch/s390/include/asm/mmu_context.h
arch/s390/include/asm/pgalloc.h
arch/s390/include/asm/pgtable.h
arch/s390/include/asm/setup.h
arch/s390/include/asm/smp.h
arch/s390/include/asm/sockios.h
arch/s390/include/asm/termbits.h
arch/s390/include/asm/todclk.h [deleted file]
arch/s390/include/asm/uaccess.h
arch/s390/kernel/Makefile
arch/s390/kernel/compat_linux.c
arch/s390/kernel/compat_linux.h
arch/s390/kernel/head64.S
arch/s390/kernel/setup.c
arch/s390/kernel/time.c
arch/s390/kernel/vdso.c
arch/s390/kvm/Kconfig
arch/s390/lib/uaccess_mvcos.c
arch/s390/lib/uaccess_pt.c
arch/s390/math-emu/math.c
arch/s390/mm/cmm.c
arch/s390/mm/fault.c
arch/s390/mm/pgtable.c
arch/s390/mm/vmem.c
arch/sh/Kconfig
arch/sh/Makefile
arch/sh/boards/Makefile
arch/sh/boards/mach-ap325rxa/Makefile [new file with mode: 0644]
arch/sh/boards/mach-ap325rxa/sdram.S [new file with mode: 0644]
arch/sh/boards/mach-ap325rxa/setup.c [moved from arch/sh/boards/board-ap325rxa.c with 87% similarity]
arch/sh/boards/mach-ecovec24/Makefile
arch/sh/boards/mach-ecovec24/sdram.S [new file with mode: 0644]
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boards/mach-highlander/setup.c
arch/sh/boards/mach-kfr2r09/Makefile
arch/sh/boards/mach-kfr2r09/sdram.S [new file with mode: 0644]
arch/sh/boards/mach-kfr2r09/setup.c
arch/sh/boards/mach-migor/Makefile
arch/sh/boards/mach-migor/sdram.S [new file with mode: 0644]
arch/sh/boards/mach-migor/setup.c
arch/sh/boards/mach-r2d/irq.c
arch/sh/boards/mach-se/7722/irq.c
arch/sh/boards/mach-se/7722/setup.c
arch/sh/boards/mach-se/7724/Makefile
arch/sh/boards/mach-se/7724/sdram.S [new file with mode: 0644]
arch/sh/boards/mach-se/7724/setup.c
arch/sh/boot/compressed/misc.c
arch/sh/boot/romimage/Makefile
arch/sh/boot/romimage/head.S
arch/sh/drivers/dma/dma-sysfs.c
arch/sh/drivers/pci/Kconfig [deleted file]
arch/sh/include/asm/addrspace.h
arch/sh/include/asm/atomic.h
arch/sh/include/asm/bitops.h
arch/sh/include/asm/bugs.h
arch/sh/include/asm/dma-mapping.h
arch/sh/include/asm/dwarf.h
arch/sh/include/asm/fixmap.h
arch/sh/include/asm/fpu.h
arch/sh/include/asm/ftrace.h
arch/sh/include/asm/gpio.h
arch/sh/include/asm/hardirq.h
arch/sh/include/asm/io.h
arch/sh/include/asm/irqflags.h
arch/sh/include/asm/irqflags_32.h [deleted file]
arch/sh/include/asm/irqflags_64.h [deleted file]
arch/sh/include/asm/mmu.h
arch/sh/include/asm/pci.h
arch/sh/include/asm/perf_event.h
arch/sh/include/asm/pgtable.h
arch/sh/include/asm/pgtable_32.h
arch/sh/include/asm/processor_32.h
arch/sh/include/asm/scatterlist.h
arch/sh/include/asm/suspend.h
arch/sh/include/asm/system.h
arch/sh/include/asm/system_32.h
arch/sh/include/asm/system_64.h
arch/sh/include/asm/thread_info.h
arch/sh/include/asm/topology.h
arch/sh/include/asm/ubc.h
arch/sh/include/asm/watchdog.h
arch/sh/include/cpu-sh4/cpu/watchdog.h
arch/sh/include/mach-common/mach/titan.h
arch/sh/include/mach-ecovec24/mach/partner-jet-setup.txt
arch/sh/include/mach-se/mach/se7722.h
arch/sh/kernel/Makefile
arch/sh/kernel/asm-offsets.c
arch/sh/kernel/cpu/Makefile
arch/sh/kernel/cpu/init.c
arch/sh/kernel/cpu/sh2a/fpu.c
arch/sh/kernel/cpu/sh3/entry.S
arch/sh/kernel/cpu/sh4/Makefile
arch/sh/kernel/cpu/sh4/fpu.c
arch/sh/kernel/cpu/sh4/perf_event.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh4a/Makefile
arch/sh/kernel/cpu/sh4a/clock-sh7724.c
arch/sh/kernel/cpu/sh4a/perf_event.c [new file with mode: 0644]
arch/sh/kernel/cpu/sh4a/setup-sh7724.c
arch/sh/kernel/cpu/sh4a/setup-shx3.c
arch/sh/kernel/cpu/sh4a/smp-shx3.c
arch/sh/kernel/cpu/sh5/entry.S
arch/sh/kernel/cpu/shmobile/cpuidle.c
arch/sh/kernel/cpu/shmobile/pm.c
arch/sh/kernel/cpu/shmobile/pm_runtime.c
arch/sh/kernel/cpu/shmobile/sleep.S
arch/sh/kernel/cpu/ubc.S [deleted file]
arch/sh/kernel/dma-nommu.c [new file with mode: 0644]
arch/sh/kernel/dwarf.c
arch/sh/kernel/entry-common.S
arch/sh/kernel/ftrace.c
arch/sh/kernel/head_32.S
arch/sh/kernel/idle.c
arch/sh/kernel/io_generic.c
arch/sh/kernel/irq.c
arch/sh/kernel/irq_32.c [new file with mode: 0644]
arch/sh/kernel/irq_64.c [new file with mode: 0644]
arch/sh/kernel/machine_kexec.c
arch/sh/kernel/machvec.c
arch/sh/kernel/module.c
arch/sh/kernel/perf_callchain.c [new file with mode: 0644]
arch/sh/kernel/perf_event.c [new file with mode: 0644]
arch/sh/kernel/process_32.c
arch/sh/kernel/process_64.c
arch/sh/kernel/return_address.c [new file with mode: 0644]
arch/sh/kernel/setup.c
arch/sh/kernel/sh_ksyms_32.c
arch/sh/kernel/sh_ksyms_64.c
arch/sh/kernel/signal_32.c
arch/sh/kernel/signal_64.c
arch/sh/kernel/smp.c
arch/sh/kernel/topology.c
arch/sh/kernel/traps.c
arch/sh/kernel/traps_32.c
arch/sh/lib/Makefile
arch/sh/lib/memset-sh4.S [new file with mode: 0644]
arch/sh/math-emu/math.c
arch/sh/mm/Kconfig
arch/sh/mm/Makefile
arch/sh/mm/cache-sh4.c
arch/sh/mm/cache-sh5.c
arch/sh/mm/cache-sh7705.c
arch/sh/mm/cache.c
arch/sh/mm/consistent.c
arch/sh/mm/init.c
arch/sh/mm/kmap.c
arch/sh/mm/numa.c
arch/sh/mm/pmb-fixed.c [deleted file]
arch/sh/mm/pmb.c
arch/sh/oprofile/Makefile
arch/sh/oprofile/common.c
arch/sh/oprofile/op_impl.h
arch/sh/oprofile/op_model_sh7750.c [deleted file]
arch/x86/boot/compressed/relocs.c
arch/x86/include/asm/amd_iommu_proto.h
arch/x86/include/asm/desc_defs.h
arch/x86/include/asm/irq_vectors.h
arch/x86/include/asm/mmzone_32.h
arch/x86/include/asm/msr.h
arch/x86/include/asm/sigcontext.h
arch/x86/include/asm/swiotlb.h
arch/x86/include/asm/system.h
arch/x86/include/asm/trampoline.h
arch/x86/include/asm/uv/uv_bau.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/amd_iommu.c
arch/x86/kernel/amd_iommu_init.c
arch/x86/kernel/aperture_64.c
arch/x86/kernel/apic/apic.c
arch/x86/kernel/apic/apic_noop.c
arch/x86/kernel/apic/es7000_32.c
arch/x86/kernel/cpu/addon_cpuid_features.c
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/cpufreq/longhaul.c
arch/x86/kernel/cpu/intel.c
arch/x86/kernel/cpu/intel_cacheinfo.c
arch/x86/kernel/cpu/mcheck/mce.c
arch/x86/kernel/cpu/mcheck/therm_throt.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/e820.c
arch/x86/kernel/head32.c
arch/x86/kernel/head64.c
arch/x86/kernel/kprobes.c
arch/x86/kernel/microcode_amd.c
arch/x86/kernel/microcode_core.c
arch/x86/kernel/microcode_intel.c
arch/x86/kernel/mpparse.c
arch/x86/kernel/msr.c
arch/x86/kernel/pci-dma.c
arch/x86/kernel/pci-gart_64.c
arch/x86/kernel/pci-swiotlb.c
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/kernel/reboot.c
arch/x86/kernel/setup.c
arch/x86/kernel/setup_percpu.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/trampoline.c
arch/x86/kernel/vmlinux.lds.S
arch/x86/kvm/i8254.c
arch/x86/lib/msr.c
arch/x86/mm/kmmio.c
arch/x86/mm/mmio-mod.c
arch/x86/xen/enlighten.c
arch/x86/xen/mmu.c
arch/x86/xen/smp.c
arch/x86/xen/suspend.c
arch/x86/xen/time.c
arch/x86/xen/xen-asm_64.S
arch/x86/xen/xen-ops.h
block/blk-iopoll.c
drivers/Kconfig
drivers/acpi/acpi_memhotplug.c
drivers/acpi/acpica/Makefile
drivers/acpi/acpica/acmacros.h
drivers/acpi/acpica/acnamesp.h
drivers/acpi/acpica/acutils.h
drivers/acpi/acpica/dsinit.c
drivers/acpi/acpica/dsmthdat.c
drivers/acpi/acpica/dsobject.c
drivers/acpi/acpica/dswload.c
drivers/acpi/acpica/evgpeblk.c
drivers/acpi/acpica/evregion.c
drivers/acpi/acpica/exconfig.c
drivers/acpi/acpica/exconvrt.c
drivers/acpi/acpica/exfield.c
drivers/acpi/acpica/exoparg1.c
drivers/acpi/acpica/exoparg6.c
drivers/acpi/acpica/nsdump.c
drivers/acpi/acpica/nsdumpdv.c
drivers/acpi/acpica/nseval.c
drivers/acpi/acpica/nsinit.c
drivers/acpi/acpica/nspredef.c
drivers/acpi/acpica/nsrepair.c
drivers/acpi/acpica/nsrepair2.c [new file with mode: 0644]
drivers/acpi/acpica/nswalk.c
drivers/acpi/acpica/nsxfeval.c
drivers/acpi/acpica/psloop.c
drivers/acpi/acpica/psparse.c
drivers/acpi/acpica/psxface.c
drivers/acpi/acpica/utmisc.c
drivers/acpi/acpica/utobject.c
drivers/acpi/container.c
drivers/acpi/dock.c
drivers/acpi/ec.c
drivers/acpi/glue.c
drivers/acpi/pci_slot.c
drivers/acpi/processor_core.c
drivers/acpi/scan.c
drivers/acpi/video_detect.c
drivers/ata/ata_piix.c
drivers/ata/libata-scsi.c
drivers/ata/libata-sff.c
drivers/ata/pata_cs5535.c
drivers/ata/sata_fsl.c
drivers/ata/sata_nv.c
drivers/atm/iphase.c
drivers/base/dd.c
drivers/block/ataflop.c
drivers/block/cciss_cmd.h
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/hci_ldisc.c
drivers/cdrom/gdrom.c
drivers/char/agp/intel-agp.c
drivers/char/keyboard.c
drivers/char/mem.c
drivers/char/mspec.c
drivers/char/n_r3964.c
drivers/char/nvram.c
drivers/char/rio/route.h
drivers/char/tty_ioctl.c
drivers/cpuidle/governor.c
drivers/crypto/hifn_795x.c
drivers/dma/at_hdmac.c
drivers/edac/amd64_edac.c
drivers/edac/amd64_edac.h
drivers/edac/edac_core.h
drivers/edac/edac_mc.c
drivers/edac/edac_mce_amd.c
drivers/firewire/core-topology.c
drivers/firmware/dmi_scan.c
drivers/gpu/drm/Makefile
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_dp_i2c_helper.c [moved from drivers/gpu/drm/i915/intel_dp_i2c.c with 79% similarity]
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_stub.c
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/dvo_ch7017.c
drivers/gpu/drm/i915/dvo_ch7xxx.c
drivers/gpu/drm/i915/dvo_ivch.c
drivers/gpu/drm/i915/dvo_sil164.c
drivers/gpu/drm/i915/dvo_tfp410.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.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_opregion.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_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_overlay.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/atom.c
drivers/gpu/drm/radeon/atom.h
drivers/gpu/drm/radeon/atombios.h
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_dp.c [new file with mode: 0644]
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r100_track.h
drivers/gpu/drm/radeon/r300.c
drivers/gpu/drm/radeon/r420.c
drivers/gpu/drm/radeon/r500_reg.h
drivers/gpu/drm/radeon/r520.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600_blit_kms.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_asic.h
drivers/gpu/drm/radeon/radeon_atombios.c
drivers/gpu/drm/radeon/radeon_benchmark.c
drivers/gpu/drm/radeon/radeon_clocks.c
drivers/gpu/drm/radeon/radeon_combios.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cp.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_drv.h
drivers/gpu/drm/radeon/radeon_encoders.c
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_fence.c
drivers/gpu/drm/radeon/radeon_fixed.h
drivers/gpu/drm/radeon/radeon_gart.c
drivers/gpu/drm/radeon/radeon_gem.c
drivers/gpu/drm/radeon/radeon_i2c.c
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_legacy_crtc.c
drivers/gpu/drm/radeon/radeon_legacy_encoders.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/radeon_object.c
drivers/gpu/drm/radeon/radeon_object.h
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_reg.h
drivers/gpu/drm/radeon/radeon_ring.c
drivers/gpu/drm/radeon/radeon_state.c
drivers/gpu/drm/radeon/radeon_test.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/radeon/rs400.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rs600d.h
drivers/gpu/drm/radeon/rs690.c
drivers/gpu/drm/radeon/rv515.c
drivers/gpu/drm/radeon/rv770.c
drivers/gpu/drm/ttm/Makefile
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/ttm/ttm_bo_vm.c
drivers/gpu/drm/ttm/ttm_execbuf_util.c [new file with mode: 0644]
drivers/gpu/drm/ttm/ttm_lock.c [new file with mode: 0644]
drivers/gpu/drm/ttm/ttm_memory.c
drivers/gpu/drm/ttm/ttm_object.c [new file with mode: 0644]
drivers/gpu/drm/ttm/ttm_tt.c
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c
drivers/hid/hid-ids.h
drivers/hid/hid-lg.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-pidff.c
drivers/hid/usbhid/hid-quirks.c
drivers/hid/usbhid/hiddev.c
drivers/hid/usbhid/usbkbd.c
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/adm1021.c
drivers/hwmon/adm1025.c
drivers/hwmon/adm1026.c
drivers/hwmon/adm1029.c
drivers/hwmon/adm1031.c
drivers/hwmon/adm9240.c
drivers/hwmon/ads7828.c
drivers/hwmon/adt7462.c
drivers/hwmon/adt7470.c
drivers/hwmon/adt7473.c
drivers/hwmon/adt7475.c
drivers/hwmon/asb100.c
drivers/hwmon/dme1737.c
drivers/hwmon/ds1621.c
drivers/hwmon/f71882fg.c
drivers/hwmon/f75375s.c
drivers/hwmon/fschmd.c
drivers/hwmon/gl518sm.c
drivers/hwmon/gl520sm.c
drivers/hwmon/it87.c
drivers/hwmon/lm63.c
drivers/hwmon/lm73.c [new file with mode: 0644]
drivers/hwmon/lm75.c
drivers/hwmon/lm77.c
drivers/hwmon/lm78.c
drivers/hwmon/lm83.c
drivers/hwmon/lm85.c
drivers/hwmon/lm87.c
drivers/hwmon/lm90.c
drivers/hwmon/lm92.c
drivers/hwmon/lm93.c
drivers/hwmon/lm95241.c
drivers/hwmon/max1619.c
drivers/hwmon/max6650.c
drivers/hwmon/mc13783-adc.c [new file with mode: 0644]
drivers/hwmon/s3c-hwmon.c
drivers/hwmon/smsc47m192.c
drivers/hwmon/thmc50.c
drivers/hwmon/tmp401.c
drivers/hwmon/tmp421.c
drivers/hwmon/w83627hf.c
drivers/hwmon/w83781d.c
drivers/hwmon/w83791d.c
drivers/hwmon/w83792d.c
drivers/hwmon/w83793.c
drivers/hwmon/w83l785ts.c
drivers/hwmon/w83l786ng.c
drivers/i2c/busses/i2c-designware.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-pnx.c
drivers/i2c/busses/i2c-scmi.c
drivers/ide/Kconfig
drivers/ide/alim15x3.c
drivers/ide/au1xxx-ide.c
drivers/ide/cmd64x.c
drivers/ide/cs5535.c
drivers/ide/cy82c693.c
drivers/ide/hpt366.c
drivers/ide/ide-tape.c
drivers/ide/ide_platform.c
drivers/ide/pdc202xx_old.c
drivers/ide/sis5513.c
drivers/ide/sl82c105.c
drivers/ide/slc90e66.c
drivers/ide/tx4938ide.c
drivers/ieee1394/dv1394.c
drivers/infiniband/hw/cxgb3/cxio_hal.c
drivers/infiniband/hw/ipath/ipath_iba6110.c
drivers/infiniband/hw/ipath/ipath_sd7220.c
drivers/infiniband/hw/mlx4/qp.c
drivers/infiniband/ulp/iser/iscsi_iser.c
drivers/infiniband/ulp/iser/iser_verbs.c
drivers/input/Kconfig
drivers/input/Makefile
drivers/input/ff-core.c
drivers/input/input-polldev.c
drivers/input/input.c
drivers/input/joystick/xpad.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/davinci_keyscan.c [new file with mode: 0644]
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/lkkbd.c
drivers/input/keyboard/matrix_keypad.c
drivers/input/keyboard/sh_keysc.c
drivers/input/misc/Kconfig
drivers/input/misc/ati_remote.c
drivers/input/misc/dm355evm_keys.c
drivers/input/misc/powermate.c
drivers/input/misc/wistron_btns.c
drivers/input/mouse/alps.c
drivers/input/mouse/elantech.c
drivers/input/mouse/hgpk.c
drivers/input/mouse/lifebook.c
drivers/input/mouse/lifebook.h
drivers/input/mouse/logips2pp.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/sentelic.c
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/mouse/synaptics_i2c.c
drivers/input/mouse/touchkit_ps2.c
drivers/input/mouse/trackpoint.c
drivers/input/mouse/vsxxxaa.c
drivers/input/serio/Kconfig
drivers/input/serio/Makefile
drivers/input/serio/altera_ps2.c [new file with mode: 0644]
drivers/input/serio/hp_sdc.c
drivers/input/serio/hp_sdc_mlc.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/sparse-keymap.c [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/atmel-wm97xx.c
drivers/input/touchscreen/atmel_tsadcc.c
drivers/input/touchscreen/dynapro.c [new file with mode: 0644]
drivers/input/touchscreen/mainstone-wm97xx.c
drivers/input/touchscreen/s3c2410_ts.c [new file with mode: 0644]
drivers/input/touchscreen/ucb1400_ts.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/input/touchscreen/zylonite-wm97xx.c
drivers/isdn/act2000/Kconfig
drivers/isdn/capi/Kconfig
drivers/isdn/capi/capidrv.c
drivers/isdn/hardware/eicon/di.c
drivers/isdn/hardware/eicon/maintidi.c
drivers/isdn/hardware/mISDN/hfcsusb.c
drivers/isdn/hardware/mISDN/hfcsusb.h
drivers/isdn/hardware/mISDN/mISDNisar.c
drivers/isdn/hisax/hfc_usb.c
drivers/isdn/hysdn/Kconfig
drivers/isdn/i4l/isdn_net.c
drivers/isdn/i4l/isdn_ppp.c
drivers/isdn/i4l/isdn_ttyfax.c
drivers/isdn/icn/Kconfig
drivers/isdn/mISDN/dsp_core.c
drivers/isdn/mISDN/tei.c
drivers/isdn/pcbit/Kconfig
drivers/isdn/sc/Kconfig
drivers/macintosh/therm_windtunnel.c
drivers/md/dm-crypt.c
drivers/md/dm-mpath.c
drivers/media/common/Makefile
drivers/media/common/ir-functions.c
drivers/media/common/ir-keymaps.c
drivers/media/common/ir-keytable.c [new file with mode: 0644]
drivers/media/common/saa7146_i2c.c
drivers/media/common/saa7146_video.c
drivers/media/common/tuners/Kconfig
drivers/media/common/tuners/Makefile
drivers/media/common/tuners/max2165.c [new file with mode: 0644]
drivers/media/common/tuners/max2165.h [new file with mode: 0644]
drivers/media/common/tuners/max2165_priv.h [new file with mode: 0644]
drivers/media/common/tuners/mxl5005s.c
drivers/media/common/tuners/mxl5005s.h
drivers/media/common/tuners/mxl5007t.c
drivers/media/common/tuners/tda18271-common.c
drivers/media/common/tuners/tda18271-fe.c
drivers/media/common/tuners/tda18271-maps.c
drivers/media/common/tuners/tda18271-priv.h
drivers/media/common/tuners/tda18271.h
drivers/media/common/tuners/tda8290.c
drivers/media/common/tuners/tda9887.c
drivers/media/common/tuners/xc5000.c
drivers/media/common/tuners/xc5000.h
drivers/media/dvb/bt8xx/dvb-bt8xx.c
drivers/media/dvb/dm1105/dm1105.c
drivers/media/dvb/dvb-core/dvb_demux.c
drivers/media/dvb/dvb-core/dvb_demux.h
drivers/media/dvb/dvb-core/dvb_frontend.c
drivers/media/dvb/dvb-core/dvb_frontend.h
drivers/media/dvb/dvb-core/dvb_net.c
drivers/media/dvb/dvb-core/dvbdev.h
drivers/media/dvb/dvb-usb/Kconfig
drivers/media/dvb/dvb-usb/Makefile
drivers/media/dvb/dvb-usb/af9015.c
drivers/media/dvb/dvb-usb/af9015.h
drivers/media/dvb/dvb-usb/anysee.c
drivers/media/dvb/dvb-usb/cxusb.c
drivers/media/dvb/dvb-usb/dib0700_devices.c
drivers/media/dvb/dvb-usb/dibusb-mb.c
drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
drivers/media/dvb/dvb-usb/dvb-usb-ids.h
drivers/media/dvb/dvb-usb/dvb-usb-remote.c
drivers/media/dvb/dvb-usb/dvb-usb.h
drivers/media/dvb/dvb-usb/ec168.c [new file with mode: 0644]
drivers/media/dvb/dvb-usb/ec168.h [new file with mode: 0644]
drivers/media/dvb/dvb-usb/friio-fe.c
drivers/media/dvb/dvb-usb/usb-urb.c
drivers/media/dvb/firewire/Kconfig
drivers/media/dvb/firewire/Makefile
drivers/media/dvb/firewire/firedtv-1394.c
drivers/media/dvb/firewire/firedtv-avc.c
drivers/media/dvb/firewire/firedtv-dvb.c
drivers/media/dvb/firewire/firedtv-fw.c [new file with mode: 0644]
drivers/media/dvb/firewire/firedtv-rc.c
drivers/media/dvb/firewire/firedtv.h
drivers/media/dvb/frontends/Kconfig
drivers/media/dvb/frontends/Makefile
drivers/media/dvb/frontends/atbm8830.c [new file with mode: 0644]
drivers/media/dvb/frontends/atbm8830.h [new file with mode: 0644]
drivers/media/dvb/frontends/atbm8830_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/au8522_decoder.c
drivers/media/dvb/frontends/au8522_priv.h
drivers/media/dvb/frontends/cx24110.c
drivers/media/dvb/frontends/cx24113.c
drivers/media/dvb/frontends/dib3000mb.c
drivers/media/dvb/frontends/dib7000p.c
drivers/media/dvb/frontends/dib7000p.h
drivers/media/dvb/frontends/dib8000.c
drivers/media/dvb/frontends/dib8000.h
drivers/media/dvb/frontends/drx397xD.c
drivers/media/dvb/frontends/ds3000.c [new file with mode: 0644]
drivers/media/dvb/frontends/ds3000.h [new file with mode: 0644]
drivers/media/dvb/frontends/ec100.c [new file with mode: 0644]
drivers/media/dvb/frontends/ec100.h [new file with mode: 0644]
drivers/media/dvb/frontends/ec100_priv.h [new file with mode: 0644]
drivers/media/dvb/frontends/lgdt330x.c
drivers/media/dvb/frontends/s5h1409.c
drivers/media/dvb/frontends/s5h1409.h
drivers/media/dvb/frontends/stb0899_drv.c
drivers/media/dvb/frontends/stb6100_proc.h [new file with mode: 0644]
drivers/media/dvb/frontends/stv0900.h
drivers/media/dvb/frontends/stv0900_core.c
drivers/media/dvb/frontends/stv0900_init.h
drivers/media/dvb/frontends/stv0900_priv.h
drivers/media/dvb/frontends/stv0900_reg.h
drivers/media/dvb/frontends/stv0900_sw.c
drivers/media/dvb/frontends/stv090x.c
drivers/media/dvb/frontends/stv090x_priv.h
drivers/media/dvb/frontends/stv090x_reg.h
drivers/media/dvb/frontends/stv6110.c
drivers/media/dvb/frontends/stv6110.h
drivers/media/dvb/frontends/stv6110x.c
drivers/media/dvb/pt1/pt1.c
drivers/media/dvb/pt1/va1j5jf8007s.c
drivers/media/dvb/pt1/va1j5jf8007t.c
drivers/media/dvb/siano/sms-cards.c
drivers/media/dvb/siano/sms-cards.h
drivers/media/dvb/siano/smscoreapi.c
drivers/media/dvb/siano/smssdio.c
drivers/media/dvb/ttpci/av7110.c
drivers/media/dvb/ttpci/budget-av.c
drivers/media/dvb/ttpci/budget-ci.c
drivers/media/dvb/ttpci/budget-patch.c
drivers/media/dvb/ttusb-dec/ttusb_dec.c
drivers/media/radio/Kconfig
drivers/media/radio/Makefile
drivers/media/radio/radio-mr800.c
drivers/media/radio/tef6862.c [new file with mode: 0644]
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/adv7180.c
drivers/media/video/au0828/au0828-video.c
drivers/media/video/bt819.c
drivers/media/video/bt8xx/bttv-input.c
drivers/media/video/cx18/cx18-av-core.c
drivers/media/video/cx18/cx18-av-core.h
drivers/media/video/cx18/cx18-cards.h
drivers/media/video/cx18/cx18-driver.c
drivers/media/video/cx18/cx18-driver.h
drivers/media/video/cx18/cx18-dvb.c
drivers/media/video/cx18/cx18-fileops.c
drivers/media/video/cx18/cx18-i2c.c
drivers/media/video/cx18/cx18-ioctl.c
drivers/media/video/cx18/cx18-mailbox.c
drivers/media/video/cx18/cx18-mailbox.h
drivers/media/video/cx18/cx18-queue.c
drivers/media/video/cx18/cx18-queue.h
drivers/media/video/cx18/cx18-scb.h
drivers/media/video/cx18/cx18-streams.c
drivers/media/video/cx18/cx18-streams.h
drivers/media/video/cx18/cx18-vbi.c
drivers/media/video/cx18/cx18-vbi.h
drivers/media/video/cx18/cx18-version.h
drivers/media/video/cx18/cx23418.h
drivers/media/video/cx231xx/cx231xx-avcore.c
drivers/media/video/cx231xx/cx231xx-input.c
drivers/media/video/cx231xx/cx231xx-video.c
drivers/media/video/cx23885/Kconfig
drivers/media/video/cx23885/Makefile
drivers/media/video/cx23885/cx23885-417.c
drivers/media/video/cx23885/cx23885-cards.c
drivers/media/video/cx23885/cx23885-core.c
drivers/media/video/cx23885/cx23885-dvb.c
drivers/media/video/cx23885/cx23885-f300.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-f300.h [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-input.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-input.h [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-ioctl.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-ioctl.h [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-ir.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-ir.h [new file with mode: 0644]
drivers/media/video/cx23885/cx23885-reg.h
drivers/media/video/cx23885/cx23885-video.c
drivers/media/video/cx23885/cx23885.h
drivers/media/video/cx23885/cx23888-ir.c [new file with mode: 0644]
drivers/media/video/cx23885/cx23888-ir.h [new file with mode: 0644]
drivers/media/video/cx25840/cx25840-audio.c
drivers/media/video/cx25840/cx25840-core.c
drivers/media/video/cx25840/cx25840-core.h
drivers/media/video/cx25840/cx25840-firmware.c
drivers/media/video/cx88/Kconfig
drivers/media/video/cx88/cx88-cards.c
drivers/media/video/cx88/cx88-core.c
drivers/media/video/cx88/cx88-dvb.c
drivers/media/video/cx88/cx88-input.c
drivers/media/video/cx88/cx88-video.c
drivers/media/video/cx88/cx88.h
drivers/media/video/davinci/dm355_ccdc.c
drivers/media/video/davinci/dm644x_ccdc.c
drivers/media/video/davinci/vpfe_capture.c
drivers/media/video/davinci/vpss.c
drivers/media/video/em28xx/em28xx-audio.c
drivers/media/video/em28xx/em28xx-cards.c
drivers/media/video/em28xx/em28xx-core.c
drivers/media/video/em28xx/em28xx-dvb.c
drivers/media/video/em28xx/em28xx-input.c
drivers/media/video/em28xx/em28xx-reg.h
drivers/media/video/em28xx/em28xx-video.c
drivers/media/video/em28xx/em28xx.h
drivers/media/video/gspca/Kconfig
drivers/media/video/gspca/Makefile
drivers/media/video/gspca/conex.c
drivers/media/video/gspca/etoms.c
drivers/media/video/gspca/finepix.c
drivers/media/video/gspca/gl860/gl860-mi1320.c
drivers/media/video/gspca/gl860/gl860-mi2020.c
drivers/media/video/gspca/gl860/gl860-ov2640.c
drivers/media/video/gspca/gl860/gl860-ov9655.c
drivers/media/video/gspca/gl860/gl860.c
drivers/media/video/gspca/gl860/gl860.h
drivers/media/video/gspca/gspca.c
drivers/media/video/gspca/gspca.h
drivers/media/video/gspca/jeilinj.c
drivers/media/video/gspca/m5602/m5602_core.c
drivers/media/video/gspca/mars.c
drivers/media/video/gspca/mr97310a.c
drivers/media/video/gspca/ov519.c
drivers/media/video/gspca/ov534.c
drivers/media/video/gspca/pac207.c
drivers/media/video/gspca/pac7302.c [new file with mode: 0644]
drivers/media/video/gspca/pac7311.c
drivers/media/video/gspca/pac_common.h
drivers/media/video/gspca/sn9c20x.c
drivers/media/video/gspca/sonixb.c
drivers/media/video/gspca/sonixj.c
drivers/media/video/gspca/spca500.c
drivers/media/video/gspca/spca501.c
drivers/media/video/gspca/spca505.c
drivers/media/video/gspca/spca506.c
drivers/media/video/gspca/spca508.c
drivers/media/video/gspca/spca561.c
drivers/media/video/gspca/sq905.c
drivers/media/video/gspca/sq905c.c
drivers/media/video/gspca/stk014.c
drivers/media/video/gspca/stv0680.c [new file with mode: 0644]
drivers/media/video/gspca/stv06xx/stv06xx.c
drivers/media/video/gspca/sunplus.c
drivers/media/video/gspca/t613.c
drivers/media/video/gspca/tv8532.c
drivers/media/video/gspca/vc032x.c
drivers/media/video/gspca/w996Xcf.c [new file with mode: 0644]
drivers/media/video/gspca/zc3xx.c
drivers/media/video/hdpvr/hdpvr-video.c
drivers/media/video/hexium_gemini.c
drivers/media/video/hexium_orion.c
drivers/media/video/ir-kbd-i2c.c
drivers/media/video/ivtv/ivtv-cards.c
drivers/media/video/ivtv/ivtv-cards.h
drivers/media/video/ivtv/ivtv-driver.c
drivers/media/video/ivtv/ivtv-driver.h
drivers/media/video/ivtv/ivtv-i2c.c
drivers/media/video/ivtv/ivtv-i2c.h
drivers/media/video/mxb.c
drivers/media/video/ov772x.c
drivers/media/video/ov9640.c [new file with mode: 0644]
drivers/media/video/ov9640.h [new file with mode: 0644]
drivers/media/video/pms.c
drivers/media/video/pvrusb2/pvrusb2-debugifc.c
drivers/media/video/pvrusb2/pvrusb2-devattr.c
drivers/media/video/pvrusb2/pvrusb2-devattr.h
drivers/media/video/pvrusb2/pvrusb2-encoder.c
drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
drivers/media/video/pvrusb2/pvrusb2-hdw.c
drivers/media/video/pvrusb2/pvrusb2-hdw.h
drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
drivers/media/video/pvrusb2/pvrusb2-v4l2.c
drivers/media/video/pwc/pwc-if.c
drivers/media/video/rj54n1cb0c.c [new file with mode: 0644]
drivers/media/video/s2255drv.c
drivers/media/video/saa7110.c
drivers/media/video/saa7134/saa7134-cards.c
drivers/media/video/saa7134/saa7134-core.c
drivers/media/video/saa7134/saa7134-dvb.c
drivers/media/video/saa7134/saa7134-input.c
drivers/media/video/saa7134/saa7134-video.c
drivers/media/video/saa7134/saa7134.h
drivers/media/video/saa7164/saa7164-core.c
drivers/media/video/saa7164/saa7164-dvb.c
drivers/media/video/saa717x.c
drivers/media/video/sh_mobile_ceu_camera.c
drivers/media/video/tuner-core.c
drivers/media/video/tvaudio.c
drivers/media/video/tvp514x.c
drivers/media/video/usbvideo/konicawc.c
drivers/media/video/usbvideo/quickcam_messenger.c
drivers/media/video/usbvision/usbvision-video.c
drivers/media/video/uvc/uvc_ctrl.c
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h
drivers/media/video/v4l2-common.c
drivers/media/video/videobuf-core.c
drivers/media/video/videobuf-dma-contig.c
drivers/media/video/videobuf-dma-sg.c
drivers/media/video/videobuf-dvb.c
drivers/media/video/videobuf-vmalloc.c
drivers/media/video/vpx3220.c
drivers/media/video/zoran/zoran.h
drivers/media/video/zoran/zoran_driver.c
drivers/media/video/zr364xx.c
drivers/message/fusion/mptbase.h
drivers/message/fusion/mptctl.c
drivers/message/fusion/mptscsih.c
drivers/message/fusion/mptscsih.h
drivers/message/i2o/i2o_block.c
drivers/message/i2o/iop.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/sh_mobile_sdhi.c [new file with mode: 0644]
drivers/mfd/ucb1400_core.c
drivers/misc/sgi-gru/grufile.c
drivers/mmc/card/sdio_uart.c
drivers/mmc/host/Kconfig
drivers/mmc/host/s3cmci.c
drivers/mtd/chips/Kconfig
drivers/mtd/devices/Kconfig
drivers/mtd/devices/slram.c
drivers/mtd/lpddr/Kconfig
drivers/mtd/maps/Kconfig
drivers/mtd/nand/Kconfig
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/nand_ecc.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/onenand/Kconfig
drivers/mtd/ubi/Kconfig
drivers/mtd/ubi/kapi.c
drivers/mtd/ubi/upd.c
drivers/net/82596.c
drivers/net/amd8111e.c
drivers/net/appletalk/cops.c
drivers/net/ariadne.h
drivers/net/atl1c/atl1c_main.c
drivers/net/benet/be_cmds.h
drivers/net/benet/be_main.c
drivers/net/bnx2x_reg.h
drivers/net/bonding/bond_alb.c
drivers/net/cxgb3/sge.c
drivers/net/ehea/ehea_ethtool.c
drivers/net/hamradio/baycom_ser_fdx.c
drivers/net/irda/smsc-ircc2.c
drivers/net/iseries_veth.c
drivers/net/lasi_82596.c
drivers/net/lib82596.c
drivers/net/mlx4/en_rx.c
drivers/net/mlx4/en_tx.c
drivers/net/mlx4/mlx4_en.h
drivers/net/ps3_gelic_net.c
drivers/net/ps3_gelic_wireless.h
drivers/net/qla3xxx.c
drivers/net/s2io.c
drivers/net/sis900.c
drivers/net/skfp/h/smc.h
drivers/net/skfp/skfddi.c
drivers/net/sky2.c
drivers/net/smsc911x.c
drivers/net/smsc911x.h
drivers/net/spider_net.c
drivers/net/stmmac/gmac.c
drivers/net/stmmac/gmac.h
drivers/net/tokenring/ibmtr.c
drivers/net/tokenring/smctr.c
drivers/net/ucc_geth.c
drivers/net/ucc_geth.h
drivers/net/usb/smsc95xx.c
drivers/net/wan/hdlc_fr.c
drivers/net/wan/lmc/lmc_main.c
drivers/net/wimax/i2400m/rx.c
drivers/net/wireless/ath/ath5k/base.h
drivers/net/wireless/ath/ath5k/eeprom.c
drivers/net/wireless/ath/ath5k/phy.c
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43legacy/main.c
drivers/net/wireless/ipw2x00/ipw2100.c
drivers/net/wireless/ipw2x00/ipw2200.c
drivers/net/wireless/ipw2x00/libipw_module.c
drivers/net/wireless/iwmc3200wifi/hal.c
drivers/net/wireless/iwmc3200wifi/rx.c
drivers/net/wireless/libertas/if_sdio.c
drivers/net/wireless/orinoco/hermes_dld.c
drivers/net/wireless/prism54/isl_ioctl.c
drivers/net/wireless/rt2x00/rt2400pci.h
drivers/net/wireless/rt2x00/rt2500pci.h
drivers/net/wireless/rt2x00/rt2500usb.h
drivers/net/wireless/rt2x00/rt61pci.h
drivers/net/wireless/rt2x00/rt73usb.h
drivers/net/wireless/zd1211rw/zd_mac.c
drivers/parisc/ccio-dma.c
drivers/pci/hotplug/acpi_pcihp.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/acpiphp_ibm.c
drivers/platform/Kconfig
drivers/platform/x86/intel_menlow.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/pnp/pnpbios/rsparser.c
drivers/ps3/ps3-sys-manager.c
drivers/rtc/rtc-ds1302.c
drivers/rtc/rtc-ds1511.c
drivers/rtc/rtc-stk17ta8.c
drivers/rtc/rtc-v3020.c
drivers/s390/block/dasd.c
drivers/s390/block/dasd_3990_erp.c
drivers/s390/block/dasd_alias.c
drivers/s390/block/dasd_diag.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_eckd.h
drivers/s390/block/dasd_eer.c
drivers/s390/block/dasd_fba.c
drivers/s390/block/dasd_int.h
drivers/s390/block/dasd_ioctl.c
drivers/s390/block/dasd_proc.c
drivers/s390/char/con3215.c
drivers/s390/char/con3270.c
drivers/s390/char/fs3270.c
drivers/s390/char/monreader.c
drivers/s390/char/monwriter.c
drivers/s390/char/sclp_cmd.c
drivers/s390/char/tape.h
drivers/s390/char/tape_34xx.c
drivers/s390/char/tape_3590.c
drivers/s390/char/tape_block.c
drivers/s390/char/tape_char.c
drivers/s390/char/tape_core.c
drivers/s390/char/tape_proc.c
drivers/s390/char/tty3270.c
drivers/s390/char/vmlogrdr.c
drivers/s390/char/vmur.c
drivers/s390/char/vmwatchdog.c
drivers/s390/cio/Makefile
drivers/s390/cio/ccwreq.c [new file with mode: 0644]
drivers/s390/cio/chp.c
drivers/s390/cio/cio.h
drivers/s390/cio/cmf.c
drivers/s390/cio/css.c
drivers/s390/cio/css.h
drivers/s390/cio/device.c
drivers/s390/cio/device.h
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_id.c
drivers/s390/cio/device_ops.c
drivers/s390/cio/device_pgid.c
drivers/s390/cio/device_status.c
drivers/s390/cio/io_sch.h
drivers/s390/crypto/ap_bus.c
drivers/s390/crypto/ap_bus.h
drivers/s390/crypto/zcrypt_api.c
drivers/s390/crypto/zcrypt_api.h
drivers/s390/crypto/zcrypt_cex2a.c
drivers/s390/crypto/zcrypt_pcica.c
drivers/s390/crypto/zcrypt_pcicc.c
drivers/s390/crypto/zcrypt_pcixcc.c
drivers/s390/net/qeth_core_mpc.h
drivers/s390/scsi/zfcp_aux.c
drivers/s390/scsi/zfcp_ccw.c
drivers/s390/scsi/zfcp_cfdc.c
drivers/s390/scsi/zfcp_dbf.c
drivers/s390/scsi/zfcp_dbf.h
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_fc.h [new file with mode: 0644]
drivers/s390/scsi/zfcp_fsf.c
drivers/s390/scsi/zfcp_fsf.h
drivers/s390/scsi/zfcp_scsi.c
drivers/s390/scsi/zfcp_sysfs.c
drivers/sbus/char/envctrl.c
drivers/scsi/3w-9xxx.c
drivers/scsi/3w-sas.c [new file with mode: 0644]
drivers/scsi/3w-sas.h [new file with mode: 0644]
drivers/scsi/3w-xxxx.c
drivers/scsi/53c700.c
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/aacraid/aacraid.h
drivers/scsi/aacraid/comminit.c
drivers/scsi/aacraid/linit.c
drivers/scsi/advansys.c
drivers/scsi/aic7xxx/aic79xx.seq
drivers/scsi/aic7xxx/aic79xx_core.c
drivers/scsi/aic7xxx/aic79xx_osm.c
drivers/scsi/aic7xxx/aic7xxx.seq
drivers/scsi/aic7xxx/aic7xxx_core.c
drivers/scsi/aic7xxx/aic7xxx_osm.c
drivers/scsi/aic94xx/aic94xx_reg_def.h
drivers/scsi/arcmsr/arcmsr_hba.c
drivers/scsi/be2iscsi/be.h
drivers/scsi/be2iscsi/be_cmds.c
drivers/scsi/be2iscsi/be_cmds.h
drivers/scsi/be2iscsi/be_iscsi.c
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/be2iscsi/be_main.h
drivers/scsi/be2iscsi/be_mgmt.c
drivers/scsi/be2iscsi/be_mgmt.h
drivers/scsi/bfa/bfa_cb_ioim_macros.h
drivers/scsi/bfa/bfa_cee.c
drivers/scsi/bfa/bfa_csdebug.c
drivers/scsi/bfa/bfa_fcpim.c
drivers/scsi/bfa/bfa_fcpim_priv.h
drivers/scsi/bfa/bfa_fcport.c
drivers/scsi/bfa/bfa_fcs_lport.c
drivers/scsi/bfa/bfa_fcxp.c
drivers/scsi/bfa/bfa_intr.c
drivers/scsi/bfa/bfa_intr_priv.h
drivers/scsi/bfa/bfa_ioc.c
drivers/scsi/bfa/bfa_ioc.h
drivers/scsi/bfa/bfa_iocfc.c
drivers/scsi/bfa/bfa_iocfc.h
drivers/scsi/bfa/bfa_ioim.c
drivers/scsi/bfa/bfa_itnim.c
drivers/scsi/bfa/bfa_log.c
drivers/scsi/bfa/bfa_port_priv.h
drivers/scsi/bfa/bfa_rport.c
drivers/scsi/bfa/bfa_tskim.c
drivers/scsi/bfa/bfa_uf.c
drivers/scsi/bfa/bfad.c
drivers/scsi/bfa/bfad_fwimg.c
drivers/scsi/bfa/bfad_im.c
drivers/scsi/bfa/bfad_im_compat.h
drivers/scsi/bfa/bfad_intr.c
drivers/scsi/bfa/fabric.c
drivers/scsi/bfa/fcbuild.c
drivers/scsi/bfa/fcbuild.h
drivers/scsi/bfa/fcpim.c
drivers/scsi/bfa/fcs.h
drivers/scsi/bfa/fdmi.c
drivers/scsi/bfa/include/aen/bfa_aen.h
drivers/scsi/bfa/include/bfa.h
drivers/scsi/bfa/include/bfa_svc.h
drivers/scsi/bfa/include/bfi/bfi.h
drivers/scsi/bfa/include/bfi/bfi_ioc.h
drivers/scsi/bfa/include/bfi/bfi_lps.h
drivers/scsi/bfa/include/bfi/bfi_rport.h
drivers/scsi/bfa/include/cs/bfa_checksum.h
drivers/scsi/bfa/include/cs/bfa_sm.h
drivers/scsi/bfa/include/cs/bfa_trc.h
drivers/scsi/bfa/include/defs/bfa_defs_pport.h
drivers/scsi/bfa/include/defs/bfa_defs_tsensor.h
drivers/scsi/bfa/include/fcs/bfa_fcs_fabric.h
drivers/scsi/bfa/include/fcs/bfa_fcs_lport.h
drivers/scsi/bfa/include/protocol/ct.h
drivers/scsi/bfa/include/protocol/fc.h
drivers/scsi/bfa/loop.c
drivers/scsi/bfa/lport_api.c
drivers/scsi/bfa/ns.c
drivers/scsi/bfa/plog.c
drivers/scsi/bfa/rport_ftrs.c
drivers/scsi/bfa/vfapi.c
drivers/scsi/bfa/vport.c
drivers/scsi/bnx2i/bnx2i.h
drivers/scsi/bnx2i/bnx2i_iscsi.c
drivers/scsi/constants.c
drivers/scsi/cxgb3i/cxgb3i_iscsi.c
drivers/scsi/dc395x.c
drivers/scsi/device_handler/scsi_dh.c
drivers/scsi/device_handler/scsi_dh_alua.c
drivers/scsi/device_handler/scsi_dh_emc.c
drivers/scsi/device_handler/scsi_dh_hp_sw.c
drivers/scsi/device_handler/scsi_dh_rdac.c
drivers/scsi/dmx3191d.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/fcoe/fcoe.h
drivers/scsi/fcoe/libfcoe.c
drivers/scsi/fnic/fnic.h
drivers/scsi/fnic/fnic_fcs.c
drivers/scsi/fnic/fnic_isr.c
drivers/scsi/fnic/fnic_main.c
drivers/scsi/fnic/fnic_res.c
drivers/scsi/fnic/fnic_res.h
drivers/scsi/fnic/fnic_scsi.c
drivers/scsi/fnic/vnic_scsi.h
drivers/scsi/hosts.c
drivers/scsi/hptiop.c
drivers/scsi/ibmvscsi/ibmvfc.c
drivers/scsi/ibmvscsi/ibmvfc.h
drivers/scsi/ibmvscsi/ibmvscsi.c
drivers/scsi/ipr.c
drivers/scsi/iscsi_tcp.c
drivers/scsi/libfc/Makefile
drivers/scsi/libfc/fc_disc.c
drivers/scsi/libfc/fc_elsct.c
drivers/scsi/libfc/fc_exch.c
drivers/scsi/libfc/fc_fcp.c
drivers/scsi/libfc/fc_frame.c
drivers/scsi/libfc/fc_libfc.c [new file with mode: 0644]
drivers/scsi/libfc/fc_libfc.h [new file with mode: 0644]
drivers/scsi/libfc/fc_lport.c
drivers/scsi/libfc/fc_npiv.c [new file with mode: 0644]
drivers/scsi/libfc/fc_rport.c
drivers/scsi/libiscsi.c
drivers/scsi/libiscsi_tcp.c
drivers/scsi/libsas/sas_scsi_host.c
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_bsg.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 [changed mode: 0644->0755]
drivers/scsi/lpfc/lpfc_hw.h
drivers/scsi/lpfc/lpfc_hw4.h
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_mbox.c
drivers/scsi/lpfc/lpfc_nportdisc.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/lpfc/lpfc_sli.h
drivers/scsi/lpfc/lpfc_sli4.h
drivers/scsi/lpfc/lpfc_version.h
drivers/scsi/lpfc/lpfc_vport.c
drivers/scsi/megaraid.h
drivers/scsi/megaraid/mbox_defs.h
drivers/scsi/megaraid/megaraid_mbox.c
drivers/scsi/megaraid/megaraid_sas.c
drivers/scsi/megaraid/megaraid_sas.h
drivers/scsi/mpt2sas/mpi/mpi2.h
drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h
drivers/scsi/mpt2sas/mpi/mpi2_ioc.h
drivers/scsi/mpt2sas/mpi/mpi2_raid.h
drivers/scsi/mpt2sas/mpi/mpi2_tool.h
drivers/scsi/mpt2sas/mpt2sas_base.c
drivers/scsi/mpt2sas/mpt2sas_base.h
drivers/scsi/mpt2sas/mpt2sas_ctl.c
drivers/scsi/mpt2sas/mpt2sas_ctl.h
drivers/scsi/mpt2sas/mpt2sas_scsih.c
drivers/scsi/mpt2sas/mpt2sas_transport.c
drivers/scsi/ncr53c8xx.c
drivers/scsi/nsp32.c
drivers/scsi/osd/osd_initiator.c
drivers/scsi/osd/osd_uld.c
drivers/scsi/pm8001/Makefile [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_chips.h [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_ctl.c [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_ctl.h [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_defs.h [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_hwi.c [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_hwi.h [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_init.c [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_sas.c [new file with mode: 0644]
drivers/scsi/pm8001/pm8001_sas.h [new file with mode: 0644]
drivers/scsi/pmcraid.c
drivers/scsi/pmcraid.h
drivers/scsi/qla2xxx/qla_attr.c
drivers/scsi/qla2xxx/qla_dbg.c
drivers/scsi/qla2xxx/qla_def.h
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_init.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_version.h
drivers/scsi/qlogicpti.h
drivers/scsi/scsi.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_devinfo.c
drivers/scsi/scsi_error.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_lib_dma.c
drivers/scsi/scsi_netlink.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_sysfs.c
drivers/scsi/scsi_transport_fc.c
drivers/scsi/scsi_transport_iscsi.c
drivers/scsi/scsi_transport_sas.c
drivers/scsi/st.c
drivers/scsi/stex.c
drivers/scsi/sym53c8xx_2/sym_glue.c
drivers/scsi/sym53c8xx_2/sym_hipd.c
drivers/scsi/sym53c8xx_2/sym_hipd.h
drivers/scsi/vmw_pvscsi.c [new file with mode: 0644]
drivers/scsi/vmw_pvscsi.h [new file with mode: 0644]
drivers/scsi/wd7000.c
drivers/serial/8250_pnp.c
drivers/serial/Kconfig
drivers/serial/mcf.c
drivers/serial/pmac_zilog.h
drivers/serial/sh-sci.c
drivers/serial/sh-sci.h
drivers/serial/ucc_uart.c
drivers/sh/Makefile
drivers/sh/intc.c
drivers/sh/maple/maple.c
drivers/sh/pfc.c [moved from arch/sh/kernel/gpio.c with 94% similarity]
drivers/spi/spidev.c
drivers/staging/go7007/Makefile
drivers/staging/go7007/go7007-driver.c
drivers/staging/go7007/go7007-priv.h
drivers/staging/go7007/go7007-usb.c
drivers/staging/go7007/go7007-v4l2.c
drivers/staging/go7007/s2250-board.c
drivers/staging/go7007/s2250-loader.c
drivers/staging/otus/80211core/pub_zfi.h
drivers/staging/otus/zdcompat.h
drivers/staging/rtl8187se/r8180.h
drivers/staging/rtl8192su/r8192S_phyreg.h
drivers/staging/wavelan/wavelan_cs.c
drivers/telephony/ixj.c
drivers/usb/atm/ueagle-atm.c
drivers/usb/class/usbtmc.c
drivers/usb/core/message.c
drivers/usb/gadget/f_acm.c
drivers/usb/gadget/pxa27x_udc.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/fhci-sched.c
drivers/usb/wusbcore/crypto.c
drivers/usb/wusbcore/wa-xfer.c
drivers/uwb/i1480/dfu/usb.c
drivers/uwb/neh.c
drivers/uwb/wlp/txrx.c
drivers/video/Kconfig
drivers/video/Makefile
drivers/video/atmel_lcdfb.c
drivers/video/aty/atyfb_base.c
drivers/video/backlight/atmel-pwm-bl.c
drivers/video/backlight/tosa_lcd.c
drivers/video/broadsheetfb.c
drivers/video/console/sticore.c
drivers/video/fb_defio.c
drivers/video/gbefb.c
drivers/video/hecubafb.c
drivers/video/metronomefb.c
drivers/video/omap/Kconfig
drivers/video/omap/blizzard.c
drivers/video/omap/dispc.c
drivers/video/omap/hwa742.c
drivers/video/omap/lcd_2430sdp.c
drivers/video/omap/lcd_ams_delta.c
drivers/video/omap/lcd_apollon.c
drivers/video/omap/lcd_h3.c
drivers/video/omap/lcd_h4.c
drivers/video/omap/lcd_htcherald.c
drivers/video/omap/lcd_inn1510.c
drivers/video/omap/lcd_inn1610.c
drivers/video/omap/lcd_ldp.c
drivers/video/omap/lcd_mipid.c
drivers/video/omap/lcd_omap2evm.c
drivers/video/omap/lcd_omap3beagle.c
drivers/video/omap/lcd_omap3evm.c
drivers/video/omap/lcd_osk.c
drivers/video/omap/lcd_overo.c
drivers/video/omap/lcd_palmte.c
drivers/video/omap/lcd_palmtt.c
drivers/video/omap/lcd_palmz71.c
drivers/video/omap/lcdc.c
drivers/video/omap/omapfb.h [moved from arch/arm/plat-omap/include/plat/omapfb.h with 60% similarity]
drivers/video/omap/omapfb_main.c
drivers/video/omap/rfbi.c
drivers/video/omap/sossi.c
drivers/video/omap2/Kconfig [new file with mode: 0644]
drivers/video/omap2/Makefile [new file with mode: 0644]
drivers/video/omap2/displays/Kconfig [new file with mode: 0644]
drivers/video/omap2/displays/Makefile [new file with mode: 0644]
drivers/video/omap2/displays/panel-generic.c [new file with mode: 0644]
drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c [new file with mode: 0644]
drivers/video/omap2/displays/panel-taal.c [new file with mode: 0644]
drivers/video/omap2/dss/Kconfig [new file with mode: 0644]
drivers/video/omap2/dss/Makefile [new file with mode: 0644]
drivers/video/omap2/dss/core.c [new file with mode: 0644]
drivers/video/omap2/dss/dispc.c [new file with mode: 0644]
drivers/video/omap2/dss/display.c [new file with mode: 0644]
drivers/video/omap2/dss/dpi.c [new file with mode: 0644]
drivers/video/omap2/dss/dsi.c [new file with mode: 0644]
drivers/video/omap2/dss/dss.c [new file with mode: 0644]
drivers/video/omap2/dss/dss.h [new file with mode: 0644]
drivers/video/omap2/dss/manager.c [new file with mode: 0644]
drivers/video/omap2/dss/overlay.c [new file with mode: 0644]
drivers/video/omap2/dss/rfbi.c [new file with mode: 0644]
drivers/video/omap2/dss/sdi.c [new file with mode: 0644]
drivers/video/omap2/dss/venc.c [new file with mode: 0644]
drivers/video/omap2/omapfb/Kconfig [new file with mode: 0644]
drivers/video/omap2/omapfb/Makefile [new file with mode: 0644]
drivers/video/omap2/omapfb/omapfb-ioctl.c [new file with mode: 0644]
drivers/video/omap2/omapfb/omapfb-main.c [new file with mode: 0644]
drivers/video/omap2/omapfb/omapfb-sysfs.c [new file with mode: 0644]
drivers/video/omap2/omapfb/omapfb.h [new file with mode: 0644]
drivers/video/omap2/vram.c [new file with mode: 0644]
drivers/video/omap2/vrfb.c [new file with mode: 0644]
drivers/video/sgivwfb.c
drivers/video/sh_mobile_lcdcfb.c
drivers/video/stifb.c
drivers/video/tdfxfb.c
drivers/video/via/dvi.c
drivers/video/vt8623fb.c
drivers/video/xen-fbfront.c
drivers/watchdog/coh901327_wdt.c
drivers/watchdog/machzwd.c
drivers/watchdog/sb_wdog.c
drivers/watchdog/wdrtas.c
drivers/xen/balloon.c
drivers/xen/events.c
drivers/xen/manage.c
drivers/xen/xenbus/xenbus_probe.c
fs/binfmt_elf.c
fs/bio.c
fs/btrfs/extent_map.c
fs/cifs/README
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/inode.c
fs/cifs/smbdes.c
fs/compat_ioctl.c
fs/debugfs/inode.c
fs/dlm/config.c
fs/dlm/debug_fs.c
fs/dlm/dir.c
fs/dlm/dlm_internal.h
fs/dlm/lock.c
fs/dlm/lockspace.c
fs/dlm/lowcomms.c
fs/dlm/member.c
fs/dlm/memory.c
fs/dlm/netlink.c
fs/dlm/plock.c
fs/dlm/rcom.c
fs/dlm/requestqueue.c
fs/dlm/user.c
fs/exofs/Kbuild
fs/exofs/common.h
fs/exofs/exofs.h
fs/exofs/inode.c
fs/exofs/ios.c [new file with mode: 0644]
fs/exofs/osd.c [deleted file]
fs/exofs/pnfs.h [new file with mode: 0644]
fs/exofs/super.c
fs/ext3/inode.c
fs/ext4/Kconfig
fs/ext4/balloc.c
fs/ext4/block_validity.c
fs/ext4/ext4.h
fs/ext4/ext4_jbd2.c
fs/ext4/ext4_jbd2.h
fs/ext4/extents.c
fs/ext4/fsync.c
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/mballoc.c
fs/ext4/migrate.c
fs/ext4/move_extent.c
fs/ext4/namei.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/xattr.c
fs/jbd2/commit.c
fs/jbd2/journal.c
fs/jffs2/compr.c
fs/jffs2/readinode.c
fs/jffs2/xattr.c
fs/jfs/jfs_dmap.c
fs/ncpfs/ioctl.c
fs/notify/inotify/inotify_user.c
fs/ntfs/compress.c
fs/ntfs/file.c
fs/ntfs/logfile.c
fs/ocfs2/alloc.c
fs/ocfs2/blockcheck.c
fs/ocfs2/dlm/dlmmaster.c
fs/ocfs2/dlmglue.c
fs/ocfs2/journal.c
fs/ocfs2/refcounttree.c
fs/omfs/bitmap.c
fs/qnx4/bitmap.c
fs/qnx4/dir.c
fs/qnx4/inode.c
fs/qnx4/namei.c
fs/reiserfs/fix_node.c
fs/ubifs/debug.c
fs/ubifs/file.c
fs/ubifs/recovery.c
fs/ubifs/super.c
fs/xfs/quota/xfs_dquot.h
include/acpi/acpixf.h
include/asm-generic/memory_model.h
include/asm-generic/unistd.h
include/drm/drm.h
include/drm/drmP.h
include/drm/drm_crtc.h
include/drm/drm_dp_helper.h [moved from drivers/gpu/drm/i915/intel_dp.h with 70% similarity]
include/drm/drm_edid.h
include/drm/drm_mm.h
include/drm/drm_mode.h
include/drm/drm_os_linux.h
include/drm/i915_drm.h
include/drm/mga_drm.h
include/drm/radeon_drm.h
include/drm/ttm/ttm_bo_api.h
include/drm/ttm/ttm_bo_driver.h
include/drm/ttm/ttm_execbuf_util.h [new file with mode: 0644]
include/drm/ttm/ttm_lock.h [new file with mode: 0644]
include/drm/ttm/ttm_memory.h
include/drm/ttm/ttm_object.h [new file with mode: 0644]
include/drm/via_drm.h
include/linux/cciss_ioctl.h
include/linux/chio.h
include/linux/dm-log-userspace.h
include/linux/fb.h
include/linux/hid.h
include/linux/ide.h
include/linux/in6.h
include/linux/input-polldev.h
include/linux/input.h
include/linux/input/matrix_keypad.h
include/linux/input/sh_keysc.h [moved from arch/sh/include/asm/sh_keysc.h with 75% similarity]
include/linux/input/sparse-keymap.h [new file with mode: 0644]
include/linux/irq.h
include/linux/libata.h
include/linux/mfd/ezx-pcap.h
include/linux/mfd/sh_mobile_sdhi.h [new file with mode: 0644]
include/linux/mtd/ubi.h
include/linux/omapfb.h [new file with mode: 0644]
include/linux/pci_ids.h
include/linux/pktcdvd.h
include/linux/serial_reg.h
include/linux/serio.h
include/linux/sh_intc.h
include/linux/sh_pfc.h [new file with mode: 0644]
include/linux/spi/spi_bitbang.h
include/linux/sysctl.h
include/linux/ucb1400.h
include/linux/usb/wusb.h
include/linux/videodev2.h
include/linux/workqueue.h
include/media/cx25840.h
include/media/davinci/vpfe_capture.h
include/media/ir-common.h
include/media/v4l2-chip-ident.h
include/media/v4l2-subdev.h
include/media/videobuf-core.h
include/media/videobuf-dma-contig.h
include/media/videobuf-dma-sg.h
include/media/videobuf-dvb.h
include/media/videobuf-vmalloc.h
include/net/sctp/structs.h
include/net/tcp.h
include/net/wimax.h
include/scsi/Kbuild
include/scsi/fc/Kbuild [new file with mode: 0644]
include/scsi/fc/fc_els.h
include/scsi/fc/fc_fcoe.h
include/scsi/fc/fc_fcp.h
include/scsi/fc/fc_fip.h
include/scsi/fc/fc_fs.h
include/scsi/fc/fc_gs.h
include/scsi/fc/fc_ns.h
include/scsi/fc_encode.h
include/scsi/fc_frame.h
include/scsi/iscsi_if.h
include/scsi/iscsi_proto.h
include/scsi/libfc.h
include/scsi/libfcoe.h
include/scsi/libiscsi.h
include/scsi/libsas.h
include/scsi/osd_initiator.h
include/scsi/osd_protocol.h
include/scsi/osd_sense.h
include/scsi/scsi.h
include/scsi/scsi_device.h
include/scsi/scsi_dh.h
include/scsi/scsi_host.h
include/scsi/scsi_transport_fc.h
include/sound/ac97_codec.h
include/sound/wm8993.h
include/trace/events/ext4.h
include/video/sgivw.h
ipc/msg.c
kernel/irq/spurious.c
kernel/perf_event.c
kernel/time/clocksource.c
kernel/workqueue.c
lib/Kconfig.debug
lib/decompress_bunzip2.c
lib/dma-debug.c
lib/idr.c
lib/swiotlb.c
mm/filemap.c
mm/kmemleak.c
mm/memcontrol.c
mm/memory-failure.c
mm/migrate.c
mm/truncate.c
net/bluetooth/bnep/core.c
net/ipv4/netfilter/ipt_ECN.c
net/irda/irlap.c
net/irda/irlap_event.c
net/irda/irlmp.c
net/mac80211/mesh_pathtbl.c
net/netlabel/netlabel_domainhash.c
net/sched/act_api.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statefuns.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/wimax/op-reset.c
samples/Kconfig
scripts/kconfig/mconf.c
security/selinux/netlabel.c
security/selinux/ss/services.c
sound/Kconfig
sound/isa/cs423x/cs4236.c
sound/isa/opti9xx/miro.c
sound/isa/opti9xx/opti92x-ad1848.c
sound/oss/Kconfig
sound/oss/dmasound/dmasound_paula.c
sound/pci/ca0106/ca0106_proc.c
sound/pci/cs46xx/imgs/cwcdma.asp
sound/pci/emu10k1/emu10k1x.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_realtek.c
sound/pci/ice1712/aureon.c
sound/pci/ice1712/juli.c
sound/pci/rme9652/hdspm.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8993.c
sound/soc/imx/mx1_mx2-pcm.c
sound/soc/s3c24xx/s3c24xx_simtec.c
sound/soc/s6000/s6000-pcm.c
sound/sound_core.c
sound/synth/emux/soundfont.c
sound/usb/usx2y/us122l.c
sound/usb/usx2y/us122l.h

diff --git a/Documentation/ABI/stable/sysfs-driver-qla2xxx b/Documentation/ABI/stable/sysfs-driver-qla2xxx
new file mode 100644 (file)
index 0000000..9a59d84
--- /dev/null
@@ -0,0 +1,8 @@
+What:          /sys/bus/pci/drivers/qla2xxx/.../devices/*
+Date:          September 2009
+Contact:       QLogic Linux Driver <linux-driver@qlogic.com>
+Description:   qla2xxx-udev.sh currently looks for uevent CHANGE events to
+               signal a firmware-dump has been generated by the driver and is
+               ready for retrieval.
+Users:         qla2xxx-udev.sh.  Proposed changes should be mailed to
+               linux-driver@qlogic.com
index 9923390..f91a973 100644 (file)
@@ -8,7 +8,7 @@ Description:
                 1 - major number
                 2 - minor mumber
                 3 - device name
-                4 - reads completed succesfully
+                4 - reads completed successfully
                 5 - reads merged
                 6 - sectors read
                 7 - time spent reading (ms)
index 5f3beda..d2f9033 100644 (file)
@@ -4,7 +4,7 @@ Contact:        Jerome Marchand <jmarchan@redhat.com>
 Description:
                The /sys/block/<disk>/stat files displays the I/O
                statistics of disk <disk>. They contain 11 fields:
-                1 - reads completed succesfully
+                1 - reads completed successfully
                 2 - reads merged
                 3 - sectors read
                 4 - time spent reading (ms)
index 01f24e9..ecad88d 100644 (file)
@@ -214,7 +214,7 @@ most specific mask.
 Here is pseudo-code showing how this might be done:
 
        #define PLAYBACK_ADDRESS_BITS   DMA_BIT_MASK(32)
-       #define RECORD_ADDRESS_BITS     0x00ffffff
+       #define RECORD_ADDRESS_BITS     DMA_BIT_MASK(24)
 
        struct my_sound_card *card;
        struct pci_dev *pdev;
@@ -224,14 +224,14 @@ Here is pseudo-code showing how this might be done:
                card->playback_enabled = 1;
        } else {
                card->playback_enabled = 0;
-               printk(KERN_WARN "%s: Playback disabled due to DMA limitations.\n",
+               printk(KERN_WARNING "%s: Playback disabled due to DMA limitations.\n",
                       card->name);
        }
        if (!pci_set_dma_mask(pdev, RECORD_ADDRESS_BITS)) {
                card->record_enabled = 1;
        } else {
                card->record_enabled = 0;
-               printk(KERN_WARN "%s: Record disabled due to DMA limitations.\n",
+               printk(KERN_WARNING "%s: Record disabled due to DMA limitations.\n",
                       card->name);
        }
 
index 94a20fe..f9a6e2c 100644 (file)
@@ -293,10 +293,23 @@ X!Idrivers/video/console/fonts.c
 
   <chapter id="input_subsystem">
      <title>Input Subsystem</title>
+     <sect1><title>Input core</title>
 !Iinclude/linux/input.h
 !Edrivers/input/input.c
 !Edrivers/input/ff-core.c
 !Edrivers/input/ff-memless.c
+     </sect1>
+     <sect1><title>Polled input devices</title>
+!Iinclude/linux/input-polldev.h
+!Edrivers/input/input-polldev.c
+     </sect1>
+     <sect1><title>Matrix keyboars/keypads</title>
+!Iinclude/linux/input/matrix_keypad.h
+     </sect1>
+     <sect1><title>Sparse keymap support</title>
+!Iinclude/linux/input/sparse-keymap.h
+!Edrivers/input/sparse-keymap.c
+     </sect1>
   </chapter>
 
   <chapter id="spi">
index 4fc5b23..63c528f 100644 (file)
 
 <revhistory>
 <!-- Put document revisions here, newest first. -->
+<revision>
+       <revnumber>2.0.2</revnumber>
+       <date>2009-10-25</date>
+       <authorinitials>mcc</authorinitials>
+       <revremark>
+               documents FE_SET_FRONTEND_TUNE_MODE and FE_DISHETWORK_SEND_LEGACY_CMD ioctls.
+       </revremark>
+</revision>
 <revision>
 <revnumber>2.0.1</revnumber>
 <date>2009-09-16</date>
@@ -85,3 +93,8 @@ Added ISDB-T test originally written by Patrick Boettcher
     &sub-examples;
   </chapter>
 <!-- END OF CHAPTERS -->
+  <appendix id="frontend_h">
+    <title>DVB Frontend Header File</title>
+    &sub-frontend-h;
+  </appendix>
+
similarity index 99%
rename from Documentation/DocBook/dvb/isdbt.xml
rename to Documentation/DocBook/dvb/dvbproperty.xml
index 9285522..5f57c7c 100644 (file)
@@ -1,3 +1,6 @@
+<section id="FE_GET_PROPERTY">
+<title>FE_GET_PROPERTY/FE_SET_PROPERTY</title>
+
 <section id="isdbt">
        <title>ISDB-T frontend</title>
        <para>This section describes shortly what are the possible parameters in the Linux
                </section>
        </section>
 </section>
+</section>
diff --git a/Documentation/DocBook/dvb/frontend.h.xml b/Documentation/DocBook/dvb/frontend.h.xml
new file mode 100644 (file)
index 0000000..b99644f
--- /dev/null
@@ -0,0 +1,415 @@
+<programlisting>
+/*
+ * frontend.h
+ *
+ * Copyright (C) 2000 Marcus Metzler &lt;marcus@convergence.de&gt;
+ *                  Ralph  Metzler &lt;ralph@convergence.de&gt;
+ *                  Holger Waechtler &lt;holger@convergence.de&gt;
+ *                  Andre Draszik &lt;ad@convergence.de&gt;
+ *                  for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _DVBFRONTEND_H_
+#define _DVBFRONTEND_H_
+
+#include &lt;linux/types.h&gt;
+
+typedef enum fe_type {
+        FE_QPSK,
+        FE_QAM,
+        FE_OFDM,
+        FE_ATSC
+} fe_type_t;
+
+
+typedef enum fe_caps {
+        FE_IS_STUPID                    = 0,
+        FE_CAN_INVERSION_AUTO           = 0x1,
+        FE_CAN_FEC_1_2                  = 0x2,
+        FE_CAN_FEC_2_3                  = 0x4,
+        FE_CAN_FEC_3_4                  = 0x8,
+        FE_CAN_FEC_4_5                  = 0x10,
+        FE_CAN_FEC_5_6                  = 0x20,
+        FE_CAN_FEC_6_7                  = 0x40,
+        FE_CAN_FEC_7_8                  = 0x80,
+        FE_CAN_FEC_8_9                  = 0x100,
+        FE_CAN_FEC_AUTO                 = 0x200,
+        FE_CAN_QPSK                     = 0x400,
+        FE_CAN_QAM_16                   = 0x800,
+        FE_CAN_QAM_32                   = 0x1000,
+        FE_CAN_QAM_64                   = 0x2000,
+        FE_CAN_QAM_128                  = 0x4000,
+        FE_CAN_QAM_256                  = 0x8000,
+        FE_CAN_QAM_AUTO                 = 0x10000,
+        FE_CAN_TRANSMISSION_MODE_AUTO   = 0x20000,
+        FE_CAN_BANDWIDTH_AUTO           = 0x40000,
+        FE_CAN_GUARD_INTERVAL_AUTO      = 0x80000,
+        FE_CAN_HIERARCHY_AUTO           = 0x100000,
+        FE_CAN_8VSB                     = 0x200000,
+        FE_CAN_16VSB                    = 0x400000,
+        FE_HAS_EXTENDED_CAPS            = 0x800000,   /* We need more bitspace for newer APIs, indicate this. */
+        FE_CAN_2G_MODULATION            = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */
+        FE_NEEDS_BENDING                = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */
+        FE_CAN_RECOVER                  = 0x40000000, /* frontend can recover from a cable unplug automatically */
+        FE_CAN_MUTE_TS                  = 0x80000000  /* frontend can stop spurious TS data output */
+} fe_caps_t;
+
+
+struct dvb_frontend_info {
+        char       name[128];
+        fe_type_t  type;
+        __u32      frequency_min;
+        __u32      frequency_max;
+        __u32      frequency_stepsize;
+        __u32      frequency_tolerance;
+        __u32      symbol_rate_min;
+        __u32      symbol_rate_max;
+        __u32      symbol_rate_tolerance;       /* ppm */
+        __u32      notifier_delay;              /* DEPRECATED */
+        fe_caps_t  caps;
+};
+
+
+/**
+ *  Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for
+ *  the meaning of this struct...
+ */
+struct dvb_diseqc_master_cmd {
+        __u8 msg [6];   /*  { framing, address, command, data [3] } */
+        __u8 msg_len;   /*  valid values are 3...6  */
+};
+
+
+struct dvb_diseqc_slave_reply {
+        __u8 msg [4];   /*  { framing, data [3] } */
+        __u8 msg_len;   /*  valid values are 0...4, 0 means no msg  */
+        int  timeout;   /*  return from ioctl after timeout ms with */
+};                      /*  errorcode when no message was received  */
+
+
+typedef enum fe_sec_voltage {
+        SEC_VOLTAGE_13,
+        SEC_VOLTAGE_18,
+        SEC_VOLTAGE_OFF
+} fe_sec_voltage_t;
+
+
+typedef enum fe_sec_tone_mode {
+        SEC_TONE_ON,
+        SEC_TONE_OFF
+} fe_sec_tone_mode_t;
+
+
+typedef enum fe_sec_mini_cmd {
+        SEC_MINI_A,
+        SEC_MINI_B
+} fe_sec_mini_cmd_t;
+
+
+typedef enum fe_status {
+        FE_HAS_SIGNAL   = 0x01,   /* found something above the noise level */
+        FE_HAS_CARRIER  = 0x02,   /* found a DVB signal  */
+        FE_HAS_VITERBI  = 0x04,   /* FEC is stable  */
+        FE_HAS_SYNC     = 0x08,   /* found sync bytes  */
+        FE_HAS_LOCK     = 0x10,   /* everything's working... */
+        FE_TIMEDOUT     = 0x20,   /* no lock within the last ~2 seconds */
+        FE_REINIT       = 0x40    /* frontend was reinitialized,  */
+} fe_status_t;                    /* application is recommended to reset */
+                                  /* DiSEqC, tone and parameters */
+
+typedef enum fe_spectral_inversion {
+        INVERSION_OFF,
+        INVERSION_ON,
+        INVERSION_AUTO
+} fe_spectral_inversion_t;
+
+
+typedef enum fe_code_rate {
+        FEC_NONE = 0,
+        FEC_1_2,
+        FEC_2_3,
+        FEC_3_4,
+        FEC_4_5,
+        FEC_5_6,
+        FEC_6_7,
+        FEC_7_8,
+        FEC_8_9,
+        FEC_AUTO,
+        FEC_3_5,
+        FEC_9_10,
+} fe_code_rate_t;
+
+
+typedef enum fe_modulation {
+        QPSK,
+        QAM_16,
+        QAM_32,
+        QAM_64,
+        QAM_128,
+        QAM_256,
+        QAM_AUTO,
+        VSB_8,
+        VSB_16,
+        PSK_8,
+        APSK_16,
+        APSK_32,
+        DQPSK,
+} fe_modulation_t;
+
+typedef enum fe_transmit_mode {
+        TRANSMISSION_MODE_2K,
+        TRANSMISSION_MODE_8K,
+        TRANSMISSION_MODE_AUTO,
+        TRANSMISSION_MODE_4K
+} fe_transmit_mode_t;
+
+typedef enum fe_bandwidth {
+        BANDWIDTH_8_MHZ,
+        BANDWIDTH_7_MHZ,
+        BANDWIDTH_6_MHZ,
+        BANDWIDTH_AUTO
+} fe_bandwidth_t;
+
+
+typedef enum fe_guard_interval {
+        GUARD_INTERVAL_1_32,
+        GUARD_INTERVAL_1_16,
+        GUARD_INTERVAL_1_8,
+        GUARD_INTERVAL_1_4,
+        GUARD_INTERVAL_AUTO
+} fe_guard_interval_t;
+
+
+typedef enum fe_hierarchy {
+        HIERARCHY_NONE,
+        HIERARCHY_1,
+        HIERARCHY_2,
+        HIERARCHY_4,
+        HIERARCHY_AUTO
+} fe_hierarchy_t;
+
+
+struct dvb_qpsk_parameters {
+        __u32           symbol_rate;  /* symbol rate in Symbols per second */
+        fe_code_rate_t  fec_inner;    /* forward error correction (see above) */
+};
+
+struct dvb_qam_parameters {
+        __u32           symbol_rate; /* symbol rate in Symbols per second */
+        fe_code_rate_t  fec_inner;   /* forward error correction (see above) */
+        fe_modulation_t modulation;  /* modulation type (see above) */
+};
+
+struct dvb_vsb_parameters {
+        fe_modulation_t modulation;  /* modulation type (see above) */
+};
+
+struct dvb_ofdm_parameters {
+        fe_bandwidth_t      bandwidth;
+        fe_code_rate_t      code_rate_HP;  /* high priority stream code rate */
+        fe_code_rate_t      code_rate_LP;  /* low priority stream code rate */
+        fe_modulation_t     constellation; /* modulation type (see above) */
+        fe_transmit_mode_t  transmission_mode;
+        fe_guard_interval_t guard_interval;
+        fe_hierarchy_t      hierarchy_information;
+};
+
+
+struct dvb_frontend_parameters {
+        __u32 frequency;     /* (absolute) frequency in Hz for QAM/OFDM/ATSC */
+                             /* intermediate frequency in kHz for QPSK */
+        fe_spectral_inversion_t inversion;
+        union {
+                struct dvb_qpsk_parameters qpsk;
+                struct dvb_qam_parameters  qam;
+                struct dvb_ofdm_parameters ofdm;
+                struct dvb_vsb_parameters vsb;
+        } u;
+};
+
+
+struct dvb_frontend_event {
+        fe_status_t status;
+        struct dvb_frontend_parameters parameters;
+};
+
+/* S2API Commands */
+#define DTV_UNDEFINED           0
+#define DTV_TUNE                1
+#define DTV_CLEAR               2
+#define DTV_FREQUENCY           3
+#define DTV_MODULATION          4
+#define DTV_BANDWIDTH_HZ        5
+#define DTV_INVERSION           6
+#define DTV_DISEQC_MASTER       7
+#define DTV_SYMBOL_RATE         8
+#define DTV_INNER_FEC           9
+#define DTV_VOLTAGE             10
+#define DTV_TONE                11
+#define DTV_PILOT               12
+#define DTV_ROLLOFF             13
+#define DTV_DISEQC_SLAVE_REPLY  14
+
+/* Basic enumeration set for querying unlimited capabilities */
+#define DTV_FE_CAPABILITY_COUNT 15
+#define DTV_FE_CAPABILITY       16
+#define DTV_DELIVERY_SYSTEM     17
+
+/* ISDB-T and ISDB-Tsb */
+#define DTV_ISDBT_PARTIAL_RECEPTION     18
+#define DTV_ISDBT_SOUND_BROADCASTING    19
+
+#define DTV_ISDBT_SB_SUBCHANNEL_ID      20
+#define DTV_ISDBT_SB_SEGMENT_IDX        21
+#define DTV_ISDBT_SB_SEGMENT_COUNT      22
+
+#define DTV_ISDBT_LAYERA_FEC                    23
+#define DTV_ISDBT_LAYERA_MODULATION             24
+#define DTV_ISDBT_LAYERA_SEGMENT_COUNT          25
+#define DTV_ISDBT_LAYERA_TIME_INTERLEAVING      26
+
+#define DTV_ISDBT_LAYERB_FEC                    27
+#define DTV_ISDBT_LAYERB_MODULATION             28
+#define DTV_ISDBT_LAYERB_SEGMENT_COUNT          29
+#define DTV_ISDBT_LAYERB_TIME_INTERLEAVING      30
+
+#define DTV_ISDBT_LAYERC_FEC                    31
+#define DTV_ISDBT_LAYERC_MODULATION             32
+#define DTV_ISDBT_LAYERC_SEGMENT_COUNT          33
+#define DTV_ISDBT_LAYERC_TIME_INTERLEAVING      34
+
+#define DTV_API_VERSION         35
+
+#define DTV_CODE_RATE_HP        36
+#define DTV_CODE_RATE_LP        37
+#define DTV_GUARD_INTERVAL      38
+#define DTV_TRANSMISSION_MODE   39
+#define DTV_HIERARCHY           40
+
+#define DTV_ISDBT_LAYER_ENABLED 41
+
+#define DTV_ISDBS_TS_ID         42
+
+#define DTV_MAX_COMMAND                         DTV_ISDBS_TS_ID
+
+typedef enum fe_pilot {
+        PILOT_ON,
+        PILOT_OFF,
+        PILOT_AUTO,
+} fe_pilot_t;
+
+typedef enum fe_rolloff {
+        ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */
+        ROLLOFF_20,
+        ROLLOFF_25,
+        ROLLOFF_AUTO,
+} fe_rolloff_t;
+
+typedef enum fe_delivery_system {
+        SYS_UNDEFINED,
+        SYS_DVBC_ANNEX_AC,
+        SYS_DVBC_ANNEX_B,
+        SYS_DVBT,
+        SYS_DSS,
+        SYS_DVBS,
+        SYS_DVBS2,
+        SYS_DVBH,
+        SYS_ISDBT,
+        SYS_ISDBS,
+        SYS_ISDBC,
+        SYS_ATSC,
+        SYS_ATSCMH,
+        SYS_DMBTH,
+        SYS_CMMB,
+        SYS_DAB,
+} fe_delivery_system_t;
+
+struct dtv_cmds_h {
+        char    *name;          /* A display name for debugging purposes */
+
+        __u32   cmd;            /* A unique ID */
+
+        /* Flags */
+        __u32   set:1;          /* Either a set or get property */
+        __u32   buffer:1;       /* Does this property use the buffer? */
+        __u32   reserved:30;    /* Align */
+};
+
+struct dtv_property {
+        __u32 cmd;
+        __u32 reserved[3];
+        union {
+                __u32 data;
+                struct {
+                        __u8 data[32];
+                        __u32 len;
+                        __u32 reserved1[3];
+                        void *reserved2;
+                } buffer;
+        } u;
+        int result;
+} __attribute__ ((packed));
+
+/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */
+#define DTV_IOCTL_MAX_MSGS 64
+
+struct dtv_properties {
+        __u32 num;
+        struct dtv_property *props;
+};
+
+#define <link linkend="FE_GET_PROPERTY">FE_SET_PROPERTY</link>            _IOW('o', 82, struct dtv_properties)
+#define <link linkend="FE_GET_PROPERTY">FE_GET_PROPERTY</link>            _IOR('o', 83, struct dtv_properties)
+
+
+/**
+ * When set, this flag will disable any zigzagging or other "normal" tuning
+ * behaviour. Additionally, there will be no automatic monitoring of the lock
+ * status, and hence no frontend events will be generated. If a frontend device
+ * is closed, this flag will be automatically turned off when the device is
+ * reopened read-write.
+ */
+#define FE_TUNE_MODE_ONESHOT 0x01
+
+
+#define <link linkend="FE_GET_INFO">FE_GET_INFO</link>                _IOR('o', 61, struct dvb_frontend_info)
+
+#define <link linkend="FE_DISEQC_RESET_OVERLOAD">FE_DISEQC_RESET_OVERLOAD</link>   _IO('o', 62)
+#define <link linkend="FE_DISEQC_SEND_MASTER_CMD">FE_DISEQC_SEND_MASTER_CMD</link>  _IOW('o', 63, struct dvb_diseqc_master_cmd)
+#define <link linkend="FE_DISEQC_RECV_SLAVE_REPLY">FE_DISEQC_RECV_SLAVE_REPLY</link> _IOR('o', 64, struct dvb_diseqc_slave_reply)
+#define <link linkend="FE_DISEQC_SEND_BURST">FE_DISEQC_SEND_BURST</link>       _IO('o', 65)  /* fe_sec_mini_cmd_t */
+
+#define <link linkend="FE_SET_TONE">FE_SET_TONE</link>                _IO('o', 66)  /* fe_sec_tone_mode_t */
+#define <link linkend="FE_SET_VOLTAGE">FE_SET_VOLTAGE</link>             _IO('o', 67)  /* fe_sec_voltage_t */
+#define <link linkend="FE_ENABLE_HIGH_LNB_VOLTAGE">FE_ENABLE_HIGH_LNB_VOLTAGE</link> _IO('o', 68)  /* int */
+
+#define <link linkend="FE_READ_STATUS">FE_READ_STATUS</link>             _IOR('o', 69, fe_status_t)
+#define <link linkend="FE_READ_BER">FE_READ_BER</link>                _IOR('o', 70, __u32)
+#define <link linkend="FE_READ_SIGNAL_STRENGTH">FE_READ_SIGNAL_STRENGTH</link>    _IOR('o', 71, __u16)
+#define <link linkend="FE_READ_SNR">FE_READ_SNR</link>                _IOR('o', 72, __u16)
+#define <link linkend="FE_READ_UNCORRECTED_BLOCKS">FE_READ_UNCORRECTED_BLOCKS</link> _IOR('o', 73, __u32)
+
+#define <link linkend="FE_SET_FRONTEND">FE_SET_FRONTEND</link>            _IOW('o', 76, struct dvb_frontend_parameters)
+#define <link linkend="FE_GET_FRONTEND">FE_GET_FRONTEND</link>            _IOR('o', 77, struct dvb_frontend_parameters)
+#define <link linkend="FE_SET_FRONTEND_TUNE_MODE">FE_SET_FRONTEND_TUNE_MODE</link>  _IO('o', 81) /* unsigned int */
+#define <link linkend="FE_GET_EVENT">FE_GET_EVENT</link>               _IOR('o', 78, struct dvb_frontend_event)
+
+#define <link linkend="FE_DISHNETWORK_SEND_LEGACY_CMD">FE_DISHNETWORK_SEND_LEGACY_CMD</link> _IO('o', 80) /* unsigned int */
+
+#endif /*_DVBFRONTEND_H_*/
+</programlisting>
index 9d89a7b..300ba1f 100644 (file)
@@ -73,7 +73,8 @@ a specific frontend type.</para>
 <section id="frontend_info">
 <title>frontend information</title>
 
-<para>Information about the frontend ca be queried with FE_GET_INFO.</para>
+<para>Information about the frontend ca be queried with
+       <link linkend="FE_GET_INFO">FE_GET_INFO</link>.</para>
 
 <programlisting>
        struct dvb_frontend_info {
@@ -338,7 +339,7 @@ modulation mode which can be one of the following:
 <entry align="char">
 <para>This system call opens a named frontend device (/dev/dvb/adapter0/frontend0)
  for subsequent use. Usually the first thing to do after a successful open is to
- find out the frontend type with FE_GET_INFO.</para>
+ find out the frontend type with <link linkend="FE_GET_INFO">FE_GET_INFO</link>.</para>
 <para>The device can be opened in read-only mode, which only allows monitoring of
  device status and statistics, or read/write mode, which allows any kind of use
  (e.g. performing tuning operations.)
@@ -478,7 +479,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_read_status">
+<section id="FE_READ_STATUS">
 <title>FE_READ_STATUS</title>
 <para>DESCRIPTION
 </para>
@@ -492,7 +493,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_READ_STATUS,
+<para>int ioctl(int fd, int request = <link linkend="FE_READ_STATUS">FE_READ_STATUS</link>,
  fe_status_t &#x22C6;status);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -511,7 +512,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_READ_STATUS for this command.</para>
+<para>Equals <link linkend="FE_READ_STATUS">FE_READ_STATUS</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -542,7 +543,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_read_ber">
+<section id="FE_READ_BER">
 <title>FE_READ_BER</title>
 <para>DESCRIPTION
 </para>
@@ -557,7 +558,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_READ_BER,
+<para>int ioctl(int fd, int request = <link linkend="FE_READ_BER">FE_READ_BER</link>,
  uint32_t &#x22C6;ber);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -575,7 +576,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_READ_BER for this command.</para>
+<para>Equals <link linkend="FE_READ_BER">FE_READ_BER</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -619,7 +620,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_read_snr">
+<section id="FE_READ_SNR">
 <title>FE_READ_SNR</title>
 
 <para>DESCRIPTION
@@ -634,7 +635,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_READ_SNR, int16_t
+<para>int ioctl(int fd, int request = <link linkend="FE_READ_SNR">FE_READ_SNR</link>, int16_t
  &#x22C6;snr);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -652,7 +653,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_READ_SNR for this command.</para>
+<para>Equals <link linkend="FE_READ_SNR">FE_READ_SNR</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -697,7 +698,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_read_signal_strength">
+<section id="FE_READ_SIGNAL_STRENGTH">
 <title>FE_READ_SIGNAL_STRENGTH</title>
 <para>DESCRIPTION
 </para>
@@ -712,7 +713,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl( int fd, int request =
FE_READ_SIGNAL_STRENGTH, int16_t &#x22C6;strength);</para>
<link linkend="FE_READ_SIGNAL_STRENGTH">FE_READ_SIGNAL_STRENGTH</link>, int16_t &#x22C6;strength);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
 
@@ -730,7 +731,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_READ_SIGNAL_STRENGTH for this
+<para>Equals <link linkend="FE_READ_SIGNAL_STRENGTH">FE_READ_SIGNAL_STRENGTH</link> for this
  command.</para>
 </entry>
  </row><row><entry
@@ -775,7 +776,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_read_ub">
+<section id="FE_READ_UNCORRECTED_BLOCKS">
 <title>FE_READ_UNCORRECTED_BLOCKS</title>
 <para>DESCRIPTION
 </para>
@@ -797,7 +798,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl( int fd, int request =
FE_READ_UNCORRECTED_BLOCKS, uint32_t &#x22C6;ublocks);</para>
<link linkend="FE_READ_UNCORRECTED_BLOCKS">FE_READ_UNCORRECTED_BLOCKS</link>, uint32_t &#x22C6;ublocks);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
 <para>PARAMETERS
@@ -814,7 +815,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_READ_UNCORRECTED_BLOCKS for this
+<para>Equals <link linkend="FE_READ_UNCORRECTED_BLOCKS">FE_READ_UNCORRECTED_BLOCKS</link> for this
  command.</para>
 </entry>
  </row><row><entry
@@ -852,7 +853,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_set_fe">
+<section id="FE_SET_FRONTEND">
 <title>FE_SET_FRONTEND</title>
 <para>DESCRIPTION
 </para>
@@ -861,8 +862,8 @@ modulation mode which can be one of the following:
 <para>This ioctl call starts a tuning operation using specified parameters. The result
  of this call will be successful if the parameters were valid and the tuning could
  be initiated. The result of the tuning operation in itself, however, will arrive
- asynchronously as an event (see documentation for FE_GET_EVENT and
- FrontendEvent.) If a new FE_SET_FRONTEND operation is initiated before
+ asynchronously as an event (see documentation for <link linkend="FE_GET_EVENT">FE_GET_EVENT</link> and
+ FrontendEvent.) If a new <link linkend="FE_SET_FRONTEND">FE_SET_FRONTEND</link> operation is initiated before
  the previous one was completed, the previous operation will be aborted in favor
  of the new one. This command requires read/write access to the device.</para>
 </entry>
@@ -872,7 +873,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_SET_FRONTEND,
+<para>int ioctl(int fd, int request = <link linkend="FE_SET_FRONTEND">FE_SET_FRONTEND</link>,
  struct dvb_frontend_parameters &#x22C6;p);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -890,7 +891,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_SET_FRONTEND for this command.</para>
+<para>Equals <link linkend="FE_SET_FRONTEND">FE_SET_FRONTEND</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -928,7 +929,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_get_fe">
+<section id="FE_GET_FRONTEND">
 <title>FE_GET_FRONTEND</title>
 <para>DESCRIPTION
 </para>
@@ -943,7 +944,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_GET_FRONTEND,
+<para>int ioctl(int fd, int request = <link linkend="FE_GET_FRONTEND">FE_GET_FRONTEND</link>,
  struct dvb_frontend_parameters &#x22C6;p);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -962,7 +963,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_SET_FRONTEND for this command.</para>
+<para>Equals <link linkend="FE_SET_FRONTEND">FE_SET_FRONTEND</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1003,7 +1004,7 @@ modulation mode which can be one of the following:
 
 </section>
 
-<section id="frontend_get_event">
+<section id="FE_GET_EVENT">
 <title>FE_GET_EVENT</title>
 <para>DESCRIPTION
 </para>
@@ -1024,7 +1025,8 @@ modulation mode which can be one of the following:
  rather small (room for 8 events), the queue must be serviced regularly to avoid
  overflow. If an overflow happens, the oldest event is discarded from the queue,
  and an error (EOVERFLOW) occurs the next time the queue is read. After
- reporting the error condition in this fashion, subsequent FE_GET_EVENT
+ reporting the error condition in this fashion, subsequent
+ <link linkend="FE_GET_EVENT">FE_GET_EVENT</link>
  calls will return events from the queue as usual.</para>
 </entry>
  </row><row><entry
@@ -1057,7 +1059,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_GET_EVENT for this command.</para>
+<para>Equals <link linkend="FE_GET_EVENT">FE_GET_EVENT</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1115,7 +1117,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_get_info">
+<section id="FE_GET_INFO">
 <title>FE_GET_INFO</title>
 <para>DESCRIPTION
 </para>
@@ -1130,7 +1132,7 @@ modulation mode which can be one of the following:
 
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para> int ioctl(int fd, int request = FE_GET_INFO, struct
+<para> int ioctl(int fd, int request = <link linkend="FE_GET_INFO">FE_GET_INFO</link>, struct
  dvb_frontend_info &#x22C6;info);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1149,7 +1151,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_GET_INFO for this command.</para>
+<para>Equals <link linkend="FE_GET_INFO">FE_GET_INFO</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1181,7 +1183,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_diseqc_reset_overload">
+<section id="FE_DISEQC_RESET_OVERLOAD">
 <title>FE_DISEQC_RESET_OVERLOAD</title>
 <para>DESCRIPTION
 </para>
@@ -1199,7 +1201,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl(int fd, int request =
FE_DISEQC_RESET_OVERLOAD);</para>
<link linkend="FE_DISEQC_RESET_OVERLOAD">FE_DISEQC_RESET_OVERLOAD</link>);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
 <para>PARAMETERS
@@ -1216,7 +1218,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_DISEQC_RESET_OVERLOAD for this
+<para>Equals <link linkend="FE_DISEQC_RESET_OVERLOAD">FE_DISEQC_RESET_OVERLOAD</link> for this
  command.</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1247,7 +1249,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_diseqc_send_master_cmd">
+<section id="FE_DISEQC_SEND_MASTER_CMD">
 <title>FE_DISEQC_SEND_MASTER_CMD</title>
 <para>DESCRIPTION
 </para>
@@ -1261,7 +1263,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl(int fd, int request =
FE_DISEQC_SEND_MASTER_CMD, struct
<link linkend="FE_DISEQC_SEND_MASTER_CMD">FE_DISEQC_SEND_MASTER_CMD</link>, struct
  dvb_diseqc_master_cmd &#x22C6;cmd);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1280,7 +1282,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_DISEQC_SEND_MASTER_CMD for this
+<para>Equals <link linkend="FE_DISEQC_SEND_MASTER_CMD">FE_DISEQC_SEND_MASTER_CMD</link> for this
  command.</para>
 </entry>
  </row><row><entry
@@ -1335,7 +1337,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_diseqc_recv_slave_reply">
+<section id="FE_DISEQC_RECV_SLAVE_REPLY">
 <title>FE_DISEQC_RECV_SLAVE_REPLY</title>
 <para>DESCRIPTION
 </para>
@@ -1350,7 +1352,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl(int fd, int request =
FE_DISEQC_RECV_SLAVE_REPLY, struct
<link linkend="FE_DISEQC_RECV_SLAVE_REPLY">FE_DISEQC_RECV_SLAVE_REPLY</link>, struct
  dvb_diseqc_slave_reply &#x22C6;reply);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1369,7 +1371,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_DISEQC_RECV_SLAVE_REPLY for this
+<para>Equals <link linkend="FE_DISEQC_RECV_SLAVE_REPLY">FE_DISEQC_RECV_SLAVE_REPLY</link> for this
  command.</para>
 </entry>
  </row><row><entry
@@ -1423,7 +1425,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_diseqc_send_burst">
+<section id="FE_DISEQC_SEND_BURST">
 <title>FE_DISEQC_SEND_BURST</title>
 <para>DESCRIPTION
 </para>
@@ -1438,7 +1440,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl(int fd, int request =
FE_DISEQC_SEND_BURST, fe_sec_mini_cmd_t burst);</para>
<link linkend="FE_DISEQC_SEND_BURST">FE_DISEQC_SEND_BURST</link>, fe_sec_mini_cmd_t burst);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
 
@@ -1456,7 +1458,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_DISEQC_SEND_BURST for this command.</para>
+<para>Equals <link linkend="FE_DISEQC_SEND_BURST">FE_DISEQC_SEND_BURST</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1509,7 +1511,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_set_tone">
+<section id="FE_SET_TONE">
 <title>FE_SET_TONE</title>
 <para>DESCRIPTION
 </para>
@@ -1523,7 +1525,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_SET_TONE,
+<para>int ioctl(int fd, int request = <link linkend="FE_SET_TONE">FE_SET_TONE</link>,
  fe_sec_tone_mode_t tone);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1541,7 +1543,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_SET_TONE for this command.</para>
+<para>Equals <link linkend="FE_SET_TONE">FE_SET_TONE</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1592,7 +1594,7 @@ modulation mode which can be one of the following:
 </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="fe_set_voltage">
+<section id="FE_SET_VOLTAGE">
 <title>FE_SET_VOLTAGE</title>
 <para>DESCRIPTION
 </para>
@@ -1606,7 +1608,7 @@ modulation mode which can be one of the following:
 </para>
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
-<para>int ioctl(int fd, int request = FE_SET_VOLTAGE,
+<para>int ioctl(int fd, int request = <link linkend="FE_SET_VOLTAGE">FE_SET_VOLTAGE</link>,
  fe_sec_voltage_t voltage);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
@@ -1625,7 +1627,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_SET_VOLTAGE for this command.</para>
+<para>Equals <link linkend="FE_SET_VOLTAGE">FE_SET_VOLTAGE</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1677,7 +1679,7 @@ modulation mode which can be one of the following:
  </row></tbody></tgroup></informaltable>
 </section>
 
-<section id="frontend_enable_high_lnb_volt">
+<section id="FE_ENABLE_HIGH_LNB_VOLTAGE">
 <title>FE_ENABLE_HIGH_LNB_VOLTAGE</title>
 <para>DESCRIPTION
 </para>
@@ -1694,7 +1696,7 @@ modulation mode which can be one of the following:
 <informaltable><tgroup cols="1"><tbody><row><entry
  align="char">
 <para>int ioctl(int fd, int request =
FE_ENABLE_HIGH_LNB_VOLTAGE, int high);</para>
<link linkend="FE_ENABLE_HIGH_LNB_VOLTAGE">FE_ENABLE_HIGH_LNB_VOLTAGE</link>, int high);</para>
 </entry>
  </row></tbody></tgroup></informaltable>
 
@@ -1712,7 +1714,7 @@ modulation mode which can be one of the following:
 <para>int request</para>
 </entry><entry
  align="char">
-<para>Equals FE_SET_VOLTAGE for this command.</para>
+<para>Equals <link linkend="FE_SET_VOLTAGE">FE_SET_VOLTAGE</link> for this command.</para>
 </entry>
  </row><row><entry
  align="char">
@@ -1762,5 +1764,82 @@ modulation mode which can be one of the following:
 </entry>
  </row></tbody></tgroup></informaltable>
 </section>
+
+<section id="FE_SET_FRONTEND_TUNE_MODE">
+<title>FE_SET_FRONTEND_TUNE_MODE</title>
+<para>DESCRIPTION</para>
+<informaltable><tgroup cols="1"><tbody><row>
+<entry align="char">
+<para>Allow setting tuner mode flags to the frontend.</para>
+</entry>
+</row></tbody></tgroup></informaltable>
+
+<para>SYNOPSIS</para>
+<informaltable><tgroup cols="1"><tbody><row>
+<entry align="char">
+<para>int ioctl(int fd, int request =
+<link linkend="FE_SET_FRONTEND_TUNE_MODE">FE_SET_FRONTEND_TUNE_MODE</link>, unsigned int flags);</para>
+</entry>
+</row></tbody></tgroup></informaltable>
+
+<para>PARAMETERS</para>
+<informaltable><tgroup cols="2"><tbody><row>
+<entry align="char">
+       <para>unsigned int flags</para>
+</entry>
+<entry align="char">
+<para>
+FE_TUNE_MODE_ONESHOT When set, this flag will disable any zigzagging or other "normal" tuning behaviour. Additionally, there will be no automatic monitoring of the lock status, and hence no frontend events will be generated. If a frontend device is closed, this flag will be automatically turned off when the device is reopened read-write.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+
+<para>ERRORS</para>
+<informaltable><tgroup cols="2"><tbody><row>
+<entry align="char"><para>EINVAL</para></entry>
+<entry align="char"><para>Invalid argument.</para></entry>
+ </row></tbody></tgroup></informaltable>
 </section>
-&sub-isdbt;
+
+<section id="FE_DISHNETWORK_SEND_LEGACY_CMD">
+       <title>FE_DISHNETWORK_SEND_LEGACY_CMD</title>
+<para>DESCRIPTION</para>
+<informaltable><tgroup cols="1"><tbody><row>
+<entry align="char">
+<para>WARNING: This is a very obscure legacy command, used only at stv0299 driver. Should not be used on newer drivers.</para>
+<para>It provides a non-standard method for selecting Diseqc voltage on the frontend, for Dish Network legacy switches.</para>
+<para>As support for this ioctl were added in 2004, this means that such dishes were already legacy in 2004.</para>
+</entry>
+</row></tbody></tgroup></informaltable>
+
+<para>SYNOPSIS</para>
+<informaltable><tgroup cols="1"><tbody><row>
+<entry align="char">
+<para>int ioctl(int fd, int request =
+       <link linkend="FE_DISHNETWORK_SEND_LEGACY_CMD">FE_DISHNETWORK_SEND_LEGACY_CMD</link>, unsigned long cmd);</para>
+</entry>
+</row></tbody></tgroup></informaltable>
+
+<para>PARAMETERS</para>
+<informaltable><tgroup cols="2"><tbody><row>
+<entry align="char">
+       <para>unsigned long cmd</para>
+</entry>
+<entry align="char">
+<para>
+sends the specified raw cmd to the dish via DISEqC.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+
+<para>ERRORS</para>
+<informaltable><tgroup cols="1"><tbody><row>
+<entry align="char">
+       <para>There are no errors in use for this call</para>
+</entry>
+</row></tbody></tgroup></informaltable>
+</section>
+
+</section>
+
+&sub-dvbproperty;
index c671a01..1448b33 100644 (file)
@@ -417,8 +417,8 @@ desc->chip->end();
       </para>
       <para>
        To make use of the split implementation, replace the call to
-       __do_IRQ by a call to desc->chip->handle_irq() and associate
-        the appropriate handler function to desc->chip->handle_irq().
+       __do_IRQ by a call to desc->handle_irq() and associate
+        the appropriate handler function to desc->handle_irq().
        In most cases the generic handler implementations should
        be sufficient.
      </para>
index 992e67e..7b3f493 100644 (file)
@@ -352,7 +352,7 @@ asmlinkage long sys_mycall(int arg)
   </para>
 
   <programlisting>
-if (signal_pending()) 
+if (signal_pending(current))
         return -ERESTARTSYS;
   </programlisting>
 
index 0eb43c1..bb5ab74 100644 (file)
 <!ENTITY sub-v4l2 SYSTEM "v4l/v4l2.xml">
 <!ENTITY sub-intro SYSTEM "dvb/intro.xml">
 <!ENTITY sub-frontend SYSTEM "dvb/frontend.xml">
-<!ENTITY sub-isdbt SYSTEM "dvb/isdbt.xml">
+<!ENTITY sub-dvbproperty SYSTEM "dvb/dvbproperty.xml">
 <!ENTITY sub-demux SYSTEM "dvb/demux.xml">
 <!ENTITY sub-video SYSTEM "dvb/video.xml">
 <!ENTITY sub-audio SYSTEM "dvb/audio.xml">
 <!ENTITY sub-net SYSTEM "dvb/net.xml">
 <!ENTITY sub-kdapi SYSTEM "dvb/kdapi.xml">
 <!ENTITY sub-examples SYSTEM "dvb/examples.xml">
+<!ENTITY sub-frontend-h SYSTEM "dvb/frontend.h.xml">
 <!ENTITY sub-dvbapi SYSTEM "dvb/dvbapi.xml">
 <!ENTITY sub-media SYSTEM "media.xml">
 <!ENTITY sub-media-entities SYSTEM "media-entities.tmpl">
index df0d089..f508a8a 100644 (file)
@@ -362,7 +362,7 @@ module_exit(board_cleanup);
        <sect1 id="Multiple_chip_control">
                <title>Multiple chip control</title>
                <para>
-                       The nand driver can control chip arrays. Therefor the
+                       The nand driver can control chip arrays. Therefore the
                        board driver must provide an own select_chip function. This
                        function must (de)select the requested chip.
                        The function pointer in the nand_chip structure must
index f492acc..f464506 100644 (file)
@@ -280,11 +280,29 @@ minimum value disables backlight compensation.</entry>
 <constant>V4L2_COLORFX_BW</constant> (1) and
 <constant>V4L2_COLORFX_SEPIA</constant> (2).</entry>
          </row>
+         <row>
+           <entry><constant>V4L2_CID_ROTATE</constant></entry>
+           <entry>integer</entry>
+           <entry>Rotates the image by specified angle. Common angles are 90,
+           270 and 180. Rotating the image to 90 and 270 will reverse the height
+           and width of the display window. It is necessary to set the new height and
+           width of the picture using the &VIDIOC-S-FMT; ioctl according to
+           the rotation angle selected.</entry>
+         </row>
+         <row>
+           <entry><constant>V4L2_CID_BG_COLOR</constant></entry>
+           <entry>integer</entry>
+           <entry>Sets the background color on the current output device.
+           Background color needs to be specified in the RGB24 format. The
+           supplied 32 bit value is interpreted as bits 0-7 Red color information,
+           bits 8-15 Green color information, bits 16-23 Blue color
+           information and bits 24-31 must be zero.</entry>
+         </row>
          <row>
            <entry><constant>V4L2_CID_LASTP1</constant></entry>
            <entry></entry>
            <entry>End of the predefined control IDs (currently
-<constant>V4L2_CID_COLORFX</constant> + 1).</entry>
+<constant>V4L2_CID_BG_COLOR</constant> + 1).</entry>
          </row>
          <row>
            <entry><constant>V4L2_CID_PRIVATE_BASE</constant></entry>
index 7d396a3..885968d 100644 (file)
@@ -770,6 +770,11 @@ kernel sources in the file <filename>Documentation/video4linux/cx2341x/README.hm
            <entry>'S920'</entry>
            <entry>YUV 4:2:0 format of the gspca sn9c20x driver.</entry>
          </row>
+         <row id="V4L2-PIX-FMT-STV0680">
+           <entry><constant>V4L2_PIX_FMT_STV0680</constant></entry>
+           <entry>'S680'</entry>
+           <entry>Bayer format of the gspca stv0680 driver.</entry>
+         </row>
          <row id="V4L2-PIX-FMT-WNVA">
            <entry><constant>V4L2_PIX_FMT_WNVA</constant></entry>
            <entry>'WNVA'</entry>
index 9700206..3e282ed 100644 (file)
@@ -363,6 +363,7 @@ struct <link linkend="v4l2-pix-format">v4l2_pix_format</link> {
 #define <link linkend="V4L2-PIX-FMT-OV511">V4L2_PIX_FMT_OV511</link>    v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
 #define <link linkend="V4L2-PIX-FMT-OV518">V4L2_PIX_FMT_OV518</link>    v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
 #define <link linkend="V4L2-PIX-FMT-TM6000">V4L2_PIX_FMT_TM6000</link>   v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
+#define <link linkend="V4L2-PIX-FMT-STV0680">V4L2_PIX_FMT_STV0680</link>  v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
 
 /*
  *      F O R M A T   E N U M E R A T I O N
@@ -492,7 +493,7 @@ struct <link linkend="v4l2-jpegcompression">v4l2_jpegcompression</link> {
                                  * you do, leave them untouched.
                                  * Inluding less markers will make the
                                  * resulting code smaller, but there will
-                                 * be fewer aplications which can read it.
+                                 * be fewer applications which can read it.
                                  * The presence of the APP and COM marker
                                  * is influenced by APP_len and COM_len
                                  * ONLY, not by this property! */
@@ -565,6 +566,7 @@ struct <link linkend="v4l2-framebuffer">v4l2_framebuffer</link> {
 #define V4L2_FBUF_CAP_LOCAL_ALPHA       0x0010
 #define V4L2_FBUF_CAP_GLOBAL_ALPHA      0x0020
 #define V4L2_FBUF_CAP_LOCAL_INV_ALPHA   0x0040
+#define V4L2_FBUF_CAP_SRC_CHROMAKEY     0x0080
 /*  Flags for the 'flags' field. */
 #define V4L2_FBUF_FLAG_PRIMARY          0x0001
 #define V4L2_FBUF_FLAG_OVERLAY          0x0002
@@ -572,6 +574,7 @@ struct <link linkend="v4l2-framebuffer">v4l2_framebuffer</link> {
 #define V4L2_FBUF_FLAG_LOCAL_ALPHA      0x0008
 #define V4L2_FBUF_FLAG_GLOBAL_ALPHA     0x0010
 #define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA  0x0020
+#define V4L2_FBUF_FLAG_SRC_CHROMAKEY    0x0040
 
 struct <link linkend="v4l2-clip">v4l2_clip</link> {
         struct <link linkend="v4l2-rect">v4l2_rect</link>        c;
@@ -914,8 +917,10 @@ enum <link linkend="v4l2-colorfx">v4l2_colorfx</link> {
 #define V4L2_CID_AUTOBRIGHTNESS                 (V4L2_CID_BASE+32)
 #define V4L2_CID_BAND_STOP_FILTER               (V4L2_CID_BASE+33)
 
+#define V4L2_CID_ROTATE                         (V4L2_CID_BASE+34)
+#define V4L2_CID_BG_COLOR                       (V4L2_CID_BASE+35)
 /* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+34)
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+36)
 
 /*  MPEG-class control IDs defined by V4L2 */
 #define V4L2_CID_MPEG_BASE                      (V4L2_CTRL_CLASS_MPEG | 0x900)
index f701706..e7dda48 100644 (file)
@@ -336,6 +336,13 @@ alpha value. Alpha blending makes no sense for destructive overlays.</entry>
 inverted alpha channel of the framebuffer or VGA signal. Alpha
 blending makes no sense for destructive overlays.</entry>
          </row>
+         <row>
+           <entry><constant>V4L2_FBUF_CAP_SRC_CHROMAKEY</constant></entry>
+           <entry>0x0080</entry>
+           <entry>The device supports Source Chroma-keying. Framebuffer pixels
+with the chroma-key colors are replaced by video pixels, which is exactly opposite of
+<constant>V4L2_FBUF_CAP_CHROMAKEY</constant></entry>
+         </row>
        </tbody>
       </tgroup>
     </table>
@@ -411,6 +418,16 @@ images, but with an inverted alpha value. The blend function is:
 output = framebuffer pixel * (1 - alpha) + video pixel * alpha. The
 actual alpha depth depends on the framebuffer pixel format.</entry>
          </row>
+         <row>
+           <entry><constant>V4L2_FBUF_FLAG_SRC_CHROMAKEY</constant></entry>
+           <entry>0x0040</entry>
+           <entry>Use source chroma-keying. The source chroma-key color is
+determined by the <structfield>chromakey</structfield> field of
+&v4l2-window; and negotiated with the &VIDIOC-S-FMT; ioctl, see <xref
+linkend="overlay" /> and <xref linkend="osd" />.
+Both chroma-keying are mutual exclusive to each other, so same
+<structfield>chromakey</structfield> field of &v4l2-window; is being used.</entry>
+         </row>
        </tbody>
       </tgroup>
     </table>
index 7a2e0e9..0d0f7b4 100644 (file)
@@ -5318,7 +5318,7 @@ struct _snd_pcm_runtime {
       pages of the given size and map them onto the virtually contiguous
       memory.  The virtual pointer is addressed in runtime-&gt;dma_area.
       The physical address (runtime-&gt;dma_addr) is set to zero,
-      because the buffer is physically non-contigous.
+      because the buffer is physically non-contiguous.
       The physical address table is set up in sgbuf-&gt;table.
       You can get the physical address at a certain offset via
       <function>snd_pcm_sgbuf_get_addr()</function>. 
diff --git a/Documentation/arm/OMAP/DSS b/Documentation/arm/OMAP/DSS
new file mode 100644 (file)
index 0000000..0af0e9e
--- /dev/null
@@ -0,0 +1,317 @@
+OMAP2/3 Display Subsystem
+-------------------------
+
+This is an almost total rewrite of the OMAP FB driver in drivers/video/omap
+(let's call it DSS1). The main differences between DSS1 and DSS2 are DSI,
+TV-out and multiple display support, but there are lots of small improvements
+also.
+
+The DSS2 driver (omapdss module) is in arch/arm/plat-omap/dss/, and the FB,
+panel and controller drivers are in drivers/video/omap2/. DSS1 and DSS2 live
+currently side by side, you can choose which one to use.
+
+Features
+--------
+
+Working and tested features include:
+
+- MIPI DPI (parallel) output
+- MIPI DSI output in command mode
+- MIPI DBI (RFBI) output
+- SDI output
+- TV output
+- All pieces can be compiled as a module or inside kernel
+- Use DISPC to update any of the outputs
+- Use CPU to update RFBI or DSI output
+- OMAP DISPC planes
+- RGB16, RGB24 packed, RGB24 unpacked
+- YUV2, UYVY
+- Scaling
+- Adjusting DSS FCK to find a good pixel clock
+- Use DSI DPLL to create DSS FCK
+
+Tested boards include:
+- OMAP3 SDP board
+- Beagle board
+- N810
+
+omapdss driver
+--------------
+
+The DSS driver does not itself have any support for Linux framebuffer, V4L or
+such like the current ones, but it has an internal kernel API that upper level
+drivers can use.
+
+The DSS driver models OMAP's overlays, overlay managers and displays in a
+flexible way to enable non-common multi-display configuration. In addition to
+modelling the hardware overlays, omapdss supports virtual overlays and overlay
+managers. These can be used when updating a display with CPU or system DMA.
+
+Panel and controller drivers
+----------------------------
+
+The drivers implement panel or controller specific functionality and are not
+usually visible to users except through omapfb driver.  They register
+themselves to the DSS driver.
+
+omapfb driver
+-------------
+
+The omapfb driver implements arbitrary number of standard linux framebuffers.
+These framebuffers can be routed flexibly to any overlays, thus allowing very
+dynamic display architecture.
+
+The driver exports some omapfb specific ioctls, which are compatible with the
+ioctls in the old driver.
+
+The rest of the non standard features are exported via sysfs. Whether the final
+implementation will use sysfs, or ioctls, is still open.
+
+V4L2 drivers
+------------
+
+V4L2 is being implemented in TI.
+
+From omapdss point of view the V4L2 drivers should be similar to framebuffer
+driver.
+
+Architecture
+--------------------
+
+Some clarification what the different components do:
+
+    - Framebuffer is a memory area inside OMAP's SRAM/SDRAM that contains the
+      pixel data for the image. Framebuffer has width and height and color
+      depth.
+    - Overlay defines where the pixels are read from and where they go on the
+      screen. The overlay may be smaller than framebuffer, thus displaying only
+      part of the framebuffer. The position of the overlay may be changed if
+      the overlay is smaller than the display.
+    - Overlay manager combines the overlays in to one image and feeds them to
+      display.
+    - Display is the actual physical display device.
+
+A framebuffer can be connected to multiple overlays to show the same pixel data
+on all of the overlays. Note that in this case the overlay input sizes must be
+the same, but, in case of video overlays, the output size can be different. Any
+framebuffer can be connected to any overlay.
+
+An overlay can be connected to one overlay manager. Also DISPC overlays can be
+connected only to DISPC overlay managers, and virtual overlays can be only
+connected to virtual overlays.
+
+An overlay manager can be connected to one display. There are certain
+restrictions which kinds of displays an overlay manager can be connected:
+
+    - DISPC TV overlay manager can be only connected to TV display.
+    - Virtual overlay managers can only be connected to DBI or DSI displays.
+    - DISPC LCD overlay manager can be connected to all displays, except TV
+      display.
+
+Sysfs
+-----
+The sysfs interface is mainly used for testing. I don't think sysfs
+interface is the best for this in the final version, but I don't quite know
+what would be the best interfaces for these things.
+
+The sysfs interface is divided to two parts: DSS and FB.
+
+/sys/class/graphics/fb? directory:
+mirror         0=off, 1=on
+rotate         Rotation 0-3 for 0, 90, 180, 270 degrees
+rotate_type    0 = DMA rotation, 1 = VRFB rotation
+overlays       List of overlay numbers to which framebuffer pixels go
+phys_addr      Physical address of the framebuffer
+virt_addr      Virtual address of the framebuffer
+size           Size of the framebuffer
+
+/sys/devices/platform/omapdss/overlay? directory:
+enabled                0=off, 1=on
+input_size     width,height (ie. the framebuffer size)
+manager                Destination overlay manager name
+name
+output_size    width,height
+position       x,y
+screen_width   width
+global_alpha           global alpha 0-255 0=transparent 255=opaque
+
+/sys/devices/platform/omapdss/manager? directory:
+display                                Destination display
+name
+alpha_blending_enabled         0=off, 1=on
+trans_key_enabled              0=off, 1=on
+trans_key_type                 gfx-destination, video-source
+trans_key_value                        transparency color key (RGB24)
+default_color                  default background color (RGB24)
+
+/sys/devices/platform/omapdss/display? directory:
+ctrl_name      Controller name
+mirror         0=off, 1=on
+update_mode    0=off, 1=auto, 2=manual
+enabled                0=off, 1=on
+name
+rotate         Rotation 0-3 for 0, 90, 180, 270 degrees
+timings                Display timings (pixclock,xres/hfp/hbp/hsw,yres/vfp/vbp/vsw)
+               When writing, two special timings are accepted for tv-out:
+               "pal" and "ntsc"
+panel_name
+tear_elim      Tearing elimination 0=off, 1=on
+
+There are also some debugfs files at <debugfs>/omapdss/ which show information
+about clocks and registers.
+
+Examples
+--------
+
+The following definitions have been made for the examples below:
+
+ovl0=/sys/devices/platform/omapdss/overlay0
+ovl1=/sys/devices/platform/omapdss/overlay1
+ovl2=/sys/devices/platform/omapdss/overlay2
+
+mgr0=/sys/devices/platform/omapdss/manager0
+mgr1=/sys/devices/platform/omapdss/manager1
+
+lcd=/sys/devices/platform/omapdss/display0
+dvi=/sys/devices/platform/omapdss/display1
+tv=/sys/devices/platform/omapdss/display2
+
+fb0=/sys/class/graphics/fb0
+fb1=/sys/class/graphics/fb1
+fb2=/sys/class/graphics/fb2
+
+Default setup on OMAP3 SDP
+--------------------------
+
+Here's the default setup on OMAP3 SDP board. All planes go to LCD. DVI
+and TV-out are not in use. The columns from left to right are:
+framebuffers, overlays, overlay managers, displays. Framebuffers are
+handled by omapfb, and the rest by the DSS.
+
+FB0 --- GFX  -\            DVI
+FB1 --- VID1 --+- LCD ---- LCD
+FB2 --- VID2 -/   TV ----- TV
+
+Example: Switch from LCD to DVI
+----------------------
+
+w=`cat $dvi/timings | cut -d "," -f 2 | cut -d "/" -f 1`
+h=`cat $dvi/timings | cut -d "," -f 3 | cut -d "/" -f 1`
+
+echo "0" > $lcd/enabled
+echo "" > $mgr0/display
+fbset -fb /dev/fb0 -xres $w -yres $h -vxres $w -vyres $h
+# at this point you have to switch the dvi/lcd dip-switch from the omap board
+echo "dvi" > $mgr0/display
+echo "1" > $dvi/enabled
+
+After this the configuration looks like:
+
+FB0 --- GFX  -\         -- DVI
+FB1 --- VID1 --+- LCD -/   LCD
+FB2 --- VID2 -/   TV ----- TV
+
+Example: Clone GFX overlay to LCD and TV
+-------------------------------
+
+w=`cat $tv/timings | cut -d "," -f 2 | cut -d "/" -f 1`
+h=`cat $tv/timings | cut -d "," -f 3 | cut -d "/" -f 1`
+
+echo "0" > $ovl0/enabled
+echo "0" > $ovl1/enabled
+
+echo "" > $fb1/overlays
+echo "0,1" > $fb0/overlays
+
+echo "$w,$h" > $ovl1/output_size
+echo "tv" > $ovl1/manager
+
+echo "1" > $ovl0/enabled
+echo "1" > $ovl1/enabled
+
+echo "1" > $tv/enabled
+
+After this the configuration looks like (only relevant parts shown):
+
+FB0 +-- GFX  ---- LCD ---- LCD
+     \- VID1 ---- TV  ---- TV
+
+Misc notes
+----------
+
+OMAP FB allocates the framebuffer memory using the OMAP VRAM allocator.
+
+Using DSI DPLL to generate pixel clock it is possible produce the pixel clock
+of 86.5MHz (max possible), and with that you get 1280x1024@57 output from DVI.
+
+Rotation and mirroring currently only supports RGB565 and RGB8888 modes. VRFB
+does not support mirroring.
+
+VRFB rotation requires much more memory than non-rotated framebuffer, so you
+probably need to increase your vram setting before using VRFB rotation. Also,
+many applications may not work with VRFB if they do not pay attention to all
+framebuffer parameters.
+
+Kernel boot arguments
+---------------------
+
+vram=<size>
+       - Amount of total VRAM to preallocate. For example, "10M". omapfb
+         allocates memory for framebuffers from VRAM.
+
+omapfb.mode=<display>:<mode>[,...]
+       - Default video mode for specified displays. For example,
+         "dvi:800x400MR-24@60".  See drivers/video/modedb.c.
+         There are also two special modes: "pal" and "ntsc" that
+         can be used to tv out.
+
+omapfb.vram=<fbnum>:<size>[@<physaddr>][,...]
+       - VRAM allocated for a framebuffer. Normally omapfb allocates vram
+         depending on the display size. With this you can manually allocate
+         more or define the physical address of each framebuffer. For example,
+         "1:4M" to allocate 4M for fb1.
+
+omapfb.debug=<y|n>
+       - Enable debug printing. You have to have OMAPFB debug support enabled
+         in kernel config.
+
+omapfb.test=<y|n>
+       - Draw test pattern to framebuffer whenever framebuffer settings change.
+         You need to have OMAPFB debug support enabled in kernel config.
+
+omapfb.vrfb=<y|n>
+       - Use VRFB rotation for all framebuffers.
+
+omapfb.rotate=<angle>
+       - Default rotation applied to all framebuffers.
+         0 - 0 degree rotation
+         1 - 90 degree rotation
+         2 - 180 degree rotation
+         3 - 270 degree rotation
+
+omapfb.mirror=<y|n>
+       - Default mirror for all framebuffers. Only works with DMA rotation.
+
+omapdss.def_disp=<display>
+       - Name of default display, to which all overlays will be connected.
+         Common examples are "lcd" or "tv".
+
+omapdss.debug=<y|n>
+       - Enable debug printing. You have to have DSS debug support enabled in
+         kernel config.
+
+TODO
+----
+
+DSS locking
+
+Error checking
+- Lots of checks are missing or implemented just as BUG()
+
+System DMA update for DSI
+- Can be used for RGB16 and RGB24P modes. Probably not for RGB24U (how
+  to skip the empty byte?)
+
+OMAP1 support
+- Not sure if needed
+
index bf2a9cd..c8238e4 100644 (file)
@@ -85,7 +85,7 @@ http://www.linuxtv.org/wiki/index.php/DVB_USB
             - moved transfer control (pid filter, fifo control) from usb driver to frontend, it seems
               better settled there (added xfer_ops-struct)
             - created a common files for frontends (mc/p/mb)
-  2004-09-28 - added support for a new device (Unkown, vendor ID is Hyper-Paltek)
+  2004-09-28 - added support for a new device (Unknown, vendor ID is Hyper-Paltek)
   2004-09-20 - added support for a new device (Compro DVB-U2000), thanks
               to Amaury Demol for reporting
             - changed usb TS transfer method (several urbs, stopping transfer
index 06f8f46..79c5332 100644 (file)
@@ -80,7 +80,7 @@ is:
 
        broken_parity_status
 
-as is located in /sys/devices/pci<XXX>/0000:XX:YY.Z directorys for
+as is located in /sys/devices/pci<XXX>/0000:XX:YY.Z directories for
 PCI devices.
 
 FUTURE HARDWARE SCANNING
@@ -288,9 +288,8 @@ Total UE count that had no information attribute fileY:
 
        'ue_noinfo_count'
 
-       This attribute file displays the number of UEs that
-       have occurred have occurred with  no informations as to which DIMM
-       slot is having errors.
+       This attribute file displays the number of UEs that have occurred
+       with no information as to which DIMM slot is having errors.
 
 
 Total Correctable Errors count attribute file:
index 591e944..eb2c138 100644 (file)
@@ -291,13 +291,6 @@ Who:       Michael Buesch <mb@bu3sch.de>
 
 ---------------------------
 
-What:  usedac i386 kernel parameter
-When:  2.6.27
-Why:   replaced by allowdac and no dac combination
-Who:   Glauber Costa <gcosta@redhat.com>
-
----------------------------
-
 What: print_fn_descriptor_symbol()
 When: October 2009
 Why:  The %pF vsprintf format provides the same functionality in a
@@ -483,3 +476,10 @@ Why:       With the recent innovations in CPU hardware acceleration technologies
 Who:   Alok N Kataria <akataria@vmware.com>
 
 ----------------------------
+
+What:  adt7473 hardware monitoring driver
+When:  February 2010
+Why:   Obsoleted by the adt7475 driver.
+Who:   Jean Delvare <khali@linux-fr.org>
+
+---------------------------
index f15621e..7001782 100644 (file)
@@ -36,6 +36,8 @@ dnotify.txt
        - info about directory notification in Linux.
 ecryptfs.txt
        - docs on eCryptfs: stacked cryptographic filesystem for Linux.
+exofs.txt
+       - info, usage, mount options, design about EXOFS.
 ext2.txt
        - info, mount options and specifications for the Ext2 filesystem.
 ext3.txt
index 0ced74c..abd2a9b 100644 (file)
@@ -60,13 +60,13 @@ USAGE
 
    mkfs.exofs --pid=65536 --format /dev/osd0
 
-   The --format is optional if not specified no OSD_FORMAT will be
-   preformed and a clean file system will be created in the specified pid,
+   The --format is optional. If not specified, no OSD_FORMAT will be
+   performed and a clean file system will be created in the specified pid,
    in the available space of the target. (Use --format=size_in_meg to limit
    the total LUN space available)
 
-   If pid already exist it will be deleted and a new one will be created in it's
-   place. Be careful.
+   If pid already exists, it will be deleted and a new one will be created in
+   its place. Be careful.
 
    An exofs lives inside a single OSD partition. You can create multiple exofs
    filesystems on the same device using multiple pids.
@@ -81,7 +81,7 @@ USAGE
 
 7. For reference (See do-exofs example script):
        do-exofs start - an example of how to perform the above steps.
-       do-exofs stop -  an example of how to unmount the file system.
+       do-exofs stop - an example of how to unmount the file system.
        do-exofs format - an example of how to format and mkfs a new exofs.
 
 8. Extra compilation flags (uncomment in fs/exofs/Kbuild):
@@ -104,8 +104,8 @@ Where:
     exofs specific options: Options are separated by commas (,)
                pid=<integer> - The partition number to mount/create as
                                 container of the filesystem.
-                                This option is mandatory
-                to=<integer>  - Timeout in ticks for a single command
+                                This option is mandatory.
+                to=<integer>  - Timeout in ticks for a single command.
                                 default is (60 * HZ) [for debugging only]
 
 ===============================================================================
@@ -116,7 +116,7 @@ DESIGN
   with a special ID (defined in common.h).
   Information included in the file system control block is used to fill the
   in-memory superblock structure at mount time. This object is created before
-  the file system is used by mkexofs.c It contains information such as:
+  the file system is used by mkexofs.c. It contains information such as:
        - The file system's magic number
        - The next inode number to be allocated
 
@@ -134,8 +134,8 @@ DESIGN
   attributes. This applies to both regular files and other types (directories,
   device files, symlinks, etc.).
 
-* Credentials are generated per object (inode and superblock) when they is
-  created in memory (read off disk or created). The credential works for all
+* Credentials are generated per object (inode and superblock) when they are
+  created in memory (read from disk or created). The credential works for all
   operations and is used as long as the object remains in memory.
 
 * Async OSD operations are used whenever possible, but the target may execute
@@ -145,7 +145,8 @@ DESIGN
   from executing in reverse order:
        - The following are handled with the OBJ_CREATED and OBJ_2BCREATED
          flags. OBJ_CREATED is set when we know the object exists on the OSD -
-         in create's callback function, and when we successfully do a read_inode.
+         in create's callback function, and when we successfully do a
+         read_inode.
          OBJ_2BCREATED is set in the beginning of the create function, so we
          know that we should wait.
                - create/delete: delete should wait until the object is created
index 6d94e06..af6885c 100644 (file)
@@ -153,8 +153,8 @@ journal_dev=devnum  When the external journal device's major/minor numbers
                        identified through its new major/minor numbers encoded
                        in devnum.
 
-noload                 Don't load the journal on mounting.  Note that
-                       if the filesystem was not unmounted cleanly,
+norecovery             Don't load the journal on mounting.  Note that
+noload                 if the filesystem was not unmounted cleanly,
                        skipping the journal replay will lead to the
                        filesystem containing inconsistencies that can
                        lead to any number of problems.
@@ -353,6 +353,12 @@ noauto_da_alloc            replacing existing files via patterns such as
                        system crashes before the delayed allocation
                        blocks are forced to disk.
 
+discard                Controls whether ext4 should issue discard/TRIM
+nodiscard(*)           commands to the underlying block device when
+                       blocks are freed.  This is useful for SSD devices
+                       and sparse/thinly-provisioned LUNs, but it is off
+                       by default until sufficient testing has been done.
+
 Data Mode
 =========
 There are 3 different data modes:
index 4af0018..94b9f20 100644 (file)
@@ -1089,8 +1089,8 @@ The "processes" line gives the number  of processes and threads created, which
 includes (but  is not limited  to) those  created by  calls to the  fork() and
 clone() system calls.
 
-The  "procs_running" line gives the  number of processes  currently running on
-CPUs.
+The "procs_running" line gives the total number of threads that are
+running or ready to run (i.e., the total number of runnable threads).
 
 The   "procs_blocked" line gives  the  number of  processes currently blocked,
 waiting for I/O to complete.
index fa4dc07..e4e7dae 100644 (file)
@@ -380,7 +380,7 @@ rare; use gpiochip_remove() when it is unavoidable.
 
 Most often a gpio_chip is part of an instance-specific structure with state
 not exposed by the GPIO interfaces, such as addressing, power management,
-and more.  Chips such as codecs will have complex non-GPIO state,
+and more.  Chips such as codecs will have complex non-GPIO state.
 
 Any debugfs dump method should normally ignore signals which haven't been
 requested as GPIOs.  They can use gpiochip_is_requested(), which returns
@@ -531,7 +531,7 @@ and have the following read/write attributes:
                This file exists only if the pin can be configured as an
                interrupt generating input pin.
 
-GPIO controllers have paths like /sys/class/gpio/chipchip42/ (for the
+GPIO controllers have paths like /sys/class/gpio/gpiochip42/ (for the
 controller implementing GPIOs starting at #42) and have the following
 read-only attributes:
 
index 1cbf671..446612b 100644 (file)
@@ -9,6 +9,8 @@ Supported chips:
 
 Author: Darrick J. Wong
 
+This driver is depreacted, please use the adt7475 driver instead.
+
 Description
 -----------
 
index a2b1abe..0502f2b 100644 (file)
-This describes the interface for the ADT7475 driver:
-
-(there are 4 fans, numbered fan1 to fan4):
-
-fanX_input             Read the current speed of the fan (in RPMs)
-fanX_min               Read/write the minimum speed of the fan.  Dropping
-                       below this sets an alarm.
-
-(there are three PWMs, numbered pwm1 to pwm3):
-
-pwmX                   Read/write the current duty cycle of the PWM.  Writes
-                       only have effect when auto mode is turned off (see
-                       below).  Range is 0 - 255.
-
-pwmX_enable            Fan speed control method:
-
-                       0 - No control (fan at full speed)
-                       1 - Manual fan speed control (using pwm[1-*])
-                       2 - Automatic fan speed control
-
-pwmX_auto_channels_temp        Select which channels affect this PWM
-
-                       1 - TEMP1 controls PWM
-                       2 - TEMP2 controls PWM
-                       4 - TEMP3 controls PWM
-                       6 - TEMP2 and TEMP3 control PWM
-                       7 - All three inputs control PWM
-
-pwmX_freq              Read/write the PWM frequency in Hz. The number
-                       should be one of the following:
-
-                       11 Hz
-                       14 Hz
-                       22 Hz
-                       29 Hz
-                       35 Hz
-                       44 Hz
-                       58 Hz
-                       88 Hz
-
-pwmX_auto_point1_pwm   Read/write the minimum PWM duty cycle in automatic mode
-
-pwmX_auto_point2_pwm   Read/write the maximum PWM duty cycle in automatic mode
-
-(there are three temperature settings numbered temp1 to temp3):
-
-tempX_input            Read the current temperature.  The value is in milli
-                       degrees of Celsius.
-
-tempX_max              Read/write the upper temperature limit - exceeding this
-                       will cause an alarm.
-
-tempX_min              Read/write the lower temperature limit - exceeding this
-                       will cause an alarm.
-
-tempX_offset           Read/write the temperature adjustment offset
-
-tempX_crit             Read/write the THERM limit for remote1.
-
-tempX_crit_hyst                Set the temperature value below crit where the
-                       fans will stay on - this helps drive the temperature
-                       low enough so it doesn't stay near the edge and
-                       cause THERM to keep tripping.
-
-tempX_auto_point1_temp Read/write the minimum temperature where the fans will
-                       turn on in automatic mode.
-
-tempX_auto_point2_temp Read/write the maximum temperature over which the fans
-                       will run in automatic mode.  tempX_auto_point1_temp
-                       and tempX_auto_point2_temp together define the
-                       range of automatic control.
-
-tempX_alarm            Read a 1 if the max/min alarm is set
-tempX_fault            Read a 1 if either temp1 or temp3 diode has a fault
-
-(There are two voltage settings, in1 and in2):
-
-inX_input              Read the current voltage on VCC.  Value is in
-                       millivolts.
-
-inX_min                        read/write the minimum voltage limit.
-                       Dropping below this causes an alarm.
-
-inX_max                        read/write the maximum voltage limit.
-                       Exceeding this causes an alarm.
-
-inX_alarm              Read a 1 if the max/min alarm is set.
+Kernel driver adt7475
+=====================
+
+Supported chips:
+  * Analog Devices ADT7473
+    Prefix: 'adt7473'
+    Addresses scanned: I2C 0x2C, 0x2D, 0x2E
+    Datasheet: Publicly available at the On Semiconductors website
+  * Analog Devices ADT7475
+    Prefix: 'adt7475'
+    Addresses scanned: I2C 0x2E
+    Datasheet: Publicly available at the On Semiconductors website
+  * Analog Devices ADT7476
+    Prefix: 'adt7476'
+    Addresses scanned: I2C 0x2C, 0x2D, 0x2E
+    Datasheet: Publicly available at the On Semiconductors website
+  * Analog Devices ADT7490
+    Prefix: 'adt7490'
+    Addresses scanned: I2C 0x2C, 0x2D, 0x2E
+    Datasheet: Publicly available at the On Semiconductors website
+
+Authors:
+       Jordan Crouse
+       Hans de Goede
+       Darrick J. Wong (documentation)
+       Jean Delvare
+
+
+Description
+-----------
+
+This driver implements support for the Analog Devices ADT7473, ADT7475,
+ADT7476 and ADT7490 chip family. The ADT7473 and ADT7475 differ only in
+minor details. The ADT7476 has additional features, including extra voltage
+measurement inputs and VID support. The ADT7490 also has additional
+features, including extra voltage measurement inputs and PECI support. All
+the supported chips will be collectively designed by the name "ADT747x" in
+the rest of this document.
+
+The ADT747x uses the 2-wire interface compatible with the SMBus 2.0
+specification. Using an analog to digital converter it measures three (3)
+temperatures and two (2) or more voltages. It has four (4) 16-bit counters
+for measuring fan speed. There are three (3) PWM outputs that can be used
+to control fan speed.
+
+A sophisticated control system for the PWM outputs is designed into the
+ADT747x that allows fan speed to be adjusted automatically based on any of the
+three temperature sensors. Each PWM output is individually adjustable and
+programmable. Once configured, the ADT747x will adjust the PWM outputs in
+response to the measured temperatures without further host intervention.
+This feature can also be disabled for manual control of the PWM's.
+
+Each of the measured inputs (voltage, temperature, fan speed) has
+corresponding high/low limit values. The ADT747x will signal an ALARM if
+any measured value exceeds either limit.
+
+The ADT747x samples all inputs continuously. The driver will not read
+the registers more often than once every other second. Further,
+configuration data is only read once per minute.
+
+Chip Differences Summary
+------------------------
+
+ADT7473:
+  * 2 voltage inputs
+  * system acoustics optimizations (not implemented)
+
+ADT7475:
+  * 2 voltage inputs
+
+ADT7476:
+  * 5 voltage inputs
+  * VID support
+
+ADT7490:
+  * 6 voltage inputs
+  * 1 Imon input (not implemented)
+  * PECI support (not implemented)
+  * 2 GPIO pins (not implemented)
+  * system acoustics optimizations (not implemented)
+
+Special Features
+----------------
+
+The ADT747x has a 10-bit ADC and can therefore measure temperatures
+with a resolution of 0.25 degree Celsius. Temperature readings can be
+configured either for two's complement format or "Offset 64" format,
+wherein 64 is subtracted from the raw value to get the temperature value.
+
+The datasheet is very detailed and describes a procedure for determining
+an optimal configuration for the automatic PWM control.
+
+Fan Speed Control
+-----------------
+
+The driver exposes two trip points per PWM channel.
+
+point1: Set the PWM speed at the lower temperature bound
+point2: Set the PWM speed at the higher temperature bound
+
+The ADT747x will scale the PWM linearly between the lower and higher PWM
+speed when the temperature is between the two temperature boundaries.
+Temperature boundaries are associated to temperature channels rather than
+PWM outputs, and a given PWM output can be controlled by several temperature
+channels. As a result, the ADT747x may compute more than one PWM value
+for a channel at a given time, in which case the maximum value (fastest
+fan speed) is applied. PWM values range from 0 (off) to 255 (full speed).
+
+Fan speed may be set to maximum when the temperature sensor associated with
+the PWM control exceeds temp#_max.
+
+Notes
+-----
+
+The nVidia binary driver presents an ADT7473 chip via an on-card i2c bus.
+Unfortunately, they fail to set the i2c adapter class, so this driver may
+fail to find the chip until the nvidia driver is patched.
index bee4c30..a7952c2 100644 (file)
@@ -14,6 +14,10 @@ Supported chips:
     Prefix: 'f71882fg'
     Addresses scanned: none, address read from Super I/O config space
     Datasheet: Available from the Fintek website
+  * Fintek F71889FG
+    Prefix: 'f71889fg'
+    Addresses scanned: none, address read from Super I/O config space
+    Datasheet: Should become available on the Fintek website soon
   * Fintek F8000
     Prefix: 'f8000'
     Addresses scanned: none, address read from Super I/O config space
@@ -51,6 +55,12 @@ supported. The right one to use depends on external circuitry on the
 motherboard, so the driver assumes that the BIOS set the method
 properly.
 
+Note that the lowest numbered temperature zone trip point corresponds to
+to the border between the highest and one but highest temperature zones, and
+vica versa. So the temperature zone trip points 1-4 (or 1-2) go from high temp
+to low temp! This is how things are implemented in the IC, and the driver
+mimicks this.
+
 There are 2 modes to specify the speed of the fan, PWM duty cycle (or DC
 voltage) mode, where 0-100% duty cycle (0-100% of 12V) is specified. And RPM
 mode where the actual RPM of the fan (as measured) is controlled and the speed
index 659315d..f9ba96c 100644 (file)
@@ -86,7 +86,6 @@ The IT8712F and IT8716F additionally feature VID inputs, used to report
 the Vcore voltage of the processor. The early IT8712F have 5 VID pins,
 the IT8716F and late IT8712F have 6. They are shared with other functions
 though, so the functionality may not be available on a given system.
-The driver dumbly assume it is there.
 
 The IT8718F and IT8720F also features VID inputs (up to 8 pins) but the value
 is stored in the Super-I/O configuration space. Due to technical limitations,
diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc
new file mode 100644 (file)
index 0000000..044531a
--- /dev/null
@@ -0,0 +1,50 @@
+Kernel driver mc13783-adc
+=========================
+
+Supported chips:
+  * Freescale Atlas MC13783
+    Prefix: 'mc13783_adc'
+    Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1
+
+Authors:
+    Sascha Hauer <s.hauer@pengutronix.de>
+    Luotao Fu <l.fu@pengutronix.de>
+
+Description
+-----------
+
+The Freescale MC13783 is a Power Management and Audio Circuit. Among
+other things it contains a 10-bit A/D converter. The converter has 16
+channels which can be used in different modes.
+The A/D converter has a resolution of 2.25mV. Channels 0-4 have
+a dedicated meaning with chip internal scaling applied. Channels 5-7
+can be used as general purpose inputs or alternatively in a dedicated
+mode. Channels 12-15 are occupied by the touchscreen if it's active.
+
+Currently the driver only supports channels 2 and 5-15 with no alternative
+modes for channels 5-7.
+
+See this table for the meaning of the different channels and their chip
+internal scaling:
+
+Channel        Signal                                          Input Range     Scaling
+-------------------------------------------------------------------------------
+0      Battery Voltage (BATT)                          2.50 - 4.65V    -2.40V
+1      Battery Current (BATT - BATTISNS)               -50 - 50 mV     x20
+2      Application Supply (BP)                         2.50 - 4.65V    -2.40V
+3      Charger Voltage (CHRGRAW)                       0 - 10V /       /5
+                                                       0 - 20V         /10
+4      Charger Current (CHRGISNSP-CHRGISNSN)           -0.25V - 0.25V  x4
+5      General Purpose ADIN5 / Battery Pack Thermistor 0 - 2.30V       No
+6      General Purpose ADIN6 / Backup Voltage (LICELL) 0 - 2.30V /     No /
+                                                       1.50 - 3.50V    -1.20V
+7      General Purpose ADIN7 / UID / Die Temperature   0 - 2.30V /     No /
+                                                       0 - 2.55V /     x0.9 / No
+8      General Purpose ADIN8                           0 - 2.30V       No
+9      General Purpose ADIN9                           0 - 2.30V       No
+10     General Purpose ADIN10                          0 - 2.30V       No
+11     General Purpose ADIN11                          0 - 2.30V       No
+12     General Purpose TSX1 / Touchscreen X-plate 1    0 - 2.30V       No
+13     General Purpose TSX2 / Touchscreen X-plate 2    0 - 2.30V       No
+14     General Purpose TSY1 / Touchscreen Y-plate 1    0 - 2.30V       No
+15     General Purpose TSY2 / Touchscreen Y-plate 2    0 - 2.30V       No
index 82def88..3de6b0b 100644 (file)
@@ -225,8 +225,6 @@ pwm[1-*]_auto_point[1-*]_temp_hyst
                to PWM output channels.
                RW
 
-OR
-
 temp[1-*]_auto_point[1-*]_pwm
 temp[1-*]_auto_point[1-*]_temp
 temp[1-*]_auto_point[1-*]_temp_hyst
@@ -235,6 +233,15 @@ temp[1-*]_auto_point[1-*]_temp_hyst
                to temperature channels.
                RW
 
+There is a third case where trip points are associated to both PWM output
+channels and temperature channels: the PWM values are associated to PWM
+output channels while the temperature values are associated to temperature
+channels. In that case, the result is determined by the mapping between
+temperature inputs and PWM outputs. When several temperature inputs are
+mapped to a given PWM output, this leads to several candidate PWM values.
+The actual result is up to the chip, but in general the highest candidate
+value (fastest fan speed) wins.
+
 
 ****************
 * Temperatures *
index 6ee36db..44dd2bc 100644 (file)
@@ -32,8 +32,6 @@ Authors:
 Module Parameters
 -----------------
 
-* force_addr: int
-  Initialize the ISA address of the sensors
 * force_i2c: int
   Initialize the I2C address of the sensors
 * init: int
@@ -70,3 +68,30 @@ doesn't help, you may just ignore the bogus VID reading with no harm done.
 For further information on this driver see the w83781d driver documentation.
 
 [1] http://www.lm-sensors.org/browser/lm-sensors/trunk/doc/vid
+
+Forcing the address
+-------------------
+
+The driver used to have a module parameter named force_addr, which could
+be used to force the base I/O address of the hardware monitoring block.
+This was meant as a workaround for mainboards with a broken BIOS. This
+module parameter is gone for technical reasons. If you need this feature,
+you can obtain the same result by using the isaset tool (part of
+lm-sensors) before loading the driver:
+
+# Enter the Super I/O config space
+isaset -y -f 0x2e 0x87
+isaset -y -f 0x2e 0x87
+
+# Select the hwmon logical device
+isaset -y 0x2e 0x2f 0x07 0x0b
+
+# Set the base I/O address (to 0x290 in this example)
+isaset -y 0x2e 0x2f 0x60 0x02
+isaset -y 0x2e 0x2f 0x61 0x90
+
+# Exit the Super-I/O config space
+isaset -y -f 0x2e 0xaa
+
+The above sequence assumes a Super-I/O config space at 0x2e/0x2f, but
+0x4e/0x4f is also possible.
index 098de5b..4220851 100644 (file)
@@ -304,7 +304,7 @@ static void *map_zeroed_pages(unsigned int num)
        addr = mmap(NULL, getpagesize() * num,
                    PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0);
        if (addr == MAP_FAILED)
-               err(1, "Mmaping %u pages of /dev/zero", num);
+               err(1, "Mmapping %u pages of /dev/zero", num);
 
        /*
         * One neat mmap feature is that you can close the fd, and it
index b152e81..c10c022 100644 (file)
@@ -257,6 +257,8 @@ characters, each representing a particular tainted value.
 
  10: 'W' if a warning has previously been issued by the kernel.
 
+ 11: 'C' if a staging driver has been loaded.
+
 The primary reason for the 'Tainted: ' string is to tell kernel
 debuggers if this is a clean kernel or if anything unusual has
 occurred.  Tainting is permanent: even if an offending module is
index 5f83fd2..cdebb51 100644 (file)
@@ -104,7 +104,7 @@ to set the limit to 500mA when supplying power.
 
 Consumers can control their supply current limit by calling :-
 
-int regulator_set_current_limit(regulator, min_uV, max_uV);
+int regulator_set_current_limit(regulator, min_uA, max_uA);
 
 Where min_uA and max_uA are the minimum and maximum acceptable current limit in
 microamps.
index c851ef4..17ffa06 100644 (file)
@@ -1,3 +1,65 @@
+1 Release Date    : Tues.  July 28, 2009 10:12:45 PST 2009 -
+                       (emaild-id:megaraidlinux@lsi.com)
+                       Bo Yang
+
+2 Current Version : 00.00.04.12
+3 Older Version   : 00.00.04.10
+
+1.     Change the AEN sys PD update from scsi_scan to
+       scsi_add_device and scsi_remove_device.
+2.     Takeoff the debug print-out in aen_polling routine.
+
+1 Release Date    : Thur.  July 02, 2009 10:12:45 PST 2009 -
+                       (emaild-id:megaraidlinux@lsi.com)
+                       Bo Yang
+
+2 Current Version : 00.00.04.10
+3 Older Version   : 00.00.04.08
+
+1.     Add the 3 mins timeout during the controller initialize.
+2.     Add the fix for 64bit sense date errors.
+
+1 Release Date    : Tues. May 05, 2009 10:12:45 PST 2009 -
+                       (emaild-id:megaraidlinux@lsi.com)
+                       Bo Yang
+
+2 Current Version : 00.00.04.08
+3 Older Version   : 00.00.04.06
+
+1.     Add the fix of pending in FW after deleted the logic drives.
+2.     Add the fix of deallocating memory after get pdlist.
+
+1 Release Date    : Tues. March 26, 2009 10:12:45 PST 2009 -
+                       (emaild-id:megaraidlinux@lsi.com)
+                       Bo Yang
+
+2 Current Version : 00.00.04.06
+3 Older Version   : 00.00.04.04
+
+1.     Add the fix of the driver cmd empty fix of the driver cmd empty.
+2.     Add the fix of the driver MSM AEN CMD cause the system slow.
+
+1 Release Date    : Tues. March 03, 2009 10:12:45 PST 2009 -
+                       (emaild-id:megaraidlinux@lsi.com)
+                       Bo Yang
+
+2 Current Version : 00.00.04.04
+3 Older Version   : 00.00.04.01
+
+1.     Add the Tape drive fix to the driver: If the command is for
+       the tape device, set the pthru timeout to the os layer timeout value.
+
+2.     Add Poll_wait mechanism to Gen-2 Linux driv.
+               In the aen handler, driver needs to wakeup poll handler similar to
+               the way it raises SIGIO.
+
+3.     Add new controller new SAS2 support to the driver.
+
+4.     Report the unconfigured PD (system PD) to OS.
+
+5.     Add the IEEE SGL support to the driver
+
+6.     Reasign the Application cmds to SAS2 controller
 
 1 Release Date    : Thur.July. 24 11:41:51 PST 2008 -
                        (emaild-id:megaraidlinux@lsi.com)
@@ -185,7 +247,7 @@ ii. FW enables WCE bit in Mode Sense cmd for drives that are configured
        Disks are exposed with WCE=1. User is advised to enable Write Back
        mode only when the controller has battery backup. At this time
        Synhronize cache is not supported by the FW. Driver will short-cycle
-       the cmd and return sucess without sending down to FW.
+       the cmd and return success without sending down to FW.
 
 1 Release Date    : Sun Jan. 14 11:21:32 PDT 2007 -
                 Sumant Patro <Sumant.Patro@lsil.com>/Bo Yang
index 9000cd8..e93afff 100644 (file)
@@ -126,6 +126,7 @@ ALC882/883/885/888/889
   mb5          Macbook 5,1
   mbp3         Macbook Pro rev3
   imac24       iMac 24'' with jack detection
+  imac91       iMac 9,1
   w2jc         ASUS W2JC
   3stack-2ch-dig       3-jack with SPDIF I/O (ALC883)
   alc883-6stack-dig    6-jack digital with SPDIF I/O (ALC883)
index 7b8a5f9..6325bec 100644 (file)
@@ -624,11 +624,13 @@ hda-verb.  The program gives you an easy-to-use GUI stuff for showing
 the widget information and adjusting the amp values, as well as the
 proc-compatible output.
 
-The hda-analyzer is a part of alsa.git repository in
-alsa-project.org:
+The hda-analyzer:
 
 - http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
 
+is a part of alsa.git repository in alsa-project.org:
+
+- git://git.alsa-project.org/alsa.git
 
 Codecgraph
 ~~~~~~~~~~
index deab51d..4884cb3 100644 (file)
@@ -538,7 +538,7 @@ SPI MESSAGE QUEUE
 The bulk of the driver will be managing the I/O queue fed by transfer().
 
 That queue could be purely conceptual.  For example, a driver used only
-for low-frequency sensor acess might be fine using synchronous PIO.
+for low-frequency sensor access might be fine using synchronous PIO.
 
 But the queue will probably be very real, using message->queue, PIO,
 often DMA (especially if the root filesystem is in SPI flash), and
index a028b92..3894eaa 100644 (file)
@@ -19,6 +19,8 @@ Currently, these files might (depending on your configuration)
 show up in /proc/sys/kernel:
 - acpi_video_flags
 - acct
+- bootloader_type           [ X86 only ]
+- bootloader_version        [ X86 only ]
 - callhome                  [ S390 only ]
 - auto_msgmni
 - core_pattern
@@ -93,6 +95,35 @@ valid for 30 seconds.
 
 ==============================================================
 
+bootloader_type:
+
+x86 bootloader identification
+
+This gives the bootloader type number as indicated by the bootloader,
+shifted left by 4, and OR'd with the low four bits of the bootloader
+version.  The reason for this encoding is that this used to match the
+type_of_loader field in the kernel header; the encoding is kept for
+backwards compatibility.  That is, if the full bootloader type number
+is 0x15 and the full version number is 0x234, this file will contain
+the value 340 = 0x154.
+
+See the type_of_loader and ext_loader_type fields in
+Documentation/x86/boot.txt for additional information.
+
+==============================================================
+
+bootloader_version:
+
+x86 bootloader version
+
+The complete bootloader version number.  In the example above, this
+file will contain the value 564 = 0x234.
+
+See the type_of_loader and ext_loader_ver fields in
+Documentation/x86/boot.txt for additional information.
+
+==============================================================
+
 callhome:
 
 Controls the kernel's callhome behavior in case of a kernel panic.
@@ -139,9 +170,9 @@ core_pattern is used to specify a core dumpfile pattern name.
 core_pipe_limit:
 
 This sysctl is only applicable when core_pattern is configured to pipe core
-files to user space helper a (when the first character of core_pattern is a '|',
+files to a user space helper (when the first character of core_pattern is a '|',
 see above).  When collecting cores via a pipe to an application, it is
-occasionally usefull for the collecting application to gather data about the
+occasionally useful for the collecting application to gather data about the
 crashing process from its /proc/pid directory.  In order to do this safely, the
 kernel must wait for the collecting process to exit, so as not to remove the
 crashing processes proc files prematurely.  This in turn creates the possibility
@@ -152,7 +183,7 @@ applications in parallel.  If this value is exceeded, then those crashing
 processes above that value are noted via the kernel log and their cores are
 skipped.  0 is a special value, indicating that unlimited processes may be
 captured in parallel, but that no waiting will take place (i.e. the collecting
-process is not guaranteed access to /proc/<crahing pid>/).  This value defaults
+process is not guaranteed access to /proc/<crashing pid>/).  This value defaults
 to 0.
 
 ==============================================================
index a6e360d..fc5790d 100644 (file)
@@ -370,7 +370,7 @@ The default is 1 percent.
 mmap_min_addr
 
 This file indicates the amount of address space  which a user process will
-be restricted from mmaping.  Since kernel null dereference bugs could
+be restricted from mmapping.  Since kernel null dereference bugs could
 accidentally operate based on the information in the first couple of pages
 of memory userspace processes should not be allowed to write to them.  By
 default this value is set to 0 and no protections will be enforced by the
index 04763a3..16d25e6 100644 (file)
@@ -3,7 +3,7 @@
 The High Precision Event Timer (HPET) hardware follows a specification
 by Intel and Microsoft which can be found at
 
-       http://www.intel.com/technology/architecture/hpetspec.htm
+       http://www.intel.com/hardwaredesign/hpetspec_1.pdf
 
 Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision")
 and up to 32 comparators.  Normally three or more comparators are provided,
index 5f33d84..7539e8f 100644 (file)
@@ -24,3 +24,5 @@
  23 -> Magic-Pro ProHDTV Extreme 2                         [14f1:8657]
  24 -> Hauppauge WinTV-HVR1850                             [0070:8541]
  25 -> Compro VideoMate E800                               [1858:e800]
+ 26 -> Hauppauge WinTV-HVR1290                             [0070:8551]
+ 27 -> Mygica X8558 PRO DMB-TH                             [14f1:8578]
index 3385f8b..7ec3c4e 100644 (file)
@@ -81,3 +81,4 @@
  80 -> Hauppauge WinTV-IR Only                             [0070:9290]
  81 -> Leadtek WinFast DTV1800 Hybrid                      [107d:6654]
  82 -> WinFast DTV2000 H rev. J                            [107d:6f2b]
+ 83 -> Prof 7301 DVB-S/S2                                  [b034:3034]
index b8afef4..0c166ff 100644 (file)
@@ -1,5 +1,5 @@
   0 -> Unknown EM2800 video grabber             (em2800)        [eb1a:2800]
-  1 -> Unknown EM2750/28xx video grabber        (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883,eb1a:2868]
+  1 -> Unknown EM2750/28xx video grabber        (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2862,eb1a:2870,eb1a:2881,eb1a:2883,eb1a:2868]
   2 -> Terratec Cinergy 250 USB                 (em2820/em2840) [0ccd:0036]
   3 -> Pinnacle PCTV USB 2                      (em2820/em2840) [2304:0208]
   4 -> Hauppauge WinTV USB 2                    (em2820/em2840) [2040:4200,2040:4201]
@@ -69,3 +69,4 @@
  71 -> Silvercrest Webcam 1.3mpix               (em2820/em2840)
  72 -> Gadmei UTV330+                           (em2861)
  73 -> Reddo DVB-C USB TV Box                   (em2870)
+ 74 -> Actionmaster/LinXcel/Digitus VC211A      (em2800)
index 2620d60..fce1e7e 100644 (file)
 171 -> Beholder BeholdTV X7                     [5ace:7595]
 172 -> RoverMedia TV Link Pro FM                [19d1:0138]
 173 -> Zolid Hybrid TV Tuner PCI                [1131:2004]
+174 -> Asus Europa Hybrid OEM                   [1043:4847]
+175 -> Leadtek Winfast DTV1000S                 [107d:6655]
index 3f61825..319d983 100644 (file)
@@ -6,7 +6,8 @@ The modules are:
 
 xxxx           vend:prod
 ----
-spca501                0000:0000       MystFromOri Unknow Camera
+spca501                0000:0000       MystFromOri Unknown Camera
+spca508                0130:0130       Clone Digital Webcam 11043
 m5602          0402:5602       ALi Video Camera Controller
 spca501                040a:0002       Kodak DVC-325
 spca500                040a:0300       Kodak EZ200
@@ -37,6 +38,7 @@ ov519         041e:405f       Creative Live! VISTA VF0330
 ov519          041e:4060       Creative Live! VISTA VF0350
 ov519          041e:4061       Creative Live! VISTA VF0400
 ov519          041e:4064       Creative Live! VISTA VF0420
+ov519          041e:4067       Creative Live! Cam Video IM (VF0350)
 ov519          041e:4068       Creative Live! VISTA VF0470
 spca561                0458:7004       Genius VideoCAM Express V2
 sunplus                0458:7006       Genius Dsc 1.3 Smart
@@ -68,12 +70,12 @@ zc3xx               046d:08a3       Logitech QC Chat
 zc3xx          046d:08a6       Logitech QCim
 zc3xx          046d:08a7       Logitech QuickCam Image
 zc3xx          046d:08a9       Logitech Notebook Deluxe
-zc3xx          046d:08aa       Labtec Webcam  Notebook
+zc3xx          046d:08aa       Labtec Webcam Notebook
 zc3xx          046d:08ac       Logitech QuickCam Cool
 zc3xx          046d:08ad       Logitech QCCommunicate STX
 zc3xx          046d:08ae       Logitech QuickCam for Notebooks
 zc3xx          046d:08af       Logitech QuickCam Cool
-zc3xx          046d:08b9       Logitech QC IM ???
+zc3xx          046d:08b9       Logitech QuickCam Express
 zc3xx          046d:08d7       Logitech QCam STX
 zc3xx          046d:08d9       Logitech QuickCam IM/Connect
 zc3xx          046d:08d8       Logitech Notebook Deluxe
@@ -82,7 +84,7 @@ zc3xx         046d:08dd       Logitech QuickCam for Notebooks
 spca500                046d:0900       Logitech Inc. ClickSmart 310
 spca500                046d:0901       Logitech Inc. ClickSmart 510
 sunplus                046d:0905       Logitech ClickSmart 820
-tv8532         046d:0920       QC Express
+tv8532         046d:0920       Logitech QuickCam Express
 tv8532         046d:0921       Labtec Webcam
 spca561                046d:0928       Logitech QC Express Etch2
 spca561                046d:0929       Labtec Webcam Elch2
@@ -91,7 +93,7 @@ spca561               046d:092b       Labtec Webcam Plus
 spca561                046d:092c       Logitech QC chat Elch2
 spca561                046d:092d       Logitech QC Elch2
 spca561                046d:092e       Logitech QC Elch2
-spca561                046d:092f       Logitech  QuickCam Express Plus
+spca561                046d:092f       Logitech QuickCam Express Plus
 sunplus                046d:0960       Logitech ClickSmart 420
 sunplus                0471:0322       Philips DMVC1300K
 zc3xx          0471:0325       Philips SPC 200 NC
@@ -187,7 +189,6 @@ sonixj              06f8:3004       Hercules Classic Silver
 sonixj         06f8:3008       Hercules Deluxe Optical Glass
 pac7311                06f8:3009       Hercules Classic Link
 spca508                0733:0110       ViewQuest VQ110
-spca508                0130:0130       Clone Digital Webcam 11043
 spca501                0733:0401       Intel Create and Share
 spca501                0733:0402       ViewQuest M318B
 spca505                0733:0430       Intel PC Camera Pro
@@ -202,6 +203,7 @@ spca500             084d:0003       D-Link DSC-350
 spca500                08ca:0103       Aiptek PocketDV
 sunplus                08ca:0104       Aiptek PocketDVII 1.3
 sunplus                08ca:0106       Aiptek Pocket DV3100+
+mr97310a       08ca:0110       Trust Spyc@m 100
 mr97310a       08ca:0111       Aiptek PenCam VGA+
 sunplus                08ca:2008       Aiptek Mini PenCam 2 M
 sunplus                08ca:2010       Aiptek PocketCam 3M
@@ -222,7 +224,7 @@ pac207              093a:2460       Qtec Webcam 100
 pac207         093a:2461       HP Webcam
 pac207         093a:2463       Philips SPC 220 NC
 pac207         093a:2464       Labtec Webcam 1200
-pac207         093a:2468       PAC207
+pac207         093a:2468       Webcam WB-1400T
 pac207         093a:2470       Genius GF112
 pac207         093a:2471       Genius VideoCam ge111
 pac207         093a:2472       Genius VideoCam ge110
@@ -230,7 +232,7 @@ pac207              093a:2474       Genius iLook 111
 pac207         093a:2476       Genius e-Messenger 112
 pac7311                093a:2600       PAC7311 Typhoon
 pac7311                093a:2601       Philips SPC 610 NC
-pac7311                093a:2603       PAC7312
+pac7311                093a:2603       Philips SPC 500 NC
 pac7311                093a:2608       Trust WB-3300p
 pac7311                093a:260e       Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350
 pac7311                093a:260f       SnakeCam
@@ -239,6 +241,7 @@ pac7311             093a:2621       PAC731x
 pac7311                093a:2622       Genius Eye 312
 pac7311                093a:2624       PAC7302
 pac7311                093a:2626       Labtec 2200
+pac7311                093a:2628       Genius iLook 300
 pac7311                093a:2629       Genious iSlim 300
 pac7311                093a:262a       Webcam 300k
 pac7311                093a:262c       Philips SPC 230 NC
@@ -250,7 +253,7 @@ vc032x              0ac8:0328       A4Tech PK-130MG
 zc3xx          0ac8:301b       Z-Star zc301b
 zc3xx          0ac8:303b       Vimicro 0x303b
 zc3xx          0ac8:305b       Z-star Vimicro zc0305b
-zc3xx          0ac8:307b       Ldlc VC302+Ov7620
+zc3xx          0ac8:307b       PC Camera (ZS0211)
 vc032x         0ac8:c001       Sony embedded vimicro
 vc032x         0ac8:c002       Sony embedded vimicro
 vc032x         0ac8:c301       Samsung Q1 Ultra Premium
index 25abdb7..2e7392a 100644 (file)
@@ -164,7 +164,7 @@ Stereo/Mono and RDS subchannels
 
 The device can also be configured using the available sub channels for
 transmission. To do that use S/G_MODULATOR ioctl and configure txsubchans properly.
-Refer to v4l2-spec for proper use of this ioctl.
+Refer to the V4L2 API specification for proper use of this ioctl.
 
 Testing
 =======
index 7f3d195..d98e4d3 100644 (file)
@@ -66,3 +66,4 @@ Vendor  Product  Distributor     Model
 0x0a17  0x004e   Pentax          Optio 50
 0x041e  0x405d   Creative        DiVi CAM 516
 0x08ca  0x2102   Aiptek          DV T300
+0x06d6  0x003d   Trust           Powerc@m 910Z
index 4793c6a..ea44ea5 100644 (file)
@@ -301,7 +301,7 @@ static char *page_flag_name(uint64_t flags)
                present = (flags >> i) & 1;
                if (!page_flag_names[i]) {
                        if (present)
-                               fatal("unkown flag bit %d\n", i);
+                               fatal("unknown flag bit %d\n", i);
                        continue;
                }
                buf[j++] = present ? page_flag_names[i][0] : '_';
index 510917f..b37300e 100644 (file)
@@ -245,7 +245,7 @@ been overwritten. Here a string of 8 characters was written into a slab that
 has the length of 8 characters. However, a 8 character string needs a
 terminating 0. That zero has overwritten the first byte of the Redzone field.
 After reporting the details of the issue encountered the FIX SLUB message
-tell us that SLUB has restored the Redzone to its proper value and then
+tells us that SLUB has restored the Redzone to its proper value and then
 system operations continue.
 
 Emergency operations:
index ea781c1..98d5ca1 100644 (file)
@@ -327,6 +327,13 @@ M: Colin Leroy <colin@colino.net>
 S:     Maintained
 F:     drivers/macintosh/therm_adt746x.c
 
+ADT7475 HARDWARE MONITOR DRIVER
+M:     Jean Delvare <khali@linux-fr.org>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     Documentation/hwmon/adt7475
+F:     drivers/hwmon/adt7475.c
+
 ADVANSYS SCSI DRIVER
 M:     Matthew Wilcox <matthew@wil.cx>
 L:     linux-scsi@vger.kernel.org
@@ -486,13 +493,6 @@ S: Maintained
 F:     drivers/net/appletalk/
 F:     net/appletalk/
 
-APPLETOUCH TOUCHPAD DRIVER
-M:     Johannes Berg <johannes@sipsolutions.net>
-L:     linux-input@vger.kernel.org
-S:     Maintained
-F:     Documentation/input/appletouch.txt
-F:     drivers/input/mouse/appletouch.c
-
 ARC FRAMEBUFFER DRIVER
 M:     Jaya Kumar <jayalk@intworks.biz>
 S:     Maintained
@@ -2423,7 +2423,9 @@ HARDWARE MONITORING
 L:     lm-sensors@lm-sensors.org
 W:     http://www.lm-sensors.org/
 S:     Orphan
+F:     Documentation/hwmon/
 F:     drivers/hwmon/
+F:     include/linux/hwmon*.h
 
 HARDWARE RANDOM NUMBER GENERATOR CORE
 M:     Matt Mackall <mpm@selenic.com>
@@ -3139,6 +3141,7 @@ S:        Supported
 F:     Documentation/s390/kvm.txt
 F:     arch/s390/include/asm/kvm*
 F:     arch/s390/kvm/
+F:     drivers/s390/kvm/
 
 KEXEC
 M:     Eric Biederman <ebiederm@xmission.com>
@@ -3318,6 +3321,12 @@ S:       Maintained
 F:     Documentation/hwmon/lis3lv02d
 F:     drivers/hwmon/lis3lv02d.*
 
+LM73 HARDWARE MONITOR DRIVER
+M:     Guillaume Ligneul <guillaume.ligneul@gmail.com>
+L:     lm-sensors@lm-sensors.org
+S:     Maintained
+F:     drivers/hwmon/lm73.c
+
 LM83 HARDWARE MONITOR DRIVER
 M:     Jean Delvare <khali@linux-fr.org>
 L:     lm-sensors@lm-sensors.org
@@ -3894,6 +3903,23 @@ L:       linux-omap@vger.kernel.org
 S:     Maintained
 F:     drivers/video/omap/
 
+OMAP DISPLAY SUBSYSTEM SUPPORT (DSS2)
+M:     Tomi Valkeinen <tomi.valkeinen@nokia.com>
+L:     linux-omap@vger.kernel.org
+L:     linux-fbdev@vger.kernel.org (moderated for non-subscribers)
+S:     Maintained
+F:     drivers/video/omap2/dss/
+F:     drivers/video/omap2/vrfb.c
+F:     drivers/video/omap2/vram.c
+F:     Documentation/arm/OMAP/DSS
+
+OMAP FRAMEBUFFER SUPPORT (FOR DSS2)
+M:     Tomi Valkeinen <tomi.valkeinen@nokia.com>
+L:     linux-omap@vger.kernel.org
+L:     linux-fbdev@vger.kernel.org (moderated for non-subscribers)
+S:     Maintained
+F:     drivers/video/omap2/omapfb/
+
 OMAP MMC SUPPORT
 M:     Jarkko Lavinen <jarkko.lavinen@nokia.com>
 L:     linux-omap@vger.kernel.org
@@ -4195,6 +4221,13 @@ W:       http://www.pmc-sierra.com/
 S:     Supported
 F:     drivers/scsi/pmcraid.*
 
+PMC SIERRA PM8001 DRIVER
+M:     jack_wang@usish.com
+M:     lindar_liu@usish.com
+L:     linux-scsi@vger.kernel.org
+S:     Supported
+F:     drivers/scsi/pm8001/
+
 POSIX CLOCKS and TIMERS
 M:     Thomas Gleixner <tglx@linutronix.de>
 S:     Supported
@@ -4553,6 +4586,7 @@ L:        linux-s390@vger.kernel.org
 W:     http://www.ibm.com/developerworks/linux/linux390/
 S:     Supported
 F:     arch/s390/
+F:     drivers/s390/
 
 S390 NETWORK DRIVERS
 M:     Ursula Braun <ursula.braun@de.ibm.com>
@@ -4568,6 +4602,7 @@ M:        Felix Beck <felix.beck@de.ibm.com>
 M:     Ralph Wuerthner <ralph.wuerthner@de.ibm.com>
 M:     linux390@de.ibm.com
 L:     linux-s390@vger.kernel.org
+W:     http://www.ibm.com/developerworks/linux/linux390/
 S:     Supported
 F:     drivers/s390/crypto/
 
@@ -5317,7 +5352,7 @@ S:        Maintained
 F:     drivers/scsi/u14-34f.c
 
 UBI FILE SYSTEM (UBIFS)
-M:     Artem Bityutskiy <dedekind@infradead.org>
+M:     Artem Bityutskiy <dedekind1@gmail.com>
 M:     Adrian Hunter <adrian.hunter@nokia.com>
 L:     linux-mtd@lists.infradead.org
 T:     git git://git.infradead.org/ubifs-2.6.git
@@ -5368,7 +5403,7 @@ F:        drivers/cdrom/cdrom.c
 F:     include/linux/cdrom.h
 
 UNSORTED BLOCK IMAGES (UBI)
-M:     Artem Bityutskiy <dedekind@infradead.org>
+M:     Artem Bityutskiy <dedekind1@gmail.com>
 W:     http://www.linux-mtd.infradead.org/
 L:     linux-mtd@lists.infradead.org
 T:     git git://git.infradead.org/ubi-2.6.git
@@ -5769,6 +5804,14 @@ L:       netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/vmxnet3/
 
+VMware PVSCSI driver
+M:     Alok Kataria <akataria@vmware.com>
+M:     VMware PV-Drivers <pv-drivers@vmware.com>
+L:     linux-scsi@vger.kernel.org
+S:     Maintained
+F:     drivers/scsi/vmw_pvscsi.c
+F:     drivers/scsi/vmw_pvscsi.h
+
 VOLTAGE AND CURRENT REGULATOR FRAMEWORK
 M:     Liam Girdwood <lrg@slimlogic.co.uk>
 M:     Mark Brown <broonie@opensource.wolfsonmicro.com>
index 10b4035..7b2c56d 100644 (file)
@@ -197,7 +197,7 @@ setup_memory_node(int nid, void *kernel_end)
        }
 
        if (bootmap_start == -1)
-               panic("couldn't find a contigous place for the bootmap");
+               panic("couldn't find a contiguous place for the bootmap");
 
        /* Allocate the bootmap and mark the whole MM as reserved.  */
        bootmap_size = init_bootmem_node(NODE_DATA(nid), bootmap_start,
index 7713a08..37bda5f 100644 (file)
@@ -82,7 +82,7 @@ static int scoop_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
        struct scoop_dev *sdev = container_of(chip, struct scoop_dev, gpio);
 
-       /* XXX: I'm usure,  but it seems so */
+       /* XXX: I'm unsure, but it seems so */
        return ioread16(sdev->base + SCOOP_GPRR) & (1 << (offset + 1));
 }
 
diff --git a/arch/arm/configs/da830_omapl137_defconfig b/arch/arm/configs/da830_omapl137_defconfig
deleted file mode 100644 (file)
index 7c8e38f..0000000
+++ /dev/null
@@ -1,1254 +0,0 @@
-#
-# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-rc2-davinci1
-# Wed May 13 15:33:29 2009
-#
-CONFIG_ARM=y
-CONFIG_SYS_SUPPORTS_APM_EMULATION=y
-CONFIG_GENERIC_GPIO=y
-CONFIG_GENERIC_TIME=y
-CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_MMU=y
-# CONFIG_NO_IOPORT is not set
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_STACKTRACE_SUPPORT=y
-CONFIG_HAVE_LATENCYTOP_SUPPORT=y
-CONFIG_LOCKDEP_SUPPORT=y
-CONFIG_TRACE_IRQFLAGS_SUPPORT=y
-CONFIG_HARDIRQS_SW_RESEND=y
-CONFIG_GENERIC_IRQ_PROBE=y
-CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_GENERIC_HWEIGHT=y
-CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_ZONE_DMA=y
-CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
-CONFIG_VECTORS_BASE=0xffff0000
-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
-
-#
-# General setup
-#
-CONFIG_EXPERIMENTAL=y
-CONFIG_BROKEN_ON_SMP=y
-CONFIG_LOCK_KERNEL=y
-CONFIG_INIT_ENV_ARG_LIMIT=32
-CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
-# CONFIG_SWAP is not set
-CONFIG_SYSVIPC=y
-CONFIG_SYSVIPC_SYSCTL=y
-CONFIG_POSIX_MQUEUE=y
-CONFIG_POSIX_MQUEUE_SYSCTL=y
-# 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=14
-CONFIG_GROUP_SCHED=y
-CONFIG_FAIR_GROUP_SCHED=y
-# CONFIG_RT_GROUP_SCHED is not set
-CONFIG_USER_SCHED=y
-# CONFIG_CGROUP_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=""
-CONFIG_RD_GZIP=y
-# CONFIG_RD_BZIP2 is not set
-# CONFIG_RD_LZMA is not set
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
-CONFIG_SYSCTL=y
-CONFIG_ANON_INODES=y
-CONFIG_EMBEDDED=y
-CONFIG_UID16=y
-CONFIG_SYSCTL_SYSCALL=y
-CONFIG_KALLSYMS=y
-# CONFIG_KALLSYMS_ALL is not set
-# CONFIG_KALLSYMS_EXTRA_PASS is not set
-# CONFIG_STRIP_ASM_SYMS is not set
-CONFIG_HOTPLUG=y
-CONFIG_PRINTK=y
-CONFIG_BUG=y
-CONFIG_ELF_CORE=y
-CONFIG_BASE_FULL=y
-CONFIG_FUTEX=y
-CONFIG_EPOLL=y
-CONFIG_SIGNALFD=y
-CONFIG_TIMERFD=y
-CONFIG_EVENTFD=y
-CONFIG_SHMEM=y
-CONFIG_AIO=y
-CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_SLUB_DEBUG=y
-CONFIG_COMPAT_BRK=y
-# CONFIG_SLAB is not set
-CONFIG_SLUB=y
-# CONFIG_SLOB is not set
-# CONFIG_PROFILING is not set
-# CONFIG_MARKERS is not set
-CONFIG_HAVE_OPROFILE=y
-# CONFIG_KPROBES is not set
-CONFIG_HAVE_KPROBES=y
-CONFIG_HAVE_KRETPROBES=y
-CONFIG_HAVE_CLK=y
-# CONFIG_SLOW_WORK is not set
-CONFIG_HAVE_GENERIC_DMA_COHERENT=y
-CONFIG_SLABINFO=y
-CONFIG_RT_MUTEXES=y
-CONFIG_BASE_SMALL=0
-CONFIG_MODULES=y
-# CONFIG_MODULE_FORCE_LOAD is not set
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODULE_FORCE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-# 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 is not set
-# CONFIG_IOSCHED_CFQ is not set
-CONFIG_DEFAULT_AS=y
-# CONFIG_DEFAULT_DEADLINE is not set
-# CONFIG_DEFAULT_CFQ is not set
-# CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="anticipatory"
-# CONFIG_FREEZER is not set
-
-#
-# System Type
-#
-# CONFIG_ARCH_AAEC2000 is not set
-# CONFIG_ARCH_INTEGRATOR is not set
-# CONFIG_ARCH_REALVIEW is not set
-# CONFIG_ARCH_VERSATILE is not set
-# CONFIG_ARCH_AT91 is not set
-# CONFIG_ARCH_CLPS711X is not set
-# CONFIG_ARCH_EBSA110 is not set
-# CONFIG_ARCH_EP93XX is not set
-# CONFIG_ARCH_GEMINI is not set
-# CONFIG_ARCH_FOOTBRIDGE is not set
-# CONFIG_ARCH_NETX is not set
-# CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_IMX is not set
-# CONFIG_ARCH_IOP13XX is not set
-# CONFIG_ARCH_IOP32X is not set
-# CONFIG_ARCH_IOP33X is not set
-# CONFIG_ARCH_IXP23XX is not set
-# CONFIG_ARCH_IXP2000 is not set
-# CONFIG_ARCH_IXP4XX is not set
-# CONFIG_ARCH_L7200 is not set
-# CONFIG_ARCH_KIRKWOOD is not set
-# CONFIG_ARCH_KS8695 is not set
-# CONFIG_ARCH_NS9XXX is not set
-# CONFIG_ARCH_LOKI is not set
-# CONFIG_ARCH_MV78XX0 is not set
-# CONFIG_ARCH_MXC is not set
-# CONFIG_ARCH_ORION5X is not set
-# CONFIG_ARCH_PNX4008 is not set
-# CONFIG_ARCH_PXA is not set
-# CONFIG_ARCH_MMP is not set
-# CONFIG_ARCH_RPC is not set
-# CONFIG_ARCH_SA1100 is not set
-# CONFIG_ARCH_S3C2410 is not set
-# CONFIG_ARCH_S3C64XX is not set
-# CONFIG_ARCH_SHARK is not set
-# CONFIG_ARCH_LH7A40X is not set
-CONFIG_ARCH_DAVINCI=y
-# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM is not set
-# CONFIG_ARCH_W90X900 is not set
-CONFIG_CP_INTC=y
-
-#
-# TI DaVinci Implementations
-#
-
-#
-# DaVinci Core Type
-#
-# CONFIG_ARCH_DAVINCI_DM644x is not set
-# CONFIG_ARCH_DAVINCI_DM646x is not set
-# CONFIG_ARCH_DAVINCI_DM355 is not set
-CONFIG_ARCH_DAVINCI_DA830=y
-
-#
-# DaVinci Board Type
-#
-CONFIG_MACH_DAVINCI_DA830_EVM=y
-CONFIG_DAVINCI_MUX=y
-# CONFIG_DAVINCI_MUX_DEBUG is not set
-# CONFIG_DAVINCI_MUX_WARNINGS is not set
-CONFIG_DAVINCI_RESET_CLOCKS=y
-
-#
-# Processor Type
-#
-CONFIG_CPU_32=y
-CONFIG_CPU_ARM926T=y
-CONFIG_CPU_32v5=y
-CONFIG_CPU_ABRT_EV5TJ=y
-CONFIG_CPU_PABRT_NOIFAR=y
-CONFIG_CPU_CACHE_VIVT=y
-CONFIG_CPU_COPY_V4WB=y
-CONFIG_CPU_TLB_V4WBI=y
-CONFIG_CPU_CP15=y
-CONFIG_CPU_CP15_MMU=y
-
-#
-# Processor Features
-#
-CONFIG_ARM_THUMB=y
-# CONFIG_CPU_ICACHE_DISABLE is not set
-# CONFIG_CPU_DCACHE_DISABLE is not set
-CONFIG_CPU_DCACHE_WRITETHROUGH=y
-# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
-# CONFIG_OUTER_CACHE is not set
-CONFIG_COMMON_CLKDEV=y
-
-#
-# Bus support
-#
-# CONFIG_PCI_SYSCALL is not set
-# CONFIG_ARCH_SUPPORTS_MSI is not set
-# CONFIG_PCCARD is not set
-
-#
-# Kernel Features
-#
-CONFIG_TICK_ONESHOT=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
-CONFIG_VMSPLIT_3G=y
-# CONFIG_VMSPLIT_2G is not set
-# CONFIG_VMSPLIT_1G is not set
-CONFIG_PAGE_OFFSET=0xC0000000
-CONFIG_PREEMPT=y
-CONFIG_HZ=100
-CONFIG_AEABI=y
-# CONFIG_OABI_COMPAT is not set
-CONFIG_ARCH_FLATMEM_HAS_HOLES=y
-# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
-# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
-# CONFIG_HIGHMEM is not set
-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=4096
-# CONFIG_PHYS_ADDR_T_64BIT is not set
-CONFIG_ZONE_DMA_FLAG=1
-CONFIG_BOUNCE=y
-CONFIG_VIRT_TO_BUS=y
-CONFIG_UNEVICTABLE_LRU=y
-CONFIG_HAVE_MLOCK=y
-CONFIG_HAVE_MLOCKED_PAGE_BIT=y
-CONFIG_LEDS=y
-# CONFIG_LEDS_CPU is not set
-CONFIG_ALIGNMENT_TRAP=y
-
-#
-# Boot options
-#
-CONFIG_ZBOOT_ROM_TEXT=0x0
-CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE=""
-# CONFIG_XIP_KERNEL is not set
-# CONFIG_KEXEC is not set
-
-#
-# CPU Power Management
-#
-# CONFIG_CPU_IDLE is not set
-
-#
-# Floating point emulation
-#
-
-#
-# At least one emulation must be selected
-#
-# CONFIG_VFP is not set
-
-#
-# Userspace binary formats
-#
-CONFIG_BINFMT_ELF=y
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-CONFIG_HAVE_AOUT=y
-# CONFIG_BINFMT_AOUT is not set
-# CONFIG_BINFMT_MISC is not set
-
-#
-# Power management options
-#
-# CONFIG_PM is not set
-CONFIG_ARCH_SUSPEND_POSSIBLE=y
-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=y
-CONFIG_IP_PNP_DHCP=y
-# CONFIG_IP_PNP_BOOTP is not set
-# CONFIG_IP_PNP_RARP is not set
-# CONFIG_NET_IPIP is not set
-# CONFIG_NET_IPGRE is not set
-# CONFIG_ARPD is not set
-# CONFIG_SYN_COOKIES is not set
-# CONFIG_INET_AH is not set
-# CONFIG_INET_ESP is not set
-# CONFIG_INET_IPCOMP is not set
-# CONFIG_INET_XFRM_TUNNEL is not set
-CONFIG_INET_TUNNEL=m
-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=m
-# CONFIG_IPV6_PRIVACY is not set
-# CONFIG_IPV6_ROUTER_PREF is not set
-# CONFIG_IPV6_OPTIMISTIC_DAD is not set
-# CONFIG_INET6_AH is not set
-# CONFIG_INET6_ESP is not set
-# CONFIG_INET6_IPCOMP is not set
-# CONFIG_IPV6_MIP6 is not set
-# CONFIG_INET6_XFRM_TUNNEL is not set
-# CONFIG_INET6_TUNNEL is not set
-CONFIG_INET6_XFRM_MODE_TRANSPORT=m
-CONFIG_INET6_XFRM_MODE_TUNNEL=m
-CONFIG_INET6_XFRM_MODE_BEET=m
-# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
-CONFIG_IPV6_SIT=m
-CONFIG_IPV6_NDISC_NODETYPE=y
-# CONFIG_IPV6_TUNNEL is not set
-# CONFIG_IPV6_MULTIPLE_TABLES is not set
-# CONFIG_IPV6_MROUTE is not set
-# CONFIG_NETWORK_SECMARK is not set
-CONFIG_NETFILTER=y
-# CONFIG_NETFILTER_DEBUG is not set
-CONFIG_NETFILTER_ADVANCED=y
-
-#
-# Core Netfilter Configuration
-#
-# CONFIG_NETFILTER_NETLINK_QUEUE is not set
-# CONFIG_NETFILTER_NETLINK_LOG is not set
-# CONFIG_NF_CONNTRACK is not set
-# CONFIG_NETFILTER_XTABLES is not set
-# CONFIG_IP_VS is not set
-
-#
-# IP: Netfilter Configuration
-#
-# CONFIG_NF_DEFRAG_IPV4 is not set
-# CONFIG_IP_NF_QUEUE is not set
-# CONFIG_IP_NF_IPTABLES is not set
-# CONFIG_IP_NF_ARPTABLES is not set
-
-#
-# IPv6: Netfilter Configuration
-#
-# CONFIG_IP6_NF_QUEUE is not set
-# CONFIG_IP6_NF_IPTABLES 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_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_STANDALONE=y
-CONFIG_PREVENT_FIRMWARE_BUILD=y
-# CONFIG_FW_LOADER is not set
-# 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_PARPORT is not set
-CONFIG_BLK_DEV=y
-# CONFIG_BLK_DEV_COW_COMMON is not set
-CONFIG_BLK_DEV_LOOP=m
-# CONFIG_BLK_DEV_CRYPTOLOOP is not set
-# CONFIG_BLK_DEV_NBD is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_COUNT=1
-CONFIG_BLK_DEV_RAM_SIZE=32768
-# CONFIG_BLK_DEV_XIP is not set
-# CONFIG_CDROM_PKTCDVD is not set
-# CONFIG_ATA_OVER_ETH is not set
-CONFIG_MISC_DEVICES=y
-# CONFIG_ICS932S401 is not set
-# CONFIG_ENCLOSURE_SERVICES is not set
-# CONFIG_ISL29003 is not set
-# CONFIG_C2PORT is not set
-
-#
-# EEPROM support
-#
-CONFIG_EEPROM_AT24=y
-# CONFIG_EEPROM_LEGACY is not set
-# CONFIG_EEPROM_93CX6 is not set
-CONFIG_HAVE_IDE=y
-# CONFIG_IDE is not set
-
-#
-# SCSI device support
-#
-# CONFIG_RAID_ATTRS is not set
-CONFIG_SCSI=m
-CONFIG_SCSI_DMA=y
-# CONFIG_SCSI_TGT is not set
-# CONFIG_SCSI_NETLINK is not set
-CONFIG_SCSI_PROC_FS=y
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
-CONFIG_BLK_DEV_SD=m
-# CONFIG_CHR_DEV_ST is not set
-# CONFIG_CHR_DEV_OSST is not set
-# CONFIG_BLK_DEV_SR 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=y
-# CONFIG_ISCSI_TCP is not set
-# CONFIG_LIBFC is not set
-# CONFIG_LIBFCOE is not set
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_DH is not set
-# CONFIG_SCSI_OSD_INITIATOR 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=m
-# CONFIG_VETH is not set
-CONFIG_PHYLIB=y
-
-#
-# MII PHY device drivers
-#
-# CONFIG_MARVELL_PHY is not set
-# CONFIG_DAVICOM_PHY is not set
-# CONFIG_QSEMI_PHY is not set
-CONFIG_LXT_PHY=y
-# CONFIG_CICADA_PHY is not set
-# 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_NATIONAL_PHY is not set
-# CONFIG_STE10XP is not set
-CONFIG_LSI_ET1011C_PHY=y
-# CONFIG_FIXED_PHY is not set
-# CONFIG_MDIO_BITBANG is not set
-CONFIG_NET_ETHERNET=y
-CONFIG_MII=y
-# CONFIG_AX88796 is not set
-# CONFIG_SMC91X is not set
-CONFIG_TI_DAVINCI_EMAC=y
-# CONFIG_DM9000 is not set
-# CONFIG_ETHOC is not set
-# CONFIG_SMC911X is not set
-# CONFIG_SMSC911X 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 is not set
-# CONFIG_NETDEV_10000 is not set
-
-#
-# 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=y
-# CONFIG_NETCONSOLE_DYNAMIC is not set
-CONFIG_NETPOLL=y
-CONFIG_NETPOLL_TRAP=y
-CONFIG_NET_POLL_CONTROLLER=y
-# CONFIG_ISDN is not set
-
-#
-# Input device support
-#
-CONFIG_INPUT=y
-# CONFIG_INPUT_FF_MEMLESS is not set
-# CONFIG_INPUT_POLLDEV is not set
-
-#
-# Userland interfaces
-#
-CONFIG_INPUT_MOUSEDEV=m
-CONFIG_INPUT_MOUSEDEV_PSAUX=y
-CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
-CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
-# CONFIG_INPUT_JOYDEV is not set
-CONFIG_INPUT_EVDEV=m
-CONFIG_INPUT_EVBUG=m
-
-#
-# Input Device Drivers
-#
-CONFIG_INPUT_KEYBOARD=y
-CONFIG_KEYBOARD_ATKBD=m
-# CONFIG_KEYBOARD_SUNKBD is not set
-# CONFIG_KEYBOARD_LKKBD is not set
-CONFIG_KEYBOARD_XTKBD=m
-# CONFIG_KEYBOARD_NEWTON is not set
-# CONFIG_KEYBOARD_STOWAWAY is not set
-CONFIG_KEYBOARD_GPIO=y
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TABLET is not set
-CONFIG_INPUT_TOUCHSCREEN=y
-# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
-# CONFIG_TOUCHSCREEN_AD7879 is not set
-# CONFIG_TOUCHSCREEN_FUJITSU is not set
-# CONFIG_TOUCHSCREEN_GUNZE is not set
-# CONFIG_TOUCHSCREEN_ELO is not set
-# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
-# CONFIG_TOUCHSCREEN_MTOUCH is not set
-# CONFIG_TOUCHSCREEN_INEXIO is not set
-# CONFIG_TOUCHSCREEN_MK712 is not set
-# CONFIG_TOUCHSCREEN_PENMOUNT is not set
-# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
-# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
-# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
-# CONFIG_TOUCHSCREEN_TSC2007 is not set
-# CONFIG_INPUT_MISC is not set
-
-#
-# Hardware I/O ports
-#
-CONFIG_SERIO=y
-CONFIG_SERIO_SERPORT=y
-CONFIG_SERIO_LIBPS2=y
-# CONFIG_SERIO_RAW is not set
-# CONFIG_GAMEPORT is not set
-
-#
-# Character devices
-#
-CONFIG_VT=y
-CONFIG_CONSOLE_TRANSLATIONS=y
-# CONFIG_VT_CONSOLE is not set
-CONFIG_HW_CONSOLE=y
-# CONFIG_VT_HW_CONSOLE_BINDING is not set
-CONFIG_DEVKMEM=y
-# CONFIG_SERIAL_NONSTANDARD is not set
-
-#
-# Serial drivers
-#
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_NR_UARTS=3
-CONFIG_SERIAL_8250_RUNTIME_UARTS=3
-# CONFIG_SERIAL_8250_EXTENDED is not set
-
-#
-# Non-8250 serial port support
-#
-CONFIG_SERIAL_CORE=y
-CONFIG_SERIAL_CORE_CONSOLE=y
-CONFIG_UNIX98_PTYS=y
-# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
-# CONFIG_IPMI_HANDLER is not set
-CONFIG_HW_RANDOM=m
-# CONFIG_HW_RANDOM_TIMERIOMEM is not set
-# CONFIG_R3964 is not set
-# CONFIG_RAW_DRIVER is not set
-# CONFIG_TCG_TPM is not set
-CONFIG_I2C=y
-CONFIG_I2C_BOARDINFO=y
-CONFIG_I2C_CHARDEV=y
-CONFIG_I2C_HELPER_AUTO=y
-
-#
-# I2C Hardware Bus support
-#
-
-#
-# I2C system bus drivers (mostly embedded / system-on-chip)
-#
-CONFIG_I2C_DAVINCI=y
-# CONFIG_I2C_GPIO is not set
-# CONFIG_I2C_OCORES is not set
-# CONFIG_I2C_SIMTEC is not set
-
-#
-# External I2C/SMBus adapter drivers
-#
-# CONFIG_I2C_PARPORT_LIGHT is not set
-# CONFIG_I2C_TAOS_EVM is not set
-
-#
-# Other I2C/SMBus bus drivers
-#
-# CONFIG_I2C_PCA_PLATFORM is not set
-# CONFIG_I2C_STUB is not set
-
-#
-# Miscellaneous I2C Chip support
-#
-# CONFIG_DS1682 is not set
-# CONFIG_SENSORS_PCA9539 is not set
-# CONFIG_SENSORS_MAX6875 is not set
-# CONFIG_SENSORS_TSL2550 is not set
-# CONFIG_I2C_DEBUG_CORE is not set
-# CONFIG_I2C_DEBUG_ALGO is not set
-# CONFIG_I2C_DEBUG_BUS is not set
-# CONFIG_I2C_DEBUG_CHIP is not set
-# CONFIG_SPI is not set
-CONFIG_ARCH_REQUIRE_GPIOLIB=y
-CONFIG_GPIOLIB=y
-# CONFIG_DEBUG_GPIO is not set
-# CONFIG_GPIO_SYSFS is not set
-
-#
-# Memory mapped GPIO expanders:
-#
-
-#
-# I2C GPIO expanders:
-#
-# CONFIG_GPIO_MAX732X is not set
-# CONFIG_GPIO_PCA953X is not set
-CONFIG_GPIO_PCF857X=m
-
-#
-# PCI GPIO expanders:
-#
-
-#
-# SPI GPIO expanders:
-#
-# 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=y
-# CONFIG_WATCHDOG_NOWAYOUT is not set
-
-#
-# Watchdog Device Drivers
-#
-# CONFIG_SOFT_WATCHDOG is not set
-# CONFIG_DAVINCI_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_MFD_ASIC3 is not set
-# CONFIG_HTC_EGPIO is not set
-# CONFIG_HTC_PASIC3 is not set
-# CONFIG_TPS65010 is not set
-# CONFIG_TWL4030_CORE is not set
-# CONFIG_MFD_TMIO is not set
-# CONFIG_MFD_T7L66XB is not set
-# CONFIG_MFD_TC6387XB is not set
-# CONFIG_MFD_TC6393XB is not set
-# CONFIG_PMIC_DA903X is not set
-# CONFIG_MFD_WM8400 is not set
-# CONFIG_MFD_WM8350_I2C is not set
-# CONFIG_MFD_PCF50633 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
-
-#
-# Console display driver support
-#
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_DUMMY_CONSOLE=y
-CONFIG_SOUND=m
-# CONFIG_SOUND_OSS_CORE is not set
-CONFIG_SND=m
-CONFIG_SND_TIMER=m
-CONFIG_SND_PCM=m
-CONFIG_SND_JACK=y
-# CONFIG_SND_SEQUENCER is not set
-# CONFIG_SND_MIXER_OSS is not set
-# CONFIG_SND_PCM_OSS is not set
-# CONFIG_SND_HRTIMER is not set
-# CONFIG_SND_DYNAMIC_MINORS is not set
-CONFIG_SND_SUPPORT_OLD_API=y
-CONFIG_SND_VERBOSE_PROCFS=y
-# CONFIG_SND_VERBOSE_PRINTK is not set
-# CONFIG_SND_DEBUG is not set
-CONFIG_SND_DRIVERS=y
-# CONFIG_SND_DUMMY is not set
-# CONFIG_SND_MTPAV is not set
-# CONFIG_SND_SERIAL_U16550 is not set
-# CONFIG_SND_MPU401 is not set
-CONFIG_SND_ARM=y
-CONFIG_SND_SOC=m
-CONFIG_SND_DAVINCI_SOC=m
-CONFIG_SND_SOC_I2C_AND_SPI=m
-# CONFIG_SND_SOC_ALL_CODECS is not set
-# CONFIG_SOUND_PRIME is not set
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
-# CONFIG_USB_MUSB_HOST is not set
-# CONFIG_USB_MUSB_PERIPHERAL is not set
-# CONFIG_USB_MUSB_OTG is not set
-# CONFIG_USB_GADGET_MUSB_HDRC 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_IMX 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_CI13XXX 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_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_ACCESSIBILITY is not set
-# CONFIG_NEW_LEDS is not set
-CONFIG_RTC_LIB=y
-# CONFIG_RTC_CLASS is not set
-# CONFIG_DMADEVICES is not set
-# CONFIG_AUXDISPLAY is not set
-# CONFIG_REGULATOR 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=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT3_FS_XATTR=y
-# CONFIG_EXT3_FS_POSIX_ACL is not set
-# CONFIG_EXT3_FS_SECURITY 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
-# CONFIG_FS_POSIX_ACL is not set
-CONFIG_FILE_LOCKING=y
-CONFIG_XFS_FS=m
-# CONFIG_XFS_QUOTA is not set
-# CONFIG_XFS_POSIX_ACL is not set
-# CONFIG_XFS_RT is not set
-# CONFIG_XFS_DEBUG is not set
-# CONFIG_OCFS2_FS is not set
-# CONFIG_BTRFS_FS is not set
-CONFIG_DNOTIFY=y
-CONFIG_INOTIFY=y
-CONFIG_INOTIFY_USER=y
-# CONFIG_QUOTA is not set
-# CONFIG_AUTOFS_FS is not set
-CONFIG_AUTOFS4_FS=m
-# 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_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
-CONFIG_FAT_DEFAULT_CODEPAGE=437
-CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
-# CONFIG_NTFS_FS is not set
-
-#
-# Pseudo filesystems
-#
-CONFIG_PROC_FS=y
-CONFIG_PROC_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=y
-# CONFIG_SQUASHFS is not set
-# CONFIG_VXFS_FS is not set
-CONFIG_MINIX_FS=m
-# 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_ROOT_NFS=y
-CONFIG_NFSD=m
-CONFIG_NFSD_V3=y
-# CONFIG_NFSD_V3_ACL is not set
-# CONFIG_NFSD_V4 is not set
-CONFIG_LOCKD=y
-CONFIG_LOCKD_V4=y
-CONFIG_EXPORTFS=m
-CONFIG_NFS_COMMON=y
-CONFIG_SUNRPC=y
-# CONFIG_RPCSEC_GSS_KRB5 is not set
-# CONFIG_RPCSEC_GSS_SPKM3 is not set
-CONFIG_SMB_FS=m
-# CONFIG_SMB_NLS_DEFAULT is not set
-# CONFIG_CIFS is not set
-# CONFIG_NCP_FS is not set
-# CONFIG_CODA_FS is not set
-# CONFIG_AFS_FS is not set
-
-#
-# Partition Types
-#
-CONFIG_PARTITION_ADVANCED=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=y
-# 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=m
-CONFIG_NLS_ISO8859_1=y
-# CONFIG_NLS_ISO8859_2 is not set
-# CONFIG_NLS_ISO8859_3 is not set
-# CONFIG_NLS_ISO8859_4 is not set
-# CONFIG_NLS_ISO8859_5 is not set
-# CONFIG_NLS_ISO8859_6 is not set
-# CONFIG_NLS_ISO8859_7 is not set
-# CONFIG_NLS_ISO8859_9 is not set
-# CONFIG_NLS_ISO8859_13 is not set
-# CONFIG_NLS_ISO8859_14 is not set
-# CONFIG_NLS_ISO8859_15 is not set
-# CONFIG_NLS_KOI8_R is not set
-# CONFIG_NLS_KOI8_U is not set
-CONFIG_NLS_UTF8=m
-# 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=y
-# 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=y
-# CONFIG_DEBUG_OBJECTS is not set
-# CONFIG_SLUB_DEBUG_ON is not set
-# CONFIG_SLUB_STATS is not set
-CONFIG_DEBUG_PREEMPT=y
-CONFIG_DEBUG_RT_MUTEXES=y
-CONFIG_DEBUG_PI_LIST=y
-# CONFIG_RT_MUTEX_TESTER is not set
-# CONFIG_DEBUG_SPINLOCK is not set
-CONFIG_DEBUG_MUTEXES=y
-# CONFIG_DEBUG_LOCK_ALLOC is not set
-# CONFIG_PROVE_LOCKING is not set
-# CONFIG_LOCK_STAT 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_BUGVERBOSE=y
-# CONFIG_DEBUG_INFO is not set
-# 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_LATENCYTOP is not set
-# CONFIG_SYSCTL_SYSCALL_CHECK is not set
-# CONFIG_PAGE_POISONING is not set
-CONFIG_HAVE_FUNCTION_TRACER=y
-CONFIG_TRACING_SUPPORT=y
-
-#
-# Tracers
-#
-# CONFIG_FUNCTION_TRACER is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_PREEMPT_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_EVENT_TRACER is not set
-# CONFIG_BOOT_TRACER is not set
-# CONFIG_TRACE_BRANCH_PROFILING is not set
-# CONFIG_STACK_TRACER is not set
-# CONFIG_KMEMTRACE is not set
-# CONFIG_WORKQUEUE_TRACER is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_DYNAMIC_DEBUG is not set
-# CONFIG_SAMPLES is not set
-CONFIG_HAVE_ARCH_KGDB=y
-# CONFIG_KGDB is not set
-CONFIG_ARM_UNWIND=y
-CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_ERRORS=y
-# CONFIG_DEBUG_STACK_USAGE is not set
-# CONFIG_DEBUG_LL is not set
-
-#
-# 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 is not set
-# CONFIG_BINARY_PRINTF is not set
-
-#
-# Library routines
-#
-CONFIG_BITREVERSE=y
-CONFIG_GENERIC_FIND_LAST_BIT=y
-CONFIG_CRC_CCITT=m
-# CONFIG_CRC16 is not set
-CONFIG_CRC_T10DIF=m
-# 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_GENERIC_ALLOCATOR=y
-CONFIG_HAS_IOMEM=y
-CONFIG_HAS_IOPORT=y
-CONFIG_HAS_DMA=y
-CONFIG_NLATTR=y
similarity index 85%
rename from arch/arm/configs/da850_omapl138_defconfig
rename to arch/arm/configs/da8xx_omapl_defconfig
index 842a70b..50bd25a 100644 (file)
@@ -1,15 +1,13 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.30-davinci1
-# Mon Jun 29 07:54:15 2009
+# Linux kernel version: 2.6.32-rc5
+# Thu Oct 22 12:19:19 2009
 #
 CONFIG_ARM=y
 CONFIG_SYS_SUPPORTS_APM_EMULATION=y
 CONFIG_GENERIC_GPIO=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_MMU=y
-# CONFIG_NO_IOPORT is not set
 CONFIG_GENERIC_HARDIRQS=y
 CONFIG_STACKTRACE_SUPPORT=y
 CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -18,14 +16,14 @@ CONFIG_TRACE_IRQFLAGS_SUPPORT=y
 CONFIG_HARDIRQS_SW_RESEND=y
 CONFIG_GENERIC_IRQ_PROBE=y
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_HAS_CPUFREQ=y
 CONFIG_GENERIC_HWEIGHT=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
 CONFIG_ZONE_DMA=y
 CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
 CONFIG_VECTORS_BASE=0xffff0000
 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
 
 #
 # General setup
@@ -48,11 +46,12 @@ CONFIG_POSIX_MQUEUE_SYSCTL=y
 #
 # RCU Subsystem
 #
-CONFIG_CLASSIC_RCU=y
-# CONFIG_TREE_RCU is not set
-# CONFIG_PREEMPT_RCU is not set
+CONFIG_TREE_RCU=y
+# CONFIG_TREE_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT 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=14
@@ -62,8 +61,7 @@ CONFIG_FAIR_GROUP_SCHED=y
 CONFIG_USER_SCHED=y
 # CONFIG_CGROUP_SCHED is not set
 # CONFIG_CGROUPS is not set
-CONFIG_SYSFS_DEPRECATED=y
-CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
 # CONFIG_RELAY is not set
 # CONFIG_NAMESPACES is not set
 CONFIG_BLK_DEV_INITRD=y
@@ -80,7 +78,6 @@ CONFIG_SYSCTL_SYSCALL=y
 CONFIG_KALLSYMS=y
 # CONFIG_KALLSYMS_ALL is not set
 # CONFIG_KALLSYMS_EXTRA_PASS is not set
-# CONFIG_STRIP_ASM_SYMS is not set
 CONFIG_HOTPLUG=y
 CONFIG_PRINTK=y
 CONFIG_BUG=y
@@ -93,6 +90,10 @@ CONFIG_TIMERFD=y
 CONFIG_EVENTFD=y
 CONFIG_SHMEM=y
 CONFIG_AIO=y
+
+#
+# Kernel Performance Events And Counters
+#
 CONFIG_VM_EVENT_COUNTERS=y
 CONFIG_SLUB_DEBUG=y
 CONFIG_COMPAT_BRK=y
@@ -100,12 +101,16 @@ CONFIG_COMPAT_BRK=y
 CONFIG_SLUB=y
 # CONFIG_SLOB is not set
 # CONFIG_PROFILING is not set
-# CONFIG_MARKERS is not set
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_KPROBES is not set
 CONFIG_HAVE_KPROBES=y
 CONFIG_HAVE_KRETPROBES=y
 CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
 # CONFIG_SLOW_WORK is not set
 CONFIG_HAVE_GENERIC_DMA_COHERENT=y
 CONFIG_SLABINFO=y
@@ -118,7 +123,7 @@ CONFIG_MODULE_FORCE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 # CONFIG_MODULE_SRCVERSION_ALL is not set
 CONFIG_BLOCK=y
-# CONFIG_LBD is not set
+CONFIG_LBDAF=y
 # CONFIG_BLK_DEV_BSG is not set
 # CONFIG_BLK_DEV_INTEGRITY is not set
 
@@ -139,19 +144,22 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 #
 # System Type
 #
+CONFIG_MMU=y
 # CONFIG_ARCH_AAEC2000 is not set
 # CONFIG_ARCH_INTEGRATOR is not set
 # CONFIG_ARCH_REALVIEW is not set
 # CONFIG_ARCH_VERSATILE is not set
 # CONFIG_ARCH_AT91 is not set
 # CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
 # CONFIG_ARCH_EBSA110 is not set
 # CONFIG_ARCH_EP93XX is not set
-# CONFIG_ARCH_GEMINI is not set
 # CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
 # CONFIG_ARCH_NETX is not set
 # CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_NOMADIK is not set
 # CONFIG_ARCH_IOP13XX is not set
 # CONFIG_ARCH_IOP32X is not set
 # CONFIG_ARCH_IOP33X is not set
@@ -160,25 +168,27 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 # CONFIG_ARCH_IXP4XX is not set
 # CONFIG_ARCH_L7200 is not set
 # CONFIG_ARCH_KIRKWOOD is not set
-# CONFIG_ARCH_KS8695 is not set
-# CONFIG_ARCH_NS9XXX is not set
 # CONFIG_ARCH_LOKI is not set
 # CONFIG_ARCH_MV78XX0 is not set
-# CONFIG_ARCH_MXC is not set
 # CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
 # CONFIG_ARCH_PNX4008 is not set
 # CONFIG_ARCH_PXA is not set
-# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_MSM is not set
 # CONFIG_ARCH_RPC is not set
 # CONFIG_ARCH_SA1100 is not set
 # CONFIG_ARCH_S3C2410 is not set
 # CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5PC1XX is not set
 # CONFIG_ARCH_SHARK is not set
 # CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
 CONFIG_ARCH_DAVINCI=y
 # CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM is not set
-# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_BCMRING is not set
 CONFIG_CP_INTC=y
 
 #
@@ -191,7 +201,7 @@ CONFIG_CP_INTC=y
 # CONFIG_ARCH_DAVINCI_DM644x is not set
 # CONFIG_ARCH_DAVINCI_DM355 is not set
 # CONFIG_ARCH_DAVINCI_DM646x is not set
-# CONFIG_ARCH_DAVINCI_DA830 is not set
+CONFIG_ARCH_DAVINCI_DA830=y
 CONFIG_ARCH_DAVINCI_DA850=y
 CONFIG_ARCH_DAVINCI_DA8XX=y
 # CONFIG_ARCH_DAVINCI_DM365 is not set
@@ -199,7 +209,14 @@ CONFIG_ARCH_DAVINCI_DA8XX=y
 #
 # DaVinci Board Type
 #
+CONFIG_MACH_DAVINCI_DA830_EVM=y
+CONFIG_DA830_UI=y
+CONFIG_DA830_UI_LCD=y
+# CONFIG_DA830_UI_NAND is not set
 CONFIG_MACH_DAVINCI_DA850_EVM=y
+CONFIG_DA850_UI_EXP=y
+CONFIG_DA850_UI_NONE=y
+# CONFIG_DA850_UI_RMII is not set
 CONFIG_DAVINCI_MUX=y
 # CONFIG_DAVINCI_MUX_DEBUG is not set
 # CONFIG_DAVINCI_MUX_WARNINGS is not set
@@ -212,7 +229,7 @@ CONFIG_CPU_32=y
 CONFIG_CPU_ARM926T=y
 CONFIG_CPU_32v5=y
 CONFIG_CPU_ABRT_EV5TJ=y
-CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_PABRT_LEGACY=y
 CONFIG_CPU_CACHE_VIVT=y
 CONFIG_CPU_COPY_V4WB=y
 CONFIG_CPU_TLB_V4WBI=y
@@ -225,9 +242,9 @@ CONFIG_CPU_CP15_MMU=y
 CONFIG_ARM_THUMB=y
 # CONFIG_CPU_ICACHE_DISABLE is not set
 # CONFIG_CPU_DCACHE_DISABLE is not set
-# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+CONFIG_CPU_DCACHE_WRITETHROUGH=y
 # CONFIG_CPU_CACHE_ROUND_ROBIN is not set
-# CONFIG_OUTER_CACHE is not set
+CONFIG_ARM_L1_CACHE_SHIFT=5
 CONFIG_COMMON_CLKDEV=y
 
 #
@@ -248,11 +265,12 @@ CONFIG_VMSPLIT_3G=y
 # CONFIG_VMSPLIT_2G is not set
 # CONFIG_VMSPLIT_1G is not set
 CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
 CONFIG_PREEMPT=y
 CONFIG_HZ=100
 CONFIG_AEABI=y
 # CONFIG_OABI_COMPAT is not set
-# CONFIG_ARCH_HAS_HOLES_MEMORYMODEL is not set
 # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
 # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
 # CONFIG_HIGHMEM is not set
@@ -268,12 +286,14 @@ CONFIG_SPLIT_PTLOCK_CPUS=4096
 CONFIG_ZONE_DMA_FLAG=1
 CONFIG_BOUNCE=y
 CONFIG_VIRT_TO_BUS=y
-CONFIG_UNEVICTABLE_LRU=y
 CONFIG_HAVE_MLOCK=y
 CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
 CONFIG_LEDS=y
 # CONFIG_LEDS_CPU is not set
 CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
 
 #
 # Boot options
@@ -287,7 +307,24 @@ CONFIG_CMDLINE=""
 #
 # CPU Power Management
 #
-# CONFIG_CPU_IDLE is not set
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+# CONFIG_CPU_FREQ_DEBUG is not set
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=m
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
 
 #
 # Floating point emulation
@@ -401,6 +438,7 @@ CONFIG_NETFILTER_ADVANCED=y
 # CONFIG_IP6_NF_IPTABLES is not set
 # CONFIG_IP_DCCP is not set
 # CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
 # CONFIG_TIPC is not set
 # CONFIG_ATM is not set
 # CONFIG_BRIDGE is not set
@@ -415,6 +453,7 @@ CONFIG_NETFILTER_ADVANCED=y
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
 # CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
 # CONFIG_NET_SCHED is not set
 # CONFIG_DCB is not set
 
@@ -440,6 +479,7 @@ CONFIG_NETFILTER_ADVANCED=y
 # Generic Driver Options
 #
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
 CONFIG_STANDALONE=y
 CONFIG_PREVENT_FIRMWARE_BUILD=y
 # CONFIG_FW_LOADER is not set
@@ -460,6 +500,7 @@ CONFIG_BLK_DEV_RAM_SIZE=32768
 # CONFIG_BLK_DEV_XIP is not set
 # CONFIG_CDROM_PKTCDVD is not set
 # CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
 CONFIG_MISC_DEVICES=y
 # CONFIG_ICS932S401 is not set
 # CONFIG_ENCLOSURE_SERVICES is not set
@@ -471,6 +512,7 @@ CONFIG_MISC_DEVICES=y
 #
 CONFIG_EEPROM_AT24=y
 # CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
 # CONFIG_EEPROM_93CX6 is not set
 CONFIG_HAVE_IDE=y
 # CONFIG_IDE is not set
@@ -494,10 +536,6 @@ CONFIG_BLK_DEV_SD=m
 # CONFIG_BLK_DEV_SR 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
@@ -522,7 +560,6 @@ CONFIG_SCSI_LOWLEVEL=y
 # 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
@@ -553,7 +590,7 @@ CONFIG_NET_ETHERNET=y
 CONFIG_MII=y
 # CONFIG_AX88796 is not set
 # CONFIG_SMC91X is not set
-# CONFIG_TI_DAVINCI_EMAC is not set
+CONFIG_TI_DAVINCI_EMAC=y
 # CONFIG_DM9000 is not set
 # CONFIG_ETHOC is not set
 # CONFIG_SMC911X is not set
@@ -567,12 +604,11 @@ CONFIG_MII=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_KS8842 is not set
+# CONFIG_KS8851_MLL is not set
 # CONFIG_NETDEV_1000 is not set
 # CONFIG_NETDEV_10000 is not set
-
-#
-# Wireless LAN
-#
+CONFIG_WLAN=y
 # CONFIG_WLAN_PRE80211 is not set
 # CONFIG_WLAN_80211 is not set
 
@@ -588,6 +624,7 @@ CONFIG_NETPOLL=y
 CONFIG_NETPOLL_TRAP=y
 CONFIG_NET_POLL_CONTROLLER=y
 # CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
 
 #
 # Input device support
@@ -611,23 +648,30 @@ CONFIG_INPUT_EVBUG=m
 # Input Device Drivers
 #
 CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
 CONFIG_KEYBOARD_ATKBD=m
-# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_QT2160 is not set
 # CONFIG_KEYBOARD_LKKBD is not set
-CONFIG_KEYBOARD_XTKBD=m
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
 # CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
 # CONFIG_KEYBOARD_STOWAWAY is not set
-CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+CONFIG_KEYBOARD_XTKBD=m
 # CONFIG_INPUT_MOUSE is not set
 # CONFIG_INPUT_JOYSTICK is not set
 # CONFIG_INPUT_TABLET is not set
 CONFIG_INPUT_TOUCHSCREEN=y
 # CONFIG_TOUCHSCREEN_AD7879_I2C is not set
 # CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
 # CONFIG_TOUCHSCREEN_FUJITSU is not set
 # CONFIG_TOUCHSCREEN_GUNZE is not set
 # CONFIG_TOUCHSCREEN_ELO is not set
 # CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
 # CONFIG_TOUCHSCREEN_MTOUCH is not set
 # CONFIG_TOUCHSCREEN_INEXIO is not set
 # CONFIG_TOUCHSCREEN_MK712 is not set
@@ -636,6 +680,7 @@ CONFIG_INPUT_TOUCHSCREEN=y
 # CONFIG_TOUCHSCREEN_TOUCHWIN is not set
 # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
 # CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
 # CONFIG_INPUT_MISC is not set
 
 #
@@ -684,6 +729,7 @@ CONFIG_HW_RANDOM=m
 # CONFIG_TCG_TPM is not set
 CONFIG_I2C=y
 CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_HELPER_AUTO=y
 
@@ -695,6 +741,7 @@ CONFIG_I2C_HELPER_AUTO=y
 # I2C system bus drivers (mostly embedded / system-on-chip)
 #
 CONFIG_I2C_DAVINCI=y
+# CONFIG_I2C_DESIGNWARE is not set
 # CONFIG_I2C_GPIO is not set
 # CONFIG_I2C_OCORES is not set
 # CONFIG_I2C_SIMTEC is not set
@@ -715,14 +762,17 @@ CONFIG_I2C_DAVINCI=y
 # Miscellaneous I2C Chip support
 #
 # CONFIG_DS1682 is not set
-# CONFIG_SENSORS_PCA9539 is not set
-# CONFIG_SENSORS_MAX6875 is not set
 # CONFIG_SENSORS_TSL2550 is not set
 # CONFIG_I2C_DEBUG_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_BUS is not set
 # CONFIG_I2C_DEBUG_CHIP is not set
 # CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
 CONFIG_ARCH_REQUIRE_GPIOLIB=y
 CONFIG_GPIOLIB=y
 # CONFIG_DEBUG_GPIO is not set
@@ -736,8 +786,8 @@ CONFIG_GPIOLIB=y
 # I2C GPIO expanders:
 #
 # CONFIG_GPIO_MAX732X is not set
-# CONFIG_GPIO_PCA953X is not set
-CONFIG_GPIO_PCF857X=m
+CONFIG_GPIO_PCA953X=y
+CONFIG_GPIO_PCF857X=y
 
 #
 # PCI GPIO expanders:
@@ -746,11 +796,14 @@ CONFIG_GPIO_PCF857X=m
 #
 # SPI GPIO expanders:
 #
+
+#
+# AC97 GPIO expanders:
+#
 # 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=y
 # CONFIG_WATCHDOG_NOWAYOUT is not set
 
@@ -782,31 +835,56 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_TC6393XB is not set
 # CONFIG_PMIC_DA903X is not set
 # CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_MFD_PCF50633 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
+# CONFIG_AB3100_CORE is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_BQ24022 is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+CONFIG_REGULATOR_TPS6507X=y
+# CONFIG_MEDIA_SUPPORT is not set
 
 #
 # Graphics support
 #
 # CONFIG_VGASTATE is not set
 # CONFIG_VIDEO_OUTPUT_CONTROL is not set
-# CONFIG_FB is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_DAVINCI is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FB_DA8XX=y
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
 # CONFIG_BACKLIGHT_LCD_SUPPORT is not set
 
 #
@@ -819,6 +897,16 @@ CONFIG_SSB_POSSIBLE=y
 #
 # CONFIG_VGA_CONSOLE is not set
 CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+CONFIG_LOGO_LINUX_CLUT224=y
 CONFIG_SOUND=m
 # CONFIG_SOUND_OSS_CORE is not set
 CONFIG_SND=m
@@ -834,6 +922,11 @@ CONFIG_SND_SUPPORT_OLD_API=y
 CONFIG_SND_VERBOSE_PROCFS=y
 # CONFIG_SND_VERBOSE_PRINTK is not set
 # CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
 CONFIG_SND_DRIVERS=y
 # CONFIG_SND_DUMMY is not set
 # CONFIG_SND_MTPAV is not set
@@ -842,6 +935,8 @@ CONFIG_SND_DRIVERS=y
 CONFIG_SND_ARM=y
 CONFIG_SND_SOC=m
 CONFIG_SND_DAVINCI_SOC=m
+# CONFIG_SND_DA830_SOC_EVM is not set
+# CONFIG_SND_DA850_SOC_EVM is not set
 CONFIG_SND_SOC_I2C_AND_SPI=m
 # CONFIG_SND_SOC_ALL_CODECS is not set
 # CONFIG_SOUND_PRIME is not set
@@ -849,14 +944,17 @@ CONFIG_SND_SOC_I2C_AND_SPI=m
 # CONFIG_USB_SUPPORT is not set
 # CONFIG_MMC is not set
 # CONFIG_MEMSTICK is not set
-# CONFIG_ACCESSIBILITY is not set
 # CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
 CONFIG_RTC_LIB=y
 # CONFIG_RTC_CLASS is not set
 # CONFIG_DMADEVICES is not set
 # CONFIG_AUXDISPLAY is not set
-# CONFIG_REGULATOR is not set
 # CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
 # CONFIG_STAGING is not set
 
 #
@@ -877,14 +975,17 @@ 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=m
 # CONFIG_XFS_QUOTA is not set
 # CONFIG_XFS_POSIX_ACL is not set
 # CONFIG_XFS_RT is not set
 # CONFIG_XFS_DEBUG is not set
+# CONFIG_GFS2_FS is not set
 # CONFIG_OCFS2_FS is not set
 # CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
 CONFIG_DNOTIFY=y
 CONFIG_INOTIFY=y
 CONFIG_INOTIFY_USER=y
@@ -943,7 +1044,6 @@ CONFIG_MINIX_FS=m
 # 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
@@ -1039,6 +1139,7 @@ CONFIG_ENABLE_WARN_DEPRECATED=y
 CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_FRAME_WARN=1024
 # CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_STRIP_ASM_SYMS is not set
 # CONFIG_UNUSED_SYMBOLS is not set
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
@@ -1056,6 +1157,7 @@ CONFIG_TIMER_STATS=y
 # CONFIG_DEBUG_OBJECTS is not set
 # CONFIG_SLUB_DEBUG_ON is not set
 # CONFIG_SLUB_STATS is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
 CONFIG_DEBUG_PREEMPT=y
 CONFIG_DEBUG_RT_MUTEXES=y
 CONFIG_DEBUG_PI_LIST=y
@@ -1076,29 +1178,29 @@ CONFIG_DEBUG_BUGVERBOSE=y
 # CONFIG_DEBUG_LIST is not set
 # CONFIG_DEBUG_SG is not set
 # CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS 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_DEBUG_FORCE_WEAK_PER_CPU is not set
 # CONFIG_FAULT_INJECTION is not set
 # CONFIG_LATENCYTOP is not set
 # CONFIG_SYSCTL_SYSCALL_CHECK is not set
 # CONFIG_PAGE_POISONING is not set
 CONFIG_HAVE_FUNCTION_TRACER=y
 CONFIG_TRACING_SUPPORT=y
-
-#
-# Tracers
-#
+CONFIG_FTRACE=y
 # CONFIG_FUNCTION_TRACER is not set
 # CONFIG_IRQSOFF_TRACER is not set
 # CONFIG_PREEMPT_TRACER is not set
 # CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
-# CONFIG_EVENT_TRACER is not set
+# CONFIG_ENABLE_DEFAULT_TRACERS is not set
 # CONFIG_BOOT_TRACER is not set
-# CONFIG_TRACE_BRANCH_PROFILING is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
 # CONFIG_STACK_TRACER is not set
 # CONFIG_KMEMTRACE is not set
 # CONFIG_WORKQUEUE_TRACER is not set
@@ -1125,7 +1227,6 @@ 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
@@ -1157,11 +1258,13 @@ CONFIG_CRYPTO=y
 #
 # CONFIG_CRYPTO_HMAC is not set
 # CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
 
 #
 # Digest
 #
 # CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_GHASH is not set
 # CONFIG_CRYPTO_MD4 is not set
 # CONFIG_CRYPTO_MD5 is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
index ddffe39..bd656e8 100644 (file)
@@ -1,14 +1,13 @@
 #
 # Automatically generated make config: don't edit
-# Linux kernel version: 2.6.31-rc3-davinci1
-# Fri Jul 17 08:26:52 2009
+# Linux kernel version: 2.6.32-rc4
+# Mon Oct 12 14:13:12 2009
 #
 CONFIG_ARM=y
 CONFIG_SYS_SUPPORTS_APM_EMULATION=y
 CONFIG_GENERIC_GPIO=y
 CONFIG_GENERIC_TIME=y
 CONFIG_GENERIC_CLOCKEVENTS=y
-CONFIG_MMU=y
 CONFIG_GENERIC_HARDIRQS=y
 CONFIG_STACKTRACE_SUPPORT=y
 CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -46,11 +45,12 @@ CONFIG_POSIX_MQUEUE_SYSCTL=y
 #
 # RCU Subsystem
 #
-CONFIG_CLASSIC_RCU=y
-# CONFIG_TREE_RCU is not set
-# CONFIG_PREEMPT_RCU is not set
+CONFIG_TREE_RCU=y
+# CONFIG_TREE_PREEMPT_RCU is not set
+# CONFIG_RCU_TRACE is not set
+CONFIG_RCU_FANOUT=32
+# CONFIG_RCU_FANOUT_EXACT 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=14
@@ -91,17 +91,15 @@ CONFIG_SHMEM=y
 CONFIG_AIO=y
 
 #
-# Performance Counters
+# Kernel Performance Events And Counters
 #
 CONFIG_VM_EVENT_COUNTERS=y
 CONFIG_SLUB_DEBUG=y
-# CONFIG_STRIP_ASM_SYMS is not set
 CONFIG_COMPAT_BRK=y
 # CONFIG_SLAB is not set
 CONFIG_SLUB=y
 # CONFIG_SLOB is not set
 # CONFIG_PROFILING is not set
-# CONFIG_MARKERS is not set
 CONFIG_HAVE_OPROFILE=y
 # CONFIG_KPROBES is not set
 CONFIG_HAVE_KPROBES=y
@@ -145,6 +143,7 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 #
 # System Type
 #
+CONFIG_MMU=y
 # CONFIG_ARCH_AAEC2000 is not set
 # CONFIG_ARCH_INTEGRATOR is not set
 # CONFIG_ARCH_REALVIEW is not set
@@ -159,6 +158,7 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 # CONFIG_ARCH_STMP3XXX is not set
 # CONFIG_ARCH_NETX is not set
 # CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_NOMADIK is not set
 # CONFIG_ARCH_IOP13XX is not set
 # CONFIG_ARCH_IOP32X is not set
 # CONFIG_ARCH_IOP33X is not set
@@ -181,11 +181,13 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
 # CONFIG_ARCH_SA1100 is not set
 # CONFIG_ARCH_S3C2410 is not set
 # CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5PC1XX is not set
 # CONFIG_ARCH_SHARK is not set
 # CONFIG_ARCH_LH7A40X is not set
 # CONFIG_ARCH_U300 is not set
 CONFIG_ARCH_DAVINCI=y
 # CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_BCMRING is not set
 CONFIG_AINTC=y
 CONFIG_ARCH_DAVINCI_DMx=y
 
@@ -208,6 +210,7 @@ CONFIG_ARCH_DAVINCI_DM365=y
 #
 CONFIG_MACH_DAVINCI_EVM=y
 CONFIG_MACH_SFFSDR=y
+CONFIG_MACH_NEUROS_OSD2=y
 CONFIG_MACH_DAVINCI_DM355_EVM=y
 CONFIG_MACH_DM355_LEOPARD=y
 CONFIG_MACH_DAVINCI_DM6467_EVM=y
@@ -224,7 +227,7 @@ CONFIG_CPU_32=y
 CONFIG_CPU_ARM926T=y
 CONFIG_CPU_32v5=y
 CONFIG_CPU_ABRT_EV5TJ=y
-CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_PABRT_LEGACY=y
 CONFIG_CPU_CACHE_VIVT=y
 CONFIG_CPU_COPY_V4WB=y
 CONFIG_CPU_TLB_V4WBI=y
@@ -239,6 +242,7 @@ CONFIG_ARM_THUMB=y
 # CONFIG_CPU_DCACHE_DISABLE is not set
 # CONFIG_CPU_DCACHE_WRITETHROUGH is not set
 # CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+CONFIG_ARM_L1_CACHE_SHIFT=5
 CONFIG_COMMON_CLKDEV=y
 
 #
@@ -259,6 +263,8 @@ CONFIG_VMSPLIT_3G=y
 # CONFIG_VMSPLIT_2G is not set
 # CONFIG_VMSPLIT_1G is not set
 CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
 CONFIG_PREEMPT=y
 CONFIG_HZ=100
 CONFIG_AEABI=y
@@ -280,6 +286,7 @@ CONFIG_BOUNCE=y
 CONFIG_VIRT_TO_BUS=y
 CONFIG_HAVE_MLOCK=y
 CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+# CONFIG_KSM is not set
 CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
 CONFIG_LEDS=y
 # CONFIG_LEDS_CPU is not set
@@ -412,6 +419,7 @@ CONFIG_NETFILTER_ADVANCED=y
 # CONFIG_IP6_NF_IPTABLES is not set
 # CONFIG_IP_DCCP is not set
 # CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
 # CONFIG_TIPC is not set
 # CONFIG_ATM is not set
 # CONFIG_BRIDGE is not set
@@ -452,6 +460,7 @@ CONFIG_NETFILTER_ADVANCED=y
 # Generic Driver Options
 #
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
 CONFIG_STANDALONE=y
 CONFIG_PREVENT_FIRMWARE_BUILD=y
 # CONFIG_FW_LOADER is not set
@@ -461,9 +470,9 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y
 # CONFIG_CONNECTOR is not set
 CONFIG_MTD=m
 # CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_TESTS is not set
 # CONFIG_MTD_CONCAT is not set
 CONFIG_MTD_PARTITIONS=y
-# CONFIG_MTD_TESTS is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_AFS_PARTS is not set
 # CONFIG_MTD_AR7_PARTS is not set
@@ -499,7 +508,7 @@ 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=m
 CONFIG_MTD_CFI_AMDSTD=m
 # CONFIG_MTD_CFI_STAA is not set
 CONFIG_MTD_CFI_UTIL=m
@@ -694,12 +703,10 @@ CONFIG_DM9000_DEBUGLEVEL=4
 # CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
 # CONFIG_B44 is not set
 # CONFIG_KS8842 is not set
+# CONFIG_KS8851_MLL is not set
 # CONFIG_NETDEV_1000 is not set
 # CONFIG_NETDEV_10000 is not set
-
-#
-# Wireless LAN
-#
+CONFIG_WLAN=y
 # CONFIG_WLAN_PRE80211 is not set
 # CONFIG_WLAN_80211 is not set
 
@@ -734,6 +741,7 @@ CONFIG_NETPOLL=y
 CONFIG_NETPOLL_TRAP=y
 CONFIG_NET_POLL_CONTROLLER=y
 # CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
 
 #
 # Input device support
@@ -745,10 +753,7 @@ CONFIG_INPUT=y
 #
 # Userland interfaces
 #
-CONFIG_INPUT_MOUSEDEV=m
-CONFIG_INPUT_MOUSEDEV_PSAUX=y
-CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
-CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_MOUSEDEV is not set
 # CONFIG_INPUT_JOYDEV is not set
 CONFIG_INPUT_EVDEV=m
 CONFIG_INPUT_EVBUG=m
@@ -757,12 +762,16 @@ CONFIG_INPUT_EVBUG=m
 # Input Device Drivers
 #
 CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ADP5588 is not set
 CONFIG_KEYBOARD_ATKBD=m
+# CONFIG_QT2160 is not set
 # CONFIG_KEYBOARD_LKKBD is not set
 CONFIG_KEYBOARD_GPIO=y
 # CONFIG_KEYBOARD_MATRIX is not set
 # CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
 # CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
 # CONFIG_KEYBOARD_STOWAWAY is not set
 # CONFIG_KEYBOARD_SUNKBD is not set
 CONFIG_KEYBOARD_XTKBD=m
@@ -777,6 +786,7 @@ CONFIG_INPUT_TOUCHSCREEN=y
 # CONFIG_TOUCHSCREEN_GUNZE is not set
 # CONFIG_TOUCHSCREEN_ELO is not set
 # CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
 # CONFIG_TOUCHSCREEN_MTOUCH is not set
 # CONFIG_TOUCHSCREEN_INEXIO is not set
 # CONFIG_TOUCHSCREEN_MK712 is not set
@@ -787,7 +797,17 @@ CONFIG_INPUT_TOUCHSCREEN=y
 # CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
 # CONFIG_TOUCHSCREEN_TSC2007 is not set
 # CONFIG_TOUCHSCREEN_W90X900 is not set
-# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+CONFIG_INPUT_DM355EVM=m
+CONFIG_INPUT_DM365EVM=m
 
 #
 # Hardware I/O ports
@@ -828,13 +848,13 @@ CONFIG_UNIX98_PTYS=y
 CONFIG_LEGACY_PTYS=y
 CONFIG_LEGACY_PTY_COUNT=256
 # CONFIG_IPMI_HANDLER is not set
-CONFIG_HW_RANDOM=m
-# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_HW_RANDOM is not set
 # CONFIG_R3964 is not set
 # CONFIG_RAW_DRIVER is not set
 # CONFIG_TCG_TPM is not set
 CONFIG_I2C=y
 CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_HELPER_AUTO=y
 
@@ -868,13 +888,17 @@ CONFIG_I2C_DAVINCI=y
 # Miscellaneous I2C Chip support
 #
 # CONFIG_DS1682 is not set
-# CONFIG_SENSORS_PCA9539 is not set
 # CONFIG_SENSORS_TSL2550 is not set
 # CONFIG_I2C_DEBUG_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
 # CONFIG_I2C_DEBUG_BUS is not set
 # CONFIG_I2C_DEBUG_CHIP is not set
 # CONFIG_SPI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
 CONFIG_ARCH_REQUIRE_GPIOLIB=y
 CONFIG_GPIOLIB=y
 # CONFIG_DEBUG_GPIO is not set
@@ -889,7 +913,7 @@ CONFIG_GPIOLIB=y
 #
 # CONFIG_GPIO_MAX732X is not set
 # CONFIG_GPIO_PCA953X is not set
-CONFIG_GPIO_PCF857X=m
+CONFIG_GPIO_PCF857X=y
 
 #
 # PCI GPIO expanders:
@@ -898,10 +922,19 @@ CONFIG_GPIO_PCF857X=m
 #
 # SPI GPIO expanders:
 #
+
+#
+# AC97 GPIO expanders:
+#
 # CONFIG_W1 is not set
 # CONFIG_POWER_SUPPLY is not set
 CONFIG_HWMON=y
 # CONFIG_HWMON_VID is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Native drivers
+#
 # CONFIG_SENSORS_AD7414 is not set
 # CONFIG_SENSORS_AD7418 is not set
 # CONFIG_SENSORS_ADM1021 is not set
@@ -950,6 +983,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_ADS7828 is not set
 # CONFIG_SENSORS_THMC50 is not set
 # CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_TMP421 is not set
 # CONFIG_SENSORS_VT1211 is not set
 # CONFIG_SENSORS_W83781D is not set
 # CONFIG_SENSORS_W83791D is not set
@@ -959,9 +993,7 @@ CONFIG_HWMON=y
 # CONFIG_SENSORS_W83L786NG 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=y
 # CONFIG_WATCHDOG_NOWAYOUT is not set
 
@@ -988,7 +1020,7 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_CORE is not set
 # CONFIG_MFD_SM501 is not set
 # CONFIG_MFD_ASIC3 is not set
-# CONFIG_MFD_DM355EVM_MSP is not set
+CONFIG_MFD_DM355EVM_MSP=y
 # CONFIG_HTC_EGPIO is not set
 # CONFIG_HTC_PASIC3 is not set
 # CONFIG_TPS65010 is not set
@@ -999,9 +1031,11 @@ CONFIG_SSB_POSSIBLE=y
 # CONFIG_MFD_TC6393XB is not set
 # CONFIG_PMIC_DA903X is not set
 # CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X is not set
 # CONFIG_MFD_WM8350_I2C is not set
 # CONFIG_MFD_PCF50633 is not set
 # CONFIG_AB3100_CORE is not set
+# CONFIG_REGULATOR is not set
 # CONFIG_MEDIA_SUPPORT is not set
 
 #
@@ -1013,9 +1047,9 @@ CONFIG_FB=y
 CONFIG_FIRMWARE_EDID=y
 # CONFIG_FB_DDC is not set
 # CONFIG_FB_BOOT_VESA_SUPPORT is not set
-# CONFIG_FB_CFB_FILLRECT is not set
-# CONFIG_FB_CFB_COPYAREA is not set
-# CONFIG_FB_CFB_IMAGEBLIT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
 # CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
 # CONFIG_FB_SYS_FILLRECT is not set
 # CONFIG_FB_SYS_COPYAREA is not set
@@ -1032,6 +1066,7 @@ CONFIG_FIRMWARE_EDID=y
 # Frame buffer hardware drivers
 #
 # CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_DAVINCI=y
 # CONFIG_FB_VIRTUAL is not set
 # CONFIG_FB_METRONOME is not set
 # CONFIG_FB_MB862XX is not set
@@ -1101,7 +1136,6 @@ CONFIG_SND_SOC_TLV320AIC3X=m
 # CONFIG_SOUND_PRIME is not set
 CONFIG_HID_SUPPORT=y
 CONFIG_HID=m
-# CONFIG_HID_DEBUG is not set
 # CONFIG_HIDRAW is not set
 
 #
@@ -1130,6 +1164,7 @@ CONFIG_HID_CYPRESS=m
 CONFIG_HID_EZKEY=m
 # CONFIG_HID_KYE is not set
 CONFIG_HID_GYRATION=m
+# CONFIG_HID_TWINHAN is not set
 # CONFIG_HID_KENSINGTON is not set
 CONFIG_HID_LOGITECH=m
 # CONFIG_LOGITECH_FF is not set
@@ -1176,6 +1211,7 @@ CONFIG_USB_MON=m
 # CONFIG_USB_OXU210HP_HCD is not set
 # CONFIG_USB_ISP116X_HCD is not set
 # CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
 # CONFIG_USB_SL811_HCD is not set
 # CONFIG_USB_R8A66597_HCD is not set
 # CONFIG_USB_HWA_HCD is not set
@@ -1269,6 +1305,7 @@ CONFIG_USB_GADGET_SELECTED=y
 # CONFIG_USB_GADGET_LH7A40X is not set
 # CONFIG_USB_GADGET_OMAP is not set
 # CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_R8A66597 is not set
 # CONFIG_USB_GADGET_PXA27X is not set
 # CONFIG_USB_GADGET_S3C_HSOTG is not set
 # CONFIG_USB_GADGET_IMX is not set
@@ -1286,6 +1323,7 @@ CONFIG_USB_ZERO=m
 # CONFIG_USB_AUDIO is not set
 CONFIG_USB_ETH=m
 CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_ETH_EEM is not set
 CONFIG_USB_GADGETFS=m
 CONFIG_USB_FILE_STORAGE=m
 # CONFIG_USB_FILE_STORAGE_TEST is not set
@@ -1316,8 +1354,10 @@ CONFIG_MMC_BLOCK=m
 # MMC/SD/SDIO Host Controller Drivers
 #
 # CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_AT91 is not set
+# CONFIG_MMC_ATMELMCI is not set
+CONFIG_MMC_DAVINCI=m
 # CONFIG_MEMSTICK is not set
-# CONFIG_ACCESSIBILITY is not set
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=m
 
@@ -1345,6 +1385,7 @@ CONFIG_LEDS_TRIGGER_HEARTBEAT=m
 #
 # iptables trigger is under Netfilter config (LED target)
 #
+# CONFIG_ACCESSIBILITY is not set
 CONFIG_RTC_LIB=y
 CONFIG_RTC_CLASS=m
 
@@ -1370,6 +1411,7 @@ CONFIG_RTC_INTF_DEV=y
 # CONFIG_RTC_DRV_PCF8563 is not set
 # CONFIG_RTC_DRV_PCF8583 is not set
 # CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_DM355EVM is not set
 # CONFIG_RTC_DRV_S35390A is not set
 # CONFIG_RTC_DRV_FM3130 is not set
 # CONFIG_RTC_DRV_RX8581 is not set
@@ -1399,8 +1441,11 @@ CONFIG_RTC_INTF_DEV=y
 #
 # CONFIG_DMADEVICES is not set
 # CONFIG_AUXDISPLAY is not set
-# CONFIG_REGULATOR is not set
 # CONFIG_UIO is not set
+
+#
+# TI VLYNQ
+#
 # CONFIG_STAGING is not set
 
 #
@@ -1429,6 +1474,7 @@ CONFIG_XFS_FS=m
 # CONFIG_GFS2_FS is not set
 # CONFIG_OCFS2_FS is not set
 # CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
 CONFIG_FILE_LOCKING=y
 CONFIG_FSNOTIFY=y
 CONFIG_DNOTIFY=y
@@ -1500,7 +1546,6 @@ CONFIG_MINIX_FS=m
 # 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
@@ -1596,6 +1641,7 @@ CONFIG_ENABLE_WARN_DEPRECATED=y
 CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_FRAME_WARN=1024
 # CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_STRIP_ASM_SYMS is not set
 # CONFIG_UNUSED_SYMBOLS is not set
 CONFIG_DEBUG_FS=y
 # CONFIG_HEADERS_CHECK is not set
@@ -1634,11 +1680,14 @@ CONFIG_DEBUG_BUGVERBOSE=y
 # CONFIG_DEBUG_LIST is not set
 # CONFIG_DEBUG_SG is not set
 # CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
 # 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_DEBUG_FORCE_WEAK_PER_CPU is not set
 # CONFIG_FAULT_INJECTION is not set
 # CONFIG_LATENCYTOP is not set
 # CONFIG_SYSCTL_SYSCALL_CHECK is not set
@@ -1663,7 +1712,7 @@ CONFIG_BRANCH_PROFILE_NONE=y
 # CONFIG_SAMPLES is not set
 CONFIG_HAVE_ARCH_KGDB=y
 # CONFIG_KGDB is not set
-CONFIG_ARM_UNWIND=y
+# CONFIG_ARM_UNWIND is not set
 CONFIG_DEBUG_USER=y
 CONFIG_DEBUG_ERRORS=y
 # CONFIG_DEBUG_STACK_USAGE is not set
@@ -1681,7 +1730,6 @@ 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
@@ -1713,11 +1761,13 @@ CONFIG_CRYPTO=y
 #
 # CONFIG_CRYPTO_HMAC is not set
 # CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
 
 #
 # Digest
 #
 # CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_GHASH is not set
 # CONFIG_CRYPTO_MD4 is not set
 # CONFIG_CRYPTO_MD5 is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
index 8482958..592457c 100644 (file)
@@ -963,10 +963,32 @@ CONFIG_FB_CFB_IMAGEBLIT=y
 #
 # CONFIG_FB_S1D13XXX is not set
 # CONFIG_FB_VIRTUAL is not set
-CONFIG_FB_OMAP=y
-# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_OMAP_LCD_VGA is not set
 # CONFIG_FB_OMAP_BOOTLOADER_INIT is not set
-CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE=2
+CONFIG_OMAP2_VRAM=y
+CONFIG_OMAP2_VRFB=y
+CONFIG_OMAP2_DSS=y
+CONFIG_OMAP2_VRAM_SIZE=4
+CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y
+# CONFIG_OMAP2_DSS_RFBI is not set
+CONFIG_OMAP2_DSS_VENC=y
+# CONFIG_OMAP2_DSS_SDI is not set
+# CONFIG_OMAP2_DSS_DSI is not set
+# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set
+CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0
+CONFIG_FB_OMAP2=y
+CONFIG_FB_OMAP2_DEBUG_SUPPORT=y
+# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set
+CONFIG_FB_OMAP2_NUM_FBS=3
+
+#
+# OMAP2/3 Display Device Drivers
+#
+CONFIG_PANEL_GENERIC=y
+CONFIG_PANEL_SHARP_LS037V7DW01=y
 # CONFIG_BACKLIGHT_LCD_SUPPORT is not set
 
 #
index a57af3e..809114d 100644 (file)
@@ -865,6 +865,57 @@ static void __init at91_add_device_rtc(void) {}
 #endif
 
 
+/* --------------------------------------------------------------------
+ *  Touchscreen
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) || defined(CONFIG_TOUCHSCREEN_ATMEL_TSADCC_MODULE)
+static u64 tsadcc_dmamask = DMA_BIT_MASK(32);
+static struct at91_tsadcc_data tsadcc_data;
+
+static struct resource tsadcc_resources[] = {
+       [0] = {
+               .start  = AT91SAM9G45_BASE_TSC,
+               .end    = AT91SAM9G45_BASE_TSC + SZ_16K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = AT91SAM9G45_ID_TSC,
+               .end    = AT91SAM9G45_ID_TSC,
+               .flags  = IORESOURCE_IRQ,
+       }
+};
+
+static struct platform_device at91sam9g45_tsadcc_device = {
+       .name           = "atmel_tsadcc",
+       .id             = -1,
+       .dev            = {
+                               .dma_mask               = &tsadcc_dmamask,
+                               .coherent_dma_mask      = DMA_BIT_MASK(32),
+                               .platform_data          = &tsadcc_data,
+       },
+       .resource       = tsadcc_resources,
+       .num_resources  = ARRAY_SIZE(tsadcc_resources),
+};
+
+void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data)
+{
+       if (!data)
+               return;
+
+       at91_set_gpio_input(AT91_PIN_PD20, 0);  /* AD0_XR */
+       at91_set_gpio_input(AT91_PIN_PD21, 0);  /* AD1_XL */
+       at91_set_gpio_input(AT91_PIN_PD22, 0);  /* AD2_YT */
+       at91_set_gpio_input(AT91_PIN_PD23, 0);  /* AD3_TB */
+
+       tsadcc_data = *data;
+       platform_device_register(&at91sam9g45_tsadcc_device);
+}
+#else
+void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data) {}
+#endif
+
+
 /* --------------------------------------------------------------------
  *  RTT
  * -------------------------------------------------------------------- */
index d345f54..53aaa94 100644 (file)
@@ -622,6 +622,7 @@ static void __init at91_add_device_tc(void) { }
 
 #if defined(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) || defined(CONFIG_TOUCHSCREEN_ATMEL_TSADCC_MODULE)
 static u64 tsadcc_dmamask = DMA_BIT_MASK(32);
+static struct at91_tsadcc_data tsadcc_data;
 
 static struct resource tsadcc_resources[] = {
        [0] = {
@@ -642,22 +643,27 @@ static struct platform_device at91sam9rl_tsadcc_device = {
        .dev            = {
                                .dma_mask               = &tsadcc_dmamask,
                                .coherent_dma_mask      = DMA_BIT_MASK(32),
+                               .platform_data          = &tsadcc_data,
        },
        .resource       = tsadcc_resources,
        .num_resources  = ARRAY_SIZE(tsadcc_resources),
 };
 
-void __init at91_add_device_tsadcc(void)
+void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data)
 {
+       if (!data)
+               return;
+
        at91_set_A_periph(AT91_PIN_PA17, 0);    /* AD0_XR */
        at91_set_A_periph(AT91_PIN_PA18, 0);    /* AD1_XL */
        at91_set_A_periph(AT91_PIN_PA19, 0);    /* AD2_YT */
        at91_set_A_periph(AT91_PIN_PA20, 0);    /* AD3_TB */
 
+       tsadcc_data = *data;
        platform_device_register(&at91sam9rl_tsadcc_device);
 }
 #else
-void __init at91_add_device_tsadcc(void) {}
+void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data) {}
 #endif
 
 
index 1cf4d86..98f9f4b 100644 (file)
@@ -228,6 +228,16 @@ static struct atmel_lcdfb_info __initdata ek_lcdc_data;
 #endif
 
 
+/*
+ * Touchscreen
+ */
+static struct at91_tsadcc_data ek_tsadcc_data = {
+       .adc_clock              = 300000,
+       .pendet_debounce        = 0x0d,
+       .ts_sample_hold_time    = 0x0a,
+};
+
+
 /*
  * GPIO Buttons
  */
@@ -379,6 +389,8 @@ static void __init ek_board_init(void)
        at91_add_device_i2c(0, NULL, 0);
        /* LCD Controller */
        at91_add_device_lcdc(&ek_lcdc_data);
+       /* Touch Screen */
+       at91_add_device_tsadcc(&ek_tsadcc_data);
        /* Push Buttons */
        ek_add_device_buttons();
        /* AC97 */
index bd28e98..7ac20f3 100644 (file)
@@ -242,6 +242,16 @@ static struct gpio_led ek_leds[] = {
 };
 
 
+/*
+ * Touchscreen
+ */
+static struct at91_tsadcc_data ek_tsadcc_data = {
+       .adc_clock              = 1000000,
+       .pendet_debounce        = 0x0f,
+       .ts_sample_hold_time    = 0x03,
+};
+
+
 /*
  * GPIO Buttons
  */
@@ -310,7 +320,7 @@ static void __init ek_board_init(void)
        /* AC97 */
        at91_add_device_ac97(&ek_ac97_data);
        /* Touch Screen Controller */
-       at91_add_device_tsadcc();
+       at91_add_device_tsadcc(&ek_tsadcc_data);
        /* LEDs */
        at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
        /* Push Buttons */
index 2295d80..bb6f6a7 100644 (file)
@@ -187,7 +187,12 @@ extern void __init at91_add_device_ac97(struct ac97c_platform_data *data);
 extern void __init at91_add_device_isi(void);
 
  /* Touchscreen Controller */
-extern void __init at91_add_device_tsadcc(void);
+struct at91_tsadcc_data {
+       unsigned int    adc_clock;
+       u8              pendet_debounce;
+       u8              ts_sample_hold_time;
+};
+extern void __init at91_add_device_tsadcc(struct at91_tsadcc_data *data);
 
 /* CAN */
 struct at91_can_data {
index e5f60bf..56654d2 100644 (file)
@@ -16,7 +16,7 @@
 /**
 *  @file    reg.h
 *
-*  @brief   Generic register defintions used in CSP
+*  @brief   Generic register definitions used in CSP
 */
 /****************************************************************************/
 
index 375066a..cbf334d 100644 (file)
@@ -83,7 +83,7 @@ typedef struct {
 *  @brief   Get next available transaction width
 *
 *
-*  @return  On sucess  : Next avail able transaction width
+*  @return  On success  : Next available transaction width
 *           On failure : dmacHw_TRANSACTION_WIDTH_8
 *
 *  @note
index 86bb58d..ad58cf8 100644 (file)
@@ -16,7 +16,7 @@
 /**
 *  @file    mm_addr.h
 *
-*  @brief   Memory Map address defintions
+*  @brief   Memory Map address definitions
 *
 *  @note
 *     None
index 847980c..1f2c531 100644 (file)
@@ -651,7 +651,7 @@ int dma_map_add_region(DMA_MemMap_t *memMap,        /* Stores state information about t
 /**
 *   Creates a descriptor ring from a memory mapping.
 *
-*   @return 0 on sucess, error code otherwise.
+*   @return 0 on success, error code otherwise.
 */
 /****************************************************************************/
 
index 40866c6..033bfed 100644 (file)
@@ -32,11 +32,13 @@ config ARCH_DAVINCI_DA830
         bool "DA830/OMAP-L137 based system"
        select CP_INTC
        select ARCH_DAVINCI_DA8XX
+       select CPU_DCACHE_WRITETHROUGH # needed on silicon revs 1.0, 1.1
 
 config ARCH_DAVINCI_DA850
        bool "DA850/OMAP-L138 based system"
        select CP_INTC
        select ARCH_DAVINCI_DA8XX
+       select ARCH_HAS_CPUFREQ
 
 config ARCH_DAVINCI_DA8XX
        bool
@@ -63,6 +65,13 @@ config MACH_SFFSDR
          Say Y here to select the Lyrtech Small Form Factor
          Software Defined Radio (SFFSDR) board.
 
+config MACH_NEUROS_OSD2
+       bool "Neuros OSD2 Open Television Set Top Box"
+       depends on ARCH_DAVINCI_DM644x
+       help
+         Configure this option to specify the whether the board used
+         for development is a Neuros OSD2 Open Set Top Box.
+
 config MACH_DAVINCI_DM355_EVM
        bool "TI DM355 EVM"
        default ARCH_DAVINCI_DM355
@@ -98,16 +107,66 @@ config MACH_DAVINCI_DA830_EVM
        bool "TI DA830/OMAP-L137 Reference Platform"
        default ARCH_DAVINCI_DA830
        depends on ARCH_DAVINCI_DA830
+       select GPIO_PCF857X
        help
          Say Y here to select the TI DA830/OMAP-L137 Evaluation Module.
 
+choice
+       prompt "Select DA830/OMAP-L137 UI board peripheral"
+       depends on MACH_DAVINCI_DA830_EVM
+       help
+         The presence of UI card on the DA830/OMAP-L137 EVM is detected
+         automatically based on successful probe of the I2C based GPIO
+         expander on that board. This option selected in this menu has
+         an effect only in case of a successful UI card detection.
+
+config DA830_UI_LCD
+       bool "LCD"
+       help
+         Say Y here to use the LCD as a framebuffer or simple character
+         display.
+
+config DA830_UI_NAND
+       bool "NAND flash"
+       help
+         Say Y here to use the NAND flash.  Do not forget to setup
+         the switch correctly.
+endchoice
+
 config MACH_DAVINCI_DA850_EVM
        bool "TI DA850/OMAP-L138 Reference Platform"
        default ARCH_DAVINCI_DA850
        depends on ARCH_DAVINCI_DA850
+       select GPIO_PCA953X
        help
          Say Y here to select the TI DA850/OMAP-L138 Evaluation Module.
 
+choice
+       prompt "Select peripherals connected to expander on UI board"
+       depends on MACH_DAVINCI_DA850_EVM
+       help
+         The presence of User Interface (UI) card on the DA850/OMAP-L138
+         EVM is detected automatically based on successful probe of the I2C
+         based GPIO expander on that card. This option selected in this
+         menu has an effect only in case of a successful UI card detection.
+
+config DA850_UI_NONE
+       bool "No peripheral is enabled"
+       help
+         Say Y if you do not want to enable any of the peripherals connected
+         to TCA6416 expander on DA850/OMAP-L138 EVM UI card
+
+config DA850_UI_RMII
+       bool "RMII Ethernet PHY"
+       help
+         Say Y if you want to use the RMII PHY on the DA850/OMAP-L138 EVM.
+         This PHY is found on the UI daughter card that is supplied with
+         the EVM.
+         NOTE: Please take care while choosing this option, MII PHY will
+         not be functional if RMII mode is selected.
+
+endchoice
+
 config DAVINCI_MUX
        bool "DAVINCI multiplexing support"
        depends on ARCH_DAVINCI
index 2e11e84..eeb9230 100644 (file)
@@ -23,9 +23,14 @@ obj-$(CONFIG_CP_INTC)                        += cp_intc.o
 # Board specific
 obj-$(CONFIG_MACH_DAVINCI_EVM)         += board-dm644x-evm.o
 obj-$(CONFIG_MACH_SFFSDR)              += board-sffsdr.o
+obj-$(CONFIG_MACH_NEUROS_OSD2)         += board-neuros-osd2.o
 obj-$(CONFIG_MACH_DAVINCI_DM355_EVM)   += board-dm355-evm.o
 obj-$(CONFIG_MACH_DM355_LEOPARD)       += board-dm355-leopard.o
 obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM)  += board-dm646x-evm.o
 obj-$(CONFIG_MACH_DAVINCI_DM365_EVM)   += board-dm365-evm.o
 obj-$(CONFIG_MACH_DAVINCI_DA830_EVM)   += board-da830-evm.o
 obj-$(CONFIG_MACH_DAVINCI_DA850_EVM)   += board-da850-evm.o
+
+# Power Management
+obj-$(CONFIG_CPU_FREQ)                 += cpufreq.o
+obj-$(CONFIG_CPU_IDLE)                 += cpuidle.o
index bfbb639..31dc990 100644 (file)
  * or implied.
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
 #include <linux/i2c.h>
+#include <linux/i2c/pcf857x.h>
 #include <linux/i2c/at24.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
-#include <mach/common.h>
-#include <mach/irqs.h>
 #include <mach/cp_intc.h>
+#include <mach/mux.h>
+#include <mach/nand.h>
 #include <mach/da8xx.h>
-#include <mach/asp.h>
+#include <mach/usb.h>
 
 #define DA830_EVM_PHY_MASK             0x0
 #define DA830_EVM_MDIO_FREQUENCY       2200000 /* PHY bus frequency */
 
-static struct at24_platform_data da830_evm_i2c_eeprom_info = {
-       .byte_len       = SZ_256K / 8,
-       .page_size      = 64,
-       .flags          = AT24_FLAG_ADDR16,
-       .setup          = davinci_get_mac_addr,
-       .context        = (void *)0x7f00,
-};
+#define DA830_EMIF25_ASYNC_DATA_CE3_BASE       0x62000000
+#define DA830_EMIF25_CONTROL_BASE              0x68000000
 
-static struct i2c_board_info __initdata da830_evm_i2c_devices[] = {
-       {
-               I2C_BOARD_INFO("24c256", 0x50),
-               .platform_data  = &da830_evm_i2c_eeprom_info,
-       },
-       {
-               I2C_BOARD_INFO("tlv320aic3x", 0x18),
-       }
+/*
+ * USB1 VBUS is controlled by GPIO1[15], over-current is reported on GPIO2[4].
+ */
+#define ON_BD_USB_DRV  GPIO_TO_PIN(1, 15)
+#define ON_BD_USB_OVC  GPIO_TO_PIN(2, 4)
+
+static const short da830_evm_usb11_pins[] = {
+       DA830_GPIO1_15, DA830_GPIO2_4,
+       -1
 };
 
-static struct davinci_i2c_platform_data da830_evm_i2c_0_pdata = {
-       .bus_freq       = 100,  /* kHz */
-       .bus_delay      = 0,    /* usec */
+static da8xx_ocic_handler_t da830_evm_usb_ocic_handler;
+
+static int da830_evm_usb_set_power(unsigned port, int on)
+{
+       gpio_set_value(ON_BD_USB_DRV, on);
+       return 0;
+}
+
+static int da830_evm_usb_get_power(unsigned port)
+{
+       return gpio_get_value(ON_BD_USB_DRV);
+}
+
+static int da830_evm_usb_get_oci(unsigned port)
+{
+       return !gpio_get_value(ON_BD_USB_OVC);
+}
+
+static irqreturn_t da830_evm_usb_ocic_irq(int, void *);
+
+static int da830_evm_usb_ocic_notify(da8xx_ocic_handler_t handler)
+{
+       int irq         = gpio_to_irq(ON_BD_USB_OVC);
+       int error       = 0;
+
+       if (handler != NULL) {
+               da830_evm_usb_ocic_handler = handler;
+
+               error = request_irq(irq, da830_evm_usb_ocic_irq, IRQF_DISABLED |
+                                   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                                   "OHCI over-current indicator", NULL);
+               if (error)
+                       printk(KERN_ERR "%s: could not request IRQ to watch "
+                              "over-current indicator changes\n", __func__);
+       } else
+               free_irq(irq, NULL);
+
+       return error;
+}
+
+static struct da8xx_ohci_root_hub da830_evm_usb11_pdata = {
+       .set_power      = da830_evm_usb_set_power,
+       .get_power      = da830_evm_usb_get_power,
+       .get_oci        = da830_evm_usb_get_oci,
+       .ocic_notify    = da830_evm_usb_ocic_notify,
+
+       /* TPS2065 switch @ 5V */
+       .potpgt         = (3 + 1) / 2,  /* 3 ms max */
 };
 
+static irqreturn_t da830_evm_usb_ocic_irq(int irq, void *dev_id)
+{
+       da830_evm_usb_ocic_handler(&da830_evm_usb11_pdata, 1);
+       return IRQ_HANDLED;
+}
+
+static __init void da830_evm_usb_init(void)
+{
+       u32 cfgchip2;
+       int ret;
+
+       /*
+        * Set up USB clock/mode in the CFGCHIP2 register.
+        * FYI:  CFGCHIP2 is 0x0000ef00 initially.
+        */
+       cfgchip2 = __raw_readl(DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG));
+
+       /* USB2.0 PHY reference clock is 24 MHz */
+       cfgchip2 &= ~CFGCHIP2_REFFREQ;
+       cfgchip2 |=  CFGCHIP2_REFFREQ_24MHZ;
+
+       /*
+        * Select internal reference clock for USB 2.0 PHY
+        * and use it as a clock source for USB 1.1 PHY
+        * (this is the default setting anyway).
+        */
+       cfgchip2 &= ~CFGCHIP2_USB1PHYCLKMUX;
+       cfgchip2 |=  CFGCHIP2_USB2PHYCLKMUX;
+
+       /*
+        * We have to override VBUS/ID signals when MUSB is configured into the
+        * host-only mode -- ID pin will float if no cable is connected, so the
+        * controller won't be able to drive VBUS thinking that it's a B-device.
+        * Otherwise, we want to use the OTG mode and enable VBUS comparators.
+        */
+       cfgchip2 &= ~CFGCHIP2_OTGMODE;
+#ifdef CONFIG_USB_MUSB_HOST
+       cfgchip2 |=  CFGCHIP2_FORCE_HOST;
+#else
+       cfgchip2 |=  CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN;
+#endif
+
+       __raw_writel(cfgchip2, DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG));
+
+       /* USB_REFCLKIN is not used. */
+       ret = davinci_cfg_reg(DA830_USB0_DRVVBUS);
+       if (ret)
+               pr_warning("%s: USB 2.0 PinMux setup failed: %d\n",
+                          __func__, ret);
+       else {
+               /*
+                * TPS2065 switch @ 5V supplies 1 A (sustains 1.5 A),
+                * with the power on to power good time of 3 ms.
+                */
+               ret = da8xx_register_usb20(1000, 3);
+               if (ret)
+                       pr_warning("%s: USB 2.0 registration failed: %d\n",
+                                  __func__, ret);
+       }
+
+       ret = da8xx_pinmux_setup(da830_evm_usb11_pins);
+       if (ret) {
+               pr_warning("%s: USB 1.1 PinMux setup failed: %d\n",
+                          __func__, ret);
+               return;
+       }
+
+       ret = gpio_request(ON_BD_USB_DRV, "ON_BD_USB_DRV");
+       if (ret) {
+               printk(KERN_ERR "%s: failed to request GPIO for USB 1.1 port "
+                      "power control: %d\n", __func__, ret);
+               return;
+       }
+       gpio_direction_output(ON_BD_USB_DRV, 0);
+
+       ret = gpio_request(ON_BD_USB_OVC, "ON_BD_USB_OVC");
+       if (ret) {
+               printk(KERN_ERR "%s: failed to request GPIO for USB 1.1 port "
+                      "over-current indicator: %d\n", __func__, ret);
+               return;
+       }
+       gpio_direction_input(ON_BD_USB_OVC);
+
+       ret = da8xx_register_usb11(&da830_evm_usb11_pdata);
+       if (ret)
+               pr_warning("%s: USB 1.1 registration failed: %d\n",
+                          __func__, ret);
+}
+
 static struct davinci_uart_config da830_evm_uart_config __initdata = {
        .enabled_uarts = 0x7,
 };
 
+static const short da830_evm_mcasp1_pins[] = {
+       DA830_AHCLKX1, DA830_ACLKX1, DA830_AFSX1, DA830_AHCLKR1, DA830_AFSR1,
+       DA830_AMUTE1, DA830_AXR1_0, DA830_AXR1_1, DA830_AXR1_2, DA830_AXR1_5,
+       DA830_ACLKR1, DA830_AXR1_6, DA830_AXR1_7, DA830_AXR1_8, DA830_AXR1_10,
+       DA830_AXR1_11,
+       -1
+};
+
 static u8 da830_iis_serializer_direction[] = {
        RX_MODE,        INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,
        INACTIVE_MODE,  TX_MODE,        INACTIVE_MODE,  INACTIVE_MODE,
@@ -74,6 +217,271 @@ static struct snd_platform_data da830_evm_snd_data = {
        .rxnumevt       = 1,
 };
 
+/*
+ * GPIO2[1] is used as MMC_SD_WP and GPIO2[2] as MMC_SD_INS.
+ */
+static const short da830_evm_mmc_sd_pins[] = {
+       DA830_MMCSD_DAT_0, DA830_MMCSD_DAT_1, DA830_MMCSD_DAT_2,
+       DA830_MMCSD_DAT_3, DA830_MMCSD_DAT_4, DA830_MMCSD_DAT_5,
+       DA830_MMCSD_DAT_6, DA830_MMCSD_DAT_7, DA830_MMCSD_CLK,
+       DA830_MMCSD_CMD,   DA830_GPIO2_1,     DA830_GPIO2_2,
+       -1
+};
+
+#define DA830_MMCSD_WP_PIN             GPIO_TO_PIN(2, 1)
+
+static int da830_evm_mmc_get_ro(int index)
+{
+       return gpio_get_value(DA830_MMCSD_WP_PIN);
+}
+
+static struct davinci_mmc_config da830_evm_mmc_config = {
+       .get_ro                 = da830_evm_mmc_get_ro,
+       .wires                  = 4,
+       .max_freq               = 50000000,
+       .caps                   = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
+       .version                = MMC_CTLR_VERSION_2,
+};
+
+static inline void da830_evm_init_mmc(void)
+{
+       int ret;
+
+       ret = da8xx_pinmux_setup(da830_evm_mmc_sd_pins);
+       if (ret) {
+               pr_warning("da830_evm_init: mmc/sd mux setup failed: %d\n",
+                               ret);
+               return;
+       }
+
+       ret = gpio_request(DA830_MMCSD_WP_PIN, "MMC WP");
+       if (ret) {
+               pr_warning("da830_evm_init: can not open GPIO %d\n",
+                          DA830_MMCSD_WP_PIN);
+               return;
+       }
+       gpio_direction_input(DA830_MMCSD_WP_PIN);
+
+       ret = da8xx_register_mmcsd0(&da830_evm_mmc_config);
+       if (ret) {
+               pr_warning("da830_evm_init: mmc/sd registration failed: %d\n",
+                               ret);
+               gpio_free(DA830_MMCSD_WP_PIN);
+       }
+}
+
+/*
+ * UI board NAND/NOR flashes only use 8-bit data bus.
+ */
+static const short da830_evm_emif25_pins[] = {
+       DA830_EMA_D_0, DA830_EMA_D_1, DA830_EMA_D_2, DA830_EMA_D_3,
+       DA830_EMA_D_4, DA830_EMA_D_5, DA830_EMA_D_6, DA830_EMA_D_7,
+       DA830_EMA_A_0, DA830_EMA_A_1, DA830_EMA_A_2, DA830_EMA_A_3,
+       DA830_EMA_A_4, DA830_EMA_A_5, DA830_EMA_A_6, DA830_EMA_A_7,
+       DA830_EMA_A_8, DA830_EMA_A_9, DA830_EMA_A_10, DA830_EMA_A_11,
+       DA830_EMA_A_12, DA830_EMA_BA_0, DA830_EMA_BA_1, DA830_NEMA_WE,
+       DA830_NEMA_CS_2, DA830_NEMA_CS_3, DA830_NEMA_OE, DA830_EMA_WAIT_0,
+       -1
+};
+
+#if defined(CONFIG_MMC_DAVINCI) || defined(CONFIG_MMC_DAVINCI_MODULE)
+#define HAS_MMC        1
+#else
+#define HAS_MMC        0
+#endif
+
+#ifdef CONFIG_DA830_UI_NAND
+static struct mtd_partition da830_evm_nand_partitions[] = {
+       /* bootloader (U-Boot, etc) in first sector */
+       [0] = {
+               .name           = "bootloader",
+               .offset         = 0,
+               .size           = SZ_128K,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
+       /* bootloader params in the next sector */
+       [1] = {
+               .name           = "params",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_128K,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
+       /* kernel */
+       [2] = {
+               .name           = "kernel",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_2M,
+               .mask_flags     = 0,
+       },
+       /* file system */
+       [3] = {
+               .name           = "filesystem",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+               .mask_flags     = 0,
+       }
+};
+
+/* flash bbt decriptors */
+static uint8_t da830_evm_nand_bbt_pattern[] = { 'B', 'b', 't', '0' };
+static uint8_t da830_evm_nand_mirror_pattern[] = { '1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr da830_evm_nand_bbt_main_descr = {
+       .options        = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE |
+                         NAND_BBT_WRITE | NAND_BBT_2BIT |
+                         NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs           = 2,
+       .len            = 4,
+       .veroffs        = 16,
+       .maxblocks      = 4,
+       .pattern        = da830_evm_nand_bbt_pattern
+};
+
+static struct nand_bbt_descr da830_evm_nand_bbt_mirror_descr = {
+       .options        = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE |
+                         NAND_BBT_WRITE | NAND_BBT_2BIT |
+                         NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+       .offs           = 2,
+       .len            = 4,
+       .veroffs        = 16,
+       .maxblocks      = 4,
+       .pattern        = da830_evm_nand_mirror_pattern
+};
+
+static struct davinci_nand_pdata da830_evm_nand_pdata = {
+       .parts          = da830_evm_nand_partitions,
+       .nr_parts       = ARRAY_SIZE(da830_evm_nand_partitions),
+       .ecc_mode       = NAND_ECC_HW,
+       .ecc_bits       = 4,
+       .options        = NAND_USE_FLASH_BBT,
+       .bbt_td         = &da830_evm_nand_bbt_main_descr,
+       .bbt_md         = &da830_evm_nand_bbt_mirror_descr,
+};
+
+static struct resource da830_evm_nand_resources[] = {
+       [0] = {         /* First memory resource is NAND I/O window */
+               .start  = DA830_EMIF25_ASYNC_DATA_CE3_BASE,
+               .end    = DA830_EMIF25_ASYNC_DATA_CE3_BASE + PAGE_SIZE - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {         /* Second memory resource is AEMIF control registers */
+               .start  = DA830_EMIF25_CONTROL_BASE,
+               .end    = DA830_EMIF25_CONTROL_BASE + SZ_32K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device da830_evm_nand_device = {
+       .name           = "davinci_nand",
+       .id             = 1,
+       .dev            = {
+               .platform_data  = &da830_evm_nand_pdata,
+       },
+       .num_resources  = ARRAY_SIZE(da830_evm_nand_resources),
+       .resource       = da830_evm_nand_resources,
+};
+
+static inline void da830_evm_init_nand(int mux_mode)
+{
+       int ret;
+
+       if (HAS_MMC) {
+               pr_warning("WARNING: both MMC/SD and NAND are "
+                               "enabled, but they share AEMIF pins.\n"
+                               "\tDisable MMC/SD for NAND support.\n");
+               return;
+       }
+
+       ret = da8xx_pinmux_setup(da830_evm_emif25_pins);
+       if (ret)
+               pr_warning("da830_evm_init: emif25 mux setup failed: %d\n",
+                               ret);
+
+       ret = platform_device_register(&da830_evm_nand_device);
+       if (ret)
+               pr_warning("da830_evm_init: NAND device not registered.\n");
+
+       gpio_direction_output(mux_mode, 1);
+}
+#else
+static inline void da830_evm_init_nand(int mux_mode) { }
+#endif
+
+#ifdef CONFIG_DA830_UI_LCD
+static inline void da830_evm_init_lcdc(int mux_mode)
+{
+       int ret;
+
+       ret = da8xx_pinmux_setup(da830_lcdcntl_pins);
+       if (ret)
+               pr_warning("da830_evm_init: lcdcntl mux setup failed: %d\n",
+                               ret);
+
+       ret = da8xx_register_lcdc(&sharp_lcd035q3dg01_pdata);
+       if (ret)
+               pr_warning("da830_evm_init: lcd setup failed: %d\n", ret);
+
+       gpio_direction_output(mux_mode, 0);
+}
+#else
+static inline void da830_evm_init_lcdc(int mux_mode) { }
+#endif
+
+static struct at24_platform_data da830_evm_i2c_eeprom_info = {
+       .byte_len       = SZ_256K / 8,
+       .page_size      = 64,
+       .flags          = AT24_FLAG_ADDR16,
+       .setup          = davinci_get_mac_addr,
+       .context        = (void *)0x7f00,
+};
+
+static int __init da830_evm_ui_expander_setup(struct i2c_client *client,
+               int gpio, unsigned ngpio, void *context)
+{
+       gpio_request(gpio + 6, "UI MUX_MODE");
+
+       /* Drive mux mode low to match the default without UI card */
+       gpio_direction_output(gpio + 6, 0);
+
+       da830_evm_init_lcdc(gpio + 6);
+
+       da830_evm_init_nand(gpio + 6);
+
+       return 0;
+}
+
+static int da830_evm_ui_expander_teardown(struct i2c_client *client, int gpio,
+               unsigned ngpio, void *context)
+{
+       gpio_free(gpio + 6);
+       return 0;
+}
+
+static struct pcf857x_platform_data __initdata da830_evm_ui_expander_info = {
+       .gpio_base      = DAVINCI_N_GPIO,
+       .setup          = da830_evm_ui_expander_setup,
+       .teardown       = da830_evm_ui_expander_teardown,
+};
+
+static struct i2c_board_info __initdata da830_evm_i2c_devices[] = {
+       {
+               I2C_BOARD_INFO("24c256", 0x50),
+               .platform_data  = &da830_evm_i2c_eeprom_info,
+       },
+       {
+               I2C_BOARD_INFO("tlv320aic3x", 0x18),
+       },
+       {
+               I2C_BOARD_INFO("pcf8574", 0x3f),
+               .platform_data  = &da830_evm_ui_expander_info,
+       },
+};
+
+static struct davinci_i2c_platform_data da830_evm_i2c_0_pdata = {
+       .bus_freq       = 100,  /* kHz */
+       .bus_delay      = 0,    /* usec */
+};
+
 static __init void da830_evm_init(void)
 {
        struct davinci_soc_info *soc_info = &davinci_soc_info;
@@ -94,6 +502,8 @@ static __init void da830_evm_init(void)
                pr_warning("da830_evm_init: i2c0 registration failed: %d\n",
                                ret);
 
+       da830_evm_usb_init();
+
        soc_info->emac_pdata->phy_mask = DA830_EVM_PHY_MASK;
        soc_info->emac_pdata->mdio_max_freq = DA830_EVM_MDIO_FREQUENCY;
        soc_info->emac_pdata->rmii_en = 1;
@@ -117,12 +527,18 @@ static __init void da830_evm_init(void)
        i2c_register_board_info(1, da830_evm_i2c_devices,
                        ARRAY_SIZE(da830_evm_i2c_devices));
 
-       ret = da8xx_pinmux_setup(da830_mcasp1_pins);
+       ret = da8xx_pinmux_setup(da830_evm_mcasp1_pins);
        if (ret)
                pr_warning("da830_evm_init: mcasp1 mux setup failed: %d\n",
                                ret);
 
-       da8xx_init_mcasp(1, &da830_evm_snd_data);
+       da8xx_register_mcasp(1, &da830_evm_snd_data);
+
+       da830_evm_init_mmc();
+
+       ret = da8xx_register_rtc();
+       if (ret)
+               pr_warning("da830_evm_init: rtc setup failed: %d\n", ret);
 }
 
 #ifdef CONFIG_SERIAL_8250_CONSOLE
@@ -146,7 +562,7 @@ static void __init da830_evm_map_io(void)
        da830_init();
 }
 
-MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP L137 EVM")
+MACHINE_START(DAVINCI_DA830_EVM, "DaVinci DA830/OMAP-L137 EVM")
        .phys_io        = IO_PHYS,
        .io_pg_offst    = (__IO_ADDRESS(IO_PHYS) >> 18) & 0xfffc,
        .boot_params    = (DA8XX_DDR_BASE + 0x100),
index c759d72..62b98bf 100644 (file)
  * or implied.
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/i2c.h>
 #include <linux/i2c/at24.h>
+#include <linux/i2c/pca953x.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
+#include <linux/regulator/machine.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
-#include <mach/common.h>
-#include <mach/irqs.h>
 #include <mach/cp_intc.h>
 #include <mach/da8xx.h>
 #include <mach/nand.h>
+#include <mach/mux.h>
 
 #define DA850_EVM_PHY_MASK             0x1
 #define DA850_EVM_MDIO_FREQUENCY       2200000 /* PHY bus frequency */
 
+#define DA850_LCD_PWR_PIN              GPIO_TO_PIN(2, 8)
 #define DA850_LCD_BL_PIN               GPIO_TO_PIN(2, 15)
-#define DA850_LCD_PWR_PIN              GPIO_TO_PIN(8, 10)
 
 #define DA850_MMCSD_CD_PIN             GPIO_TO_PIN(4, 0)
 #define DA850_MMCSD_WP_PIN             GPIO_TO_PIN(4, 1)
 
+#define DA850_MII_MDIO_CLKEN_PIN       GPIO_TO_PIN(2, 6)
+
 static struct mtd_partition da850_evm_norflash_partition[] = {
        {
                .name           = "NOR filesystem",
@@ -143,10 +145,149 @@ static struct platform_device da850_evm_nandflash_device = {
        .resource       = da850_evm_nandflash_resource,
 };
 
+static struct platform_device *da850_evm_devices[] __initdata = {
+       &da850_evm_nandflash_device,
+       &da850_evm_norflash_device,
+};
+
+#define DA8XX_AEMIF_CE2CFG_OFFSET      0x10
+#define DA8XX_AEMIF_ASIZE_16BIT                0x1
+
+static void __init da850_evm_init_nor(void)
+{
+       void __iomem *aemif_addr;
+
+       aemif_addr = ioremap(DA8XX_AEMIF_CTL_BASE, SZ_32K);
+
+       /* Configure data bus width of CS2 to 16 bit */
+       writel(readl(aemif_addr + DA8XX_AEMIF_CE2CFG_OFFSET) |
+               DA8XX_AEMIF_ASIZE_16BIT,
+               aemif_addr + DA8XX_AEMIF_CE2CFG_OFFSET);
+
+       iounmap(aemif_addr);
+}
+
+static u32 ui_card_detected;
+
+#if defined(CONFIG_MMC_DAVINCI) || \
+    defined(CONFIG_MMC_DAVINCI_MODULE)
+#define HAS_MMC 1
+#else
+#define HAS_MMC 0
+#endif
+
+static __init void da850_evm_setup_nor_nand(void)
+{
+       int ret = 0;
+
+       if (ui_card_detected & !HAS_MMC) {
+               ret = da8xx_pinmux_setup(da850_nand_pins);
+               if (ret)
+                       pr_warning("da850_evm_init: nand mux setup failed: "
+                                       "%d\n", ret);
+
+               ret = da8xx_pinmux_setup(da850_nor_pins);
+               if (ret)
+                       pr_warning("da850_evm_init: nor mux setup failed: %d\n",
+                               ret);
+
+               da850_evm_init_nor();
+
+               platform_add_devices(da850_evm_devices,
+                                       ARRAY_SIZE(da850_evm_devices));
+       }
+}
+
+#ifdef CONFIG_DA850_UI_RMII
+static inline void da850_evm_setup_emac_rmii(int rmii_sel)
+{
+       struct davinci_soc_info *soc_info = &davinci_soc_info;
+
+       soc_info->emac_pdata->rmii_en = 1;
+       gpio_set_value(rmii_sel, 0);
+}
+#else
+static inline void da850_evm_setup_emac_rmii(int rmii_sel) { }
+#endif
+
+static int da850_evm_ui_expander_setup(struct i2c_client *client, unsigned gpio,
+                                               unsigned ngpio, void *c)
+{
+       int sel_a, sel_b, sel_c, ret;
+
+       sel_a = gpio + 7;
+       sel_b = gpio + 6;
+       sel_c = gpio + 5;
+
+       ret = gpio_request(sel_a, "sel_a");
+       if (ret) {
+               pr_warning("Cannot open UI expander pin %d\n", sel_a);
+               goto exp_setup_sela_fail;
+       }
+
+       ret = gpio_request(sel_b, "sel_b");
+       if (ret) {
+               pr_warning("Cannot open UI expander pin %d\n", sel_b);
+               goto exp_setup_selb_fail;
+       }
+
+       ret = gpio_request(sel_c, "sel_c");
+       if (ret) {
+               pr_warning("Cannot open UI expander pin %d\n", sel_c);
+               goto exp_setup_selc_fail;
+       }
+
+       /* deselect all functionalities */
+       gpio_direction_output(sel_a, 1);
+       gpio_direction_output(sel_b, 1);
+       gpio_direction_output(sel_c, 1);
+
+       ui_card_detected = 1;
+       pr_info("DA850/OMAP-L138 EVM UI card detected\n");
+
+       da850_evm_setup_nor_nand();
+
+       da850_evm_setup_emac_rmii(sel_a);
+
+       return 0;
+
+exp_setup_selc_fail:
+       gpio_free(sel_b);
+exp_setup_selb_fail:
+       gpio_free(sel_a);
+exp_setup_sela_fail:
+       return ret;
+}
+
+static int da850_evm_ui_expander_teardown(struct i2c_client *client,
+                                       unsigned gpio, unsigned ngpio, void *c)
+{
+       /* deselect all functionalities */
+       gpio_set_value(gpio + 5, 1);
+       gpio_set_value(gpio + 6, 1);
+       gpio_set_value(gpio + 7, 1);
+
+       gpio_free(gpio + 5);
+       gpio_free(gpio + 6);
+       gpio_free(gpio + 7);
+
+       return 0;
+}
+
+static struct pca953x_platform_data da850_evm_ui_expander_info = {
+       .gpio_base      = DAVINCI_N_GPIO,
+       .setup          = da850_evm_ui_expander_setup,
+       .teardown       = da850_evm_ui_expander_teardown,
+};
+
 static struct i2c_board_info __initdata da850_evm_i2c_devices[] = {
        {
                I2C_BOARD_INFO("tlv320aic3x", 0x18),
-       }
+       },
+       {
+               I2C_BOARD_INFO("tca6416", 0x20),
+               .platform_data = &da850_evm_ui_expander_info,
+       },
 };
 
 static struct davinci_i2c_platform_data da850_evm_i2c_0_pdata = {
@@ -158,11 +299,6 @@ static struct davinci_uart_config da850_evm_uart_config __initdata = {
        .enabled_uarts = 0x7,
 };
 
-static struct platform_device *da850_evm_devices[] __initdata = {
-       &da850_evm_nandflash_device,
-       &da850_evm_norflash_device,
-};
-
 /* davinci da850 evm audio machine driver */
 static u8 da850_iis_serializer_direction[] = {
        INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,
@@ -198,6 +334,8 @@ static struct davinci_mmc_config da850_mmc_config = {
        .get_ro         = da850_evm_mmc_get_ro,
        .get_cd         = da850_evm_mmc_get_cd,
        .wires          = 4,
+       .max_freq       = 50000000,
+       .caps           = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED,
        .version        = MMC_CTLR_VERSION_2,
 };
 
@@ -233,56 +371,227 @@ static int da850_lcd_hw_init(void)
        return 0;
 }
 
-#define DA8XX_AEMIF_CE2CFG_OFFSET      0x10
-#define DA8XX_AEMIF_ASIZE_16BIT                0x1
+/* TPS65070 voltage regulator support */
 
-static void __init da850_evm_init_nor(void)
-{
-       void __iomem *aemif_addr;
+/* 3.3V */
+struct regulator_consumer_supply tps65070_dcdc1_consumers[] = {
+       {
+               .supply = "usb0_vdda33",
+       },
+       {
+               .supply = "usb1_vdda33",
+       },
+};
 
-       aemif_addr = ioremap(DA8XX_AEMIF_CTL_BASE, SZ_32K);
+/* 3.3V or 1.8V */
+struct regulator_consumer_supply tps65070_dcdc2_consumers[] = {
+       {
+               .supply = "dvdd3318_a",
+       },
+       {
+               .supply = "dvdd3318_b",
+       },
+       {
+               .supply = "dvdd3318_c",
+       },
+};
 
-       /* Configure data bus width of CS2 to 16 bit */
-       writel(readl(aemif_addr + DA8XX_AEMIF_CE2CFG_OFFSET) |
-               DA8XX_AEMIF_ASIZE_16BIT,
-               aemif_addr + DA8XX_AEMIF_CE2CFG_OFFSET);
+/* 1.2V */
+struct regulator_consumer_supply tps65070_dcdc3_consumers[] = {
+       {
+               .supply = "cvdd",
+       },
+};
 
-       iounmap(aemif_addr);
-}
+/* 1.8V LDO */
+struct regulator_consumer_supply tps65070_ldo1_consumers[] = {
+       {
+               .supply = "sata_vddr",
+       },
+       {
+               .supply = "usb0_vdda18",
+       },
+       {
+               .supply = "usb1_vdda18",
+       },
+       {
+               .supply = "ddr_dvdd18",
+       },
+};
 
-#if defined(CONFIG_MTD_PHYSMAP) || \
-    defined(CONFIG_MTD_PHYSMAP_MODULE)
-#define HAS_NOR 1
-#else
-#define HAS_NOR 0
-#endif
+/* 1.2V LDO */
+struct regulator_consumer_supply tps65070_ldo2_consumers[] = {
+       {
+               .supply = "sata_vdd",
+       },
+       {
+               .supply = "pll0_vdda",
+       },
+       {
+               .supply = "pll1_vdda",
+       },
+       {
+               .supply = "usbs_cvdd",
+       },
+       {
+               .supply = "vddarnwa1",
+       },
+};
 
-#if defined(CONFIG_MMC_DAVINCI) || \
-    defined(CONFIG_MMC_DAVINCI_MODULE)
-#define HAS_MMC 1
-#else
-#define HAS_MMC 0
-#endif
+struct regulator_init_data tps65070_regulator_data[] = {
+       /* dcdc1 */
+       {
+               .constraints = {
+                       .min_uV = 3150000,
+                       .max_uV = 3450000,
+                       .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS),
+                       .boot_on = 1,
+               },
+               .num_consumer_supplies = ARRAY_SIZE(tps65070_dcdc1_consumers),
+               .consumer_supplies = tps65070_dcdc1_consumers,
+       },
 
-static __init void da850_evm_init(void)
+       /* dcdc2 */
+       {
+               .constraints = {
+                       .min_uV = 1710000,
+                       .max_uV = 3450000,
+                       .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS),
+                       .boot_on = 1,
+               },
+               .num_consumer_supplies = ARRAY_SIZE(tps65070_dcdc2_consumers),
+               .consumer_supplies = tps65070_dcdc2_consumers,
+       },
+
+       /* dcdc3 */
+       {
+               .constraints = {
+                       .min_uV = 950000,
+                       .max_uV = 1320000,
+                       .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS),
+                       .boot_on = 1,
+               },
+               .num_consumer_supplies = ARRAY_SIZE(tps65070_dcdc3_consumers),
+               .consumer_supplies = tps65070_dcdc3_consumers,
+       },
+
+       /* ldo1 */
+       {
+               .constraints = {
+                       .min_uV = 1710000,
+                       .max_uV = 1890000,
+                       .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS),
+                       .boot_on = 1,
+               },
+               .num_consumer_supplies = ARRAY_SIZE(tps65070_ldo1_consumers),
+               .consumer_supplies = tps65070_ldo1_consumers,
+       },
+
+       /* ldo2 */
+       {
+               .constraints = {
+                       .min_uV = 1140000,
+                       .max_uV = 1320000,
+                       .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+                               REGULATOR_CHANGE_STATUS),
+                       .boot_on = 1,
+               },
+               .num_consumer_supplies = ARRAY_SIZE(tps65070_ldo2_consumers),
+               .consumer_supplies = tps65070_ldo2_consumers,
+       },
+};
+
+static struct i2c_board_info __initdata da850evm_tps65070_info[] = {
+       {
+               I2C_BOARD_INFO("tps6507x", 0x48),
+               .platform_data = &tps65070_regulator_data[0],
+       },
+};
+
+static int __init pmic_tps65070_init(void)
 {
-       struct davinci_soc_info *soc_info = &davinci_soc_info;
+       return i2c_register_board_info(1, da850evm_tps65070_info,
+                                       ARRAY_SIZE(da850evm_tps65070_info));
+}
+
+static const short da850_evm_lcdc_pins[] = {
+       DA850_GPIO2_8, DA850_GPIO2_15,
+       -1
+};
+
+static int __init da850_evm_config_emac(void)
+{
+       void __iomem *cfg_chip3_base;
        int ret;
+       u32 val;
+       struct davinci_soc_info *soc_info = &davinci_soc_info;
+       u8 rmii_en = soc_info->emac_pdata->rmii_en;
+
+       if (!machine_is_davinci_da850_evm())
+               return 0;
+
+       cfg_chip3_base = DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP3_REG);
+
+       val = __raw_readl(cfg_chip3_base);
+
+       if (rmii_en) {
+               val |= BIT(8);
+               ret = da8xx_pinmux_setup(da850_rmii_pins);
+               pr_info("EMAC: RMII PHY configured, MII PHY will not be"
+                                                       " functional\n");
+       } else {
+               val &= ~BIT(8);
+               ret = da8xx_pinmux_setup(da850_cpgmac_pins);
+               pr_info("EMAC: MII PHY configured, RMII PHY will not be"
+                                                       " functional\n");
+       }
 
-       ret = da8xx_pinmux_setup(da850_nand_pins);
        if (ret)
-               pr_warning("da850_evm_init: nand mux setup failed: %d\n",
+               pr_warning("da850_evm_init: cpgmac/rmii mux setup failed: %d\n",
                                ret);
 
-       ret = da8xx_pinmux_setup(da850_nor_pins);
+       /* configure the CFGCHIP3 register for RMII or MII */
+       __raw_writel(val, cfg_chip3_base);
+
+       ret = davinci_cfg_reg(DA850_GPIO2_6);
        if (ret)
-               pr_warning("da850_evm_init: nor mux setup failed: %d\n",
+               pr_warning("da850_evm_init:GPIO(2,6) mux setup "
+                                                       "failed\n");
+
+       ret = gpio_request(DA850_MII_MDIO_CLKEN_PIN, "mdio_clk_en");
+       if (ret) {
+               pr_warning("Cannot open GPIO %d\n",
+                                       DA850_MII_MDIO_CLKEN_PIN);
+               return ret;
+       }
+
+       /* Enable/Disable MII MDIO clock */
+       gpio_direction_output(DA850_MII_MDIO_CLKEN_PIN, rmii_en);
+
+       soc_info->emac_pdata->phy_mask = DA850_EVM_PHY_MASK;
+       soc_info->emac_pdata->mdio_max_freq = DA850_EVM_MDIO_FREQUENCY;
+
+       ret = da8xx_register_emac();
+       if (ret)
+               pr_warning("da850_evm_init: emac registration failed: %d\n",
                                ret);
 
-       da850_evm_init_nor();
+       return 0;
+}
+device_initcall(da850_evm_config_emac);
+
+static __init void da850_evm_init(void)
+{
+       int ret;
 
-       platform_add_devices(da850_evm_devices,
-                               ARRAY_SIZE(da850_evm_devices));
+       ret = pmic_tps65070_init();
+       if (ret)
+               pr_warning("da850_evm_init: TPS65070 PMIC init failed: %d\n",
+                               ret);
 
        ret = da8xx_register_edma();
        if (ret)
@@ -299,19 +608,6 @@ static __init void da850_evm_init(void)
                pr_warning("da850_evm_init: i2c0 registration failed: %d\n",
                                ret);
 
-       soc_info->emac_pdata->phy_mask = DA850_EVM_PHY_MASK;
-       soc_info->emac_pdata->mdio_max_freq = DA850_EVM_MDIO_FREQUENCY;
-       soc_info->emac_pdata->rmii_en = 0;
-
-       ret = da8xx_pinmux_setup(da850_cpgmac_pins);
-       if (ret)
-               pr_warning("da850_evm_init: cpgmac mux setup failed: %d\n",
-                               ret);
-
-       ret = da8xx_register_emac();
-       if (ret)
-               pr_warning("da850_evm_init: emac registration failed: %d\n",
-                               ret);
 
        ret = da8xx_register_watchdog();
        if (ret)
@@ -319,11 +615,6 @@ static __init void da850_evm_init(void)
                                ret);
 
        if (HAS_MMC) {
-               if (HAS_NOR)
-                       pr_warning("WARNING: both NOR Flash and MMC/SD are "
-                               "enabled, but they share AEMIF pins.\n"
-                               "\tDisable one of them.\n");
-
                ret = da8xx_pinmux_setup(da850_mmcsd0_pins);
                if (ret)
                        pr_warning("da850_evm_init: mmcsd0 mux setup failed:"
@@ -365,22 +656,42 @@ static __init void da850_evm_init(void)
                pr_warning("da850_evm_init: mcasp mux setup failed: %d\n",
                                ret);
 
-       da8xx_init_mcasp(0, &da850_evm_snd_data);
+       da8xx_register_mcasp(0, &da850_evm_snd_data);
 
        ret = da8xx_pinmux_setup(da850_lcdcntl_pins);
        if (ret)
                pr_warning("da850_evm_init: lcdcntl mux setup failed: %d\n",
                                ret);
 
+       /* Handle board specific muxing for LCD here */
+       ret = da8xx_pinmux_setup(da850_evm_lcdc_pins);
+       if (ret)
+               pr_warning("da850_evm_init: evm specific lcd mux setup "
+                               "failed: %d\n", ret);
+
        ret = da850_lcd_hw_init();
        if (ret)
                pr_warning("da850_evm_init: lcd initialization failed: %d\n",
                                ret);
 
-       ret = da8xx_register_lcdc();
+       ret = da8xx_register_lcdc(&sharp_lk043t1dg01_pdata);
        if (ret)
                pr_warning("da850_evm_init: lcdc registration failed: %d\n",
                                ret);
+
+       ret = da8xx_register_rtc();
+       if (ret)
+               pr_warning("da850_evm_init: rtc setup failed: %d\n", ret);
+
+       ret = da850_register_cpufreq();
+       if (ret)
+               pr_warning("da850_evm_init: cpufreq registration failed: %d\n",
+                               ret);
+
+       ret = da8xx_register_cpuidle();
+       if (ret)
+               pr_warning("da850_evm_init: cpuidle registration failed: %d\n",
+                               ret);
 }
 
 #ifdef CONFIG_SERIAL_8250_CONSOLE
index 77e8067..a9b650d 100644 (file)
@@ -9,15 +9,13 @@
  * or implied.
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/dma-mapping.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
 #include <linux/i2c.h>
-#include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
 #include <linux/videodev2.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/eeprom.h>
 
-#include <asm/setup.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/flash.h>
 
-#include <mach/hardware.h>
 #include <mach/dm355.h>
-#include <mach/psc.h>
-#include <mach/common.h>
 #include <mach/i2c.h>
 #include <mach/serial.h>
 #include <mach/nand.h>
 #include <mach/mmc.h>
+#include <mach/usb.h>
 
 #define DAVINCI_ASYNC_EMIF_CONTROL_BASE                0x01e10000
 #define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE       0x02000000
@@ -86,8 +79,9 @@ static struct davinci_nand_pdata davinci_nand_data = {
        .mask_chipsel           = BIT(14),
        .parts                  = davinci_nand_partitions,
        .nr_parts               = ARRAY_SIZE(davinci_nand_partitions),
-       .ecc_mode               = NAND_ECC_HW_SYNDROME,
+       .ecc_mode               = NAND_ECC_HW,
        .options                = NAND_USE_FLASH_BBT,
+       .ecc_bits               = 4,
 };
 
 static struct resource davinci_nand_resources[] = {
@@ -344,7 +338,7 @@ static __init void dm355_evm_init(void)
        gpio_request(2, "usb_id_toggle");
        gpio_direction_output(2, USB_ID_VALUE);
        /* irlml6401 switches over 1A in under 8 msec */
-       setup_usb(500, 8);
+       davinci_setup_usb(1000, 8);
 
        davinci_setup_mmc(0, &dm355evm_mmc_config);
        davinci_setup_mmc(1, &dm355evm_mmc_config);
index 84ad5d1..21f32eb 100644 (file)
@@ -8,34 +8,27 @@
  * warranty of any kind, whether express or implied.
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/dma-mapping.h>
+#include <linux/err.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
 #include <linux/i2c.h>
-#include <linux/io.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/eeprom.h>
 
-#include <asm/setup.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/flash.h>
 
-#include <mach/hardware.h>
 #include <mach/dm355.h>
-#include <mach/psc.h>
-#include <mach/common.h>
 #include <mach/i2c.h>
 #include <mach/serial.h>
 #include <mach/nand.h>
 #include <mach/mmc.h>
+#include <mach/usb.h>
 
 #define DAVINCI_ASYNC_EMIF_CONTROL_BASE                0x01e10000
 #define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE       0x02000000
@@ -270,7 +263,7 @@ static __init void dm355_leopard_init(void)
        gpio_request(2, "usb_id_toggle");
        gpio_direction_output(2, USB_ID_VALUE);
        /* irlml6401 switches over 1A in under 8 msec */
-       setup_usb(500, 8);
+       davinci_setup_usb(1000, 8);
 
        davinci_setup_mmc(0, &dm355leopard_mmc_config);
        davinci_setup_mmc(1, &dm355leopard_mmc_config);
index 52dd804..289fe1b 100644 (file)
@@ -13,9 +13,8 @@
  * GNU General Public License for more details.
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/dma-mapping.h>
+#include <linux/err.h>
 #include <linux/i2c.h>
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
-#include <asm/setup.h>
+#include <linux/input.h>
+
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
+
 #include <mach/mux.h>
-#include <mach/hardware.h>
 #include <mach/dm365.h>
-#include <mach/psc.h>
 #include <mach/common.h>
 #include <mach/i2c.h>
 #include <mach/serial.h>
 #include <mach/mmc.h>
 #include <mach/nand.h>
-
+#include <mach/keyscan.h>
 
 static inline int have_imager(void)
 {
@@ -144,6 +142,7 @@ static struct davinci_nand_pdata davinci_nand_data = {
        .nr_parts               = ARRAY_SIZE(davinci_nand_partitions),
        .ecc_mode               = NAND_ECC_HW,
        .options                = NAND_USE_FLASH_BBT,
+       .ecc_bits               = 4,
 };
 
 static struct resource davinci_nand_resources[] = {
@@ -176,11 +175,16 @@ static struct at24_platform_data eeprom_info = {
        .context        = (void *)0x7f00,
 };
 
+static struct snd_platform_data dm365_evm_snd_data;
+
 static struct i2c_board_info i2c_info[] = {
        {
                I2C_BOARD_INFO("24c256", 0x50),
                .platform_data  = &eeprom_info,
        },
+       {
+               I2C_BOARD_INFO("tlv320aic3x", 0x18),
+       },
 };
 
 static struct davinci_i2c_platform_data i2c_pdata = {
@@ -188,6 +192,38 @@ static struct davinci_i2c_platform_data i2c_pdata = {
        .bus_delay      = 0     /* usec */,
 };
 
+#ifdef CONFIG_KEYBOARD_DAVINCI
+static unsigned short dm365evm_keymap[] = {
+       KEY_KP2,
+       KEY_LEFT,
+       KEY_EXIT,
+       KEY_DOWN,
+       KEY_ENTER,
+       KEY_UP,
+       KEY_KP1,
+       KEY_RIGHT,
+       KEY_MENU,
+       KEY_RECORD,
+       KEY_REWIND,
+       KEY_KPMINUS,
+       KEY_STOP,
+       KEY_FASTFORWARD,
+       KEY_KPPLUS,
+       KEY_PLAYPAUSE,
+       0
+};
+
+static struct davinci_ks_platform_data dm365evm_ks_data = {
+       .keymap         = dm365evm_keymap,
+       .keymapsize     = ARRAY_SIZE(dm365evm_keymap),
+       .rep            = 1,
+       /* Scan period = strobe + interval */
+       .strobe         = 0x5,
+       .interval       = 0x2,
+       .matrix_type    = DAVINCI_KEYSCAN_MATRIX_4X4,
+};
+#endif
+
 static int cpld_mmc_get_cd(int module)
 {
        if (!cpld)
@@ -472,6 +508,13 @@ static __init void dm365_evm_init(void)
 
        /* maybe setup mmc1/etc ... _after_ mmc0 */
        evm_init_cpld();
+
+       dm365_init_asp(&dm365_evm_snd_data);
+       dm365_init_rtc();
+
+#ifdef CONFIG_KEYBOARD_DAVINCI
+       dm365_init_ks(&dm365evm_ks_data);
+#endif
 }
 
 static __init void dm365_evm_irq_init(void)
index 1213a00..fd0398b 100644 (file)
@@ -9,45 +9,34 @@
  * or implied.
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
-#include <linux/leds.h>
-#include <linux/memory.h>
-
 #include <linux/i2c.h>
 #include <linux/i2c/pcf857x.h>
 #include <linux/i2c/at24.h>
-#include <linux/etherdevice.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
-#include <linux/io.h>
 #include <linux/phy.h>
 #include <linux/clk.h>
 #include <linux/videodev2.h>
 
 #include <media/tvp514x.h>
 
-#include <asm/setup.h>
 #include <asm/mach-types.h>
-
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/flash.h>
 
 #include <mach/dm644x.h>
 #include <mach/common.h>
 #include <mach/i2c.h>
 #include <mach/serial.h>
 #include <mach/mux.h>
-#include <mach/psc.h>
 #include <mach/nand.h>
 #include <mach/mmc.h>
-#include <mach/emac.h>
+#include <mach/usb.h>
 
 #define DM644X_EVM_PHY_MASK            (0x2)
 #define DM644X_EVM_MDIO_FREQUENCY      (2200000) /* PHY bus frequency */
@@ -477,7 +466,7 @@ evm_u35_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c)
        /* irlml6401 switches over 1A, in under 8 msec;
         * now it can be managed by nDRV_VBUS ...
         */
-       setup_usb(500, 8);
+       davinci_setup_usb(1000, 8);
 
        return 0;
 }
index 24e0e13..8d0b0e0 100644 (file)
  **************************************************************************/
 
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/major.h>
-#include <linux/root_dev.h>
-#include <linux/dma-mapping.h>
-#include <linux/serial.h>
-#include <linux/serial_8250.h>
 #include <linux/leds.h>
 #include <linux/gpio.h>
-#include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
 #include <linux/i2c/at24.h>
 #include <linux/i2c/pcf857x.h>
-#include <linux/etherdevice.h>
 
 #include <media/tvp514x.h>
 
-#include <asm/setup.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/flash.h>
 
 #include <mach/dm646x.h>
 #include <mach/common.h>
-#include <mach/psc.h>
 #include <mach/serial.h>
 #include <mach/i2c.h>
-#include <mach/mmc.h>
-#include <mach/emac.h>
+#include <mach/nand.h>
 
 #if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \
     defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE)
 #define HAS_ATA 0
 #endif
 
+#define DAVINCI_ASYNC_EMIF_CONTROL_BASE                0x20008000
+#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE       0x42000000
+
+#define NAND_BLOCK_SIZE                SZ_128K
+
 /* CPLD Register 0 bits to control ATA */
 #define DM646X_EVM_ATA_RST             BIT(0)
 #define DM646X_EVM_ATA_PWD             BIT(1)
@@ -92,6 +87,63 @@ static struct davinci_uart_config uart_config __initdata = {
        .enabled_uarts = (1 << 0),
 };
 
+/* Note: We are setting first partition as 'bootloader' constituting UBL, U-Boot
+ * and U-Boot environment this avoids dependency on any particular combination
+ * of UBL, U-Boot or flashing tools etc.
+ */
+static struct mtd_partition davinci_nand_partitions[] = {
+       {
+               /* UBL, U-Boot with environment */
+               .name           = "bootloader",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 16 * NAND_BLOCK_SIZE,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       }, {
+               .name           = "kernel",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_4M,
+               .mask_flags     = 0,
+       }, {
+               .name           = "filesystem",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+               .mask_flags     = 0,
+       }
+};
+
+static struct davinci_nand_pdata davinci_nand_data = {
+       .mask_cle               = 0x80000,
+       .mask_ale               = 0x40000,
+       .parts                  = davinci_nand_partitions,
+       .nr_parts               = ARRAY_SIZE(davinci_nand_partitions),
+       .ecc_mode               = NAND_ECC_HW,
+       .options                = 0,
+};
+
+static struct resource davinci_nand_resources[] = {
+       {
+               .start          = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE,
+               .end            = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + SZ_32M - 1,
+               .flags          = IORESOURCE_MEM,
+       }, {
+               .start          = DAVINCI_ASYNC_EMIF_CONTROL_BASE,
+               .end            = DAVINCI_ASYNC_EMIF_CONTROL_BASE + SZ_4K - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device davinci_nand_device = {
+       .name                   = "davinci_nand",
+       .id                     = 0,
+
+       .num_resources          = ARRAY_SIZE(davinci_nand_resources),
+       .resource               = davinci_nand_resources,
+
+       .dev                    = {
+               .platform_data  = &davinci_nand_data,
+       },
+};
+
 /* CPLD Register 0 Client: used for I/O Control */
 static int cpld_reg0_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
@@ -142,7 +194,7 @@ static struct gpio_led evm_leds[] = {
        { .name = "DS4", .active_low = 1, },
 };
 
-static __initconst struct gpio_led_platform_data evm_led_data = {
+static const struct gpio_led_platform_data evm_led_data = {
        .num_leds = ARRAY_SIZE(evm_leds),
        .leds     = evm_leds,
 };
@@ -647,6 +699,8 @@ static __init void evm_init(void)
        dm646x_init_mcasp0(&dm646x_evm_snd_data[0]);
        dm646x_init_mcasp1(&dm646x_evm_snd_data[1]);
 
+       platform_device_register(&davinci_nand_device);
+
        if (HAS_ATA)
                dm646x_init_ide();
 
diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c
new file mode 100644 (file)
index 0000000..bd9ca07
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * Neuros Technologies OSD2 board support
+ *
+ * Modified from original 644X-EVM board support.
+ * 2008 (c) Neuros Technology, LLC.
+ * 2009 (c) Jorge Luis Zapata Muga <jorgeluis.zapata@gmail.com>
+ * 2009 (c) Andrey A. Porodko <Andrey.Porodko@gmail.com>
+ *
+ * The Neuros OSD 2.0 is the hardware component of the Neuros Open
+ * Internet Television Platform. Hardware is very close to TI
+ * DM644X-EVM board. It has:
+ *     DM6446M02 module with 256MB NAND, 256MB RAM, TLV320AIC32 AIC,
+ *     USB, Ethernet, SD/MMC, UART, THS8200, TVP7000 for video.
+ *     Additionaly realtime clock, IR remote control receiver,
+ *     IR Blaster based on MSP430 (firmware although is different
+ *     from used in DM644X-EVM), internal ATA-6 3.5” HDD drive
+ *     with PATA interface, two muxed red-green leds.
+ *
+ * For more information please refer to
+ *             http://wiki.neurostechnology.com/index.php/OSD_2.0_HD
+ *
+ * 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/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include <mach/dm644x.h>
+#include <mach/i2c.h>
+#include <mach/serial.h>
+#include <mach/mux.h>
+#include <mach/nand.h>
+#include <mach/mmc.h>
+#include <mach/usb.h>
+
+#define NEUROS_OSD2_PHY_MASK           0x2
+#define NEUROS_OSD2_MDIO_FREQUENCY     2200000 /* PHY bus frequency */
+
+#define DAVINCI_CFC_ATA_BASE            0x01C66000
+
+#define DAVINCI_ASYNC_EMIF_CONTROL_BASE         0x01e00000
+#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE 0x02000000
+
+#define LXT971_PHY_ID                  0x001378e2
+#define LXT971_PHY_MASK                        0xfffffff0
+
+#define        NTOSD2_AUDIOSOC_I2C_ADDR        0x18
+#define        NTOSD2_MSP430_I2C_ADDR          0x59
+#define        NTOSD2_MSP430_IRQ               2
+
+/* Neuros OSD2 has a Samsung 256 MByte NAND flash (Dev ID of 0xAA,
+ * 2048 blocks in the device, 64 pages per block, 2048 bytes per
+ * page.
+ */
+
+#define NAND_BLOCK_SIZE                SZ_128K
+
+struct mtd_partition davinci_ntosd2_nandflash_partition[] = {
+       {
+               /* UBL (a few copies) plus U-Boot */
+               .name           = "bootloader",
+               .offset         = 0,
+               .size           = 15 * NAND_BLOCK_SIZE,
+               .mask_flags     = MTD_WRITEABLE, /* force read-only */
+       }, {
+               /* U-Boot environment */
+               .name           = "params",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 1 * NAND_BLOCK_SIZE,
+               .mask_flags     = 0,
+       }, {
+               /* Kernel */
+               .name           = "kernel",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_4M,
+               .mask_flags     = 0,
+       }, {
+               /* File System */
+               .name           = "filesystem",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+               .mask_flags     = 0,
+       }
+       /* A few blocks at end hold a flash Bad Block Table. */
+};
+
+static struct davinci_nand_pdata davinci_ntosd2_nandflash_data = {
+       .parts          = davinci_ntosd2_nandflash_partition,
+       .nr_parts       = ARRAY_SIZE(davinci_ntosd2_nandflash_partition),
+       .ecc_mode       = NAND_ECC_HW,
+       .options        = NAND_USE_FLASH_BBT,
+};
+
+static struct resource davinci_ntosd2_nandflash_resource[] = {
+       {
+               .start          = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE,
+               .end            = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + SZ_16M - 1,
+               .flags          = IORESOURCE_MEM,
+       }, {
+               .start          = DAVINCI_ASYNC_EMIF_CONTROL_BASE,
+               .end            = DAVINCI_ASYNC_EMIF_CONTROL_BASE + SZ_4K - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device davinci_ntosd2_nandflash_device = {
+       .name           = "davinci_nand",
+       .id             = 0,
+       .dev            = {
+               .platform_data  = &davinci_ntosd2_nandflash_data,
+       },
+       .num_resources  = ARRAY_SIZE(davinci_ntosd2_nandflash_resource),
+       .resource       = davinci_ntosd2_nandflash_resource,
+};
+
+static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device davinci_fb_device = {
+       .name           = "davincifb",
+       .id             = -1,
+       .dev = {
+               .dma_mask               = &davinci_fb_dma_mask,
+               .coherent_dma_mask      = DMA_BIT_MASK(32),
+       },
+       .num_resources = 0,
+};
+
+static struct resource ide_resources[] = {
+       {
+               .start          = DAVINCI_CFC_ATA_BASE,
+               .end            = DAVINCI_CFC_ATA_BASE + 0x7ff,
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               .start          = IRQ_IDE,
+               .end            = IRQ_IDE,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static u64 ide_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device ide_dev = {
+       .name           = "palm_bk3710",
+       .id             = -1,
+       .resource       = ide_resources,
+       .num_resources  = ARRAY_SIZE(ide_resources),
+       .dev = {
+               .dma_mask               = &ide_dma_mask,
+               .coherent_dma_mask      = DMA_BIT_MASK(32),
+       },
+};
+
+static struct snd_platform_data dm644x_ntosd2_snd_data;
+
+static struct gpio_led ntosd2_leds[] = {
+       { .name = "led1_green", .gpio = GPIO(10), },
+       { .name = "led1_red",   .gpio = GPIO(11), },
+       { .name = "led2_green", .gpio = GPIO(12), },
+       { .name = "led2_red",   .gpio = GPIO(13), },
+};
+
+static struct gpio_led_platform_data ntosd2_leds_data = {
+       .num_leds       = ARRAY_SIZE(ntosd2_leds),
+       .leds           = ntosd2_leds,
+};
+
+static struct platform_device ntosd2_leds_dev = {
+       .name = "leds-gpio",
+       .id   = -1,
+       .dev = {
+               .platform_data          = &ntosd2_leds_data,
+       },
+};
+
+
+static struct platform_device *davinci_ntosd2_devices[] __initdata = {
+       &davinci_fb_device,
+       &ntosd2_leds_dev,
+};
+
+static struct davinci_uart_config uart_config __initdata = {
+       .enabled_uarts = (1 << 0),
+};
+
+static void __init davinci_ntosd2_map_io(void)
+{
+       dm644x_init();
+}
+
+/*
+ I2C initialization
+*/
+static struct davinci_i2c_platform_data ntosd2_i2c_pdata = {
+       .bus_freq       = 20 /* kHz */,
+       .bus_delay      = 100 /* usec */,
+};
+
+static struct i2c_board_info __initdata ntosd2_i2c_info[] =  {
+};
+
+static int ntosd2_init_i2c(void)
+{
+       int     status;
+
+       davinci_init_i2c(&ntosd2_i2c_pdata);
+       status = gpio_request(NTOSD2_MSP430_IRQ, ntosd2_i2c_info[0].type);
+       if (status == 0) {
+               status = gpio_direction_input(NTOSD2_MSP430_IRQ);
+               if (status == 0) {
+                       status = gpio_to_irq(NTOSD2_MSP430_IRQ);
+                       if (status > 0) {
+                               ntosd2_i2c_info[0].irq = status;
+                               i2c_register_board_info(1,
+                                       ntosd2_i2c_info,
+                                       ARRAY_SIZE(ntosd2_i2c_info));
+                       }
+               }
+       }
+       return status;
+}
+
+static struct davinci_mmc_config davinci_ntosd2_mmc_config = {
+       .wires          = 4,
+       .version        = MMC_CTLR_VERSION_1
+};
+
+
+#if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \
+       defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE)
+#define HAS_ATA 1
+#else
+#define HAS_ATA 0
+#endif
+
+#if defined(CONFIG_MTD_NAND_DAVINCI) || \
+       defined(CONFIG_MTD_NAND_DAVINCI_MODULE)
+#define HAS_NAND 1
+#else
+#define HAS_NAND 0
+#endif
+
+static __init void davinci_ntosd2_init(void)
+{
+       struct clk *aemif_clk;
+       struct davinci_soc_info *soc_info = &davinci_soc_info;
+       int     status;
+
+       aemif_clk = clk_get(NULL, "aemif");
+       clk_enable(aemif_clk);
+
+       if (HAS_ATA) {
+               if (HAS_NAND)
+                       pr_warning("WARNING: both IDE and Flash are "
+                               "enabled, but they share AEMIF pins.\n"
+                               "\tDisable IDE for NAND/NOR support.\n");
+               davinci_cfg_reg(DM644X_HPIEN_DISABLE);
+               davinci_cfg_reg(DM644X_ATAEN);
+               davinci_cfg_reg(DM644X_HDIREN);
+               platform_device_register(&ide_dev);
+       } else if (HAS_NAND) {
+               davinci_cfg_reg(DM644X_HPIEN_DISABLE);
+               davinci_cfg_reg(DM644X_ATAEN_DISABLE);
+
+               /* only one device will be jumpered and detected */
+               if (HAS_NAND)
+                       platform_device_register(
+                                       &davinci_ntosd2_nandflash_device);
+       }
+
+       platform_add_devices(davinci_ntosd2_devices,
+                               ARRAY_SIZE(davinci_ntosd2_devices));
+
+       /* Initialize I2C interface specific for this board */
+       status = ntosd2_init_i2c();
+       if (status < 0)
+               pr_warning("davinci_ntosd2_init: msp430 irq setup failed:"
+                                               "        %d\n", status);
+
+       davinci_serial_init(&uart_config);
+       dm644x_init_asp(&dm644x_ntosd2_snd_data);
+
+       soc_info->emac_pdata->phy_mask = NEUROS_OSD2_PHY_MASK;
+       soc_info->emac_pdata->mdio_max_freq = NEUROS_OSD2_MDIO_FREQUENCY;
+
+       davinci_setup_usb(1000, 8);
+       /*
+        * Mux the pins to be GPIOs, VLYNQEN is already done at startup.
+        * The AEAWx are five new AEAW pins that can be muxed by separately.
+        * They are a bitmask for GPIO management. According TI
+        * documentation (http://www.ti.com/lit/gpn/tms320dm6446) to employ
+        * gpio(10,11,12,13) for leds any combination of bits works except
+        * four last. So we are to reset all five.
+        */
+       davinci_cfg_reg(DM644X_AEAW0);
+       davinci_cfg_reg(DM644X_AEAW1);
+       davinci_cfg_reg(DM644X_AEAW2);
+       davinci_cfg_reg(DM644X_AEAW3);
+       davinci_cfg_reg(DM644X_AEAW4);
+
+       davinci_setup_mmc(0, &davinci_ntosd2_mmc_config);
+}
+
+static __init void davinci_ntosd2_irq_init(void)
+{
+       davinci_irq_init();
+}
+
+MACHINE_START(NEUROS_OSD2, "Neuros OSD2")
+       /* Maintainer: Neuros Technologies <neuros@groups.google.com> */
+       .phys_io        = IO_PHYS,
+       .io_pg_offst    = (__IO_ADDRESS(IO_PHYS) >> 18) & 0xfffc,
+       .boot_params    = (DAVINCI_DDR_BASE + 0x100),
+       .map_io          = davinci_ntosd2_map_io,
+       .init_irq       = davinci_ntosd2_irq_init,
+       .timer          = &davinci_timer,
+       .init_machine = davinci_ntosd2_init,
+MACHINE_END
index 7acdfd8..08d373b 100644 (file)
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
-#include <linux/gpio.h>
-
 #include <linux/i2c.h>
 #include <linux/i2c/at24.h>
-#include <linux/etherdevice.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/io.h>
 
-#include <asm/setup.h>
 #include <asm/mach-types.h>
-
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
 #include <asm/mach/flash.h>
 
 #include <mach/dm644x.h>
 #include <mach/common.h>
 #include <mach/i2c.h>
 #include <mach/serial.h>
-#include <mach/psc.h>
 #include <mach/mux.h>
+#include <mach/usb.h>
 
 #define SFFSDR_PHY_MASK                (0x2)
 #define SFFSDR_MDIO_FREQUENCY  (2200000) /* PHY bus frequency */
@@ -107,11 +96,6 @@ static struct platform_device davinci_sffsdr_nandflash_device = {
        .resource       = davinci_sffsdr_nandflash_resource,
 };
 
-static struct emac_platform_data sffsdr_emac_pdata = {
-       .phy_mask       = SFFSDR_PHY_MASK,
-       .mdio_max_freq  = SFFSDR_MDIO_FREQUENCY,
-};
-
 static struct at24_platform_data eeprom_info = {
        .byte_len       = (64*1024) / 8,
        .page_size      = 32,
@@ -164,7 +148,7 @@ static __init void davinci_sffsdr_init(void)
        davinci_serial_init(&uart_config);
        soc_info->emac_pdata->phy_mask = SFFSDR_PHY_MASK;
        soc_info->emac_pdata->mdio_max_freq = SFFSDR_MDIO_FREQUENCY;
-       setup_usb(0, 0); /* We support only peripheral mode. */
+       davinci_setup_usb(0, 0); /* We support only peripheral mode. */
 
        /* mux VLYNQ pins */
        davinci_cfg_reg(DM644X_VLYNQEN);
index 83d54d5..baece65 100644 (file)
@@ -17,8 +17,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
-#include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/delay.h>
 
 #include <mach/hardware.h>
 
@@ -42,8 +42,7 @@ static void __clk_enable(struct clk *clk)
        if (clk->parent)
                __clk_enable(clk->parent);
        if (clk->usecount++ == 0 && (clk->flags & CLK_PSC))
-               davinci_psc_config(psc_domain(clk), clk->psc_ctlr,
-                               clk->lpsc, 1);
+               davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 1);
 }
 
 static void __clk_disable(struct clk *clk)
@@ -51,8 +50,7 @@ static void __clk_disable(struct clk *clk)
        if (WARN_ON(clk->usecount == 0))
                return;
        if (--clk->usecount == 0 && !(clk->flags & CLK_PLL))
-               davinci_psc_config(psc_domain(clk), clk->psc_ctlr,
-                               clk->lpsc, 0);
+               davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 0);
        if (clk->parent)
                __clk_disable(clk->parent);
 }
@@ -99,20 +97,74 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
        if (clk == NULL || IS_ERR(clk))
                return -EINVAL;
 
+       if (clk->round_rate)
+               return clk->round_rate(clk, rate);
+
        return clk->rate;
 }
 EXPORT_SYMBOL(clk_round_rate);
 
+/* Propagate rate to children */
+static void propagate_rate(struct clk *root)
+{
+       struct clk *clk;
+
+       list_for_each_entry(clk, &root->children, childnode) {
+               if (clk->recalc)
+                       clk->rate = clk->recalc(clk);
+               propagate_rate(clk);
+       }
+}
+
 int clk_set_rate(struct clk *clk, unsigned long rate)
 {
+       unsigned long flags;
+       int ret = -EINVAL;
+
        if (clk == NULL || IS_ERR(clk))
-               return -EINVAL;
+               return ret;
 
-       /* changing the clk rate is not supported */
-       return -EINVAL;
+       spin_lock_irqsave(&clockfw_lock, flags);
+       if (clk->set_rate)
+               ret = clk->set_rate(clk, rate);
+       if (ret == 0) {
+               if (clk->recalc)
+                       clk->rate = clk->recalc(clk);
+               propagate_rate(clk);
+       }
+       spin_unlock_irqrestore(&clockfw_lock, flags);
+
+       return ret;
 }
 EXPORT_SYMBOL(clk_set_rate);
 
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+       unsigned long flags;
+
+       if (clk == NULL || IS_ERR(clk))
+               return -EINVAL;
+
+       /* Cannot change parent on enabled clock */
+       if (WARN_ON(clk->usecount))
+               return -EINVAL;
+
+       mutex_lock(&clocks_mutex);
+       clk->parent = parent;
+       list_del_init(&clk->childnode);
+       list_add(&clk->childnode, &clk->parent->children);
+       mutex_unlock(&clocks_mutex);
+
+       spin_lock_irqsave(&clockfw_lock, flags);
+       if (clk->recalc)
+               clk->rate = clk->recalc(clk);
+       propagate_rate(clk);
+       spin_unlock_irqrestore(&clockfw_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
 int clk_register(struct clk *clk)
 {
        if (clk == NULL || IS_ERR(clk))
@@ -123,16 +175,24 @@ int clk_register(struct clk *clk)
                        clk->name, clk->parent->name))
                return -EINVAL;
 
+       INIT_LIST_HEAD(&clk->children);
+
        mutex_lock(&clocks_mutex);
        list_add_tail(&clk->node, &clocks);
+       if (clk->parent)
+               list_add_tail(&clk->childnode, &clk->parent->children);
        mutex_unlock(&clocks_mutex);
 
        /* If rate is already set, use it */
        if (clk->rate)
                return 0;
 
+       /* Else, see if there is a way to calculate it */
+       if (clk->recalc)
+               clk->rate = clk->recalc(clk);
+
        /* Otherwise, default to parent rate */
-       if (clk->parent)
+       else if (clk->parent)
                clk->rate = clk->parent->rate;
 
        return 0;
@@ -146,6 +206,7 @@ void clk_unregister(struct clk *clk)
 
        mutex_lock(&clocks_mutex);
        list_del(&clk->node);
+       list_del(&clk->childnode);
        mutex_unlock(&clocks_mutex);
 }
 EXPORT_SYMBOL(clk_unregister);
@@ -166,11 +227,11 @@ static int __init clk_disable_unused(void)
                        continue;
 
                /* ignore if in Disabled or SwRstDisable states */
-               if (!davinci_psc_is_clk_active(ck->psc_ctlr, ck->lpsc))
+               if (!davinci_psc_is_clk_active(ck->gpsc, ck->lpsc))
                        continue;
 
                pr_info("Clocks: disable unused %s\n", ck->name);
-               davinci_psc_config(psc_domain(ck), ck->psc_ctlr, ck->lpsc, 0);
+               davinci_psc_config(psc_domain(ck), ck->gpsc, ck->lpsc, 0);
        }
        spin_unlock_irq(&clockfw_lock);
 
@@ -179,50 +240,62 @@ static int __init clk_disable_unused(void)
 late_initcall(clk_disable_unused);
 #endif
 
-static void clk_sysclk_recalc(struct clk *clk)
+static unsigned long clk_sysclk_recalc(struct clk *clk)
 {
        u32 v, plldiv;
        struct pll_data *pll;
+       unsigned long rate = clk->rate;
 
        /* If this is the PLL base clock, no more calculations needed */
        if (clk->pll_data)
-               return;
+               return rate;
 
        if (WARN_ON(!clk->parent))
-               return;
+               return rate;
 
-       clk->rate = clk->parent->rate;
+       rate = clk->parent->rate;
 
        /* Otherwise, the parent must be a PLL */
        if (WARN_ON(!clk->parent->pll_data))
-               return;
+               return rate;
 
        pll = clk->parent->pll_data;
 
        /* If pre-PLL, source clock is before the multiplier and divider(s) */
        if (clk->flags & PRE_PLL)
-               clk->rate = pll->input_rate;
+               rate = pll->input_rate;
 
        if (!clk->div_reg)
-               return;
+               return rate;
 
        v = __raw_readl(pll->base + clk->div_reg);
        if (v & PLLDIV_EN) {
                plldiv = (v & PLLDIV_RATIO_MASK) + 1;
                if (plldiv)
-                       clk->rate /= plldiv;
+                       rate /= plldiv;
        }
+
+       return rate;
+}
+
+static unsigned long clk_leafclk_recalc(struct clk *clk)
+{
+       if (WARN_ON(!clk->parent))
+               return clk->rate;
+
+       return clk->parent->rate;
 }
 
-static void __init clk_pll_init(struct clk *clk)
+static unsigned long clk_pllclk_recalc(struct clk *clk)
 {
        u32 ctrl, mult = 1, prediv = 1, postdiv = 1;
        u8 bypass;
        struct pll_data *pll = clk->pll_data;
+       unsigned long rate = clk->rate;
 
        pll->base = IO_ADDRESS(pll->phys_base);
        ctrl = __raw_readl(pll->base + PLLCTL);
-       clk->rate = pll->input_rate = clk->parent->rate;
+       rate = pll->input_rate = clk->parent->rate;
 
        if (ctrl & PLLCTL_PLLEN) {
                bypass = 0;
@@ -255,9 +328,9 @@ static void __init clk_pll_init(struct clk *clk)
        }
 
        if (!bypass) {
-               clk->rate /= prediv;
-               clk->rate *= mult;
-               clk->rate /= postdiv;
+               rate /= prediv;
+               rate *= mult;
+               rate /= postdiv;
        }
 
        pr_debug("PLL%d: input = %lu MHz [ ",
@@ -270,8 +343,90 @@ static void __init clk_pll_init(struct clk *clk)
                pr_debug("* %d ", mult);
        if (postdiv > 1)
                pr_debug("/ %d ", postdiv);
-       pr_debug("] --> %lu MHz output.\n", clk->rate / 1000000);
+       pr_debug("] --> %lu MHz output.\n", rate / 1000000);
+
+       return rate;
+}
+
+/**
+ * davinci_set_pllrate - set the output rate of a given PLL.
+ *
+ * Note: Currently tested to work with OMAP-L138 only.
+ *
+ * @pll: pll whose rate needs to be changed.
+ * @prediv: The pre divider value. Passing 0 disables the pre-divider.
+ * @pllm: The multiplier value. Passing 0 leads to multiply-by-one.
+ * @postdiv: The post divider value. Passing 0 disables the post-divider.
+ */
+int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv,
+                                       unsigned int mult, unsigned int postdiv)
+{
+       u32 ctrl;
+       unsigned int locktime;
+
+       if (pll->base == NULL)
+               return -EINVAL;
+
+       /*
+        *  PLL lock time required per OMAP-L138 datasheet is
+        * (2000 * prediv)/sqrt(pllm) OSCIN cycles. We approximate sqrt(pllm)
+        * as 4 and OSCIN cycle as 25 MHz.
+        */
+       if (prediv) {
+               locktime = ((2000 * prediv) / 100);
+               prediv = (prediv - 1) | PLLDIV_EN;
+       } else {
+               locktime = 20;
+       }
+       if (postdiv)
+               postdiv = (postdiv - 1) | PLLDIV_EN;
+       if (mult)
+               mult = mult - 1;
+
+       ctrl = __raw_readl(pll->base + PLLCTL);
+
+       /* Switch the PLL to bypass mode */
+       ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
+       __raw_writel(ctrl, pll->base + PLLCTL);
+
+       /*
+        * Wait for 4 OSCIN/CLKIN cycles to ensure that the PLLC has switched
+        * to bypass mode. Delay of 1us ensures we are good for all > 4MHz
+        * OSCIN/CLKIN inputs. Typically the input is ~25MHz.
+        */
+       udelay(1);
+
+       /* Reset and enable PLL */
+       ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS);
+       __raw_writel(ctrl, pll->base + PLLCTL);
+
+       if (pll->flags & PLL_HAS_PREDIV)
+               __raw_writel(prediv, pll->base + PREDIV);
+
+       __raw_writel(mult, pll->base + PLLM);
+
+       if (pll->flags & PLL_HAS_POSTDIV)
+               __raw_writel(postdiv, pll->base + POSTDIV);
+
+       /*
+        * Wait for PLL to reset properly, OMAP-L138 datasheet says
+        * 'min' time = 125ns
+        */
+       udelay(1);
+
+       /* Bring PLL out of reset */
+       ctrl |= PLLCTL_PLLRST;
+       __raw_writel(ctrl, pll->base + PLLCTL);
+
+       udelay(locktime);
+
+       /* Remove PLL from bypass mode */
+       ctrl |= PLLCTL_PLLEN;
+       __raw_writel(ctrl, pll->base + PLLCTL);
+
+       return 0;
 }
+EXPORT_SYMBOL(davinci_set_pllrate);
 
 int __init davinci_clk_init(struct davinci_clk *clocks)
   {
@@ -281,12 +436,23 @@ int __init davinci_clk_init(struct davinci_clk *clocks)
        for (c = clocks; c->lk.clk; c++) {
                clk = c->lk.clk;
 
-               if (clk->pll_data)
-                       clk_pll_init(clk);
+               if (!clk->recalc) {
+
+                       /* Check if clock is a PLL */
+                       if (clk->pll_data)
+                               clk->recalc = clk_pllclk_recalc;
+
+                       /* Else, if it is a PLL-derived clock */
+                       else if (clk->flags & CLK_PLL)
+                               clk->recalc = clk_sysclk_recalc;
+
+                       /* Otherwise, it is a leaf clock (PSC clock) */
+                       else if (clk->parent)
+                               clk->recalc = clk_leafclk_recalc;
+               }
 
-               /* Calculate rates for PLL-derived clocks */
-               else if (clk->flags & CLK_PLL)
-                       clk_sysclk_recalc(clk);
+               if (clk->recalc)
+                       clk->rate = clk->recalc(clk);
 
                if (clk->lpsc)
                        clk->flags |= CLK_PSC;
@@ -352,9 +518,8 @@ dump_clock(struct seq_file *s, unsigned nest, struct clk *parent)
        /* REVISIT show device associations too */
 
        /* cost is now small, but not linear... */
-       list_for_each_entry(clk, &clocks, node) {
-               if (clk->parent == parent)
-                       dump_clock(s, nest + NEST_DELTA, clk);
+       list_for_each_entry(clk, &parent->children, childnode) {
+               dump_clock(s, nest + NEST_DELTA, clk);
        }
 }
 
index 27233cb..c92d77a 100644 (file)
 /* PLL/Reset register offsets */
 #define PLLCTL          0x100
 #define PLLCTL_PLLEN    BIT(0)
+#define PLLCTL_PLLPWRDN        BIT(1)
+#define PLLCTL_PLLRST  BIT(3)
+#define PLLCTL_PLLDIS  BIT(4)
+#define PLLCTL_PLLENSRC        BIT(5)
 #define PLLCTL_CLKMODE  BIT(8)
 
 #define PLLM           0x110
@@ -65,15 +69,20 @@ struct clk {
        const char              *name;
        unsigned long           rate;
        u8                      usecount;
-       u8                      flags;
        u8                      lpsc;
-       u8                      psc_ctlr;
+       u8                      gpsc;
+       u32                     flags;
        struct clk              *parent;
+       struct list_head        children;       /* list of children */
+       struct list_head        childnode;      /* parent's child list node */
        struct pll_data         *pll_data;
        u32                     div_reg;
+       unsigned long (*recalc) (struct clk *);
+       int (*set_rate) (struct clk *clk, unsigned long rate);
+       int (*round_rate) (struct clk *clk, unsigned long rate);
 };
 
-/* Clock flags */
+/* Clock flags: SoC-specific flags start at BIT(16) */
 #define ALWAYS_ENABLED         BIT(1)
 #define CLK_PSC                 BIT(2)
 #define PSC_DSP                 BIT(3) /* PSC uses DSP domain, not ARM */
@@ -94,6 +103,8 @@ struct davinci_clk {
        }
 
 int davinci_clk_init(struct davinci_clk *clocks);
+int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv,
+                               unsigned int mult, unsigned int postdiv);
 
 extern struct platform_device davinci_wdt_device;
 
index 61ede19..c2de94c 100644 (file)
@@ -86,6 +86,8 @@ void __init davinci_common_init(struct davinci_soc_info *soc_info)
        dip = davinci_get_id(davinci_soc_info.jtag_id);
        if (!dip) {
                ret = -EINVAL;
+               pr_err("Unknown DaVinci JTAG ID 0x%x\n",
+                                               davinci_soc_info.jtag_id);
                goto err;
        }
 
@@ -104,5 +106,5 @@ void __init davinci_common_init(struct davinci_soc_info *soc_info)
        return;
 
 err:
-       pr_err("davinci_common_init: SoC Initialization failed\n");
+       panic("davinci_common_init: SoC Initialization failed\n");
 }
index 96c8e97..52b287c 100644 (file)
@@ -10,9 +10,6 @@
  */
 
 #include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 
diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c
new file mode 100644 (file)
index 0000000..d3fa6de
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * CPU frequency scaling for DaVinci
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Based on linux/arch/arm/plat-omap/cpu-omap.c. Original Copyright follows:
+ *
+ *  Copyright (C) 2005 Nokia Corporation
+ *  Written by Tony Lindgren <tony@atomide.com>
+ *
+ *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
+ *
+ * Copyright (C) 2007-2008 Texas Instruments, Inc.
+ * Updated to support OMAP3
+ * Rajendra Nayak <rnayak@ti.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/types.h>
+#include <linux/cpufreq.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <mach/cpufreq.h>
+#include <mach/common.h>
+
+#include "clock.h"
+
+struct davinci_cpufreq {
+       struct device *dev;
+       struct clk *armclk;
+};
+static struct davinci_cpufreq cpufreq;
+
+static int davinci_verify_speed(struct cpufreq_policy *policy)
+{
+       struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
+       struct cpufreq_frequency_table *freq_table = pdata->freq_table;
+       struct clk *armclk = cpufreq.armclk;
+
+       if (freq_table)
+               return cpufreq_frequency_table_verify(policy, freq_table);
+
+       if (policy->cpu)
+               return -EINVAL;
+
+       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+                                    policy->cpuinfo.max_freq);
+
+       policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000;
+       policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000;
+       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+                                               policy->cpuinfo.max_freq);
+       return 0;
+}
+
+static unsigned int davinci_getspeed(unsigned int cpu)
+{
+       if (cpu)
+               return 0;
+
+       return clk_get_rate(cpufreq.armclk) / 1000;
+}
+
+static int davinci_target(struct cpufreq_policy *policy,
+                               unsigned int target_freq, unsigned int relation)
+{
+       int ret = 0;
+       unsigned int idx;
+       struct cpufreq_freqs freqs;
+       struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
+       struct clk *armclk = cpufreq.armclk;
+
+       /*
+        * Ensure desired rate is within allowed range.  Some govenors
+        * (ondemand) will just pass target_freq=0 to get the minimum.
+        */
+       if (target_freq < policy->cpuinfo.min_freq)
+               target_freq = policy->cpuinfo.min_freq;
+       if (target_freq > policy->cpuinfo.max_freq)
+               target_freq = policy->cpuinfo.max_freq;
+
+       freqs.old = davinci_getspeed(0);
+       freqs.new = clk_round_rate(armclk, target_freq * 1000) / 1000;
+       freqs.cpu = 0;
+
+       if (freqs.old == freqs.new)
+               return ret;
+
+       cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER,
+                       dev_driver_string(cpufreq.dev),
+                       "transition: %u --> %u\n", freqs.old, freqs.new);
+
+       ret = cpufreq_frequency_table_target(policy, pdata->freq_table,
+                                               freqs.new, relation, &idx);
+       if (ret)
+               return -EINVAL;
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+       /* if moving to higher frequency, up the voltage beforehand */
+       if (pdata->set_voltage && freqs.new > freqs.old)
+               pdata->set_voltage(idx);
+
+       ret = clk_set_rate(armclk, idx);
+
+       /* if moving to lower freq, lower the voltage after lowering freq */
+       if (pdata->set_voltage && freqs.new < freqs.old)
+               pdata->set_voltage(idx);
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+       return ret;
+}
+
+static int __init davinci_cpu_init(struct cpufreq_policy *policy)
+{
+       int result = 0;
+       struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
+       struct cpufreq_frequency_table *freq_table = pdata->freq_table;
+
+       if (policy->cpu != 0)
+               return -EINVAL;
+
+       /* Finish platform specific initialization */
+       if (pdata->init) {
+               result = pdata->init();
+               if (result)
+                       return result;
+       }
+
+       policy->cur = policy->min = policy->max = davinci_getspeed(0);
+
+       if (freq_table) {
+               result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+               if (!result)
+                       cpufreq_frequency_table_get_attr(freq_table,
+                                                       policy->cpu);
+       } else {
+               policy->cpuinfo.min_freq = policy->min;
+               policy->cpuinfo.max_freq = policy->max;
+       }
+
+       policy->min = policy->cpuinfo.min_freq;
+       policy->max = policy->cpuinfo.max_freq;
+       policy->cur = davinci_getspeed(0);
+
+       /*
+        * Time measurement across the target() function yields ~1500-1800us
+        * time taken with no drivers on notification list.
+        * Setting the latency to 2000 us to accomodate addition of drivers
+        * to pre/post change notification list.
+        */
+       policy->cpuinfo.transition_latency = 2000 * 1000;
+       return 0;
+}
+
+static int davinci_cpu_exit(struct cpufreq_policy *policy)
+{
+       cpufreq_frequency_table_put_attr(policy->cpu);
+       return 0;
+}
+
+static struct freq_attr *davinci_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+static struct cpufreq_driver davinci_driver = {
+       .flags          = CPUFREQ_STICKY,
+       .verify         = davinci_verify_speed,
+       .target         = davinci_target,
+       .get            = davinci_getspeed,
+       .init           = davinci_cpu_init,
+       .exit           = davinci_cpu_exit,
+       .name           = "davinci",
+       .attr           = davinci_cpufreq_attr,
+};
+
+static int __init davinci_cpufreq_probe(struct platform_device *pdev)
+{
+       struct davinci_cpufreq_config *pdata = pdev->dev.platform_data;
+
+       if (!pdata)
+               return -EINVAL;
+       if (!pdata->freq_table)
+               return -EINVAL;
+
+       cpufreq.dev = &pdev->dev;
+
+       cpufreq.armclk = clk_get(NULL, "arm");
+       if (IS_ERR(cpufreq.armclk)) {
+               dev_err(cpufreq.dev, "Unable to get ARM clock\n");
+               return PTR_ERR(cpufreq.armclk);
+       }
+
+       return cpufreq_register_driver(&davinci_driver);
+}
+
+static int __exit davinci_cpufreq_remove(struct platform_device *pdev)
+{
+       clk_put(cpufreq.armclk);
+
+       return cpufreq_unregister_driver(&davinci_driver);
+}
+
+static struct platform_driver davinci_cpufreq_driver = {
+       .driver = {
+               .name    = "cpufreq-davinci",
+               .owner   = THIS_MODULE,
+       },
+       .remove = __exit_p(davinci_cpufreq_remove),
+};
+
+static int __init davinci_cpufreq_init(void)
+{
+       return platform_driver_probe(&davinci_cpufreq_driver,
+                                                       davinci_cpufreq_probe);
+}
+late_initcall(davinci_cpufreq_init);
+
diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c
new file mode 100644 (file)
index 0000000..97a90f3
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * CPU idle for DaVinci SoCs
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated. http://www.ti.com/
+ *
+ * Derived from Marvell Kirkwood CPU idle code
+ * (arch/arm/mach-kirkwood/cpuidle.c)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/cpuidle.h>
+#include <linux/io.h>
+#include <asm/proc-fns.h>
+
+#include <mach/cpuidle.h>
+
+#define DAVINCI_CPUIDLE_MAX_STATES     2
+
+struct davinci_ops {
+       void (*enter) (u32 flags);
+       void (*exit) (u32 flags);
+       u32 flags;
+};
+
+/* fields in davinci_ops.flags */
+#define DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN        BIT(0)
+
+static struct cpuidle_driver davinci_idle_driver = {
+       .name   = "cpuidle-davinci",
+       .owner  = THIS_MODULE,
+};
+
+static DEFINE_PER_CPU(struct cpuidle_device, davinci_cpuidle_device);
+static void __iomem *ddr2_reg_base;
+
+#define DDR2_SDRCR_OFFSET      0xc
+#define DDR2_SRPD_BIT          BIT(23)
+#define DDR2_LPMODEN_BIT       BIT(31)
+
+static void davinci_save_ddr_power(int enter, bool pdown)
+{
+       u32 val;
+
+       val = __raw_readl(ddr2_reg_base + DDR2_SDRCR_OFFSET);
+
+       if (enter) {
+               if (pdown)
+                       val |= DDR2_SRPD_BIT;
+               else
+                       val &= ~DDR2_SRPD_BIT;
+               val |= DDR2_LPMODEN_BIT;
+       } else {
+               val &= ~(DDR2_SRPD_BIT | DDR2_LPMODEN_BIT);
+       }
+
+       __raw_writel(val, ddr2_reg_base + DDR2_SDRCR_OFFSET);
+}
+
+static void davinci_c2state_enter(u32 flags)
+{
+       davinci_save_ddr_power(1, !!(flags & DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN));
+}
+
+static void davinci_c2state_exit(u32 flags)
+{
+       davinci_save_ddr_power(0, !!(flags & DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN));
+}
+
+static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = {
+       [1] = {
+               .enter  = davinci_c2state_enter,
+               .exit   = davinci_c2state_exit,
+       },
+};
+
+/* Actual code that puts the SoC in different idle states */
+static int davinci_enter_idle(struct cpuidle_device *dev,
+                                               struct cpuidle_state *state)
+{
+       struct davinci_ops *ops = cpuidle_get_statedata(state);
+       struct timeval before, after;
+       int idle_time;
+
+       local_irq_disable();
+       do_gettimeofday(&before);
+
+       if (ops && ops->enter)
+               ops->enter(ops->flags);
+       /* Wait for interrupt state */
+       cpu_do_idle();
+       if (ops && ops->exit)
+               ops->exit(ops->flags);
+
+       do_gettimeofday(&after);
+       local_irq_enable();
+       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
+                       (after.tv_usec - before.tv_usec);
+       return idle_time;
+}
+
+static int __init davinci_cpuidle_probe(struct platform_device *pdev)
+{
+       int ret;
+       struct cpuidle_device *device;
+       struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;
+       struct resource *ddr2_regs;
+       resource_size_t len;
+
+       device = &per_cpu(davinci_cpuidle_device, smp_processor_id());
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "cannot get platform data\n");
+               return -ENOENT;
+       }
+
+       ddr2_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!ddr2_regs) {
+               dev_err(&pdev->dev, "cannot get DDR2 controller register base");
+               return -ENODEV;
+       }
+
+       len = resource_size(ddr2_regs);
+
+       ddr2_regs = request_mem_region(ddr2_regs->start, len, ddr2_regs->name);
+       if (!ddr2_regs)
+               return -EBUSY;
+
+       ddr2_reg_base = ioremap(ddr2_regs->start, len);
+       if (!ddr2_reg_base) {
+               ret = -ENOMEM;
+               goto ioremap_fail;
+       }
+
+       ret = cpuidle_register_driver(&davinci_idle_driver);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register driver\n");
+               goto driver_register_fail;
+       }
+
+       /* Wait for interrupt state */
+       device->states[0].enter = davinci_enter_idle;
+       device->states[0].exit_latency = 1;
+       device->states[0].target_residency = 10000;
+       device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
+       strcpy(device->states[0].name, "WFI");
+       strcpy(device->states[0].desc, "Wait for interrupt");
+
+       /* Wait for interrupt and DDR self refresh state */
+       device->states[1].enter = davinci_enter_idle;
+       device->states[1].exit_latency = 10;
+       device->states[1].target_residency = 10000;
+       device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
+       strcpy(device->states[1].name, "DDR SR");
+       strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
+       if (pdata->ddr2_pdown)
+               davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN;
+       cpuidle_set_statedata(&device->states[1], &davinci_states[1]);
+
+       device->state_count = DAVINCI_CPUIDLE_MAX_STATES;
+
+       ret = cpuidle_register_device(device);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register device\n");
+               goto device_register_fail;
+       }
+
+       return 0;
+
+device_register_fail:
+       cpuidle_unregister_driver(&davinci_idle_driver);
+driver_register_fail:
+       iounmap(ddr2_reg_base);
+ioremap_fail:
+       release_mem_region(ddr2_regs->start, len);
+       return ret;
+}
+
+static struct platform_driver davinci_cpuidle_driver = {
+       .driver = {
+               .name   = "cpuidle-davinci",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init davinci_cpuidle_init(void)
+{
+       return platform_driver_probe(&davinci_cpuidle_driver,
+                                               davinci_cpuidle_probe);
+}
+device_initcall(davinci_cpuidle_init);
+
index 19b2748..b22b5cf 100644 (file)
@@ -8,22 +8,17 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
-#include <linux/platform_device.h>
 
 #include <asm/mach/map.h>
 
-#include <mach/clock.h>
 #include <mach/psc.h>
-#include <mach/mux.h>
 #include <mach/irqs.h>
 #include <mach/cputype.h>
 #include <mach/common.h>
 #include <mach/time.h>
 #include <mach/da8xx.h>
-#include <mach/asp.h>
 
 #include "clock.h"
 #include "mux.h"
@@ -193,14 +188,14 @@ static struct clk uart1_clk = {
        .name           = "uart1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_UART1,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk uart2_clk = {
        .name           = "uart2",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_UART2,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk spi0_clk = {
@@ -213,98 +208,98 @@ static struct clk spi1_clk = {
        .name           = "spi1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_SPI1,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk ecap0_clk = {
        .name           = "ecap0",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_ECAP,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk ecap1_clk = {
        .name           = "ecap1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_ECAP,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk ecap2_clk = {
        .name           = "ecap2",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_ECAP,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk pwm0_clk = {
        .name           = "pwm0",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_PWM,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk pwm1_clk = {
        .name           = "pwm1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_PWM,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk pwm2_clk = {
        .name           = "pwm2",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_PWM,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk eqep0_clk = {
        .name           = "eqep0",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA830_LPSC1_EQEP,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk eqep1_clk = {
        .name           = "eqep1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA830_LPSC1_EQEP,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk lcdc_clk = {
        .name           = "lcdc",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_LCDC,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk mcasp0_clk = {
        .name           = "mcasp0",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_McASP0,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk mcasp1_clk = {
        .name           = "mcasp1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA830_LPSC1_McASP1,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk mcasp2_clk = {
        .name           = "mcasp2",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA830_LPSC1_McASP2,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk usb20_clk = {
        .name           = "usb20",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_USB20,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk aemif_clk = {
@@ -332,36 +327,36 @@ static struct clk emac_clk = {
        .name           = "emac",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_CPGMAC,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk gpio_clk = {
        .name           = "gpio",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_GPIO,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk i2c1_clk = {
        .name           = "i2c1",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_I2C,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk usb11_clk = {
        .name           = "usb11",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_USB11,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk emif3_clk = {
        .name           = "emif3",
        .parent         = &pll0_sysclk5,
        .lpsc           = DA8XX_LPSC1_EMIF3C,
+       .gpsc           = 1,
        .flags          = ALWAYS_ENABLED,
-       .psc_ctlr       = 1,
 };
 
 static struct clk arm_clk = {
@@ -411,7 +406,7 @@ static struct davinci_clk da830_clks[] = {
        CLK(NULL,               "pwm2",         &pwm2_clk),
        CLK("eqep.0",           NULL,           &eqep0_clk),
        CLK("eqep.1",           NULL,           &eqep1_clk),
-       CLK("da830_lcdc",       NULL,           &lcdc_clk),
+       CLK("da8xx_lcdc.0",     NULL,           &lcdc_clk),
        CLK("davinci-mcasp.0",  NULL,           &mcasp0_clk),
        CLK("davinci-mcasp.1",  NULL,           &mcasp1_clk),
        CLK("davinci-mcasp.2",  NULL,           &mcasp2_clk),
@@ -1143,7 +1138,21 @@ static struct davinci_id da830_ids[] = {
                .part_no        = 0xb7df,
                .manufacturer   = 0x017,        /* 0x02f >> 1 */
                .cpu_id         = DAVINCI_CPU_ID_DA830,
-               .name           = "da830/omap l137",
+               .name           = "da830/omap-l137 rev1.0",
+       },
+       {
+               .variant        = 0x8,
+               .part_no        = 0xb7df,
+               .manufacturer   = 0x017,
+               .cpu_id         = DAVINCI_CPU_ID_DA830,
+               .name           = "da830/omap-l137 rev1.1",
+       },
+       {
+               .variant        = 0x9,
+               .part_no        = 0xb7df,
+               .manufacturer   = 0x017,
+               .cpu_id         = DAVINCI_CPU_ID_DA830,
+               .name           = "da830/omap-l137 rev2.0",
        },
 };
 
@@ -1178,13 +1187,11 @@ static struct davinci_timer_info da830_timer_info = {
 static struct davinci_soc_info davinci_soc_info_da830 = {
        .io_desc                = da830_io_desc,
        .io_desc_num            = ARRAY_SIZE(da830_io_desc),
-       .jtag_id_base           = IO_ADDRESS(DA8XX_JTAG_ID_REG),
        .ids                    = da830_ids,
        .ids_num                = ARRAY_SIZE(da830_ids),
        .cpu_clks               = da830_clks,
        .psc_bases              = da830_psc_bases,
        .psc_bases_num          = ARRAY_SIZE(da830_psc_bases),
-       .pinmux_base            = IO_ADDRESS(DA8XX_BOOT_CFG_BASE + 0x120),
        .pinmux_pins            = da830_pins,
        .pinmux_pins_num        = ARRAY_SIZE(da830_pins),
        .intc_base              = (void __iomem *)DA8XX_CP_INTC_VIRT,
@@ -1201,5 +1208,13 @@ static struct davinci_soc_info davinci_soc_info_da830 = {
 
 void __init da830_init(void)
 {
+       da8xx_syscfg_base = ioremap(DA8XX_SYSCFG_BASE, SZ_4K);
+       if (WARN(!da8xx_syscfg_base, "Unable to map syscfg module"))
+               return;
+
+       davinci_soc_info_da830.jtag_id_base =
+                                       DA8XX_SYSCFG_VIRT(DA8XX_JTAG_ID_REG);
+       davinci_soc_info_da830.pinmux_base = DA8XX_SYSCFG_VIRT(0x120);
+
        davinci_common_init(&davinci_soc_info_da830);
 }
index 192d719..717806c 100644 (file)
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/regulator/consumer.h>
 
 #include <asm/mach/map.h>
 
-#include <mach/clock.h>
 #include <mach/psc.h>
-#include <mach/mux.h>
 #include <mach/irqs.h>
 #include <mach/cputype.h>
 #include <mach/common.h>
 #include <mach/time.h>
 #include <mach/da8xx.h>
+#include <mach/cpufreq.h>
 
 #include "clock.h"
 #include "mux.h"
 
+/* SoC specific clock flags */
+#define DA850_CLK_ASYNC3       BIT(16)
+
 #define DA850_PLL1_BASE                0x01e1a000
 #define DA850_TIMER64P2_BASE   0x01f0c000
 #define DA850_TIMER64P3_BASE   0x01f0d000
 
 #define DA850_REF_FREQ         24000000
 
+#define CFGCHIP3_ASYNC3_CLKSRC BIT(4)
+#define CFGCHIP0_PLL_MASTER_LOCK       BIT(4)
+
+static int da850_set_armrate(struct clk *clk, unsigned long rate);
+static int da850_round_armrate(struct clk *clk, unsigned long rate);
+static int da850_set_pll0rate(struct clk *clk, unsigned long armrate);
+
 static struct pll_data pll0_data = {
        .num            = 1,
        .phys_base      = DA8XX_PLL0_BASE,
@@ -52,6 +62,7 @@ static struct clk pll0_clk = {
        .parent         = &ref_clk,
        .pll_data       = &pll0_data,
        .flags          = CLK_PLL,
+       .set_rate       = da850_set_pll0rate,
 };
 
 static struct clk pll0_aux_clk = {
@@ -210,16 +221,16 @@ static struct clk tpcc1_clk = {
        .name           = "tpcc1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA850_LPSC1_TPCC1,
+       .gpsc           = 1,
        .flags          = CLK_PSC | ALWAYS_ENABLED,
-       .psc_ctlr       = 1,
 };
 
 static struct clk tptc2_clk = {
        .name           = "tptc2",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA850_LPSC1_TPTC2,
+       .gpsc           = 1,
        .flags          = ALWAYS_ENABLED,
-       .psc_ctlr       = 1,
 };
 
 static struct clk uart0_clk = {
@@ -232,14 +243,16 @@ static struct clk uart1_clk = {
        .name           = "uart1",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_UART1,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
+       .flags          = DA850_CLK_ASYNC3,
 };
 
 static struct clk uart2_clk = {
        .name           = "uart2",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_UART2,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
+       .flags          = DA850_CLK_ASYNC3,
 };
 
 static struct clk aintc_clk = {
@@ -253,22 +266,22 @@ static struct clk gpio_clk = {
        .name           = "gpio",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_GPIO,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk i2c1_clk = {
        .name           = "i2c1",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_I2C,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk emif3_clk = {
        .name           = "emif3",
        .parent         = &pll0_sysclk5,
        .lpsc           = DA8XX_LPSC1_EMIF3C,
+       .gpsc           = 1,
        .flags          = ALWAYS_ENABLED,
-       .psc_ctlr       = 1,
 };
 
 static struct clk arm_clk = {
@@ -276,6 +289,8 @@ static struct clk arm_clk = {
        .parent         = &pll0_sysclk6,
        .lpsc           = DA8XX_LPSC0_ARM,
        .flags          = ALWAYS_ENABLED,
+       .set_rate       = da850_set_armrate,
+       .round_rate     = da850_round_armrate,
 };
 
 static struct clk rmii_clk = {
@@ -287,21 +302,22 @@ static struct clk emac_clk = {
        .name           = "emac",
        .parent         = &pll0_sysclk4,
        .lpsc           = DA8XX_LPSC1_CPGMAC,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk mcasp_clk = {
        .name           = "mcasp",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_McASP0,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
+       .flags          = DA850_CLK_ASYNC3,
 };
 
 static struct clk lcdc_clk = {
        .name           = "lcdc",
        .parent         = &pll0_sysclk2,
        .lpsc           = DA8XX_LPSC1_LCDC,
-       .psc_ctlr       = 1,
+       .gpsc           = 1,
 };
 
 static struct clk mmcsd_clk = {
@@ -404,6 +420,14 @@ static const struct mux_config da850_pins[] = {
        MUX_CFG(DA850, MII_RXD_0,       3,      28,     15,     8,      false)
        MUX_CFG(DA850, MDIO_CLK,        4,      0,      15,     8,      false)
        MUX_CFG(DA850, MDIO_D,          4,      4,      15,     8,      false)
+       MUX_CFG(DA850, RMII_TXD_0,      14,     12,     15,     8,      false)
+       MUX_CFG(DA850, RMII_TXD_1,      14,     8,      15,     8,      false)
+       MUX_CFG(DA850, RMII_TXEN,       14,     16,     15,     8,      false)
+       MUX_CFG(DA850, RMII_CRS_DV,     15,     4,      15,     8,      false)
+       MUX_CFG(DA850, RMII_RXD_0,      14,     24,     15,     8,      false)
+       MUX_CFG(DA850, RMII_RXD_1,      14,     20,     15,     8,      false)
+       MUX_CFG(DA850, RMII_RXER,       14,     28,     15,     8,      false)
+       MUX_CFG(DA850, RMII_MHZ_50_CLK, 15,     0,      15,     0,      false)
        /* McASP function */
        MUX_CFG(DA850,  ACLKR,          0,      0,      15,     1,      false)
        MUX_CFG(DA850,  ACLKX,          0,      4,      15,     1,      false)
@@ -506,8 +530,9 @@ static const struct mux_config da850_pins[] = {
        MUX_CFG(DA850, EMA_WAIT_1,      6,      24,     15,     1,      false)
        MUX_CFG(DA850, NEMA_CS_2,       7,      0,      15,     1,      false)
        /* GPIO function */
+       MUX_CFG(DA850, GPIO2_6,         6,      4,      15,     8,      false)
+       MUX_CFG(DA850, GPIO2_8,         5,      28,     15,     8,      false)
        MUX_CFG(DA850, GPIO2_15,        5,      0,      15,     8,      false)
-       MUX_CFG(DA850, GPIO8_10,        18,     28,     15,     8,      false)
        MUX_CFG(DA850, GPIO4_0,         10,     28,     15,     8,      false)
        MUX_CFG(DA850, GPIO4_1,         10,     24,     15,     8,      false)
 #endif
@@ -547,6 +572,14 @@ const short da850_cpgmac_pins[] __initdata = {
        -1
 };
 
+const short da850_rmii_pins[] __initdata = {
+       DA850_RMII_TXD_0, DA850_RMII_TXD_1, DA850_RMII_TXEN,
+       DA850_RMII_CRS_DV, DA850_RMII_RXD_0, DA850_RMII_RXD_1,
+       DA850_RMII_RXER, DA850_RMII_MHZ_50_CLK, DA850_MDIO_CLK,
+       DA850_MDIO_D,
+       -1
+};
+
 const short da850_mcasp_pins[] __initdata = {
        DA850_AHCLKX, DA850_ACLKX, DA850_AFSX,
        DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, DA850_AMUTE,
@@ -555,12 +588,11 @@ const short da850_mcasp_pins[] __initdata = {
 };
 
 const short da850_lcdcntl_pins[] __initdata = {
-       DA850_LCD_D_1, DA850_LCD_D_2, DA850_LCD_D_3, DA850_LCD_D_4,
-       DA850_LCD_D_5, DA850_LCD_D_6, DA850_LCD_D_7, DA850_LCD_D_8,
-       DA850_LCD_D_9, DA850_LCD_D_10, DA850_LCD_D_11, DA850_LCD_D_12,
-       DA850_LCD_D_13, DA850_LCD_D_14, DA850_LCD_D_15, DA850_LCD_PCLK,
-       DA850_LCD_HSYNC, DA850_LCD_VSYNC, DA850_NLCD_AC_ENB_CS, DA850_GPIO2_15,
-       DA850_GPIO8_10,
+       DA850_LCD_D_0, DA850_LCD_D_1, DA850_LCD_D_2, DA850_LCD_D_3,
+       DA850_LCD_D_4, DA850_LCD_D_5, DA850_LCD_D_6, DA850_LCD_D_7,
+       DA850_LCD_D_8, DA850_LCD_D_9, DA850_LCD_D_10, DA850_LCD_D_11,
+       DA850_LCD_D_12, DA850_LCD_D_13, DA850_LCD_D_14, DA850_LCD_D_15,
+       DA850_LCD_PCLK, DA850_LCD_HSYNC, DA850_LCD_VSYNC, DA850_NLCD_AC_ENB_CS,
        -1
 };
 
@@ -790,16 +822,221 @@ static struct davinci_timer_info da850_timer_info = {
        .clocksource_id = T0_TOP,
 };
 
+static void da850_set_async3_src(int pllnum)
+{
+       struct clk *clk, *newparent = pllnum ? &pll1_sysclk2 : &pll0_sysclk2;
+       struct davinci_clk *c;
+       unsigned int v;
+       int ret;
+
+       for (c = da850_clks; c->lk.clk; c++) {
+               clk = c->lk.clk;
+               if (clk->flags & DA850_CLK_ASYNC3) {
+                       ret = clk_set_parent(clk, newparent);
+                       WARN(ret, "DA850: unable to re-parent clock %s",
+                                                               clk->name);
+               }
+       }
+
+       v = __raw_readl(DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP3_REG));
+       if (pllnum)
+               v |= CFGCHIP3_ASYNC3_CLKSRC;
+       else
+               v &= ~CFGCHIP3_ASYNC3_CLKSRC;
+       __raw_writel(v, DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP3_REG));
+}
+
+#ifdef CONFIG_CPU_FREQ
+/*
+ * Notes:
+ * According to the TRM, minimum PLLM results in maximum power savings.
+ * The OPP definitions below should keep the PLLM as low as possible.
+ *
+ * The output of the PLLM must be between 400 to 600 MHz.
+ * This rules out prediv of anything but divide-by-one for 24Mhz OSC input.
+ */
+struct da850_opp {
+       unsigned int    freq;   /* in KHz */
+       unsigned int    prediv;
+       unsigned int    mult;
+       unsigned int    postdiv;
+       unsigned int    cvdd_min; /* in uV */
+       unsigned int    cvdd_max; /* in uV */
+};
+
+static const struct da850_opp da850_opp_300 = {
+       .freq           = 300000,
+       .prediv         = 1,
+       .mult           = 25,
+       .postdiv        = 2,
+       .cvdd_min       = 1140000,
+       .cvdd_max       = 1320000,
+};
+
+static const struct da850_opp da850_opp_200 = {
+       .freq           = 200000,
+       .prediv         = 1,
+       .mult           = 25,
+       .postdiv        = 3,
+       .cvdd_min       = 1050000,
+       .cvdd_max       = 1160000,
+};
+
+static const struct da850_opp da850_opp_96 = {
+       .freq           = 96000,
+       .prediv         = 1,
+       .mult           = 20,
+       .postdiv        = 5,
+       .cvdd_min       = 950000,
+       .cvdd_max       = 1050000,
+};
+
+#define OPP(freq)              \
+       {                               \
+               .index = (unsigned int) &da850_opp_##freq,      \
+               .frequency = freq * 1000, \
+       }
+
+static struct cpufreq_frequency_table da850_freq_table[] = {
+       OPP(300),
+       OPP(200),
+       OPP(96),
+       {
+               .index          = 0,
+               .frequency      = CPUFREQ_TABLE_END,
+       },
+};
+
+#ifdef CONFIG_REGULATOR
+static struct regulator *cvdd;
+
+static int da850_set_voltage(unsigned int index)
+{
+       struct da850_opp *opp;
+
+       if (!cvdd)
+               return -ENODEV;
+
+       opp = (struct da850_opp *) da850_freq_table[index].index;
+
+       return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max);
+}
+
+static int da850_regulator_init(void)
+{
+       cvdd = regulator_get(NULL, "cvdd");
+       if (WARN(IS_ERR(cvdd), "Unable to obtain voltage regulator for CVDD;"
+                                       " voltage scaling unsupported\n")) {
+               return PTR_ERR(cvdd);
+       }
+
+       return 0;
+}
+#endif
+
+static struct davinci_cpufreq_config cpufreq_info = {
+       .freq_table = &da850_freq_table[0],
+#ifdef CONFIG_REGULATOR
+       .init = da850_regulator_init,
+       .set_voltage = da850_set_voltage,
+#endif
+};
+
+static struct platform_device da850_cpufreq_device = {
+       .name                   = "cpufreq-davinci",
+       .dev = {
+               .platform_data  = &cpufreq_info,
+       },
+};
+
+int __init da850_register_cpufreq(void)
+{
+       return platform_device_register(&da850_cpufreq_device);
+}
+
+static int da850_round_armrate(struct clk *clk, unsigned long rate)
+{
+       int i, ret = 0, diff;
+       unsigned int best = (unsigned int) -1;
+
+       rate /= 1000; /* convert to kHz */
+
+       for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+               diff = da850_freq_table[i].frequency - rate;
+               if (diff < 0)
+                       diff = -diff;
+
+               if (diff < best) {
+                       best = diff;
+                       ret = da850_freq_table[i].frequency;
+               }
+       }
+
+       return ret * 1000;
+}
+
+static int da850_set_armrate(struct clk *clk, unsigned long index)
+{
+       struct clk *pllclk = &pll0_clk;
+
+       return clk_set_rate(pllclk, index);
+}
+
+static int da850_set_pll0rate(struct clk *clk, unsigned long index)
+{
+       unsigned int prediv, mult, postdiv;
+       struct da850_opp *opp;
+       struct pll_data *pll = clk->pll_data;
+       unsigned int v;
+       int ret;
+
+       opp = (struct da850_opp *) da850_freq_table[index].index;
+       prediv = opp->prediv;
+       mult = opp->mult;
+       postdiv = opp->postdiv;
+
+       /* Unlock writing to PLL registers */
+       v = __raw_readl(DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP0_REG));
+       v &= ~CFGCHIP0_PLL_MASTER_LOCK;
+       __raw_writel(v, DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP0_REG));
+
+       ret = davinci_set_pllrate(pll, prediv, mult, postdiv);
+       if (WARN_ON(ret))
+               return ret;
+
+       return 0;
+}
+#else
+int __init da850_register_cpufreq(void)
+{
+       return 0;
+}
+
+static int da850_set_armrate(struct clk *clk, unsigned long rate)
+{
+       return -EINVAL;
+}
+
+static int da850_set_pll0rate(struct clk *clk, unsigned long armrate)
+{
+       return -EINVAL;
+}
+
+static int da850_round_armrate(struct clk *clk, unsigned long rate)
+{
+       return clk->rate;
+}
+#endif
+
+
 static struct davinci_soc_info davinci_soc_info_da850 = {
        .io_desc                = da850_io_desc,
        .io_desc_num            = ARRAY_SIZE(da850_io_desc),
-       .jtag_id_base           = IO_ADDRESS(DA8XX_JTAG_ID_REG),
        .ids                    = da850_ids,
        .ids_num                = ARRAY_SIZE(da850_ids),
        .cpu_clks               = da850_clks,
        .psc_bases              = da850_psc_bases,
        .psc_bases_num          = ARRAY_SIZE(da850_psc_bases),
-       .pinmux_base            = IO_ADDRESS(DA8XX_BOOT_CFG_BASE + 0x120),
        .pinmux_pins            = da850_pins,
        .pinmux_pins_num        = ARRAY_SIZE(da850_pins),
        .intc_base              = (void __iomem *)DA8XX_CP_INTC_VIRT,
@@ -816,5 +1053,22 @@ static struct davinci_soc_info davinci_soc_info_da850 = {
 
 void __init da850_init(void)
 {
+       da8xx_syscfg_base = ioremap(DA8XX_SYSCFG_BASE, SZ_4K);
+       if (WARN(!da8xx_syscfg_base, "Unable to map syscfg module"))
+               return;
+
+       davinci_soc_info_da850.jtag_id_base =
+                                       DA8XX_SYSCFG_VIRT(DA8XX_JTAG_ID_REG);
+       davinci_soc_info_da850.pinmux_base = DA8XX_SYSCFG_VIRT(0x120);
+
        davinci_common_init(&davinci_soc_info_da850);
+
+       /*
+        * Move the clock source of Async3 domain to PLL1 SYSCLK2.
+        * This helps keeping the peripherals on this domain insulated
+        * from CPU frequency changes caused by DVFS. The firmware sets
+        * both PLL0 and PLL1 to the same frequency so, there should not
+        * be any noticible change even in non-DVFS use cases.
+        */
+       da850_set_async3_src(1);
 }
index 58ad5b6..dd2d32c 100644 (file)
@@ -10,8 +10,6 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
-#include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
@@ -21,7 +19,7 @@
 #include <mach/common.h>
 #include <mach/time.h>
 #include <mach/da8xx.h>
-#include <video/da8xx-fb.h>
+#include <mach/cpuidle.h>
 
 #include "clock.h"
 
@@ -30,6 +28,7 @@
 #define DA8XX_TPTC1_BASE               0x01c08400
 #define DA8XX_WDOG_BASE                        0x01c21000 /* DA8XX_TIMER64P1_BASE */
 #define DA8XX_I2C0_BASE                        0x01c22000
+#define DA8XX_RTC_BASE                 0x01C23000
 #define DA8XX_EMAC_CPPI_PORT_BASE      0x01e20000
 #define DA8XX_EMAC_CPGMACSS_BASE       0x01e22000
 #define DA8XX_EMAC_CPGMAC_BASE         0x01e23000
@@ -43,6 +42,8 @@
 #define DA8XX_MDIO_REG_OFFSET          0x4000
 #define DA8XX_EMAC_CTRL_RAM_SIZE       SZ_8K
 
+void __iomem *da8xx_syscfg_base;
+
 static struct plat_serial8250_port da8xx_serial_pdata[] = {
        {
                .mapbase        = DA8XX_UART0_BASE,
@@ -282,6 +283,11 @@ static struct platform_device da8xx_emac_device = {
        .resource       = da8xx_emac_resources,
 };
 
+int __init da8xx_register_emac(void)
+{
+       return platform_device_register(&da8xx_emac_device);
+}
+
 static struct resource da830_mcasp1_resources[] = {
        {
                .name   = "mcasp1",
@@ -338,12 +344,7 @@ static struct platform_device da850_mcasp_device = {
        .resource       = da850_mcasp_resources,
 };
 
-int __init da8xx_register_emac(void)
-{
-       return platform_device_register(&da8xx_emac_device);
-}
-
-void __init da8xx_init_mcasp(int id, struct snd_platform_data *pdata)
+void __init da8xx_register_mcasp(int id, struct snd_platform_data *pdata)
 {
        /* DA830/OMAP-L137 has 3 instances of McASP */
        if (cpu_is_davinci_da830() && id == 1) {
@@ -379,10 +380,16 @@ static struct lcd_ctrl_config lcd_cfg = {
        .raster_order           = 0,
 };
 
-static struct da8xx_lcdc_platform_data da850_evm_lcdc_pdata = {
-       .manu_name = "sharp",
-       .controller_data = &lcd_cfg,
-       .type = "Sharp_LK043T1DG01",
+struct da8xx_lcdc_platform_data sharp_lcd035q3dg01_pdata = {
+       .manu_name              = "sharp",
+       .controller_data        = &lcd_cfg,
+       .type                   = "Sharp_LCD035Q3DG01",
+};
+
+struct da8xx_lcdc_platform_data sharp_lk043t1dg01_pdata = {
+       .manu_name              = "sharp",
+       .controller_data        = &lcd_cfg,
+       .type                   = "Sharp_LK043T1DG01",
 };
 
 static struct resource da8xx_lcdc_resources[] = {
@@ -398,19 +405,17 @@ static struct resource da8xx_lcdc_resources[] = {
        },
 };
 
-static struct platform_device da850_lcdc_device = {
+static struct platform_device da8xx_lcdc_device = {
        .name           = "da8xx_lcdc",
        .id             = 0,
        .num_resources  = ARRAY_SIZE(da8xx_lcdc_resources),
        .resource       = da8xx_lcdc_resources,
-       .dev = {
-               .platform_data = &da850_evm_lcdc_pdata,
-       }
 };
 
-int __init da8xx_register_lcdc(void)
+int __init da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata)
 {
-       return platform_device_register(&da850_lcdc_device);
+       da8xx_lcdc_device.dev.platform_data = pdata;
+       return platform_device_register(&da8xx_lcdc_device);
 }
 
 static struct resource da8xx_mmcsd0_resources[] = {
@@ -448,3 +453,66 @@ int __init da8xx_register_mmcsd0(struct davinci_mmc_config *config)
        da8xx_mmcsd0_device.dev.platform_data = config;
        return platform_device_register(&da8xx_mmcsd0_device);
 }
+
+static struct resource da8xx_rtc_resources[] = {
+       {
+               .start          = DA8XX_RTC_BASE,
+               .end            = DA8XX_RTC_BASE + SZ_4K - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+       { /* timer irq */
+               .start          = IRQ_DA8XX_RTC,
+               .end            = IRQ_DA8XX_RTC,
+               .flags          = IORESOURCE_IRQ,
+       },
+       { /* alarm irq */
+               .start          = IRQ_DA8XX_RTC,
+               .end            = IRQ_DA8XX_RTC,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device da8xx_rtc_device = {
+       .name           = "omap_rtc",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(da8xx_rtc_resources),
+       .resource       = da8xx_rtc_resources,
+};
+
+int da8xx_register_rtc(void)
+{
+       /* Unlock the rtc's registers */
+       __raw_writel(0x83e70b13, IO_ADDRESS(DA8XX_RTC_BASE + 0x6c));
+       __raw_writel(0x95a4f1e0, IO_ADDRESS(DA8XX_RTC_BASE + 0x70));
+
+       return platform_device_register(&da8xx_rtc_device);
+}
+
+static struct resource da8xx_cpuidle_resources[] = {
+       {
+               .start          = DA8XX_DDR2_CTL_BASE,
+               .end            = DA8XX_DDR2_CTL_BASE + SZ_32K - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+/* DA8XX devices support DDR2 power down */
+static struct davinci_cpuidle_config da8xx_cpuidle_pdata = {
+       .ddr2_pdown     = 1,
+};
+
+
+static struct platform_device da8xx_cpuidle_device = {
+       .name                   = "cpuidle-davinci",
+       .num_resources          = ARRAY_SIZE(da8xx_cpuidle_resources),
+       .resource               = da8xx_cpuidle_resources,
+       .dev = {
+               .platform_data  = &da8xx_cpuidle_pdata,
+       },
+};
+
+int __init da8xx_register_cpuidle(void)
+{
+       return platform_device_register(&da8xx_cpuidle_device);
+}
+
index a55b650..1479496 100644 (file)
@@ -9,15 +9,11 @@
  * (at your option) any later version.
  */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 
-#include <asm/mach/map.h>
-
 #include <mach/hardware.h>
 #include <mach/i2c.h>
 #include <mach/irqs.h>
@@ -177,7 +173,7 @@ void __init davinci_setup_mmc(int module, struct davinci_mmc_config *config)
                        mmcsd1_resources[0].start = DM365_MMCSD1_BASE;
                        mmcsd1_resources[0].end = DM365_MMCSD1_BASE +
                                                        SZ_4K - 1;
-                       mmcsd0_resources[2].start = IRQ_DM365_SDIOINT1;
+                       mmcsd1_resources[2].start = IRQ_DM365_SDIOINT1;
                } else
                        break;
 
index 0596700..dedf4d4 100644 (file)
@@ -8,7 +8,6 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
 #include <linux/serial_8250.h>
@@ -21,7 +20,6 @@
 #include <asm/mach/map.h>
 
 #include <mach/dm355.h>
-#include <mach/clock.h>
 #include <mach/cputype.h>
 #include <mach/edma.h>
 #include <mach/psc.h>
index e815174..2ec619e 100644 (file)
@@ -12,7 +12,6 @@
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
 #include <linux/serial_8250.h>
@@ -23,7 +22,6 @@
 #include <asm/mach/map.h>
 
 #include <mach/dm365.h>
-#include <mach/clock.h>
 #include <mach/cputype.h>
 #include <mach/edma.h>
 #include <mach/psc.h>
@@ -32,6 +30,8 @@
 #include <mach/time.h>
 #include <mach/serial.h>
 #include <mach/common.h>
+#include <mach/asp.h>
+#include <mach/keyscan.h>
 
 #include "clock.h"
 #include "mux.h"
@@ -369,7 +369,7 @@ static struct clk timer3_clk = {
 
 static struct clk usb_clk = {
        .name           = "usb",
-       .parent         = &pll2_sysclk1,
+       .parent         = &pll1_aux_clk,
        .lpsc           = DAVINCI_LPSC_USB,
 };
 
@@ -456,7 +456,7 @@ static struct davinci_clk dm365_clks[] = {
        CLK(NULL, "usb", &usb_clk),
        CLK("davinci_emac.1", NULL, &emac_clk),
        CLK("voice_codec", NULL, &voicecodec_clk),
-       CLK("soc-audio.0", NULL, &asp0_clk),
+       CLK("davinci-asp.0", NULL, &asp0_clk),
        CLK(NULL, "rto", &rto_clk),
        CLK(NULL, "mjcp", &mjcp_clk),
        CLK(NULL, NULL, NULL),
@@ -531,7 +531,7 @@ MUX_CFG(DM365,  EMAC_CRS,   3,   2,     1,    1,     false)
 MUX_CFG(DM365,  EMAC_MDIO,     3,   1,     1,    1,     false)
 MUX_CFG(DM365,  EMAC_MDCLK,    3,   0,     1,    1,     false)
 
-MUX_CFG(DM365, KEYPAD,         2,   0,     0x3f, 0x3f,  false)
+MUX_CFG(DM365, KEYSCAN,        2,   0,     0x3f, 0x3f,  false)
 
 MUX_CFG(DM365, PWM0,           1,   0,     3,    2,     false)
 MUX_CFG(DM365, PWM0_G23,       3,   26,    3,    3,     false)
@@ -603,6 +603,9 @@ INT_CFG(DM365,  INT_IMX1_ENABLE,     24,    1,    1,     false)
 INT_CFG(DM365,  INT_IMX1_DISABLE,    24,    1,    0,     false)
 INT_CFG(DM365,  INT_NSF_ENABLE,      25,    1,    1,     false)
 INT_CFG(DM365,  INT_NSF_DISABLE,     25,    1,    0,     false)
+
+EVT_CFG(DM365, EVT2_ASP_TX,         0,     1,    0,     false)
+EVT_CFG(DM365, EVT3_ASP_RX,         1,     1,    0,     false)
 #endif
 };
 
@@ -696,6 +699,7 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = {
        [IRQ_I2C]                       = 3,
        [IRQ_UARTINT0]                  = 3,
        [IRQ_UARTINT1]                  = 3,
+       [IRQ_DM365_RTCINT]              = 3,
        [IRQ_DM365_SPIINT0_0]           = 3,
        [IRQ_DM365_SPIINT3_0]           = 3,
        [IRQ_DM365_GPIO0]               = 3,
@@ -806,6 +810,50 @@ static struct platform_device dm365_edma_device = {
        .resource               = edma_resources,
 };
 
+static struct resource dm365_asp_resources[] = {
+       {
+               .start  = DAVINCI_DM365_ASP0_BASE,
+               .end    = DAVINCI_DM365_ASP0_BASE + SZ_8K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = DAVINCI_DMA_ASP0_TX,
+               .end    = DAVINCI_DMA_ASP0_TX,
+               .flags  = IORESOURCE_DMA,
+       },
+       {
+               .start  = DAVINCI_DMA_ASP0_RX,
+               .end    = DAVINCI_DMA_ASP0_RX,
+               .flags  = IORESOURCE_DMA,
+       },
+};
+
+static struct platform_device dm365_asp_device = {
+       .name           = "davinci-asp",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(dm365_asp_resources),
+       .resource       = dm365_asp_resources,
+};
+
+static struct resource dm365_rtc_resources[] = {
+       {
+               .start = DM365_RTC_BASE,
+               .end = DM365_RTC_BASE + SZ_1K - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = IRQ_DM365_RTCINT,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device dm365_rtc_device = {
+       .name = "rtc_davinci",
+       .id = 0,
+       .num_resources = ARRAY_SIZE(dm365_rtc_resources),
+       .resource = dm365_rtc_resources,
+};
+
 static struct map_desc dm365_io_desc[] = {
        {
                .virtual        = IO_VIRT,
@@ -822,6 +870,28 @@ static struct map_desc dm365_io_desc[] = {
        },
 };
 
+static struct resource dm365_ks_resources[] = {
+       {
+               /* registers */
+               .start = DM365_KEYSCAN_BASE,
+               .end = DM365_KEYSCAN_BASE + SZ_1K - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               /* interrupt */
+               .start = IRQ_DM365_KEYINT,
+               .end = IRQ_DM365_KEYINT,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device dm365_ks_device = {
+       .name           = "davinci_keyscan",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(dm365_ks_resources),
+       .resource       = dm365_ks_resources,
+};
+
 /* Contents of JTAG ID register used to identify exact cpu type */
 static struct davinci_id dm365_ids[] = {
        {
@@ -907,6 +977,33 @@ static struct davinci_soc_info davinci_soc_info_dm365 = {
        .sram_len               = SZ_32K,
 };
 
+void __init dm365_init_asp(struct snd_platform_data *pdata)
+{
+       davinci_cfg_reg(DM365_MCBSP0_BDX);
+       davinci_cfg_reg(DM365_MCBSP0_X);
+       davinci_cfg_reg(DM365_MCBSP0_BFSX);
+       davinci_cfg_reg(DM365_MCBSP0_BDR);
+       davinci_cfg_reg(DM365_MCBSP0_R);
+       davinci_cfg_reg(DM365_MCBSP0_BFSR);
+       davinci_cfg_reg(DM365_EVT2_ASP_TX);
+       davinci_cfg_reg(DM365_EVT3_ASP_RX);
+       dm365_asp_device.dev.platform_data = pdata;
+       platform_device_register(&dm365_asp_device);
+}
+
+void __init dm365_init_ks(struct davinci_ks_platform_data *pdata)
+{
+       davinci_cfg_reg(DM365_KEYSCAN);
+       dm365_ks_device.dev.platform_data = pdata;
+       platform_device_register(&dm365_ks_device);
+}
+
+void __init dm365_init_rtc(void)
+{
+       davinci_cfg_reg(DM365_INT_PRTCSS);
+       platform_device_register(&dm365_rtc_device);
+}
+
 void __init dm365_init(void)
 {
        davinci_common_init(&davinci_soc_info_dm365);
index d6e0fa5..2cd0081 100644 (file)
@@ -8,7 +8,6 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
 #include <linux/serial_8250.h>
@@ -18,7 +17,6 @@
 #include <asm/mach/map.h>
 
 #include <mach/dm644x.h>
-#include <mach/clock.h>
 #include <mach/cputype.h>
 #include <mach/edma.h>
 #include <mach/irqs.h>
@@ -370,6 +368,11 @@ MUX_CFG(DM644X, ATAEN_DISABLE,     0,   17,    1,    0,     true)
 MUX_CFG(DM644X, HPIEN_DISABLE, 0,   29,    1,    0,     true)
 
 MUX_CFG(DM644X, AEAW,          0,   0,     31,   31,    true)
+MUX_CFG(DM644X, AEAW0,         0,   0,     1,    0,     true)
+MUX_CFG(DM644X, AEAW1,         0,   1,     1,    0,     true)
+MUX_CFG(DM644X, AEAW2,         0,   2,     1,    0,     true)
+MUX_CFG(DM644X, AEAW3,         0,   3,     1,    0,     true)
+MUX_CFG(DM644X, AEAW4,         0,   4,     1,    0,     true)
 
 MUX_CFG(DM644X, MSTK,          1,   9,     1,    0,     false)
 
index 0976049..829a44b 100644 (file)
@@ -8,7 +8,6 @@
  * is licensed "as is" without any warranty of any kind, whether express
  * or implied.
  */
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/clk.h>
 #include <linux/serial_8250.h>
@@ -18,7 +17,6 @@
 #include <asm/mach/map.h>
 
 #include <mach/dm646x.h>
-#include <mach/clock.h>
 #include <mach/cputype.h>
 #include <mach/edma.h>
 #include <mach/irqs.h>
@@ -789,7 +787,14 @@ static struct davinci_id dm646x_ids[] = {
                .part_no        = 0xb770,
                .manufacturer   = 0x017,
                .cpu_id         = DAVINCI_CPU_ID_DM6467,
-               .name           = "dm6467",
+               .name           = "dm6467_rev1.x",
+       },
+       {
+               .variant        = 0x1,
+               .part_no        = 0xb770,
+               .manufacturer   = 0x017,
+               .cpu_id         = DAVINCI_CPU_ID_DM6467,
+               .name           = "dm6467_rev3.x",
        },
 };
 
index f2e57d2..648fbb7 100644 (file)
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/compiler.h>
 #include <linux/io.h>
 
-#include <mach/cputype.h>
-#include <mach/memory.h>
-#include <mach/hardware.h>
-#include <mach/irqs.h>
 #include <mach/edma.h>
-#include <mach/mux.h>
-
 
 /* Offsets matching "struct edmacc_param" */
 #define PARM_OPT               0x00
@@ -509,43 +500,59 @@ static irqreturn_t dma_tc1err_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int reserve_contiguous_params(int ctlr, unsigned int id,
-                                    unsigned int num_params,
-                                    unsigned int start_param)
+static int reserve_contiguous_slots(int ctlr, unsigned int id,
+                                    unsigned int num_slots,
+                                    unsigned int start_slot)
 {
        int i, j;
-       unsigned int count = num_params;
+       unsigned int count = num_slots;
+       int stop_slot = start_slot;
+       DECLARE_BITMAP(tmp_inuse, EDMA_MAX_PARAMENTRY);
 
-       for (i = start_param; i < edma_info[ctlr]->num_slots; ++i) {
+       for (i = start_slot; i < edma_info[ctlr]->num_slots; ++i) {
                j = EDMA_CHAN_SLOT(i);
-               if (!test_and_set_bit(j, edma_info[ctlr]->edma_inuse))
+               if (!test_and_set_bit(j, edma_info[ctlr]->edma_inuse)) {
+                       /* Record our current beginning slot */
+                       if (count == num_slots)
+                               stop_slot = i;
+
                        count--;
+                       set_bit(j, tmp_inuse);
+
                        if (count == 0)
                                break;
-               else if (id == EDMA_CONT_PARAMS_FIXED_EXACT)
-                       break;
-               else
-                       count = num_params;
+               } else {
+                       clear_bit(j, tmp_inuse);
+
+                       if (id == EDMA_CONT_PARAMS_FIXED_EXACT) {
+                               stop_slot = i;
+                               break;
+                       } else
+                               count = num_slots;
+               }
        }
 
        /*
         * We have to clear any bits that we set
-        * if we run out parameter RAMs, i.e we do find a set
-        * of contiguous parameter RAMs but do not find the exact number
-        * requested as we may reach the total number of parameter RAMs
+        * if we run out parameter RAM slots, i.e we do find a set
+        * of contiguous parameter RAM slots but do not find the exact number
+        * requested as we may reach the total number of parameter RAM slots
         */
-       if (count) {
-               for (j = i - num_params + count + 1; j <= i ; ++j)
+       if (i == edma_info[ctlr]->num_slots)
+               stop_slot = i;
+
+       for (j = start_slot; j < stop_slot; j++)
+               if (test_bit(j, tmp_inuse))
                        clear_bit(j, edma_info[ctlr]->edma_inuse);
 
+       if (count)
                return -EBUSY;
-       }
 
-       for (j = i - num_params + 1; j <= i; ++j)
+       for (j = i - num_slots + 1; j <= i; ++j)
                memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(j),
                        &dummy_paramset, PARM_SIZE);
 
-       return EDMA_CTLR_CHAN(ctlr, i - num_params + 1);
+       return EDMA_CTLR_CHAN(ctlr, i - num_slots + 1);
 }
 
 /*-----------------------------------------------------------------------*/
@@ -743,26 +750,27 @@ EXPORT_SYMBOL(edma_free_slot);
 /**
  * edma_alloc_cont_slots- alloc contiguous parameter RAM slots
  * The API will return the starting point of a set of
- * contiguous PARAM's that have been requested
+ * contiguous parameter RAM slots that have been requested
  *
  * @id: can only be EDMA_CONT_PARAMS_ANY or EDMA_CONT_PARAMS_FIXED_EXACT
  * or EDMA_CONT_PARAMS_FIXED_NOT_EXACT
- * @count: number of contiguous Paramter RAM's
- * @param  - the start value of Parameter RAM that should be passed if id
+ * @count: number of contiguous Paramter RAM slots
+ * @slot  - the start value of Parameter RAM slot that should be passed if id
  * is EDMA_CONT_PARAMS_FIXED_EXACT or EDMA_CONT_PARAMS_FIXED_NOT_EXACT
  *
  * If id is EDMA_CONT_PARAMS_ANY then the API starts looking for a set of
- * contiguous Parameter RAMs from parameter RAM 64 in the case of DaVinci SOCs
- * and 32 in the case of Primus
+ * contiguous Parameter RAM slots from parameter RAM 64 in the case of
+ * DaVinci SOCs and 32 in the case of DA8xx SOCs.
  *
  * If id is EDMA_CONT_PARAMS_FIXED_EXACT then the API starts looking for a
- * set of contiguous parameter RAMs from the "param" that is passed as an
+ * set of contiguous parameter RAM slots from the "slot" that is passed as an
  * argument to the API.
  *
  * If id is EDMA_CONT_PARAMS_FIXED_NOT_EXACT then the API initially tries
- * starts looking for a set of contiguous parameter RAMs from the "param"
+ * starts looking for a set of contiguous parameter RAMs from the "slot"
  * that is passed as an argument to the API. On failure the API will try to
- * find a set of contiguous Parameter RAMs in the remaining Parameter RAMs
+ * find a set of contiguous Parameter RAM slots from the remaining Parameter
+ * RAM slots
  */
 int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count)
 {
@@ -771,12 +779,13 @@ int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count)
         * the number of channels and lesser than the total number
         * of slots
         */
-       if (slot < edma_info[ctlr]->num_channels ||
-               slot >= edma_info[ctlr]->num_slots)
+       if ((id != EDMA_CONT_PARAMS_ANY) &&
+               (slot < edma_info[ctlr]->num_channels ||
+               slot >= edma_info[ctlr]->num_slots))
                return -EINVAL;
 
        /*
-        * The number of parameter RAMs requested cannot be less than 1
+        * The number of parameter RAM slots requested cannot be less than 1
         * and cannot be more than the number of slots minus the number of
         * channels
         */
@@ -786,11 +795,11 @@ int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count)
 
        switch (id) {
        case EDMA_CONT_PARAMS_ANY:
-               return reserve_contiguous_params(ctlr, id, count,
+               return reserve_contiguous_slots(ctlr, id, count,
                                                 edma_info[ctlr]->num_channels);
        case EDMA_CONT_PARAMS_FIXED_EXACT:
        case EDMA_CONT_PARAMS_FIXED_NOT_EXACT:
-               return reserve_contiguous_params(ctlr, id, count, slot);
+               return reserve_contiguous_slots(ctlr, id, count, slot);
        default:
                return -EINVAL;
        }
@@ -799,21 +808,21 @@ int edma_alloc_cont_slots(unsigned ctlr, unsigned int id, int slot, int count)
 EXPORT_SYMBOL(edma_alloc_cont_slots);
 
 /**
- * edma_free_cont_slots - deallocate DMA parameter RAMs
- * @slot: first parameter RAM of a set of parameter RAMs to be freed
- * @count: the number of contiguous parameter RAMs to be freed
+ * edma_free_cont_slots - deallocate DMA parameter RAM slots
+ * @slot: first parameter RAM of a set of parameter RAM slots to be freed
+ * @count: the number of contiguous parameter RAM slots to be freed
  *
  * This deallocates the parameter RAM slots allocated by
  * edma_alloc_cont_slots.
  * Callers/applications need to keep track of sets of contiguous
- * parameter RAMs that have been allocated using the edma_alloc_cont_slots
+ * parameter RAM slots that have been allocated using the edma_alloc_cont_slots
  * API.
  * Callers are responsible for ensuring the slots are inactive, and will
  * not be activated.
  */
 int edma_free_cont_slots(unsigned slot, int count)
 {
-       unsigned ctlr;
+       unsigned ctlr, slot_to_free;
        int i;
 
        ctlr = EDMA_CTLR(slot);
@@ -826,11 +835,11 @@ int edma_free_cont_slots(unsigned slot, int count)
 
        for (i = slot; i < slot + count; ++i) {
                ctlr = EDMA_CTLR(i);
-               slot = EDMA_CHAN_SLOT(i);
+               slot_to_free = EDMA_CHAN_SLOT(i);
 
-               memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot),
+               memcpy_toio(edmacc_regs_base[ctlr] + PARM_OFFSET(slot_to_free),
                        &dummy_paramset, PARM_SIZE);
-               clear_bit(slot, edma_info[ctlr]->edma_inuse);
+               clear_bit(slot_to_free, edma_info[ctlr]->edma_inuse);
        }
 
        return 0;
index f6ea9db..744755b 100644 (file)
 
 #include <linux/errno.h>
 #include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/bitops.h>
 
-#include <mach/cputype.h>
-#include <mach/irqs.h>
-#include <mach/hardware.h>
-#include <mach/common.h>
 #include <mach/gpio.h>
 
 #include <asm/mach/irq.h>
 
-
 static DEFINE_SPINLOCK(gpio_lock);
 
 struct davinci_gpio {
index e07f70e..834725f 100644 (file)
@@ -11,6 +11,9 @@
 #define DAVINCI_ASP0_BASE      0x01E02000
 #define DAVINCI_ASP1_BASE      0x01E04000
 
+/* Bases of dm365 register banks */
+#define DAVINCI_DM365_ASP0_BASE        0x01D02000
+
 /* Bases of dm646x register banks */
 #define        DAVINCI_DM646X_MCASP0_REG_BASE          0x01D01000
 #define DAVINCI_DM646X_MCASP1_REG_BASE         0x01D01800
index 1fd3917..6ca2c9a 100644 (file)
@@ -20,12 +20,6 @@ extern void davinci_irq_init(void);
 extern void __iomem *davinci_intc_base;
 extern int davinci_intc_type;
 
-/* parameters describe VBUS sourcing for host mode */
-extern void setup_usb(unsigned mA, unsigned potpgt_msec);
-
-/* parameters describe VBUS sourcing for host mode */
-extern void setup_usb(unsigned mA, unsigned potpgt_msec);
-
 struct davinci_timer_instance {
        void __iomem    *base;
        u32             bottom_irq;
diff --git a/arch/arm/mach-davinci/include/mach/cpufreq.h b/arch/arm/mach-davinci/include/mach/cpufreq.h
new file mode 100644 (file)
index 0000000..3c089cf
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * TI DaVinci CPUFreq platform support.
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc. http://www.ti.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 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.
+ */
+#ifndef _MACH_DAVINCI_CPUFREQ_H
+#define _MACH_DAVINCI_CPUFREQ_H
+
+#include <linux/cpufreq.h>
+
+struct davinci_cpufreq_config {
+       struct cpufreq_frequency_table *freq_table;
+       int (*set_voltage) (unsigned int index);
+       int (*init) (void);
+};
+
+#endif
diff --git a/arch/arm/mach-davinci/include/mach/cpuidle.h b/arch/arm/mach-davinci/include/mach/cpuidle.h
new file mode 100644 (file)
index 0000000..cbfc6a9
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * TI DaVinci cpuidle platform support
+ *
+ * 2009 (C) Texas Instruments, Inc. http://www.ti.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.
+ */
+#ifndef _MACH_DAVINCI_CPUIDLE_H
+#define _MACH_DAVINCI_CPUIDLE_H
+
+struct davinci_cpuidle_config {
+       u32 ddr2_pdown;
+};
+
+#endif
index d4095d0..9070491 100644 (file)
 #ifndef __ASM_ARCH_DAVINCI_DA8XX_H
 #define __ASM_ARCH_DAVINCI_DA8XX_H
 
+#include <video/da8xx-fb.h>
+
 #include <mach/serial.h>
 #include <mach/edma.h>
 #include <mach/i2c.h>
 #include <mach/emac.h>
 #include <mach/asp.h>
 #include <mach/mmc.h>
+#include <mach/usb.h>
+
+extern void __iomem *da8xx_syscfg_base;
 
 /*
  * The cp_intc interrupt controller for the da8xx isn't in the same
 #define DA8XX_CP_INTC_SIZE     SZ_8K
 #define DA8XX_CP_INTC_VIRT     (IO_VIRT - DA8XX_CP_INTC_SIZE - SZ_4K)
 
-#define DA8XX_BOOT_CFG_BASE    (IO_PHYS + 0x14000)
+#define DA8XX_SYSCFG_BASE      (IO_PHYS + 0x14000)
+#define DA8XX_SYSCFG_VIRT(x)   (da8xx_syscfg_base + (x))
+#define DA8XX_JTAG_ID_REG      0x18
+#define DA8XX_CFGCHIP0_REG     0x17c
+#define DA8XX_CFGCHIP2_REG     0x184
+#define DA8XX_CFGCHIP3_REG     0x188
 
 #define DA8XX_PSC0_BASE                0x01c10000
 #define DA8XX_PLL0_BASE                0x01c11000
-#define DA8XX_JTAG_ID_REG      0x01c14018
 #define DA8XX_TIMER64P0_BASE   0x01c20000
 #define DA8XX_TIMER64P1_BASE   0x01c21000
 #define DA8XX_GPIO_BASE                0x01e26000
@@ -43,6 +52,7 @@
 #define DA8XX_AEMIF_CS2_BASE   0x60000000
 #define DA8XX_AEMIF_CS3_BASE   0x62000000
 #define DA8XX_AEMIF_CTL_BASE   0x68000000
+#define DA8XX_DDR2_CTL_BASE    0xb0000000
 
 #define PINMUX0                        0x00
 #define PINMUX1                        0x04
@@ -71,13 +81,20 @@ void __init da850_init(void);
 int da8xx_register_edma(void);
 int da8xx_register_i2c(int instance, struct davinci_i2c_platform_data *pdata);
 int da8xx_register_watchdog(void);
+int da8xx_register_usb20(unsigned mA, unsigned potpgt);
+int da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata);
 int da8xx_register_emac(void);
-int da8xx_register_lcdc(void);
+int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata);
 int da8xx_register_mmcsd0(struct davinci_mmc_config *config);
-void __init da8xx_init_mcasp(int id, struct snd_platform_data *pdata);
+void __init da8xx_register_mcasp(int id, struct snd_platform_data *pdata);
+int da8xx_register_rtc(void);
+int da850_register_cpufreq(void);
+int da8xx_register_cpuidle(void);
 
 extern struct platform_device da8xx_serial_device;
 extern struct emac_platform_data da8xx_emac_pdata;
+extern struct da8xx_lcdc_platform_data sharp_lcd035q3dg01_pdata;
+extern struct da8xx_lcdc_platform_data sharp_lk043t1dg01_pdata;
 
 extern const short da830_emif25_pins[];
 extern const short da830_spi0_pins[];
@@ -110,6 +127,7 @@ extern const short da850_uart2_pins[];
 extern const short da850_i2c0_pins[];
 extern const short da850_i2c1_pins[];
 extern const short da850_cpgmac_pins[];
+extern const short da850_rmii_pins[];
 extern const short da850_mcasp_pins[];
 extern const short da850_lcdcntl_pins[];
 extern const short da850_mmcsd0_pins[];
index 09db434..f1710a3 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/platform_device.h>
 #include <mach/hardware.h>
 #include <mach/emac.h>
+#include <mach/asp.h>
+#include <mach/keyscan.h>
 
 #define DM365_EMAC_BASE                        (0x01D07000)
 #define DM365_EMAC_CNTRL_OFFSET                (0x0000)
 #define DM365_EMAC_MDIO_OFFSET         (0x4000)
 #define DM365_EMAC_CNTRL_RAM_SIZE      (0x2000)
 
+/* Base of key scan register bank */
+#define DM365_KEYSCAN_BASE             (0x01C69400)
+
+#define DM365_RTC_BASE                 (0x01C69000)
+
 void __init dm365_init(void);
+void __init dm365_init_asp(struct snd_platform_data *pdata);
+void __init dm365_init_ks(struct davinci_ks_platform_data *pdata);
+void __init dm365_init_rtc(void);
 
 #endif /* __ASM_ARCH_DM365_H */
index 0efb738..44e8f0f 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __ASM_ARCH_DM644X_H
 #define __ASM_ARCH_DM644X_H
 
-#include <linux/platform_device.h>
 #include <mach/hardware.h>
 #include <mach/emac.h>
 #include <mach/asp.h>
index 3c918a7..354af71 100644 (file)
 #define IRQ_DM365_SDIOINT0     23
 #define IRQ_DM365_MMCINT1      27
 #define IRQ_DM365_PWMINT3      28
+#define IRQ_DM365_RTCINT       29
 #define IRQ_DM365_SDIOINT1     31
 #define IRQ_DM365_SPIINT0_0    42
 #define IRQ_DM365_SPIINT3_0    43
diff --git a/arch/arm/mach-davinci/include/mach/keyscan.h b/arch/arm/mach-davinci/include/mach/keyscan.h
new file mode 100644 (file)
index 0000000..b4e21a2
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DAVINCI_KEYSCAN_H
+#define DAVINCI_KEYSCAN_H
+
+#include <linux/io.h>
+
+enum davinci_matrix_types {
+       DAVINCI_KEYSCAN_MATRIX_4X4,
+       DAVINCI_KEYSCAN_MATRIX_5X3,
+};
+
+struct davinci_ks_platform_data {
+       unsigned short  *keymap;
+       u32             keymapsize;
+       u8              rep:1;
+       u8              strobe;
+       u8              interval;
+       u8              matrix_type;
+};
+
+#endif
+
index bb84893..b60c693 100644 (file)
@@ -40,6 +40,11 @@ enum davinci_dm644x_index {
 
        /* AEAW functions */
        DM644X_AEAW,
+       DM644X_AEAW0,
+       DM644X_AEAW1,
+       DM644X_AEAW2,
+       DM644X_AEAW3,
+       DM644X_AEAW4,
 
        /* Memory Stick */
        DM644X_MSTK,
@@ -237,8 +242,8 @@ enum davinci_dm365_index {
        DM365_EMAC_MDIO,
        DM365_EMAC_MDCLK,
 
-       /* Keypad */
-       DM365_KEYPAD,
+       /* Key Scan */
+       DM365_KEYSCAN,
 
        /* PWM */
        DM365_PWM0,
@@ -774,6 +779,14 @@ enum davinci_da850_index {
        DA850_MII_RXD_0,
        DA850_MDIO_CLK,
        DA850_MDIO_D,
+       DA850_RMII_TXD_0,
+       DA850_RMII_TXD_1,
+       DA850_RMII_TXEN,
+       DA850_RMII_CRS_DV,
+       DA850_RMII_RXD_0,
+       DA850_RMII_RXD_1,
+       DA850_RMII_RXER,
+       DA850_RMII_MHZ_50_CLK,
 
        /* McASP function */
        DA850_ACLKR,
@@ -881,8 +894,9 @@ enum davinci_da850_index {
        DA850_NEMA_CS_2,
 
        /* GPIO function */
+       DA850_GPIO2_6,
+       DA850_GPIO2_8,
        DA850_GPIO2_15,
-       DA850_GPIO8_10,
        DA850_GPIO4_0,
        DA850_GPIO4_1,
 };
index 8e4f10f..5a7d758 100644 (file)
@@ -11,9 +11,6 @@
 #ifndef __ASM_ARCH_SYSTEM_H
 #define __ASM_ARCH_SYSTEM_H
 
-#include <linux/io.h>
-#include <mach/hardware.h>
-
 extern void davinci_watchdog_reset(void);
 
 static inline void arch_idle(void)
diff --git a/arch/arm/mach-davinci/include/mach/usb.h b/arch/arm/mach-davinci/include/mach/usb.h
new file mode 100644 (file)
index 0000000..e0bc4ab
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * USB related definitions
+ *
+ * Copyright (C) 2009 MontaVista Software, Inc. <source@mvista.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.
+ */
+
+#ifndef __ASM_ARCH_USB_H
+#define __ASM_ARCH_USB_H
+
+/* DA8xx CFGCHIP2 (USB 2.0 PHY Control) register bits */
+#define CFGCHIP2_PHYCLKGD      (1 << 17)
+#define CFGCHIP2_VBUSSENSE     (1 << 16)
+#define CFGCHIP2_RESET         (1 << 15)
+#define CFGCHIP2_OTGMODE       (3 << 13)
+#define CFGCHIP2_NO_OVERRIDE   (0 << 13)
+#define CFGCHIP2_FORCE_HOST    (1 << 13)
+#define CFGCHIP2_FORCE_DEVICE  (2 << 13)
+#define CFGCHIP2_FORCE_HOST_VBUS_LOW (3 << 13)
+#define CFGCHIP2_USB1PHYCLKMUX (1 << 12)
+#define CFGCHIP2_USB2PHYCLKMUX (1 << 11)
+#define CFGCHIP2_PHYPWRDN      (1 << 10)
+#define CFGCHIP2_OTGPWRDN      (1 << 9)
+#define CFGCHIP2_DATPOL        (1 << 8)
+#define CFGCHIP2_USB1SUSPENDM  (1 << 7)
+#define CFGCHIP2_PHY_PLLON     (1 << 6)        /* override PLL suspend */
+#define CFGCHIP2_SESENDEN      (1 << 5)        /* Vsess_end comparator */
+#define CFGCHIP2_VBDTCTEN      (1 << 4)        /* Vbus comparator */
+#define CFGCHIP2_REFFREQ       (0xf << 0)
+#define CFGCHIP2_REFFREQ_12MHZ (1 << 0)
+#define CFGCHIP2_REFFREQ_24MHZ (2 << 0)
+#define CFGCHIP2_REFFREQ_48MHZ (3 << 0)
+
+struct da8xx_ohci_root_hub;
+
+typedef void (*da8xx_ocic_handler_t)(struct da8xx_ohci_root_hub *hub,
+                                    unsigned port);
+
+/* Passed as the platform data to the OHCI driver */
+struct da8xx_ohci_root_hub {
+       /* Switch the port power on/off */
+       int     (*set_power)(unsigned port, int on);
+       /* Read the port power status */
+       int     (*get_power)(unsigned port);
+       /* Read the port over-current indicator */
+       int     (*get_oci)(unsigned port);
+       /* Over-current indicator change notification (pass NULL to disable) */
+       int     (*ocic_notify)(da8xx_ocic_handler_t handler);
+
+       /* Time from power on to power good (in 2 ms units) */
+       u8      potpgt;
+};
+
+void davinci_setup_usb(unsigned mA, unsigned potpgt_ms);
+
+#endif /* ifndef __ASM_ARCH_USB_H */
index 898905e..f757e83 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/module.h>
 #include <linux/spinlock.h>
 
-#include <mach/hardware.h>
 #include <mach/mux.h>
 #include <mach/common.h>
 
index a78b657..04a3cb7 100644 (file)
  *
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/io.h>
 
 #include <mach/cputype.h>
-#include <mach/hardware.h>
 #include <mach/psc.h>
-#include <mach/mux.h>
 
 /* PSC register offsets */
 #define EPCPR          0x070
index c530c73..7ce5ba0 100644 (file)
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <asm/irq.h>
-#include <mach/hardware.h>
 #include <mach/serial.h>
-#include <mach/irqs.h>
 #include <mach/cputype.h>
-#include <mach/common.h>
-
-#include "clock.h"
 
 static inline unsigned int serial_read_reg(struct plat_serial8250_port *up,
                                           int offset)
index 4f1fc9b..db0f778 100644 (file)
@@ -9,15 +9,12 @@
  * (at your option) any later version.
  */
 #include <linux/module.h>
-#include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/genalloc.h>
 
 #include <mach/common.h>
-#include <mach/memory.h>
 #include <mach/sram.h>
 
-
 static struct gen_pool *sram_pool;
 
 void *sram_alloc(size_t len, dma_addr_t *dma)
index 0d1b6d4..42d985b 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/clocksource.h>
 #include <linux/clockchips.h>
-#include <linux/spinlock.h>
 #include <linux/io.h>
 #include <linux/clk.h>
 #include <linux/err.h>
-#include <linux/device.h>
 #include <linux/platform_device.h>
 
 #include <mach/hardware.h>
-#include <asm/system.h>
-#include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/mach/time.h>
-#include <asm/errno.h>
-#include <mach/io.h>
 #include <mach/cputype.h>
 #include <mach/time.h>
 #include "clock.h"
index 06f5593..31f0cbe 100644 (file)
@@ -1,21 +1,21 @@
 /*
  * USB
  */
-#include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 
 #include <linux/usb/musb.h>
-#include <linux/usb/otg.h>
 
 #include <mach/common.h>
-#include <mach/hardware.h>
 #include <mach/irqs.h>
 #include <mach/cputype.h>
+#include <mach/usb.h>
 
-#define DAVINCI_USB_OTG_BASE 0x01C64000
+#define DAVINCI_USB_OTG_BASE   0x01c64000
+
+#define DA8XX_USB0_BASE        0x01e00000
+#define DA8XX_USB1_BASE        0x01e25000
 
 #if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE)
 static struct musb_hdrc_eps_bits musb_eps[] = {
@@ -85,10 +85,10 @@ static struct platform_device usb_dev = {
        .num_resources  = ARRAY_SIZE(usb_resources),
 };
 
-void __init setup_usb(unsigned mA, unsigned potpgt_msec)
+void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms)
 {
-       usb_data.power = mA / 2;
-       usb_data.potpgt = potpgt_msec / 2;
+       usb_data.power = mA > 510 ? 255 : mA / 2;
+       usb_data.potpgt = (potpgt_ms + 1) / 2;
 
        if (cpu_is_davinci_dm646x()) {
                /* Override the defaults as DM6467 uses different IRQs. */
@@ -100,11 +100,77 @@ void __init setup_usb(unsigned mA, unsigned potpgt_msec)
        platform_device_register(&usb_dev);
 }
 
+#ifdef CONFIG_ARCH_DAVINCI_DA8XX
+static struct resource da8xx_usb20_resources[] = {
+       {
+               .start          = DA8XX_USB0_BASE,
+               .end            = DA8XX_USB0_BASE + SZ_64K - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+       {
+               .start          = IRQ_DA8XX_USB_INT,
+               .flags          = IORESOURCE_IRQ,
+       },
+};
+
+int __init da8xx_register_usb20(unsigned mA, unsigned potpgt)
+{
+       usb_data.clock  = "usb20";
+       usb_data.power  = mA > 510 ? 255 : mA / 2;
+       usb_data.potpgt = (potpgt + 1) / 2;
+
+       usb_dev.resource = da8xx_usb20_resources;
+       usb_dev.num_resources = ARRAY_SIZE(da8xx_usb20_resources);
+
+       return platform_device_register(&usb_dev);
+}
+#endif /* CONFIG_DAVINCI_DA8XX */
+
 #else
 
-void __init setup_usb(unsigned mA, unsigned potpgt_msec)
+void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms)
 {
 }
 
+#ifdef CONFIG_ARCH_DAVINCI_DA8XX
+int __init da8xx_register_usb20(unsigned mA, unsigned potpgt)
+{
+       return 0;
+}
+#endif
+
 #endif  /* CONFIG_USB_MUSB_HDRC */
 
+#ifdef CONFIG_ARCH_DAVINCI_DA8XX
+static struct resource da8xx_usb11_resources[] = {
+       [0] = {
+               .start  = DA8XX_USB1_BASE,
+               .end    = DA8XX_USB1_BASE + SZ_4K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = IRQ_DA8XX_IRQN,
+               .end    = IRQ_DA8XX_IRQN,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static u64 da8xx_usb11_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device da8xx_usb11_device = {
+       .name           = "ohci",
+       .id             = 0,
+       .dev = {
+               .dma_mask               = &da8xx_usb11_dma_mask,
+               .coherent_dma_mask      = DMA_BIT_MASK(32),
+       },
+       .num_resources  = ARRAY_SIZE(da8xx_usb11_resources),
+       .resource       = da8xx_usb11_resources,
+};
+
+int __init da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata)
+{
+       da8xx_usb11_device.dev.platform_data = pdata;
+       return platform_device_register(&da8xx_usb11_device);
+}
+#endif /* CONFIG_DAVINCI_DA8XX */
index 48e827d..59d2ace 100644 (file)
@@ -31,7 +31,7 @@
 /*
  * This __REG() version gives the same results as the one above,  except
  * that we are fooling gcc somehow so it generates far better and smaller
- * assembly code for access to contigous registers.  It's a shame that gcc
+ * assembly code for access to contiguous registers.  It's a shame that gcc
  * doesn't guess this by itself.
  */
 #include <asm/types.h>
index 5a275ba..71e1a3f 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/clk.h>
+#include <linux/omapfb.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
@@ -32,7 +33,6 @@
 #include <plat/keypad.h>
 #include <plat/common.h>
 #include <plat/dsp_common.h>
-#include <plat/omapfb.h>
 #include <plat/hwa742.h>
 #include <plat/lcd_mipid.h>
 #include <plat/mmc.h>
index 491364e..5bda9fd 100644 (file)
@@ -37,6 +37,7 @@
 #include <plat/common.h>
 #include <plat/dma.h>
 #include <plat/gpmc.h>
+#include <plat/display.h>
 
 #include <plat/control.h>
 #include <plat/gpmc-smc91x.h>
@@ -152,31 +153,152 @@ static struct spi_board_info sdp3430_spi_board_info[] __initdata = {
        },
 };
 
-static struct platform_device sdp3430_lcd_device = {
-       .name           = "sdp2430_lcd",
-       .id             = -1,
+
+#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO       8
+#define SDP3430_LCD_PANEL_ENABLE_GPIO          5
+
+static unsigned backlight_gpio;
+static unsigned enable_gpio;
+static int lcd_enabled;
+static int dvi_enabled;
+
+static void __init sdp3430_display_init(void)
+{
+       int r;
+
+       enable_gpio    = SDP3430_LCD_PANEL_ENABLE_GPIO;
+       backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO;
+
+       r = gpio_request(enable_gpio, "LCD reset");
+       if (r) {
+               printk(KERN_ERR "failed to get LCD reset GPIO\n");
+               goto err0;
+       }
+
+       r = gpio_request(backlight_gpio, "LCD Backlight");
+       if (r) {
+               printk(KERN_ERR "failed to get LCD backlight GPIO\n");
+               goto err1;
+       }
+
+       gpio_direction_output(enable_gpio, 0);
+       gpio_direction_output(backlight_gpio, 0);
+
+       return;
+err1:
+       gpio_free(enable_gpio);
+err0:
+       return;
+}
+
+static int sdp3430_panel_enable_lcd(struct omap_dss_device *dssdev)
+{
+       if (dvi_enabled) {
+               printk(KERN_ERR "cannot enable LCD, DVI is enabled\n");
+               return -EINVAL;
+       }
+
+       gpio_direction_output(enable_gpio, 1);
+       gpio_direction_output(backlight_gpio, 1);
+
+       lcd_enabled = 1;
+
+       return 0;
+}
+
+static void sdp3430_panel_disable_lcd(struct omap_dss_device *dssdev)
+{
+       lcd_enabled = 0;
+
+       gpio_direction_output(enable_gpio, 0);
+       gpio_direction_output(backlight_gpio, 0);
+}
+
+static int sdp3430_panel_enable_dvi(struct omap_dss_device *dssdev)
+{
+       if (lcd_enabled) {
+               printk(KERN_ERR "cannot enable DVI, LCD is enabled\n");
+               return -EINVAL;
+       }
+
+       dvi_enabled = 1;
+
+       return 0;
+}
+
+static void sdp3430_panel_disable_dvi(struct omap_dss_device *dssdev)
+{
+       dvi_enabled = 0;
+}
+
+static int sdp3430_panel_enable_tv(struct omap_dss_device *dssdev)
+{
+       return 0;
+}
+
+static void sdp3430_panel_disable_tv(struct omap_dss_device *dssdev)
+{
+}
+
+
+static struct omap_dss_device sdp3430_lcd_device = {
+       .name                   = "lcd",
+       .driver_name            = "sharp_ls_panel",
+       .type                   = OMAP_DISPLAY_TYPE_DPI,
+       .phy.dpi.data_lines     = 16,
+       .platform_enable        = sdp3430_panel_enable_lcd,
+       .platform_disable       = sdp3430_panel_disable_lcd,
 };
 
-static struct regulator_consumer_supply sdp3430_vdac_supply = {
-       .supply         = "vdac",
-       .dev            = &sdp3430_lcd_device.dev,
+static struct omap_dss_device sdp3430_dvi_device = {
+       .name                   = "dvi",
+       .driver_name            = "generic_panel",
+       .type                   = OMAP_DISPLAY_TYPE_DPI,
+       .phy.dpi.data_lines     = 24,
+       .platform_enable        = sdp3430_panel_enable_dvi,
+       .platform_disable       = sdp3430_panel_disable_dvi,
 };
 
-static struct regulator_consumer_supply sdp3430_vdvi_supply = {
-       .supply         = "vdvi",
-       .dev            = &sdp3430_lcd_device.dev,
+static struct omap_dss_device sdp3430_tv_device = {
+       .name                   = "tv",
+       .driver_name            = "venc",
+       .type                   = OMAP_DISPLAY_TYPE_VENC,
+       .phy.venc.type          = OMAP_DSS_VENC_TYPE_SVIDEO,
+       .platform_enable        = sdp3430_panel_enable_tv,
+       .platform_disable       = sdp3430_panel_disable_tv,
 };
 
-static struct platform_device *sdp3430_devices[] __initdata = {
+
+static struct omap_dss_device *sdp3430_dss_devices[] = {
        &sdp3430_lcd_device,
+       &sdp3430_dvi_device,
+       &sdp3430_tv_device,
 };
 
-static struct omap_lcd_config sdp3430_lcd_config __initdata = {
-       .ctrl_name      = "internal",
+static struct omap_dss_board_info sdp3430_dss_data = {
+       .num_devices    = ARRAY_SIZE(sdp3430_dss_devices),
+       .devices        = sdp3430_dss_devices,
+       .default_device = &sdp3430_lcd_device,
+};
+
+static struct platform_device sdp3430_dss_device = {
+       .name           = "omapdss",
+       .id             = -1,
+       .dev            = {
+               .platform_data = &sdp3430_dss_data,
+       },
+};
+
+static struct regulator_consumer_supply sdp3430_vdda_dac_supply = {
+       .supply         = "vdda_dac",
+       .dev            = &sdp3430_dss_device.dev,
+};
+
+static struct platform_device *sdp3430_devices[] __initdata = {
+       &sdp3430_dss_device,
 };
 
 static struct omap_board_config_kernel sdp3430_config[] __initdata = {
-       { OMAP_TAG_LCD,         &sdp3430_lcd_config },
 };
 
 static void __init omap_3430sdp_init_irq(void)
@@ -392,22 +514,34 @@ static struct regulator_init_data sdp3430_vdac = {
                                        | REGULATOR_CHANGE_STATUS,
        },
        .num_consumer_supplies  = 1,
-       .consumer_supplies      = &sdp3430_vdac_supply,
+       .consumer_supplies      = &sdp3430_vdda_dac_supply,
 };
 
 /* VPLL2 for digital video outputs */
+static struct regulator_consumer_supply sdp3430_vpll2_supplies[] = {
+       {
+               .supply         = "vdvi",
+               .dev            = &sdp3430_lcd_device.dev,
+       },
+       {
+               .supply         = "vdds_dsi",
+               .dev            = &sdp3430_dss_device.dev,
+       }
+};
+
 static struct regulator_init_data sdp3430_vpll2 = {
        .constraints = {
                .name                   = "VDVI",
                .min_uV                 = 1800000,
                .max_uV                 = 1800000,
+               .apply_uV               = true,
                .valid_modes_mask       = REGULATOR_MODE_NORMAL
                                        | REGULATOR_MODE_STANDBY,
                .valid_ops_mask         = REGULATOR_CHANGE_MODE
                                        | REGULATOR_CHANGE_STATUS,
        },
-       .num_consumer_supplies  = 1,
-       .consumer_supplies      = &sdp3430_vdvi_supply,
+       .num_consumer_supplies  = ARRAY_SIZE(sdp3430_vpll2_supplies),
+       .consumer_supplies      = sdp3430_vpll2_supplies,
 };
 
 static struct twl4030_codec_audio_data sdp3430_audio = {
@@ -521,6 +655,7 @@ static void __init omap_3430sdp_init(void)
        omap_serial_init();
        usb_musb_init();
        board_smc91x_init();
+       sdp3430_display_init();
        enable_board_wakeup_source();
        usb_ehci_init(&ehci_pdata);
 }
index e70e7e0..845b478 100644 (file)
@@ -116,10 +116,10 @@ static struct omap_clk omap24xx_clks[] = {
        CLK(NULL,       "mdm_ick",      &mdm_ick,       CK_243X),
        CLK(NULL,       "mdm_osc_ck",   &mdm_osc_ck,    CK_243X),
        /* DSS domain clocks */
-       CLK("omapfb",   "ick",          &dss_ick,       CK_243X | CK_242X),
-       CLK("omapfb",   "dss1_fck",     &dss1_fck,      CK_243X | CK_242X),
-       CLK("omapfb",   "dss2_fck",     &dss2_fck,      CK_243X | CK_242X),
-       CLK("omapfb",   "tv_fck",       &dss_54m_fck,   CK_243X | CK_242X),
+       CLK("omapdss",  "ick",          &dss_ick,       CK_243X | CK_242X),
+       CLK("omapdss",  "dss1_fck",     &dss1_fck,      CK_243X | CK_242X),
+       CLK("omapdss",  "dss2_fck",     &dss2_fck,      CK_243X | CK_242X),
+       CLK("omapdss",  "tv_fck",       &dss_54m_fck,   CK_243X | CK_242X),
        /* L3 domain clocks */
        CLK(NULL,       "core_l3_ck",   &core_l3_ck,    CK_243X | CK_242X),
        CLK(NULL,       "ssi_fck",      &ssi_ssr_sst_fck, CK_243X | CK_242X),
index 9f2feaf..ecbb5cd 100644 (file)
@@ -236,13 +236,13 @@ static struct omap_clk omap34xx_clks[] = {
        CLK("omap_rng", "ick",          &rng_ick,       CK_343X),
        CLK(NULL,       "sha11_ick",    &sha11_ick,     CK_343X),
        CLK(NULL,       "des1_ick",     &des1_ick,      CK_343X),
-       CLK("omapfb",   "dss1_fck",     &dss1_alwon_fck_3430es1, CK_3430ES1),
-       CLK("omapfb",   "dss1_fck",     &dss1_alwon_fck_3430es2, CK_3430ES2),
-       CLK("omapfb",   "tv_fck",       &dss_tv_fck,    CK_343X),
-       CLK("omapfb",   "video_fck",    &dss_96m_fck,   CK_343X),
-       CLK("omapfb",   "dss2_fck",     &dss2_alwon_fck, CK_343X),
-       CLK("omapfb",   "ick",          &dss_ick_3430es1,       CK_3430ES1),
-       CLK("omapfb",   "ick",          &dss_ick_3430es2,       CK_3430ES2),
+       CLK("omapdss",  "dss1_fck",     &dss1_alwon_fck_3430es1, CK_3430ES1),
+       CLK("omapdss",  "dss1_fck",     &dss1_alwon_fck_3430es2, CK_3430ES2),
+       CLK("omapdss",  "tv_fck",       &dss_tv_fck,    CK_343X),
+       CLK("omapdss",  "video_fck",    &dss_96m_fck,   CK_343X),
+       CLK("omapdss",  "dss2_fck",     &dss2_alwon_fck, CK_343X),
+       CLK("omapdss",  "ick",          &dss_ick_3430es1,       CK_3430ES1),
+       CLK("omapdss",  "ick",          &dss_ick_3430es2,       CK_3430ES2),
        CLK(NULL,       "cam_mclk",     &cam_mclk,      CK_343X),
        CLK(NULL,       "cam_ick",      &cam_ick,       CK_343X),
        CLK(NULL,       "csi2_96m_fck", &csi2_96m_fck,  CK_343X),
index 59d28b2..6a4d8e4 100644 (file)
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/clk.h>
+#include <linux/omapfb.h>
 
 #include <asm/tlb.h>
 
 #include <asm/mach/map.h>
 
 #include <plat/mux.h>
-#include <plat/omapfb.h>
 #include <plat/sram.h>
 #include <plat/sdrc.h>
 #include <plat/gpmc.h>
 #include <plat/serial.h>
+#include <plat/vram.h>
 
 #ifndef CONFIG_ARCH_OMAP4      /* FIXME: Remove this once clkdev is ready */
 #include "clock.h"
@@ -264,6 +265,7 @@ void __init omap2_map_common_io(void)
        omap2_check_revision();
        omap_sram_init();
        omapfb_reserve_sdram();
+       omap_vram_reserve_sdram();
 }
 
 /*
index 9a59219..cbfbd14 100644 (file)
@@ -160,3 +160,19 @@ void __init omap2_sdrc_init(struct omap_sdrc_params *sdrc_cs0,
        sdrc_write_reg(l, SDRC_POWER);
        omap2_sms_save_context();
 }
+
+void omap2_sms_write_rot_control(u32 val, unsigned ctx)
+{
+       sms_write_reg(val, SMS_ROT_CONTROL(ctx));
+}
+
+void omap2_sms_write_rot_size(u32 val, unsigned ctx)
+{
+       sms_write_reg(val, SMS_ROT_SIZE(ctx));
+}
+
+void omap2_sms_write_rot_physical_ba(u32 val, unsigned ctx)
+{
+       sms_write_reg(val, SMS_ROT_PHYSICAL_BA(ctx));
+}
+
index 36dc541..bdf96eb 100644 (file)
@@ -463,7 +463,7 @@ static void __init orion5x_setup_pci_wins(struct mbus_dram_target_info *dram)
        writel(win_enable, PCI_BAR_ENABLE);
 
        /*
-        * Disable automatic update of address remaping when writing to BARs.
+        * Disable automatic update of address remapping when writing to BARs.
         */
        orion5x_setbits(PCI_ADDR_DECODE_CTRL, 1);
 }
index 8721b80..ae536e8 100644 (file)
@@ -91,7 +91,7 @@
 /* BATTERY */
 #define PALMLD_BAT_MAX_VOLTAGE         4000    /* 4.00V maximum voltage */
 #define PALMLD_BAT_MIN_VOLTAGE         3550    /* 3.55V critical voltage */
-#define PALMLD_BAT_MAX_CURRENT         0       /* unknokn */
+#define PALMLD_BAT_MAX_CURRENT         0       /* unknown */
 #define PALMLD_BAT_MIN_CURRENT         0       /* unknown */
 #define PALMLD_BAT_MAX_CHARGE          1       /* unknown */
 #define PALMLD_BAT_MIN_CHARGE          1       /* unknown */
index d15662a..6baf746 100644 (file)
@@ -66,7 +66,7 @@
 /* BATTERY */
 #define PALMT5_BAT_MAX_VOLTAGE         4000    /* 4.00v current voltage */
 #define PALMT5_BAT_MIN_VOLTAGE         3550    /* 3.55v critical voltage */
-#define PALMT5_BAT_MAX_CURRENT         0       /* unknokn */
+#define PALMT5_BAT_MAX_CURRENT         0       /* unknown */
 #define PALMT5_BAT_MIN_CURRENT         0       /* unknown */
 #define PALMT5_BAT_MAX_CHARGE          1       /* unknown */
 #define PALMT5_BAT_MIN_CHARGE          1       /* unknown */
index 3dc9b07..3f9dd3f 100644 (file)
@@ -68,7 +68,7 @@
 /* BATTERY */
 #define PALMTC_BAT_MAX_VOLTAGE         4000    /* 4.00V maximum voltage */
 #define PALMTC_BAT_MIN_VOLTAGE         3550    /* 3.55V critical voltage */
-#define PALMTC_BAT_MAX_CURRENT         0       /* unknokn */
+#define PALMTC_BAT_MAX_CURRENT         0       /* unknown */
 #define PALMTC_BAT_MIN_CURRENT         0       /* unknown */
 #define PALMTC_BAT_MAX_CHARGE          1       /* unknown */
 #define PALMTC_BAT_MIN_CHARGE          1       /* unknown */
index 1236134..f89e989 100644 (file)
@@ -59,7 +59,7 @@
 /* BATTERY */
 #define PALMTE2_BAT_MAX_VOLTAGE                4000    /* 4.00v current voltage */
 #define PALMTE2_BAT_MIN_VOLTAGE                3550    /* 3.55v critical voltage */
-#define PALMTE2_BAT_MAX_CURRENT                0       /* unknokn */
+#define PALMTE2_BAT_MAX_CURRENT                0       /* unknown */
 #define PALMTE2_BAT_MIN_CURRENT                0       /* unknown */
 #define PALMTE2_BAT_MAX_CHARGE         1       /* unknown */
 #define PALMTE2_BAT_MIN_CHARGE         1       /* unknown */
index 1be0db6..10abc4f 100644 (file)
@@ -94,7 +94,7 @@
 /* BATTERY */
 #define PALMTX_BAT_MAX_VOLTAGE         4000    /* 4.00v current voltage */
 #define PALMTX_BAT_MIN_VOLTAGE         3550    /* 3.55v critical voltage */
-#define PALMTX_BAT_MAX_CURRENT         0       /* unknokn */
+#define PALMTX_BAT_MAX_CURRENT         0       /* unknown */
 #define PALMTX_BAT_MIN_CURRENT         0       /* unknown */
 #define PALMTX_BAT_MAX_CHARGE          1       /* unknown */
 #define PALMTX_BAT_MIN_CHARGE          1       /* unknown */
index 2806ef6..2bbcf70 100644 (file)
@@ -49,7 +49,7 @@
 /* Battery */
 #define PALMZ72_BAT_MAX_VOLTAGE                4000    /* 4.00v current voltage */
 #define PALMZ72_BAT_MIN_VOLTAGE                3550    /* 3.55v critical voltage */
-#define PALMZ72_BAT_MAX_CURRENT                0       /* unknokn */
+#define PALMZ72_BAT_MAX_CURRENT                0       /* unknown */
 #define PALMZ72_BAT_MIN_CURRENT                0       /* unknown */
 #define PALMZ72_BAT_MAX_CHARGE         1       /* unknown */
 #define PALMZ72_BAT_MIN_CHARGE         1       /* unknown */
index deab072..fdd8f5e 100644 (file)
@@ -1,13 +1,7 @@
-# arch/arm/mach-s3c2400/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
 
-
-
 menu "S3C2400 Machines"
 
-
 endmenu
-
index dd1fcc7..5547318 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c2410/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
index c2bdc46..9a8c065 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c2412/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
@@ -90,6 +88,4 @@ config MACH_VSTMS
        help
          Say Y here if you are using an VSTMS board
 
-
 endmenu
-
index cf10e14..8087935 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c2440/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
@@ -122,4 +120,3 @@ config MACH_MINI2440
          available via various sources. It can come with a 3.5" or 7" touch LCD.
 
 endmenu
-
index 103e913..8d38118 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c2442/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
@@ -36,6 +34,4 @@ config MACH_NEO1973_GTA02
        help
           Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone
 
-
 endmenu
-
index 212141b..4314c44 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c2443/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
index 770b720..a250bf6 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c6400/Kconfig
-#
 # Copyright 2008 Openmoko, Inc.
 #      Simtec Electronics, Ben Dooks <ben@simtec.co.uk>
 #
index b93dafb..1039937 100644 (file)
@@ -30,7 +30,7 @@ char *s3c6400_hsmmc_clksrcs[4] = {
        [0] = "hsmmc",
        [1] = "hsmmc",
        [2] = "mmc_bus",
-       /* [3] = "48m", - note not succesfully used yet */
+       /* [3] = "48m", - note not successfully used yet */
 };
 
 void s3c6400_setup_sdhci_cfg_card(struct platform_device *dev,
index 72d4b11..162f456 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s3c6410/Kconfig
-#
 # Copyright 2008 Openmoko, Inc.
 # Copyright 2008 Simtec Electronics
 #
index 20666f3..816d2d9 100644 (file)
@@ -30,7 +30,7 @@ char *s3c6410_hsmmc_clksrcs[4] = {
        [0] = "hsmmc",
        [1] = "hsmmc",
        [2] = "mmc_bus",
-       /* [3] = "48m", - note not succesfully used yet */
+       /* [3] = "48m", - note not successfully used yet */
 };
 
 
index 0dd2b8c..27ec167 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/mach-s5pc100/Kconfig
-#
 # Copyright 2009 Samsung Electronics Co.
 #      Byungho Min <bhmin@samsung.com>
 #
index cb4521a..ad66035 100644 (file)
@@ -65,7 +65,7 @@ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
 
 
 /**
- *     sa1100_request_dma - allocate one of the SA11x0's DMA chanels
+ *     sa1100_request_dma - allocate one of the SA11x0's DMA channels
  *     @device: The SA11x0 peripheral targeted by this request
  *     @device_id: An ascii name for the claiming device
  *     @callback: Function to be called when the DMA completes
index 88333df..56721a0 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (C) 2006-2009 ST-Ericsson AB
  * License terms: GNU General Public License (GPL) version 2
  * Basic register address definitions in physical memory and
- * some block defintions for core devices like the timer.
+ * some block definitions for core devices like the timer.
  * Author: Linus Walleij <linus.walleij@stericsson.com>
  */
 
index eaabd4e..e1fc6da 100644 (file)
@@ -112,7 +112,7 @@ enum iomux_gp_func {
  * setups a single pin:
  *     - reserves the pin so that it is not claimed by another driver
  *     - setups the iomux according to the configuration
- *     - if the pin is configured as a GPIO, we claim it throug kernel gpiolib
+ *     - if the pin is configured as a GPIO, we claim it through kernel gpiolib
  */
 int mxc_iomux_alloc_pin(const unsigned int pin, const char *label);
 /*
index 9f13061..3887f3f 100644 (file)
@@ -48,7 +48,7 @@
  * setups a single pin:
  *     - reserves the pin so that it is not claimed by another driver
  *     - setups the iomux according to the configuration
- *     - if the pin is configured as a GPIO, we claim it throug kernel gpiolib
+ *     - if the pin is configured as a GPIO, we claim it through kernel gpiolib
  */
 int mxc_iomux_alloc_pin(const unsigned int pin_mode, const char *label);
 /*
index 5cdbd60..4ff6dfe 100644 (file)
@@ -94,7 +94,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
                 * register to follow the ratio of duty_ns vs. period_ns
                 * accordingly.
                 *
-                * This is good enought for programming the brightness of
+                * This is good enough for programming the brightness of
                 * the LCD backlight.
                 *
                 * The real implementation would divide PERCLK[0] first by
index be4ce07..d17375e 100644 (file)
@@ -1252,7 +1252,7 @@ static void create_dma_lch_chain(int lch_head, int lch_queue)
  *                                           OMAP_DMA_DYNAMIC_CHAIN
  * @params - Channel parameters
  *
- * @return - Succes : 0
+ * @return - Success : 0
  *          Failure: -EINVAL/-ENOMEM
  */
 int omap_request_dma_chain(int dev_id, const char *dev_name,
index 78a4ce5..d3eea4f 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/bootmem.h>
 #include <linux/io.h>
+#include <linux/omapfb.h>
 
 #include <mach/hardware.h>
 #include <asm/mach/map.h>
 
 #include <plat/board.h>
 #include <plat/sram.h>
-#include <plat/omapfb.h>
 
 #if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
 
@@ -55,6 +55,10 @@ static struct platform_device omap_fb_device = {
        .num_resources = 0,
 };
 
+void omapfb_set_platform_data(struct omapfb_platform_data *data)
+{
+}
+
 static inline int ranges_overlap(unsigned long start1, unsigned long size1,
                                 unsigned long start2, unsigned long size2)
 {
@@ -327,7 +331,33 @@ static inline int omap_init_fb(void)
 
 arch_initcall(omap_init_fb);
 
-#else
+#elif defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
+
+static u64 omap_fb_dma_mask = ~(u32)0;
+static struct omapfb_platform_data omapfb_config;
+
+static struct platform_device omap_fb_device = {
+       .name           = "omapfb",
+       .id             = -1,
+       .dev = {
+               .dma_mask               = &omap_fb_dma_mask,
+               .coherent_dma_mask      = ~(u32)0,
+               .platform_data          = &omapfb_config,
+       },
+       .num_resources = 0,
+};
+
+void omapfb_set_platform_data(struct omapfb_platform_data *data)
+{
+       omapfb_config = *data;
+}
+
+static inline int omap_init_fb(void)
+{
+       return platform_device_register(&omap_fb_device);
+}
+
+arch_initcall(omap_init_fb);
 
 void omapfb_reserve_sdram(void) {}
 unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
@@ -339,5 +369,20 @@ unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
        return 0;
 }
 
+#else
+
+void omapfb_set_platform_data(struct omapfb_platform_data *data)
+{
+}
+
+void omapfb_reserve_sdram(void) {}
+unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
+                                 unsigned long sram_vstart,
+                                 unsigned long sram_size,
+                                 unsigned long start_avail,
+                                 unsigned long size_avail)
+{
+       return 0;
+}
 
 #endif
diff --git a/arch/arm/plat-omap/include/plat/display.h b/arch/arm/plat-omap/include/plat/display.h
new file mode 100644 (file)
index 0000000..c66e464
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * linux/include/asm-arm/arch-omap/display.h
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASM_ARCH_OMAP_DISPLAY_H
+#define __ASM_ARCH_OMAP_DISPLAY_H
+
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <asm/atomic.h>
+
+#define DISPC_IRQ_FRAMEDONE            (1 << 0)
+#define DISPC_IRQ_VSYNC                        (1 << 1)
+#define DISPC_IRQ_EVSYNC_EVEN          (1 << 2)
+#define DISPC_IRQ_EVSYNC_ODD           (1 << 3)
+#define DISPC_IRQ_ACBIAS_COUNT_STAT    (1 << 4)
+#define DISPC_IRQ_PROG_LINE_NUM                (1 << 5)
+#define DISPC_IRQ_GFX_FIFO_UNDERFLOW   (1 << 6)
+#define DISPC_IRQ_GFX_END_WIN          (1 << 7)
+#define DISPC_IRQ_PAL_GAMMA_MASK       (1 << 8)
+#define DISPC_IRQ_OCP_ERR              (1 << 9)
+#define DISPC_IRQ_VID1_FIFO_UNDERFLOW  (1 << 10)
+#define DISPC_IRQ_VID1_END_WIN         (1 << 11)
+#define DISPC_IRQ_VID2_FIFO_UNDERFLOW  (1 << 12)
+#define DISPC_IRQ_VID2_END_WIN         (1 << 13)
+#define DISPC_IRQ_SYNC_LOST            (1 << 14)
+#define DISPC_IRQ_SYNC_LOST_DIGIT      (1 << 15)
+#define DISPC_IRQ_WAKEUP               (1 << 16)
+
+struct omap_dss_device;
+struct omap_overlay_manager;
+
+enum omap_display_type {
+       OMAP_DISPLAY_TYPE_NONE          = 0,
+       OMAP_DISPLAY_TYPE_DPI           = 1 << 0,
+       OMAP_DISPLAY_TYPE_DBI           = 1 << 1,
+       OMAP_DISPLAY_TYPE_SDI           = 1 << 2,
+       OMAP_DISPLAY_TYPE_DSI           = 1 << 3,
+       OMAP_DISPLAY_TYPE_VENC          = 1 << 4,
+};
+
+enum omap_plane {
+       OMAP_DSS_GFX    = 0,
+       OMAP_DSS_VIDEO1 = 1,
+       OMAP_DSS_VIDEO2 = 2
+};
+
+enum omap_channel {
+       OMAP_DSS_CHANNEL_LCD    = 0,
+       OMAP_DSS_CHANNEL_DIGIT  = 1,
+};
+
+enum omap_color_mode {
+       OMAP_DSS_COLOR_CLUT1    = 1 << 0,  /* BITMAP 1 */
+       OMAP_DSS_COLOR_CLUT2    = 1 << 1,  /* BITMAP 2 */
+       OMAP_DSS_COLOR_CLUT4    = 1 << 2,  /* BITMAP 4 */
+       OMAP_DSS_COLOR_CLUT8    = 1 << 3,  /* BITMAP 8 */
+       OMAP_DSS_COLOR_RGB12U   = 1 << 4,  /* RGB12, 16-bit container */
+       OMAP_DSS_COLOR_ARGB16   = 1 << 5,  /* ARGB16 */
+       OMAP_DSS_COLOR_RGB16    = 1 << 6,  /* RGB16 */
+       OMAP_DSS_COLOR_RGB24U   = 1 << 7,  /* RGB24, 32-bit container */
+       OMAP_DSS_COLOR_RGB24P   = 1 << 8,  /* RGB24, 24-bit container */
+       OMAP_DSS_COLOR_YUV2     = 1 << 9,  /* YUV2 4:2:2 co-sited */
+       OMAP_DSS_COLOR_UYVY     = 1 << 10, /* UYVY 4:2:2 co-sited */
+       OMAP_DSS_COLOR_ARGB32   = 1 << 11, /* ARGB32 */
+       OMAP_DSS_COLOR_RGBA32   = 1 << 12, /* RGBA32 */
+       OMAP_DSS_COLOR_RGBX32   = 1 << 13, /* RGBx32 */
+
+       OMAP_DSS_COLOR_GFX_OMAP2 =
+               OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
+               OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
+               OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P,
+
+       OMAP_DSS_COLOR_VID_OMAP2 =
+               OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+               OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+               OMAP_DSS_COLOR_UYVY,
+
+       OMAP_DSS_COLOR_GFX_OMAP3 =
+               OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 |
+               OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 |
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
+               OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+               OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 |
+               OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
+
+       OMAP_DSS_COLOR_VID1_OMAP3 =
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 |
+               OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P |
+               OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY,
+
+       OMAP_DSS_COLOR_VID2_OMAP3 =
+               OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 |
+               OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
+               OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 |
+               OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 |
+               OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32,
+};
+
+enum omap_lcd_display_type {
+       OMAP_DSS_LCD_DISPLAY_STN,
+       OMAP_DSS_LCD_DISPLAY_TFT,
+};
+
+enum omap_dss_load_mode {
+       OMAP_DSS_LOAD_CLUT_AND_FRAME    = 0,
+       OMAP_DSS_LOAD_CLUT_ONLY         = 1,
+       OMAP_DSS_LOAD_FRAME_ONLY        = 2,
+       OMAP_DSS_LOAD_CLUT_ONCE_FRAME   = 3,
+};
+
+enum omap_dss_trans_key_type {
+       OMAP_DSS_COLOR_KEY_GFX_DST = 0,
+       OMAP_DSS_COLOR_KEY_VID_SRC = 1,
+};
+
+enum omap_rfbi_te_mode {
+       OMAP_DSS_RFBI_TE_MODE_1 = 1,
+       OMAP_DSS_RFBI_TE_MODE_2 = 2,
+};
+
+enum omap_panel_config {
+       OMAP_DSS_LCD_IVS                = 1<<0,
+       OMAP_DSS_LCD_IHS                = 1<<1,
+       OMAP_DSS_LCD_IPC                = 1<<2,
+       OMAP_DSS_LCD_IEO                = 1<<3,
+       OMAP_DSS_LCD_RF                 = 1<<4,
+       OMAP_DSS_LCD_ONOFF              = 1<<5,
+
+       OMAP_DSS_LCD_TFT                = 1<<20,
+};
+
+enum omap_dss_venc_type {
+       OMAP_DSS_VENC_TYPE_COMPOSITE,
+       OMAP_DSS_VENC_TYPE_SVIDEO,
+};
+
+enum omap_display_caps {
+       OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE      = 1 << 0,
+       OMAP_DSS_DISPLAY_CAP_TEAR_ELIM          = 1 << 1,
+};
+
+enum omap_dss_update_mode {
+       OMAP_DSS_UPDATE_DISABLED = 0,
+       OMAP_DSS_UPDATE_AUTO,
+       OMAP_DSS_UPDATE_MANUAL,
+};
+
+enum omap_dss_display_state {
+       OMAP_DSS_DISPLAY_DISABLED = 0,
+       OMAP_DSS_DISPLAY_ACTIVE,
+       OMAP_DSS_DISPLAY_SUSPENDED,
+};
+
+/* XXX perhaps this should be removed */
+enum omap_dss_overlay_managers {
+       OMAP_DSS_OVL_MGR_LCD,
+       OMAP_DSS_OVL_MGR_TV,
+};
+
+enum omap_dss_rotation_type {
+       OMAP_DSS_ROT_DMA = 0,
+       OMAP_DSS_ROT_VRFB = 1,
+};
+
+/* clockwise rotation angle */
+enum omap_dss_rotation_angle {
+       OMAP_DSS_ROT_0   = 0,
+       OMAP_DSS_ROT_90  = 1,
+       OMAP_DSS_ROT_180 = 2,
+       OMAP_DSS_ROT_270 = 3,
+};
+
+enum omap_overlay_caps {
+       OMAP_DSS_OVL_CAP_SCALE = 1 << 0,
+       OMAP_DSS_OVL_CAP_DISPC = 1 << 1,
+};
+
+enum omap_overlay_manager_caps {
+       OMAP_DSS_OVL_MGR_CAP_DISPC = 1 << 0,
+};
+
+/* RFBI */
+
+struct rfbi_timings {
+       int cs_on_time;
+       int cs_off_time;
+       int we_on_time;
+       int we_off_time;
+       int re_on_time;
+       int re_off_time;
+       int we_cycle_time;
+       int re_cycle_time;
+       int cs_pulse_width;
+       int access_time;
+
+       int clk_div;
+
+       u32 tim[5];             /* set by rfbi_convert_timings() */
+
+       int converted;
+};
+
+void omap_rfbi_write_command(const void *buf, u32 len);
+void omap_rfbi_read_data(void *buf, u32 len);
+void omap_rfbi_write_data(const void *buf, u32 len);
+void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
+               u16 x, u16 y,
+               u16 w, u16 h);
+int omap_rfbi_enable_te(bool enable, unsigned line);
+int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode,
+                            unsigned hs_pulse_time, unsigned vs_pulse_time,
+                            int hs_pol_inv, int vs_pol_inv, int extif_div);
+
+/* DSI */
+void dsi_bus_lock(void);
+void dsi_bus_unlock(void);
+int dsi_vc_dcs_write(int channel, u8 *data, int len);
+int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len);
+int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen);
+int dsi_vc_set_max_rx_packet_size(int channel, u16 len);
+int dsi_vc_send_null(int channel);
+int dsi_vc_send_bta_sync(int channel);
+
+/* Board specific data */
+struct omap_dss_board_info {
+       int (*get_last_off_on_transaction_id)(struct device *dev);
+       int num_devices;
+       struct omap_dss_device **devices;
+       struct omap_dss_device *default_device;
+};
+
+struct omap_video_timings {
+       /* Unit: pixels */
+       u16 x_res;
+       /* Unit: pixels */
+       u16 y_res;
+       /* Unit: KHz */
+       u32 pixel_clock;
+       /* Unit: pixel clocks */
+       u16 hsw;        /* Horizontal synchronization pulse width */
+       /* Unit: pixel clocks */
+       u16 hfp;        /* Horizontal front porch */
+       /* Unit: pixel clocks */
+       u16 hbp;        /* Horizontal back porch */
+       /* Unit: line clocks */
+       u16 vsw;        /* Vertical synchronization pulse width */
+       /* Unit: line clocks */
+       u16 vfp;        /* Vertical front porch */
+       /* Unit: line clocks */
+       u16 vbp;        /* Vertical back porch */
+};
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+/* Hardcoded timings for tv modes. Venc only uses these to
+ * identify the mode, and does not actually use the configs
+ * itself. However, the configs should be something that
+ * a normal monitor can also show */
+const extern struct omap_video_timings omap_dss_pal_timings;
+const extern struct omap_video_timings omap_dss_ntsc_timings;
+#endif
+
+struct omap_overlay_info {
+       bool enabled;
+
+       u32 paddr;
+       void __iomem *vaddr;
+       u16 screen_width;
+       u16 width;
+       u16 height;
+       enum omap_color_mode color_mode;
+       u8 rotation;
+       enum omap_dss_rotation_type rotation_type;
+       bool mirror;
+
+       u16 pos_x;
+       u16 pos_y;
+       u16 out_width;  /* if 0, out_width == width */
+       u16 out_height; /* if 0, out_height == height */
+       u8 global_alpha;
+};
+
+struct omap_overlay {
+       struct kobject kobj;
+       struct list_head list;
+
+       /* static fields */
+       const char *name;
+       int id;
+       enum omap_color_mode supported_modes;
+       enum omap_overlay_caps caps;
+
+       /* dynamic fields */
+       struct omap_overlay_manager *manager;
+       struct omap_overlay_info info;
+
+       /* if true, info has been changed, but not applied() yet */
+       bool info_dirty;
+
+       int (*set_manager)(struct omap_overlay *ovl,
+               struct omap_overlay_manager *mgr);
+       int (*unset_manager)(struct omap_overlay *ovl);
+
+       int (*set_overlay_info)(struct omap_overlay *ovl,
+                       struct omap_overlay_info *info);
+       void (*get_overlay_info)(struct omap_overlay *ovl,
+                       struct omap_overlay_info *info);
+
+       int (*wait_for_go)(struct omap_overlay *ovl);
+};
+
+struct omap_overlay_manager_info {
+       u32 default_color;
+
+       enum omap_dss_trans_key_type trans_key_type;
+       u32 trans_key;
+       bool trans_enabled;
+
+       bool alpha_enabled;
+};
+
+struct omap_overlay_manager {
+       struct kobject kobj;
+       struct list_head list;
+
+       /* static fields */
+       const char *name;
+       int id;
+       enum omap_overlay_manager_caps caps;
+       int num_overlays;
+       struct omap_overlay **overlays;
+       enum omap_display_type supported_displays;
+
+       /* dynamic fields */
+       struct omap_dss_device *device;
+       struct omap_overlay_manager_info info;
+
+       bool device_changed;
+       /* if true, info has been changed but not applied() yet */
+       bool info_dirty;
+
+       int (*set_device)(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dssdev);
+       int (*unset_device)(struct omap_overlay_manager *mgr);
+
+       int (*set_manager_info)(struct omap_overlay_manager *mgr,
+                       struct omap_overlay_manager_info *info);
+       void (*get_manager_info)(struct omap_overlay_manager *mgr,
+                       struct omap_overlay_manager_info *info);
+
+       int (*apply)(struct omap_overlay_manager *mgr);
+       int (*wait_for_go)(struct omap_overlay_manager *mgr);
+};
+
+struct omap_dss_device {
+       struct device dev;
+
+       enum omap_display_type type;
+
+       union {
+               struct {
+                       u8 data_lines;
+               } dpi;
+
+               struct {
+                       u8 channel;
+                       u8 data_lines;
+               } rfbi;
+
+               struct {
+                       u8 datapairs;
+               } sdi;
+
+               struct {
+                       u8 clk_lane;
+                       u8 clk_pol;
+                       u8 data1_lane;
+                       u8 data1_pol;
+                       u8 data2_lane;
+                       u8 data2_pol;
+
+                       struct {
+                               u16 regn;
+                               u16 regm;
+                               u16 regm3;
+                               u16 regm4;
+
+                               u16 lp_clk_div;
+
+                               u16 lck_div;
+                               u16 pck_div;
+                       } div;
+
+                       bool ext_te;
+                       u8 ext_te_gpio;
+               } dsi;
+
+               struct {
+                       enum omap_dss_venc_type type;
+                       bool invert_polarity;
+               } venc;
+       } phy;
+
+       struct {
+               struct omap_video_timings timings;
+
+               int acbi;       /* ac-bias pin transitions per interrupt */
+               /* Unit: line clocks */
+               int acb;        /* ac-bias pin frequency */
+
+               enum omap_panel_config config;
+
+               u8 recommended_bpp;
+
+               struct omap_dss_device *ctrl;
+       } panel;
+
+       struct {
+               u8 pixel_size;
+               struct rfbi_timings rfbi_timings;
+               struct omap_dss_device *panel;
+       } ctrl;
+
+       int reset_gpio;
+
+       int max_backlight_level;
+
+       const char *name;
+
+       /* used to match device to driver */
+       const char *driver_name;
+
+       void *data;
+
+       struct omap_dss_driver *driver;
+
+       /* helper variable for driver suspend/resume */
+       bool activate_after_resume;
+
+       enum omap_display_caps caps;
+
+       struct omap_overlay_manager *manager;
+
+       enum omap_dss_display_state state;
+
+       int (*enable)(struct omap_dss_device *dssdev);
+       void (*disable)(struct omap_dss_device *dssdev);
+
+       int (*suspend)(struct omap_dss_device *dssdev);
+       int (*resume)(struct omap_dss_device *dssdev);
+
+       void (*get_resolution)(struct omap_dss_device *dssdev,
+                       u16 *xres, u16 *yres);
+       int (*get_recommended_bpp)(struct omap_dss_device *dssdev);
+
+       int (*check_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*set_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       void (*get_timings)(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings);
+       int (*update)(struct omap_dss_device *dssdev,
+                              u16 x, u16 y, u16 w, u16 h);
+       int (*sync)(struct omap_dss_device *dssdev);
+       int (*wait_vsync)(struct omap_dss_device *dssdev);
+
+       int (*set_update_mode)(struct omap_dss_device *dssdev,
+                       enum omap_dss_update_mode);
+       enum omap_dss_update_mode (*get_update_mode)
+               (struct omap_dss_device *dssdev);
+
+       int (*enable_te)(struct omap_dss_device *dssdev, bool enable);
+       int (*get_te)(struct omap_dss_device *dssdev);
+
+       u8 (*get_rotate)(struct omap_dss_device *dssdev);
+       int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate);
+
+       bool (*get_mirror)(struct omap_dss_device *dssdev);
+       int (*set_mirror)(struct omap_dss_device *dssdev, bool enable);
+
+       int (*run_test)(struct omap_dss_device *dssdev, int test);
+       int (*memory_read)(struct omap_dss_device *dssdev,
+                       void *buf, size_t size,
+                       u16 x, u16 y, u16 w, u16 h);
+
+       int (*set_wss)(struct omap_dss_device *dssdev, u32 wss);
+       u32 (*get_wss)(struct omap_dss_device *dssdev);
+
+       /* platform specific  */
+       int (*platform_enable)(struct omap_dss_device *dssdev);
+       void (*platform_disable)(struct omap_dss_device *dssdev);
+       int (*set_backlight)(struct omap_dss_device *dssdev, int level);
+       int (*get_backlight)(struct omap_dss_device *dssdev);
+};
+
+struct omap_dss_driver {
+       struct device_driver driver;
+
+       int (*probe)(struct omap_dss_device *);
+       void (*remove)(struct omap_dss_device *);
+
+       int (*enable)(struct omap_dss_device *display);
+       void (*disable)(struct omap_dss_device *display);
+       int (*suspend)(struct omap_dss_device *display);
+       int (*resume)(struct omap_dss_device *display);
+       int (*run_test)(struct omap_dss_device *display, int test);
+
+       void (*setup_update)(struct omap_dss_device *dssdev,
+                       u16 x, u16 y, u16 w, u16 h);
+
+       int (*enable_te)(struct omap_dss_device *dssdev, bool enable);
+       int (*wait_for_te)(struct omap_dss_device *dssdev);
+
+       u8 (*get_rotate)(struct omap_dss_device *dssdev);
+       int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate);
+
+       bool (*get_mirror)(struct omap_dss_device *dssdev);
+       int (*set_mirror)(struct omap_dss_device *dssdev, bool enable);
+
+       int (*memory_read)(struct omap_dss_device *dssdev,
+                       void *buf, size_t size,
+                       u16 x, u16 y, u16 w, u16 h);
+};
+
+int omap_dss_register_driver(struct omap_dss_driver *);
+void omap_dss_unregister_driver(struct omap_dss_driver *);
+
+int omap_dss_register_device(struct omap_dss_device *);
+void omap_dss_unregister_device(struct omap_dss_device *);
+
+void omap_dss_get_device(struct omap_dss_device *dssdev);
+void omap_dss_put_device(struct omap_dss_device *dssdev);
+#define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL)
+struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from);
+struct omap_dss_device *omap_dss_find_device(void *data,
+               int (*match)(struct omap_dss_device *dssdev, void *data));
+
+int omap_dss_start_device(struct omap_dss_device *dssdev);
+void omap_dss_stop_device(struct omap_dss_device *dssdev);
+
+int omap_dss_get_num_overlay_managers(void);
+struct omap_overlay_manager *omap_dss_get_overlay_manager(int num);
+
+int omap_dss_get_num_overlays(void);
+struct omap_overlay *omap_dss_get_overlay(int num);
+
+typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
+int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
+int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
+
+int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout);
+int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
+               unsigned long timeout);
+
+#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver)
+#define to_dss_device(x) container_of((x), struct omap_dss_device, dev)
+
+#endif
index 0e69b50..7560b4d 100644 (file)
 #define TIPB_SWITCH_BASE                (0xfffbc800)
 #define OMAP16XX_MMCSD2_SSW_MPU_CONF   (TIPB_SWITCH_BASE + 0x160)
 
-/* UART3 Registers Maping through MPU bus */
+/* UART3 Registers Mapping through MPU bus */
 #define UART3_RHR               (OMAP_UART3_BASE + 0)
 #define UART3_THR               (OMAP_UART3_BASE + 0)
 #define UART3_DLL               (OMAP_UART3_BASE + 0)
index f704030..7b76f50 100644 (file)
 
 /* SMS register offsets - read/write with sms_{read,write}_reg() */
 
-#define SMS_SYSCONFIG          0x010
+#define SMS_SYSCONFIG                  0x010
+#define SMS_ROT_CONTROL(context)       (0x180 + 0x10 * context)
+#define SMS_ROT_SIZE(context)          (0x184 + 0x10 * context)
+#define SMS_ROT_PHYSICAL_BA(context)   (0x188 + 0x10 * context)
 /* REVISIT: fill in other SMS registers here */
 
 
@@ -129,6 +132,10 @@ int omap2_sdrc_get_params(unsigned long r,
 void omap2_sms_save_context(void);
 void omap2_sms_restore_context(void);
 
+void omap2_sms_write_rot_control(u32 val, unsigned ctx);
+void omap2_sms_write_rot_size(u32 val, unsigned ctx);
+void omap2_sms_write_rot_physical_ba(u32 val, unsigned ctx);
+
 #ifdef CONFIG_ARCH_OMAP2
 
 struct memory_timings {
diff --git a/arch/arm/plat-omap/include/plat/vram.h b/arch/arm/plat-omap/include/plat/vram.h
new file mode 100644 (file)
index 0000000..edd4987
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * VRAM manager for OMAP
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 __OMAP_VRAM_H__
+#define __OMAP_VRAM_H__
+
+#include <linux/types.h>
+
+#define OMAP_VRAM_MEMTYPE_SDRAM                0
+#define OMAP_VRAM_MEMTYPE_SRAM         1
+#define OMAP_VRAM_MEMTYPE_MAX          1
+
+extern int omap_vram_add_region(unsigned long paddr, size_t size);
+extern int omap_vram_free(unsigned long paddr, size_t size);
+extern int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr);
+extern int omap_vram_reserve(unsigned long paddr, size_t size);
+extern void omap_vram_get_info(unsigned long *vram, unsigned long *free_vram,
+               unsigned long *largest_free_block);
+
+#ifdef CONFIG_OMAP2_VRAM
+extern void omap_vram_set_sdram_vram(u32 size, u32 start);
+extern void omap_vram_set_sram_vram(u32 size, u32 start);
+
+extern void omap_vram_reserve_sdram(void);
+extern unsigned long omap_vram_reserve_sram(unsigned long sram_pstart,
+                                           unsigned long sram_vstart,
+                                           unsigned long sram_size,
+                                           unsigned long pstart_avail,
+                                           unsigned long size_avail);
+#else
+static inline void omap_vram_set_sdram_vram(u32 size, u32 start) { }
+static inline void omap_vram_set_sram_vram(u32 size, u32 start) { }
+
+static inline void omap_vram_reserve_sdram(void) { }
+static inline unsigned long omap_vram_reserve_sram(unsigned long sram_pstart,
+                                           unsigned long sram_vstart,
+                                           unsigned long sram_size,
+                                           unsigned long pstart_avail,
+                                           unsigned long size_avail)
+{
+       return 0;
+}
+#endif
+
+#endif
diff --git a/arch/arm/plat-omap/include/plat/vrfb.h b/arch/arm/plat-omap/include/plat/vrfb.h
new file mode 100644 (file)
index 0000000..d8a03ce
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * VRFB Rotation Engine
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 __OMAP_VRFB_H__
+#define __OMAP_VRFB_H__
+
+#define OMAP_VRFB_LINE_LEN 2048
+
+struct vrfb {
+       u8 context;
+       void __iomem *vaddr[4];
+       unsigned long paddr[4];
+       u16 xres;
+       u16 yres;
+       u16 xoffset;
+       u16 yoffset;
+       u8 bytespp;
+       bool yuv_mode;
+};
+
+extern int omap_vrfb_request_ctx(struct vrfb *vrfb);
+extern void omap_vrfb_release_ctx(struct vrfb *vrfb);
+extern void omap_vrfb_adjust_size(u16 *width, u16 *height,
+               u8 bytespp);
+extern u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp);
+extern u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp);
+extern void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr,
+               u16 width, u16 height,
+               unsigned bytespp, bool yuv_mode);
+extern int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot);
+extern void omap_vrfb_restore_context(void);
+
+#endif /* __VRFB_H */
index 3e92366..ad2bf07 100644 (file)
@@ -28,6 +28,7 @@
 #include <plat/sram.h>
 #include <plat/board.h>
 #include <plat/cpu.h>
+#include <plat/vram.h>
 
 #include <plat/control.h>
 
@@ -185,6 +186,13 @@ void __init omap_detect_sram(void)
                                       omap_sram_start + SRAM_BOOTLOADER_SZ,
                                       omap_sram_size - SRAM_BOOTLOADER_SZ);
        omap_sram_size -= reserved;
+
+       reserved = omap_vram_reserve_sram(omap_sram_start, omap_sram_base,
+                       omap_sram_size,
+                       omap_sram_start + SRAM_BOOTLOADER_SZ,
+                       omap_sram_size - SRAM_BOOTLOADER_SZ);
+       omap_sram_size -= reserved;
+
        omap_sram_ceil = omap_sram_base + omap_sram_size;
 }
 
index e139a72..9e9d028 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/plat-s3c/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
index 4fdc5b3..ef019f2 100644 (file)
@@ -368,7 +368,7 @@ static int s3c_pwm_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int s3c_pwm_remove(struct platform_device *pdev)
+static int __devexit s3c_pwm_remove(struct platform_device *pdev)
 {
        struct pwm_device *pwm = platform_get_drvdata(pdev);
 
index 20fbf93..342647e 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/plat-s3c24xx/Kconfig
-#
 # Copyright 2007 Simtec Electronics
 #
 # Licensed under GPLv2
index df47322..ce47627 100644 (file)
@@ -365,7 +365,7 @@ static int s3c_adc_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int s3c_adc_remove(struct platform_device *pdev)
+static int __devexit s3c_adc_remove(struct platform_device *pdev)
 {
        struct adc_device *adc = platform_get_drvdata(pdev);
 
index f65192d..f0ea794 100644 (file)
@@ -1403,11 +1403,13 @@ static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
                ord = &dma_order->channels[channel];
 
                for (ch = 0; ch < dma_channels; ch++) {
+                       int tmp;
                        if (!is_channel_valid(ord->list[ch]))
                                continue;
 
-                       if (s3c2410_chans[ord->list[ch]].in_use == 0) {
-                               ch = ord->list[ch] & ~DMA_CH_VALID;
+                       tmp = ord->list[ch] & ~DMA_CH_VALID;
+                       if (s3c2410_chans[tmp].in_use == 0) {
+                               ch = tmp;
                                goto found;
                        }
                }
index c4d1334..bd534d3 100644 (file)
@@ -64,7 +64,7 @@
 /* the calculation for the VA of this must ensure that
  * it is the same distance apart from the UART in the
  * phsyical address space, as the initial mapping for the IO
- * is done as a 1:1 maping. This puts it (currently) at
+ * is done as a 1:1 mapping. This puts it (currently) at
  * 0xFA800000, which is not in the way of any current mapping
  * by the base system.
 */
index ef0f521..ad0d44e 100644 (file)
@@ -522,6 +522,8 @@ int s3c24xx_set_fiq(unsigned int irq, bool on)
        __raw_writel(intmod, S3C2410_INTMOD);
        return 0;
 }
+
+EXPORT_SYMBOL_GPL(s3c24xx_set_fiq);
 #endif
 
 
index bcfa778..e6da87a 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/plat-s3c64xx/Kconfig
-#
 # Copyright 2008 Openmoko, Inc.
 # Copyright 2008 Simtec Electronics
 #      Ben Dooks <ben@simtec.co.uk>
index 1608e62..b7b9e91 100644 (file)
@@ -1,5 +1,3 @@
-# arch/arm/plat-s5pc1xx/Kconfig
-#
 # Copyright 2009 Samsung Electronics Co.
 #      Byungho Min <bhmin@samsung.com>
 #
index fda2331..5c13d78 100644 (file)
@@ -24,7 +24,7 @@ config BOARD_HAMMERHEAD_SND
        bool "Atmel AC97 Sound support"
        help
          This enables Sound support for the Hammerhead board. You may
-         also go trough the ALSA settings to get it working.
+         also go through the ALSA settings to get it working.
 
          Choose 'Y' here if you have ordered a Corona daugther board and
          want to make your board funky.
index 6b7325d..78cb3d3 100644 (file)
@@ -619,7 +619,7 @@ asmlinkage notrace void trap_c(struct pt_regs *fp)
 
 /*
  * Similar to get_user, do some address checking, then dereference
- * Return true on sucess, false on bad address
+ * Return true on success, false on bad address
  */
 static bool get_instruction(unsigned short *val, unsigned short *address)
 {
index e06f411..f9fd2b2 100644 (file)
 #define HMDMA0_CONTROL         0xFFC03300      /* Handshake MDMA0 Control Register                                     */
 #define HMDMA0_ECINIT          0xFFC03304      /* HMDMA0 Initial Edge Count Register                           */
 #define HMDMA0_BCINIT          0xFFC03308      /* HMDMA0 Initial Block Count Register                          */
-#define HMDMA0_ECURGENT                0xFFC0330C      /* HMDMA0 Urgent Edge Count Threshhold Register         */
+#define HMDMA0_ECURGENT                0xFFC0330C      /* HMDMA0 Urgent Edge Count Threshold Register          */
 #define HMDMA0_ECOVERFLOW      0xFFC03310      /* HMDMA0 Edge Count Overflow Interrupt Register        */
 #define HMDMA0_ECOUNT          0xFFC03314      /* HMDMA0 Current Edge Count Register                           */
 #define HMDMA0_BCOUNT          0xFFC03318      /* HMDMA0 Current Block Count Register                          */
 #define HMDMA1_CONTROL         0xFFC03340      /* Handshake MDMA1 Control Register                                     */
 #define HMDMA1_ECINIT          0xFFC03344      /* HMDMA1 Initial Edge Count Register                           */
 #define HMDMA1_BCINIT          0xFFC03348      /* HMDMA1 Initial Block Count Register                          */
-#define HMDMA1_ECURGENT                0xFFC0334C      /* HMDMA1 Urgent Edge Count Threshhold Register         */
+#define HMDMA1_ECURGENT                0xFFC0334C      /* HMDMA1 Urgent Edge Count Threshold Register          */
 #define HMDMA1_ECOVERFLOW      0xFFC03350      /* HMDMA1 Edge Count Overflow Interrupt Register        */
 #define HMDMA1_ECOUNT          0xFFC03354      /* HMDMA1 Current Edge Count Register                           */
 #define HMDMA1_BCOUNT          0xFFC03358      /* HMDMA1 Current Block Count Register                          */
index f821700..b9dbb73 100644 (file)
 #define HMDMA0_CONTROL         0xFFC03300      /* Handshake MDMA0 Control Register                                     */
 #define HMDMA0_ECINIT          0xFFC03304      /* HMDMA0 Initial Edge Count Register                           */
 #define HMDMA0_BCINIT          0xFFC03308      /* HMDMA0 Initial Block Count Register                          */
-#define HMDMA0_ECURGENT                0xFFC0330C      /* HMDMA0 Urgent Edge Count Threshhold Register         */
+#define HMDMA0_ECURGENT                0xFFC0330C      /* HMDMA0 Urgent Edge Count Threshold Register          */
 #define HMDMA0_ECOVERFLOW      0xFFC03310      /* HMDMA0 Edge Count Overflow Interrupt Register        */
 #define HMDMA0_ECOUNT          0xFFC03314      /* HMDMA0 Current Edge Count Register                           */
 #define HMDMA0_BCOUNT          0xFFC03318      /* HMDMA0 Current Block Count Register                          */
 #define HMDMA1_CONTROL         0xFFC03340      /* Handshake MDMA1 Control Register                                     */
 #define HMDMA1_ECINIT          0xFFC03344      /* HMDMA1 Initial Edge Count Register                           */
 #define HMDMA1_BCINIT          0xFFC03348      /* HMDMA1 Initial Block Count Register                          */
-#define HMDMA1_ECURGENT                0xFFC0334C      /* HMDMA1 Urgent Edge Count Threshhold Register         */
+#define HMDMA1_ECURGENT                0xFFC0334C      /* HMDMA1 Urgent Edge Count Threshold Register          */
 #define HMDMA1_ECOVERFLOW      0xFFC03350      /* HMDMA1 Edge Count Overflow Interrupt Register        */
 #define HMDMA1_ECOUNT          0xFFC03354      /* HMDMA1 Current Edge Count Register                           */
 #define HMDMA1_BCOUNT          0xFFC03358      /* HMDMA1 Current Block Count Register                          */
index cebb14f..a6d20ca 100644 (file)
 #define HMDMA0_CONTROL         0xFFC03300      /* Handshake MDMA0 Control Register                                     */
 #define HMDMA0_ECINIT          0xFFC03304      /* HMDMA0 Initial Edge Count Register                           */
 #define HMDMA0_BCINIT          0xFFC03308      /* HMDMA0 Initial Block Count Register                          */
-#define HMDMA0_ECURGENT                0xFFC0330C      /* HMDMA0 Urgent Edge Count Threshhold Register         */
+#define HMDMA0_ECURGENT                0xFFC0330C      /* HMDMA0 Urgent Edge Count Threshold Register         */
 #define HMDMA0_ECOVERFLOW      0xFFC03310      /* HMDMA0 Edge Count Overflow Interrupt Register        */
 #define HMDMA0_ECOUNT          0xFFC03314      /* HMDMA0 Current Edge Count Register                           */
 #define HMDMA0_BCOUNT          0xFFC03318      /* HMDMA0 Current Block Count Register                          */
 #define HMDMA1_CONTROL         0xFFC03340      /* Handshake MDMA1 Control Register                                     */
 #define HMDMA1_ECINIT          0xFFC03344      /* HMDMA1 Initial Edge Count Register                           */
 #define HMDMA1_BCINIT          0xFFC03348      /* HMDMA1 Initial Block Count Register                          */
-#define HMDMA1_ECURGENT                0xFFC0334C      /* HMDMA1 Urgent Edge Count Threshhold Register         */
+#define HMDMA1_ECURGENT                0xFFC0334C      /* HMDMA1 Urgent Edge Count Threshold Register         */
 #define HMDMA1_ECOVERFLOW      0xFFC03350      /* HMDMA1 Edge Count Overflow Interrupt Register        */
 #define HMDMA1_ECOUNT          0xFFC03354      /* HMDMA1 Current Edge Count Register                           */
 #define HMDMA1_BCOUNT          0xFFC03358      /* HMDMA1 Current Block Count Register                          */
index dd414ae..39f588d 100644 (file)
 #define                   HMDMA0_CONTROL  0xffc04500   /* Handshake MDMA0 Control Register */
 #define                    HMDMA0_ECINIT  0xffc04504   /* Handshake MDMA0 Initial Edge Count Register */
 #define                    HMDMA0_BCINIT  0xffc04508   /* Handshake MDMA0 Initial Block Count Register */
-#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshhold Register */
+#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshold Register */
 #define                HMDMA0_ECOVERFLOW  0xffc04510   /* Handshake MDMA0 Edge Count Overflow Interrupt Register */
 #define                    HMDMA0_ECOUNT  0xffc04514   /* Handshake MDMA0 Current Edge Count Register */
 #define                    HMDMA0_BCOUNT  0xffc04518   /* Handshake MDMA0 Current Block Count Register */
 #define                   HMDMA1_CONTROL  0xffc04540   /* Handshake MDMA1 Control Register */
 #define                    HMDMA1_ECINIT  0xffc04544   /* Handshake MDMA1 Initial Edge Count Register */
 #define                    HMDMA1_BCINIT  0xffc04548   /* Handshake MDMA1 Initial Block Count Register */
-#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshhold Register */
+#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshold Register */
 #define                HMDMA1_ECOVERFLOW  0xffc04550   /* Handshake MDMA1 Edge Count Overflow Interrupt Register */
 #define                    HMDMA1_ECOUNT  0xffc04554   /* Handshake MDMA1 Current Edge Count Register */
 #define                    HMDMA1_BCOUNT  0xffc04558   /* Handshake MDMA1 Current Block Count Register */
index 5a9dbab..c4dcf30 100644 (file)
 #define                   HMDMA0_CONTROL  0xffc04500   /* Handshake MDMA0 Control Register */
 #define                    HMDMA0_ECINIT  0xffc04504   /* Handshake MDMA0 Initial Edge Count Register */
 #define                    HMDMA0_BCINIT  0xffc04508   /* Handshake MDMA0 Initial Block Count Register */
-#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshhold Register */
+#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshold Register */
 #define                HMDMA0_ECOVERFLOW  0xffc04510   /* Handshake MDMA0 Edge Count Overflow Interrupt Register */
 #define                    HMDMA0_ECOUNT  0xffc04514   /* Handshake MDMA0 Current Edge Count Register */
 #define                    HMDMA0_BCOUNT  0xffc04518   /* Handshake MDMA0 Current Block Count Register */
 #define                   HMDMA1_CONTROL  0xffc04540   /* Handshake MDMA1 Control Register */
 #define                    HMDMA1_ECINIT  0xffc04544   /* Handshake MDMA1 Initial Edge Count Register */
 #define                    HMDMA1_BCINIT  0xffc04548   /* Handshake MDMA1 Initial Block Count Register */
-#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshhold Register */
+#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshold Register */
 #define                HMDMA1_ECOVERFLOW  0xffc04550   /* Handshake MDMA1 Edge Count Overflow Interrupt Register */
 #define                    HMDMA1_ECOUNT  0xffc04554   /* Handshake MDMA1 Current Edge Count Register */
 #define                    HMDMA1_BCOUNT  0xffc04558   /* Handshake MDMA1 Current Block Count Register */
index 82cd593..a507998 100644 (file)
 #define                   HMDMA0_CONTROL  0xffc04500   /* Handshake MDMA0 Control Register */
 #define                    HMDMA0_ECINIT  0xffc04504   /* Handshake MDMA0 Initial Edge Count Register */
 #define                    HMDMA0_BCINIT  0xffc04508   /* Handshake MDMA0 Initial Block Count Register */
-#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshhold Register */
+#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshold Register */
 #define                HMDMA0_ECOVERFLOW  0xffc04510   /* Handshake MDMA0 Edge Count Overflow Interrupt Register */
 #define                    HMDMA0_ECOUNT  0xffc04514   /* Handshake MDMA0 Current Edge Count Register */
 #define                    HMDMA0_BCOUNT  0xffc04518   /* Handshake MDMA0 Current Block Count Register */
 #define                   HMDMA1_CONTROL  0xffc04540   /* Handshake MDMA1 Control Register */
 #define                    HMDMA1_ECINIT  0xffc04544   /* Handshake MDMA1 Initial Edge Count Register */
 #define                    HMDMA1_BCINIT  0xffc04548   /* Handshake MDMA1 Initial Block Count Register */
-#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshhold Register */
+#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshold Register */
 #define                HMDMA1_ECOVERFLOW  0xffc04550   /* Handshake MDMA1 Edge Count Overflow Interrupt Register */
 #define                    HMDMA1_ECOUNT  0xffc04554   /* Handshake MDMA1 Current Edge Count Register */
 #define                    HMDMA1_BCOUNT  0xffc04558   /* Handshake MDMA1 Current Block Count Register */
index 6fc6e39..f7f0435 100644 (file)
 #define                   HMDMA0_CONTROL  0xffc04500   /* Handshake MDMA0 Control Register */
 #define                    HMDMA0_ECINIT  0xffc04504   /* Handshake MDMA0 Initial Edge Count Register */
 #define                    HMDMA0_BCINIT  0xffc04508   /* Handshake MDMA0 Initial Block Count Register */
-#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshhold Register */
+#define                  HMDMA0_ECURGENT  0xffc0450c   /* Handshake MDMA0 Urgent Edge Count Threshold Register */
 #define                HMDMA0_ECOVERFLOW  0xffc04510   /* Handshake MDMA0 Edge Count Overflow Interrupt Register */
 #define                    HMDMA0_ECOUNT  0xffc04514   /* Handshake MDMA0 Current Edge Count Register */
 #define                    HMDMA0_BCOUNT  0xffc04518   /* Handshake MDMA0 Current Block Count Register */
 #define                   HMDMA1_CONTROL  0xffc04540   /* Handshake MDMA1 Control Register */
 #define                    HMDMA1_ECINIT  0xffc04544   /* Handshake MDMA1 Initial Edge Count Register */
 #define                    HMDMA1_BCINIT  0xffc04548   /* Handshake MDMA1 Initial Block Count Register */
-#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshhold Register */
+#define                  HMDMA1_ECURGENT  0xffc0454c   /* Handshake MDMA1 Urgent Edge Count Threshold Register */
 #define                HMDMA1_ECOVERFLOW  0xffc04550   /* Handshake MDMA1 Edge Count Overflow Interrupt Register */
 #define                    HMDMA1_ECOUNT  0xffc04554   /* Handshake MDMA1 Current Edge Count Register */
 #define                    HMDMA1_BCOUNT  0xffc04558   /* Handshake MDMA1 Current Block Count Register */
index 087a209..ed60588 100644 (file)
@@ -80,8 +80,7 @@ handle_mmu_bus_fault(struct pt_regs *regs)
         * do_page_fault may have flushed the TLB so we have to restore
         * the MMU registers.
         */
-       local_save_flags(flags);
-       local_irq_disable();
+       local_irq_save(flags);
        pmd = (pmd_t *)(pgd + pgd_index(address));
        if (pmd_none(*pmd))
                goto exit;
index 4a496e4..21d78c5 100644 (file)
@@ -134,28 +134,6 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
        local_irq_restore(flags);
 }
 
-/* dump the entire TLB for debug purposes */
-
-#if 0
-void
-dump_tlb_all(void)
-{
-       int i;
-       unsigned long flags;
-
-       printk("TLB dump. LO is: pfn | reserved | global | valid | kernel | we  |\n");
-
-       local_save_flags(flags);
-       local_irq_disable();
-       for(i = 0; i < NUM_TLB_ENTRIES; i++) {
-               *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) );
-               printk("Entry %d: HI 0x%08lx, LO 0x%08lx\n",
-                      i, *R_TLB_HI, *R_TLB_LO);
-       }
-       local_irq_restore(flags);
-}
-#endif
-
 /*
  * Initialize the context related info for a new mm_struct
  * instance.
index 4a7cdd9..380df1a 100644 (file)
@@ -209,7 +209,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs,
        /* Are we prepared to handle this kernel fault?
         *
         * (The kernel has valid exception-points in the source
-        *  when it acesses user-memory. When it fails in one
+        *  when it accesses user-memory. When it fails in one
         *  of those points, we find it in a table and do a jump
         *  to some fixup code that loads an appropriate error
         *  code)
index 674a837..f332e3f 100644 (file)
@@ -1381,7 +1381,7 @@ sba_coalesce_chunks(struct ioc *ioc, struct device *dev,
 #endif
 
                        /*
-                       ** Not virtually contigous.
+                       ** Not virtually contiguous.
                        ** Terminate prev chunk.
                        ** Start a new chunk.
                        **
index 10c3751..2fd7479 100644 (file)
@@ -79,7 +79,7 @@ GLOBAL_ENTRY(ia32_ret_from_clone)
 (p6)   br.cond.spnt .ia32_strace_check_retval
        ;;                                      // prevent RAW on r8
 END(ia32_ret_from_clone)
-       // fall thrugh
+       // fall through
 GLOBAL_ENTRY(ia32_ret_from_syscall)
        PT_REGS_UNWIND_INFO(0)
 
index 48822c0..74724b2 100644 (file)
@@ -67,7 +67,7 @@ typedef struct {
         unsigned long   ip;                     /* where did the overflow interrupt happened  */
         unsigned long   tstamp;                 /* ar.itc when entering perfmon intr. handler */
 
-        unsigned short  cpu;                    /* cpu on which the overfow occured */
+        unsigned short  cpu;                    /* cpu on which the overflow occured */
         unsigned short  set;                    /* event set active when overflow ocurred   */
         int                    tgid;                   /* thread group id (for NPTL, this is getpid()) */
 } pfm_default_smpl_entry_t;
index 22a6f18..6052422 100644 (file)
@@ -3289,7 +3289,7 @@ typedef ii_icrb0_e_u_t icrbe_t;
 #define IIO_IIDSR_LVL_SHIFT     0
 #define IIO_IIDSR_LVL_MASK      0x000000ff
 
-/* Xtalk timeout threshhold register (IIO_IXTT) */
+/* Xtalk timeout threshold register (IIO_IXTT) */
 #define IXTT_RRSP_TO_SHFT      55      /* read response timeout */
 #define IXTT_RRSP_TO_MASK      (0x1FULL << IXTT_RRSP_TO_SHFT)
 #define IXTT_RRSP_PS_SHFT      32      /* read responsed TO prescalar */
index 9c72e36..10a8f21 100644 (file)
 #define __NR_preadv                    1319
 #define __NR_pwritev                   1320
 #define __NR_rt_tgsigqueueinfo         1321
-#define __NR_rt_recvmmsg               1322
+#define __NR_recvmmsg                  1322
 
 #ifdef __KERNEL__
 
index d5764a3..b091111 100644 (file)
@@ -84,7 +84,7 @@ static int __init esi_init (void)
                      case ESI_DESC_ENTRY_POINT:
                        break;
                      default:
-                       printk(KERN_WARNING "Unkown table type %d found in "
+                       printk(KERN_WARNING "Unknown table type %d found in "
                               "ESI table, ignoring rest of table\n", *p);
                        return -ENODEV;
                }
index 402698b..599b233 100644 (file)
@@ -3517,7 +3517,7 @@ pfm_use_debug_registers(struct task_struct *task)
  * IA64_THREAD_DBG_VALID set. This indicates a task which was
  * able to use the debug registers for debugging purposes via
  * ptrace(). Therefore we know it was not using them for
- * perfmormance monitoring, so we only decrement the number
+ * performance monitoring, so we only decrement the number
  * of "ptraced" debug register users to keep the count up to date
  */
 int
index fd50ff9..66f633b 100644 (file)
@@ -390,7 +390,7 @@ sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info,
        pcidev_match.handle = NULL;
 
        acpi_walk_namespace(ACPI_TYPE_DEVICE, rootbus_handle, ACPI_UINT32_MAX,
-                           find_matching_device, &pcidev_match, NULL);
+                           find_matching_device, NULL, &pcidev_match, NULL);
 
        if (!pcidev_match.handle) {
                printk(KERN_ERR
index 6c1a9a2..73613b5 100644 (file)
@@ -753,7 +753,7 @@ fovfl_ovfl_on:
 
        bra.l           _real_ovfl
 
-# overflow occurred but is disabled. meanwhile, inexact is enabled. therefore,
+# overflow occurred but is disabled. meanwhile, inexact is enabled. Therefore,
 # we must jump to real_inex().
 fovfl_inex_on:
 
@@ -1015,7 +1015,7 @@ funfl_unfl_on2:
 
        bra.l           _real_unfl
 
-# undeflow occurred but is disabled. meanwhile, inexact is enabled. therefore,
+# underflow occurred but is disabled. meanwhile, inexact is enabled. Therefore,
 # we must jump to real_inex().
 funfl_inex_on:
 
@@ -2963,7 +2963,7 @@ iea_disabled:
 
        tst.w           %d0                     # is instr fmovm?
        bmi.b           iea_dis_fmovm           # yes
-# instruction is using an extended precision immediate operand. therefore,
+# instruction is using an extended precision immediate operand. Therefore,
 # the total instruction length is 16 bytes.
 iea_dis_immed:
        mov.l           &0x10,%d0               # 16 bytes of instruction
@@ -9624,7 +9624,7 @@ sok_dnrm:
        bge.b           sok_norm2               # thank goodness no
 
 # the multiply factor that we're trying to create should be a denorm
-# for the multiply to work. therefore, we're going to actually do a
+# for the multiply to work. Therefore, we're going to actually do a
 # multiply with a denorm which will cause an unimplemented data type
 # exception to be put into the machine which will be caught and corrected
 # later. we don't do this with the DENORMs above because this method
@@ -12216,7 +12216,7 @@ fin_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow or inexact is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fin_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -12746,7 +12746,7 @@ fdiv_zero_load_p:
 
 #
 # The destination was In Range and the source was a ZERO. The result,
-# therefore, is an INF w/ the proper sign.
+# Therefore, is an INF w/ the proper sign.
 # So, determine the sign and return a new INF (w/ the j-bit cleared).
 #
        global          fdiv_inf_load           # global for fsgldiv
@@ -12996,7 +12996,7 @@ fneg_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fneg_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -13611,7 +13611,7 @@ fabs_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fabs_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -14973,7 +14973,7 @@ fadd_zero_2:
 
 #
 # the ZEROes have opposite signs:
-# - therefore, we return +ZERO if the rounding modes are RN,RZ, or RP.
+# - Therefore, we return +ZERO if the rounding modes are RN,RZ, or RP.
 # - -ZERO is returned in the case of RM.
 #
 fadd_zero_2_chk_rm:
@@ -15425,7 +15425,7 @@ fsub_zero_2:
 
 #
 # the ZEROes have the same signs:
-# - therefore, we return +ZERO if the rounding mode is RN,RZ, or RP
+# - Therefore, we return +ZERO if the rounding mode is RN,RZ, or RP
 # - -ZERO is returned in the case of RM.
 #
 fsub_zero_2_chk_rm:
@@ -15693,7 +15693,7 @@ fsqrt_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fsqrt_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -21000,7 +21000,7 @@ fout_pack_type:
        tst.l           %d0
        bne.b           fout_pack_set
 # "mantissa" is all zero which means that the answer is zero. but, the '040
-# algorithm allows the exponent to be non-zero. the 881/2 do not. therefore,
+# algorithm allows the exponent to be non-zero. the 881/2 do not. Therefore,
 # if the mantissa is zero, I will zero the exponent, too.
 # the question now is whether the exponents sign bit is allowed to be non-zero
 # for a zero, also...
@@ -21743,7 +21743,7 @@ denorm_set_stky:
        rts
 
 #                                                                      #
-# dnrm_lp(): normalize exponent/mantissa to specified threshhold       #
+# dnrm_lp(): normalize exponent/mantissa to specified threshold                #
 #                                                                      #
 # INPUT:                                                               #
 #      %a0        : points to the operand to be denormalized           #
@@ -22402,7 +22402,7 @@ unnorm_shift:
        bgt.b           unnorm_nrm_zero         # yes; denorm only until exp = 0
 
 #
-# exponent would not go < 0. therefore, number stays normalized
+# exponent would not go < 0. Therefore, number stays normalized
 #
        sub.w           %d0, %d1                # shift exponent value
        mov.w           FTEMP_EX(%a0), %d0      # load old exponent
index 51b9f7d..e71ba0a 100644 (file)
@@ -752,7 +752,7 @@ fovfl_ovfl_on:
 
        bra.l           _real_ovfl
 
-# overflow occurred but is disabled. meanwhile, inexact is enabled. therefore,
+# overflow occurred but is disabled. meanwhile, inexact is enabled. Therefore,
 # we must jump to real_inex().
 fovfl_inex_on:
 
@@ -1014,7 +1014,7 @@ funfl_unfl_on2:
 
        bra.l           _real_unfl
 
-# undeflow occurred but is disabled. meanwhile, inexact is enabled. therefore,
+# underflow occurred but is disabled. meanwhile, inexact is enabled. Therefore,
 # we must jump to real_inex().
 funfl_inex_on:
 
@@ -2962,7 +2962,7 @@ iea_disabled:
 
        tst.w           %d0                     # is instr fmovm?
        bmi.b           iea_dis_fmovm           # yes
-# instruction is using an extended precision immediate operand. therefore,
+# instruction is using an extended precision immediate operand. Therefore,
 # the total instruction length is 16 bytes.
 iea_dis_immed:
        mov.l           &0x10,%d0               # 16 bytes of instruction
@@ -5865,7 +5865,7 @@ denorm_set_stky:
        rts
 
 #                                                                      #
-# dnrm_lp(): normalize exponent/mantissa to specified threshhold       #
+# dnrm_lp(): normalize exponent/mantissa to specified threshold                #
 #                                                                      #
 # INPUT:                                                               #
 #      %a0        : points to the operand to be denormalized           #
@@ -6524,7 +6524,7 @@ unnorm_shift:
        bgt.b           unnorm_nrm_zero         # yes; denorm only until exp = 0
 
 #
-# exponent would not go < 0. therefore, number stays normalized
+# exponent would not go < 0. Therefore, number stays normalized
 #
        sub.w           %d0, %d1                # shift exponent value
        mov.w           FTEMP_EX(%a0), %d0      # load old exponent
@@ -7901,7 +7901,7 @@ fout_pack_type:
        tst.l           %d0
        bne.b           fout_pack_set
 # "mantissa" is all zero which means that the answer is zero. but, the '040
-# algorithm allows the exponent to be non-zero. the 881/2 do not. therefore,
+# algorithm allows the exponent to be non-zero. the 881/2 do not. Therefore,
 # if the mantissa is zero, I will zero the exponent, too.
 # the question now is whether the exponents sign bit is allowed to be non-zero
 # for a zero, also...
@@ -8647,7 +8647,7 @@ fin_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow or inexact is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fin_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -9177,7 +9177,7 @@ fdiv_zero_load_p:
 
 #
 # The destination was In Range and the source was a ZERO. The result,
-# therefore, is an INF w/ the proper sign.
+# Therefore, is an INF w/ the proper sign.
 # So, determine the sign and return a new INF (w/ the j-bit cleared).
 #
        global          fdiv_inf_load           # global for fsgldiv
@@ -9427,7 +9427,7 @@ fneg_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fneg_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -10042,7 +10042,7 @@ fabs_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fabs_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
@@ -11404,7 +11404,7 @@ fadd_zero_2:
 
 #
 # the ZEROes have opposite signs:
-# - therefore, we return +ZERO if the rounding modes are RN,RZ, or RP.
+# - Therefore, we return +ZERO if the rounding modes are RN,RZ, or RP.
 # - -ZERO is returned in the case of RM.
 #
 fadd_zero_2_chk_rm:
@@ -11856,7 +11856,7 @@ fsub_zero_2:
 
 #
 # the ZEROes have the same signs:
-# - therefore, we return +ZERO if the rounding mode is RN,RZ, or RP
+# - Therefore, we return +ZERO if the rounding mode is RN,RZ, or RP
 # - -ZERO is returned in the case of RM.
 #
 fsub_zero_2_chk_rm:
@@ -12124,7 +12124,7 @@ fsqrt_sd_unfl_dis:
 
 #
 # operand will underflow AND underflow is enabled.
-# therefore, we must return the result rounded to extended precision.
+# Therefore, we must return the result rounded to extended precision.
 #
 fsqrt_sd_unfl_ena:
        mov.l           FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
index fb8a06b..67e7a78 100644 (file)
@@ -145,7 +145,7 @@ struct bi_record {
 
     /*
      *  Macintosh hardware profile data - unused, see macintosh.h for
-     *  resonable type values
+     *  reasonable type values
      */
 
 #define BI_MAC_VIA1BASE                0x8010  /* Mac VIA1 base address (always present) */
index 74fd674..cbd3d47 100644 (file)
@@ -165,6 +165,8 @@ unsigned long get_wchan(struct task_struct *p);
        eip; })
 #define        KSTK_ESP(tsk)   ((tsk) == current ? rdusp() : (tsk)->thread.usp)
 
+#define task_pt_regs(tsk)      ((struct pt_regs *) ((tsk)->thread.esp0))
+
 #define cpu_relax()    barrier()
 
 #endif
index a6ab663..ee4011c 100644 (file)
@@ -86,7 +86,7 @@ struct switch_stack {
 extern void show_regs(struct pt_regs *);
 
 /*
- * These are defined as per linux/ptrace.h, which see.
+ * These are defined as per linux/ptrace.h.
  */
 struct task_struct;
 
index c2bde5e..a6512bf 100644 (file)
@@ -12,8 +12,6 @@
 
 #ifdef __KERNEL__
 
-#ifndef __ASSEMBLY__
-
 /*
  * Size of kernel stack for each process. This must be a power of 2...
  */
@@ -28,6 +26,8 @@
  */
 #define THREAD_SIZE (PAGE_SIZE<<THREAD_SIZE_ORDER)
 
+#ifndef __ASSEMBLY__
+
 /*
  * low level task data.
  */
index e2201b9..064f591 100644 (file)
@@ -533,6 +533,13 @@ config AVNET
        default y
        depends on (AVNET5282)
 
+config UBOOT
+       bool "Support for U-Boot command line parameters"
+       help
+         If you say Y here kernel will try to collect command
+         line parameters from the initial u-boot stack.
+       default n
+
 config 4KSTACKS
        bool "Use 4Kb for kernel stacks instead of 8Kb"
        default y
index 5c2bb3e..ba92b90 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/bootmem.h>
 #include <linux/seq_file.h>
 #include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
 
 #include <asm/setup.h>
 #include <asm/irq.h>
@@ -52,7 +54,6 @@ void (*mach_reset)(void);
 void (*mach_halt)(void);
 void (*mach_power_off)(void);
 
-
 #ifdef CONFIG_M68000
        #define CPU "MC68000"
 #endif
@@ -111,6 +112,69 @@ void (*mach_power_off)(void);
 extern int _stext, _etext, _sdata, _edata, _sbss, _ebss, _end;
 extern int _ramstart, _ramend;
 
+#if defined(CONFIG_UBOOT)
+/*
+ * parse_uboot_commandline
+ *
+ * Copies u-boot commandline arguments and store them in the proper linux
+ * variables.
+ *
+ * Assumes:
+ *     _init_sp global contains the address in the stack pointer when the
+ *     kernel starts (see head.S::_start)
+ *
+ *     U-Boot calling convention:
+ *     (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end);
+ *
+ *     _init_sp can be parsed as such
+ *
+ *     _init_sp+00 = u-boot cmd after jsr into kernel (skip)
+ *     _init_sp+04 = &kernel board_info (residual data)
+ *     _init_sp+08 = &initrd_start
+ *     _init_sp+12 = &initrd_end
+ *     _init_sp+16 = &cmd_start
+ *     _init_sp+20 = &cmd_end
+ *
+ *     This also assumes that the memory locations pointed to are still
+ *     unmodified. U-boot places them near the end of external SDRAM.
+ *
+ * Argument(s):
+ *     commandp = the linux commandline arg container to fill.
+ *     size     = the sizeof commandp.
+ *
+ * Returns:
+ */
+void parse_uboot_commandline(char *commandp, int size)
+{
+       extern unsigned long _init_sp;
+       unsigned long *sp;
+       unsigned long uboot_kbd;
+       unsigned long uboot_initrd_start, uboot_initrd_end;
+       unsigned long uboot_cmd_start, uboot_cmd_end;
+
+
+       sp = (unsigned long *)_init_sp;
+       uboot_kbd = sp[1];
+       uboot_initrd_start = sp[2];
+       uboot_initrd_end = sp[3];
+       uboot_cmd_start = sp[4];
+       uboot_cmd_end = sp[5];
+
+       if (uboot_cmd_start && uboot_cmd_end)
+               strncpy(commandp, (const char *)uboot_cmd_start, size);
+#if defined(CONFIG_BLK_DEV_INITRD)
+       if (uboot_initrd_start && uboot_initrd_end &&
+               (uboot_initrd_end > uboot_initrd_start)) {
+               initrd_start = uboot_initrd_start;
+               initrd_end = uboot_initrd_end;
+               ROOT_DEV = Root_RAM0;
+               printk(KERN_INFO "initrd at 0x%lx:0x%lx\n",
+                       initrd_start, initrd_end);
+       }
+#endif /* if defined(CONFIG_BLK_DEV_INITRD) */
+}
+#endif /* #if defined(CONFIG_UBOOT) */
+
 void __init setup_arch(char **cmdline_p)
 {
        int bootmap_size;
@@ -128,7 +192,24 @@ void __init setup_arch(char **cmdline_p)
 #if defined(CONFIG_BOOTPARAM)
        strncpy(&command_line[0], CONFIG_BOOTPARAM_STRING, sizeof(command_line));
        command_line[sizeof(command_line) - 1] = 0;
-#endif
+#endif /* CONFIG_BOOTPARAM */
+
+#if defined(CONFIG_UBOOT)
+       /* CONFIG_UBOOT and CONFIG_BOOTPARAM defined, concatenate cmdline */
+       #if defined(CONFIG_BOOTPARAM)
+               /* Add the whitespace separator */
+               command_line[strlen(CONFIG_BOOTPARAM_STRING)] = ' ';
+               /* Parse uboot command line into the rest of the buffer */
+               parse_uboot_commandline(
+                       &command_line[(strlen(CONFIG_BOOTPARAM_STRING)+1)],
+                       (sizeof(command_line) -
+                       (strlen(CONFIG_BOOTPARAM_STRING)+1)));
+       /* Only CONFIG_UBOOT defined, create cmdline */
+       #else
+               parse_uboot_commandline(&command_line[0], sizeof(command_line));
+       #endif /* CONFIG_BOOTPARAM */
+       command_line[sizeof(command_line) - 1] = 0;
+#endif /* CONFIG_UBOOT */
 
        printk(KERN_INFO "\x0F\r\n\nuClinux/" CPU "\n");
 
@@ -204,6 +285,13 @@ void __init setup_arch(char **cmdline_p)
        free_bootmem(memory_start, memory_end - memory_start);
        reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT);
 
+#if defined(CONFIG_UBOOT) && defined(CONFIG_BLK_DEV_INITRD)
+       if ((initrd_start > 0) && (initrd_start < initrd_end) &&
+                       (initrd_end < memory_end))
+               reserve_bootmem(initrd_start, initrd_end - initrd_start,
+                                BOOTMEM_DEFAULT);
+#endif /* if defined(CONFIG_BLK_DEV_INITRD) */
+
        /*
         * Get kmalloc into gear.
         */
index 2736a5e..9f1784f 100644 (file)
@@ -8,6 +8,8 @@
  */
 
 #include <asm-generic/vmlinux.lds.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
 
 #if defined(CONFIG_RAMKERNEL)
 #define        RAM_START       CONFIG_KERNELBASE
@@ -15,7 +17,7 @@
 #define        TEXT            ram
 #define        DATA            ram
 #define        INIT            ram
-#define        BSS             ram
+#define        BSSS            ram
 #endif
 #if defined(CONFIG_ROMKERNEL) || defined(CONFIG_HIMEMKERNEL)
 #define        RAM_START       CONFIG_RAMBASE
@@ -27,7 +29,7 @@
 #define        TEXT            rom
 #define        DATA            ram
 #define        INIT            ram
-#define        BSS             ram
+#define        BSSS            ram
 #endif
 
 #ifndef DATA_ADDR
@@ -147,40 +149,19 @@ SECTIONS {
                . = ALIGN(4);
                _sdata = . ;
                DATA_DATA
-               . = ALIGN(32);
-               *(.data.cacheline_aligned)
-               . = ALIGN(8192) ;
-               *(.data.init_task)
+               CACHELINE_ALIGNED_DATA(32)
+               INIT_TASK_DATA(THREAD_SIZE)
                _edata = . ;
        } > DATA
 
-       .init : {
-               . = ALIGN(4096);
+       .init.text : {
+               . = ALIGN(PAGE_SIZE);
                __init_begin = .;
-               _sinittext = .;
-               INIT_TEXT
-               _einittext = .;
-               INIT_DATA
-               . = ALIGN(16);
-               __setup_start = .;
-               *(.init.setup)
-               __setup_end = .;
-               __initcall_start = .;
-               INITCALLS
-               __initcall_end = .;
-               __con_initcall_start = .;
-               *(.con_initcall.init)
-               __con_initcall_end = .;
-               __security_initcall_start = .;
-               *(.security_initcall.init)
-               __security_initcall_end = .;
-#ifdef CONFIG_BLK_DEV_INITRD
-               . = ALIGN(4);
-               __initramfs_start = .;
-               *(.init.ramfs)
-               __initramfs_end = .;
-#endif
-               . = ALIGN(4096);
+       } > INIT
+       INIT_TEXT_SECTION(PAGE_SIZE) > INIT
+       INIT_DATA_SECTION(16) > INIT
+       .init.data : {
+               . = ALIGN(PAGE_SIZE);
                __init_end = .;
        } > INIT
 
@@ -192,7 +173,7 @@ SECTIONS {
                . = ALIGN(4) ;
                _ebss = . ;
                _end = . ;
-       } > BSS
+       } > BSSS
 
        DISCARDS
 }
index f02840d..a8842dc 100644 (file)
@@ -30,7 +30,8 @@ static struct mcf_gpio_chip mcf_gpio_chips[] = {
                        .direction_output       = mcf_gpio_direction_output,
                        .get                    = mcf_gpio_get_value,
                        .set                    = mcf_gpio_set_value,
-                       .ngpio                  = 8,
+                       .base                   = 1,
+                       .ngpio                  = 7,
                },
                .pddr                           = MCFEPORT_EPDDR,
                .podr                           = MCFEPORT_EPDR,
@@ -244,7 +245,7 @@ static struct mcf_gpio_chip mcf_gpio_chips[] = {
                        .get                    = mcf_gpio_get_value,
                        .set                    = mcf_gpio_set_value_fast,
                        .base                   = 96,
-                       .ngpio                  = 4,
+                       .ngpio                  = 8,
                },
                .pddr                           = MCFGPIO_PDDR_TIMER,
                .podr                           = MCFGPIO_PODR_TIMER,
index 1028142..0b56e19 100644 (file)
@@ -31,7 +31,8 @@ static struct mcf_gpio_chip mcf_gpio_chips[] = {
                        .direction_output       = mcf_gpio_direction_output,
                        .get                    = mcf_gpio_get_value,
                        .set                    = mcf_gpio_set_value,
-                       .ngpio                  = 8,
+                       .base                   = 1,
+                       .ngpio                  = 7,
                },
                .pddr                           = MCFEPORT_EPDDR,
                .podr                           = MCFEPORT_EPDR,
@@ -263,7 +264,8 @@ static struct mcf_gpio_chip mcf_gpio_chips[] = {
                        .direction_output       = mcf_gpio_direction_output,
                        .get                    = mcf_gpio_get_value,
                        .set                    = mcf_gpio_set_value,
-                       .ngpio                  = 8,
+                       .base                   = 1,
+                       .ngpio                  = 7,
                },
                .pddr                           = MCFEPORT_EPDDR,
                .podr                           = MCFEPORT_EPDR,
index ec59395..eedaf0a 100644 (file)
@@ -31,7 +31,7 @@ static struct mcf_gpio_chip mcf_gpio_chips[] = {
                        .get                    = mcf_gpio_get_value,
                        .set                    = mcf_gpio_set_value,
                        .base                   = 1,
-                       .ngpio                  = 8,
+                       .ngpio                  = 7,
                },
                .pddr                           = MCFEPORT_EPDDR,
                .podr                           = MCFEPORT_EPDR,
index 7cdbf44..9f1260c 100644 (file)
@@ -9,6 +9,7 @@
 /***************************************************************************/
 
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/clk.h>
 #include <asm/coldfire.h>
 
@@ -18,23 +19,27 @@ struct clk *clk_get(struct device *dev, const char *id)
 {
        return NULL;
 }
+EXPORT_SYMBOL(clk_get);
 
 int clk_enable(struct clk *clk)
 {
        return 0;
 }
+EXPORT_SYMBOL(clk_enable);
 
 void clk_disable(struct clk *clk)
 {
 }
+EXPORT_SYMBOL(clk_disable);
 
 void clk_put(struct clk *clk)
 {
 }
+EXPORT_SYMBOL(clk_put);
 
 unsigned long clk_get_rate(struct clk *clk)
 {
        return MCF_CLK;
 }
-
+EXPORT_SYMBOL(clk_get_rate);
 /***************************************************************************/
index 2b0d73c..4b91aa2 100644 (file)
 .global _ramvec
 .global        _ramstart
 .global        _ramend
+#if defined(CONFIG_UBOOT)
+.global        _init_sp
+#endif
 
 /*****************************************************************************/
 
@@ -124,6 +127,10 @@ _ramstart:
 .long  0
 _ramend:
 .long  0
+#if defined(CONFIG_UBOOT)
+_init_sp:
+.long  0
+#endif
 
 /*****************************************************************************/
 
@@ -137,6 +144,9 @@ __HEAD
 _start:
        nop                                     /* filler */
        movew   #0x2700, %sr                    /* no interrupts */
+#if defined(CONFIG_UBOOT)
+       movel   %sp,_init_sp                    /* save initial stack pointer */
+#endif
 
        /*
         *      Do any platform or board specific setup now. Most boards
index 6a907c5..cc2108b 100644 (file)
@@ -9,7 +9,7 @@
  * It is based on demo code originally Copyright 2001 by Intel Corp, taken from
  * http://www.embedded.com/showArticle.jhtml?articleID=19205567
  *
- * Attempts were made, unsuccesfully, to contact the original
+ * Attempts were made, unsuccessfully, to contact the original
  * author of this code (Michael Morrow, Intel).  Below is the original
  * copyright notice.
  *
index d4e9f49..0929198 100644 (file)
@@ -9,7 +9,7 @@
  * It is based on demo code originally Copyright 2001 by Intel Corp, taken from
  * http://www.embedded.com/showArticle.jhtml?articleID=19205567
  *
- * Attempts were made, unsuccesfully, to contact the original
+ * Attempts were made, unsuccessfully, to contact the original
  * author of this code (Michael Morrow, Intel).  Below is the original
  * copyright notice.
  *
index 941dc8f..4df851d 100644 (file)
@@ -9,7 +9,7 @@
  * It is based on demo code originally Copyright 2001 by Intel Corp, taken from
  * http://www.embedded.com/showArticle.jhtml?articleID=19205567
  *
- * Attempts were made, unsuccesfully, to contact the original
+ * Attempts were made, unsuccessfully, to contact the original
  * author of this code (Michael Morrow, Intel).  Below is the original
  * copyright notice.
  *
index 8de0eb9..ed3a88d 100644 (file)
@@ -24,7 +24,7 @@
 
 /* BIG FAT WARNING: races danger!
    No protections exist here. Current users are only early init code,
-   when locking is not needed because no cuncurency yet exists there,
+   when locking is not needed because no concurrency yet exists there,
    and GPIO IRQ dispatcher, which does locking.
    However, if many uses will ever happen, proper locking will be needed
    - including locking between different uses
index 343ed15..57a9719 100644 (file)
@@ -164,7 +164,7 @@ struct sgioc_regs {
        u32 _unused5;
        u8 _write[3];
        volatile u8 write;
-#define SGIOC_WRITE_NTHRESH    0x01    /* use 4.5db threshhold */
+#define SGIOC_WRITE_NTHRESH    0x01    /* use 4.5db threshold */
 #define SGIOC_WRITE_TPSPEED    0x02    /* use 100ohm TP speed */
 #define SGIOC_WRITE_EPSEL      0x04    /* force cable mode: 1=AUI 0=TP */
 #define SGIOC_WRITE_EASEL      0x08    /* 1=autoselect 0=manual cable selection */
index b6faf08..591b906 100644 (file)
 #define G_MAC_TXD_WEIGHT1(x)        _SB_GETVALUE(x, S_MAC_TXD_WEIGHT1, M_MAC_TXD_WEIGHT1)
 
 /*
- * MAC Fifo Threshhold registers (Table 9-14)
+ * MAC Fifo Threshold registers (Table 9-14)
  * Register: MAC_THRSH_CFG_0
  * Register: MAC_THRSH_CFG_1
  * Register: MAC_THRSH_CFG_2
index d0c29d4..31c76c0 100644 (file)
@@ -825,7 +825,7 @@ typedef union iprb_u {
        struct {
            u64 rsvd1:  15,
                error:  1,      /* Widget rcvd wr resp pkt w/ error */
-               ovflow: 5,      /* Over flow count. perf measurement */
+               ovflow: 5,      /* Overflow count. perf measurement */
                fire_and_forget: 1, /* Launch Write without response */
                mode:   2,      /* Widget operation Mode        */
                rsvd2:  2,
index 24630fd..a38e3ee 100644 (file)
@@ -1331,7 +1331,7 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
                if (!((asid += ASID_INC) & ASID_MASK) ) {
                        if (cpu_has_vtag_icache)
                                flush_icache_all();
-                       /* Traverse all online CPUs (hack requires contigous range) */
+                       /* Traverse all online CPUs (hack requires contiguous range) */
                        for_each_online_cpu(i) {
                                /*
                                 * We don't need to worry about our own CPU, nor those of
index b30c5b1..a2127d6 100644 (file)
@@ -110,7 +110,7 @@ ieee754dp ieee754dp_sub(ieee754dp x, ieee754dp y)
 
        case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
                DPDNORMX;
-               /* FAAL THOROUGH */
+               /* FALL THROUGH */
 
        case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_DNORM):
                /* normalize ym,ye */
index a2b2d62..8ebc384 100644 (file)
@@ -117,7 +117,7 @@ unsigned long __init smsc_fdc37m81x_init(unsigned long port)
        if (chip_id == SMSC_FDC37M81X_CHIP_ID)
                smsc_fdc37m81x_config_end();
        else {
-               printk(KERN_WARNING "%s: unknow chip id 0x%02x\n", __func__,
+               printk(KERN_WARNING "%s: unknown chip id 0x%02x\n", __func__,
                       chip_id);
                g_smsc_fdc37m81x_base = 0;
        }
index 75099ef..f9f6783 100644 (file)
@@ -24,7 +24,7 @@
  *
  *  This driver programs the PCX-U/PCX-W performance counters
  *  on the PA-RISC 2.0 chips.  The driver keeps all images now
- *  internally to the kernel to hopefully eliminate the possiblity
+ *  internally to the kernel to hopefully eliminate the possibility
  *  of a bad image halting the CPU.  Also, there are different
  *  images for the PCX-W and later chips vs the PCX-U chips.
  *
index 1e180a5..0de404d 100644 (file)
@@ -39,7 +39,7 @@
 #define PMRN_PMLCB2    0x112   /* PM Local Control B2 */
 #define PMRN_PMLCB3    0x113   /* PM Local Control B3 */
 
-#define PMLCB_THRESHMUL_MASK   0x0700  /* Threshhold Multiple Field */
+#define PMLCB_THRESHMUL_MASK   0x0700  /* Threshold Multiple Field */
 #define PMLCB_THRESHMUL_SHIFT  8
 
 #define PMLCB_THRESHOLD_MASK   0x003f  /* Threshold Field */
index 641c74b..b6bd1ea 100644 (file)
@@ -52,7 +52,7 @@ static struct hard_trap_info
        { 0x2030, 0x08 /* SIGFPE */  },         /* spe fp data */
        { 0x2040, 0x08 /* SIGFPE */  },         /* spe fp data */
        { 0x2050, 0x08 /* SIGFPE */  },         /* spe fp round */
-       { 0x2060, 0x0e /* SIGILL */  },         /* performace monitor */
+       { 0x2060, 0x0e /* SIGILL */  },         /* performance monitor */
        { 0x2900, 0x08 /* SIGFPE */  },         /* apu unavailable */
        { 0x3100, 0x0e /* SIGALRM */ },         /* fixed interval timer */
        { 0x3200, 0x02 /* SIGINT */  },         /* watchdog */
index c3a56d6..a753b72 100644 (file)
@@ -59,7 +59,7 @@ void set_thresholds(unsigned long cpu)
        mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TIE | THRM1_TID);
 
        /* setup THRM2,
-        * threshold, valid bit, enable interrupts, interrupt when above threshhold
+        * threshold, valid bit, enable interrupts, interrupt when above threshold
         */
        mtspr (SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V | THRM1_TIE);
 #else
index 137dc22..d84d192 100644 (file)
@@ -721,7 +721,7 @@ static int __init vdso_init(void)
 
 #ifdef CONFIG_PPC64
        /*
-        * Fill up the "systemcfg" stuff for backward compatiblity
+        * Fill up the "systemcfg" stuff for backward compatibility
         */
        strcpy((char *)vdso_data->eye_catcher, "SYSTEMCFG:PPC64");
        vdso_data->version.major = SYSTEMCFG_MAJOR;
index ae06c62..2c9e522 100644 (file)
@@ -248,7 +248,7 @@ static int pm_rtas_activate_signals(u32 node, u32 count)
         * There is no debug setup required for the cycles event.
         * Note that only events in the same group can be used.
         * Otherwise, there will be conflicts in correctly routing
-        * the signals on the debug bus.  It is the responsiblity
+        * the signals on the debug bus.  It is the responsibility
         * of the OProfile user tool to check the events are in
         * the same group.
         */
@@ -1594,7 +1594,7 @@ static void cell_handle_interrupt_spu(struct pt_regs *regs,
                 * to a latch.  The new values (interrupt setting bits, reset
                 * counter value etc.) are not copied to the actual registers
                 * until the performance monitor is enabled.  In order to get
-                * this to work as desired, the permormance monitor needs to
+                * this to work as desired, the performance monitor needs to
                 * be disabled while writing to the latches.  This is a
                 * HW design issue.
                 */
@@ -1668,7 +1668,7 @@ static void cell_handle_interrupt_ppu(struct pt_regs *regs,
                 * to a latch.  The new values (interrupt setting bits, reset
                 * counter value etc.) are not copied to the actual registers
                 * until the performance monitor is enabled.  In order to get
-                * this to work as desired, the permormance monitor needs to
+                * this to work as desired, the performance monitor needs to
                 * be disabled while writing to the latches.  This is a
                 * HW design issue.
                 */
index dd43114..da110bd 100644 (file)
@@ -100,7 +100,7 @@ const struct of_device_id mpc52xx_pci_ids[] __initdata = {
 };
 
 /* ======================================================================== */
-/* PCI configuration acess                                                  */
+/* PCI configuration access                                                 */
 /* ======================================================================== */
 
 static int
index e81403b..ab2027c 100644 (file)
@@ -302,7 +302,7 @@ static void __init setup_chaos(struct pci_controller *hose,
  *  1 -> Skip the device but act as if the access was successfull
  *       (return 0xff's on reads, eventually, cache config space
  *       accesses in a later version)
- * -1 -> Hide the device (unsuccessful acess)
+ * -1 -> Hide the device (unsuccessful access)
  */
 static int u3_ht_skip_device(struct pci_controller *hose,
                             struct pci_bus *bus, unsigned int devfn)
index ae3c4db..bafc3f8 100644 (file)
@@ -160,7 +160,7 @@ static int dart_build(struct iommu_table *tbl, long index,
 
        dp = ((unsigned int*)tbl->it_base) + index;
 
-       /* On U3, all memory is contigous, so we can move this
+       /* On U3, all memory is contiguous, so we can move this
         * out of the loop.
         */
        l = npages;
index 16c6730..c802352 100644 (file)
@@ -220,23 +220,8 @@ config AUDIT_ARCH
        bool
        default y
 
-config S390_SWITCH_AMODE
-       bool "Switch kernel/user addressing modes"
-       help
-         This option allows to switch the addressing modes of kernel and user
-         space. The kernel parameter switch_amode=on will enable this feature,
-         default is disabled. Enabling this (via kernel parameter) on machines
-         earlier than IBM System z9-109 EC/BC will reduce system performance.
-
-         Note that this option will also be selected by selecting the execute
-         protection option below. Enabling the execute protection via the
-         noexec kernel parameter will also switch the addressing modes,
-         independent of the switch_amode kernel parameter.
-
-
 config S390_EXEC_PROTECT
        bool "Data execute protection"
-       select S390_SWITCH_AMODE
        help
          This option allows to enable a buffer overflow protection for user
          space programs and it also selects the addressing mode option above.
index ab44644..f4e53c6 100644 (file)
@@ -185,7 +185,6 @@ CONFIG_HOTPLUG_CPU=y
 CONFIG_COMPAT=y
 CONFIG_SYSVIPC_COMPAT=y
 CONFIG_AUDIT_ARCH=y
-CONFIG_S390_SWITCH_AMODE=y
 CONFIG_S390_EXEC_PROTECT=y
 
 #
index ae7c8f9..2a113d6 100644 (file)
@@ -21,7 +21,7 @@
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
 
 #define __CS_LOOP(ptr, op_val, op_string) ({                           \
-       typeof(ptr->counter) old_val, new_val;                          \
+       int old_val, new_val;                                           \
        asm volatile(                                                   \
                "       l       %0,%2\n"                                \
                "0:     lr      %1,%0\n"                                \
@@ -38,7 +38,7 @@
 #else /* __GNUC__ */
 
 #define __CS_LOOP(ptr, op_val, op_string) ({                           \
-       typeof(ptr->counter) old_val, new_val;                          \
+       int old_val, new_val;                                           \
        asm volatile(                                                   \
                "       l       %0,0(%3)\n"                             \
                "0:     lr      %1,%0\n"                                \
@@ -143,7 +143,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
 
 #define __CSG_LOOP(ptr, op_val, op_string) ({                          \
-       typeof(ptr->counter) old_val, new_val;                          \
+       long long old_val, new_val;                                     \
        asm volatile(                                                   \
                "       lg      %0,%2\n"                                \
                "0:     lgr     %1,%0\n"                                \
@@ -160,7 +160,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
 #else /* __GNUC__ */
 
 #define __CSG_LOOP(ptr, op_val, op_string) ({                          \
-       typeof(ptr->counter) old_val, new_val;                          \
+       long long old_val, new_val;                                     \
        asm volatile(                                                   \
                "       lg      %0,0(%3)\n"                             \
                "0:     lgr     %1,%0\n"                                \
index 2a54195..f4bd346 100644 (file)
@@ -142,6 +142,8 @@ struct ccw1;
 extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long);
 extern int ccw_device_set_options(struct ccw_device *, unsigned long);
 extern void ccw_device_clear_options(struct ccw_device *, unsigned long);
+int ccw_device_is_pathgroup(struct ccw_device *cdev);
+int ccw_device_is_multipath(struct ccw_device *cdev);
 
 /* Allow for i/o completion notification after primary interrupt status. */
 #define CCWDEV_EARLY_NOTIFICATION      0x0001
@@ -151,6 +153,8 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long);
 #define CCWDEV_DO_PATHGROUP             0x0004
 /* Allow forced onlining of boxed devices. */
 #define CCWDEV_ALLOW_FORCE              0x0008
+/* Try to use multipath mode. */
+#define CCWDEV_DO_MULTIPATH            0x0010
 
 extern int ccw_device_start(struct ccw_device *, struct ccw1 *,
                            unsigned long, __u8, unsigned long);
index fc7edd6..976e273 100644 (file)
@@ -36,7 +36,7 @@ static inline int init_new_context(struct task_struct *tsk,
                mm->context.has_pgste = 1;
                mm->context.alloc_pgste = 1;
        } else {
-               mm->context.noexec = s390_noexec;
+               mm->context.noexec = (user_mode == SECONDARY_SPACE_MODE);
                mm->context.has_pgste = 0;
                mm->context.alloc_pgste = 0;
        }
@@ -58,7 +58,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
        pgd_t *pgd = mm->pgd;
 
        S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
-       if (switch_amode) {
+       if (user_mode != HOME_SPACE_MODE) {
                /* Load primary space page table origin. */
                pgd = mm->context.noexec ? get_shadow_table(pgd) : pgd;
                S390_lowcore.user_exec_asce = mm->context.asce_bits | __pa(pgd);
index ddad590..68940d0 100644 (file)
@@ -143,7 +143,8 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm)
        spin_lock_init(&mm->context.list_lock);
        INIT_LIST_HEAD(&mm->context.crst_list);
        INIT_LIST_HEAD(&mm->context.pgtable_list);
-       return (pgd_t *) crst_table_alloc(mm, s390_noexec);
+       return (pgd_t *)
+               crst_table_alloc(mm, user_mode == SECONDARY_SPACE_MODE);
 }
 #define pgd_free(mm, pgd) crst_table_free(mm, (unsigned long *) pgd)
 
index 60a7b1a..e2fa79c 100644 (file)
@@ -169,12 +169,13 @@ extern unsigned long VMALLOC_START;
  * STL Segment-Table-Length:  Segment-table length (STL+1*16 entries -> up to 2048)
  *
  * A 64 bit pagetable entry of S390 has following format:
- * |                     PFRA                         |0IP0|  OS  |
+ * |                    PFRA                         |0IPC|  OS  |
  * 0000000000111111111122222222223333333333444444444455555555556666
  * 0123456789012345678901234567890123456789012345678901234567890123
  *
  * I Page-Invalid Bit:    Page is not available for address-translation
  * P Page-Protection Bit: Store access not possible for page
+ * C Change-bit override: HW is not required to set change bit
  *
  * A 64 bit segmenttable entry of S390 has following format:
  * |        P-table origin                              |      TT
@@ -218,6 +219,7 @@ extern unsigned long VMALLOC_START;
  */
 
 /* Hardware bits in the page table entry */
+#define _PAGE_CO       0x100           /* HW Change-bit override */
 #define _PAGE_RO       0x200           /* HW read-only bit  */
 #define _PAGE_INVALID  0x400           /* HW invalid bit    */
 
index e37478e..52a779c 100644 (file)
@@ -49,17 +49,12 @@ extern unsigned long memory_end;
 
 void detect_memory_layout(struct mem_chunk chunk[]);
 
-#ifdef CONFIG_S390_SWITCH_AMODE
-extern unsigned int switch_amode;
-#else
-#define switch_amode   (0)
-#endif
-
-#ifdef CONFIG_S390_EXEC_PROTECT
-extern unsigned int s390_noexec;
-#else
-#define s390_noexec    (0)
-#endif
+#define PRIMARY_SPACE_MODE     0
+#define ACCESS_REGISTER_MODE   1
+#define SECONDARY_SPACE_MODE   2
+#define HOME_SPACE_MODE                3
+
+extern unsigned int user_mode;
 
 /*
  * Machine features detected in head.S
index a868b27..2ab1141 100644 (file)
@@ -1,57 +1,22 @@
 /*
- *  include/asm-s390/smp.h
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
- *               Martin Schwidefsky (schwidefsky@de.ibm.com)
- *               Heiko Carstens (heiko.carstens@de.ibm.com)
+ *    Copyright IBM Corp. 1999,2009
+ *    Author(s): Denis Joseph Barrow,
+ *              Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ *              Heiko Carstens <heiko.carstens@de.ibm.com>,
  */
 #ifndef __ASM_SMP_H
 #define __ASM_SMP_H
 
-#include <linux/threads.h>
-#include <linux/cpumask.h>
-#include <linux/bitops.h>
+#ifdef CONFIG_SMP
 
-#if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__)
-
-#include <asm/lowcore.h>
-#include <asm/sigp.h>
-#include <asm/ptrace.h>
 #include <asm/system.h>
-
-/*
-  s390 specific smp.c headers
- */
-typedef struct
-{
-       int        intresting;
-       sigp_ccode ccode; 
-       __u32      status;
-       __u16      cpu;
-} sigp_info;
+#include <asm/sigp.h>
 
 extern void machine_restart_smp(char *);
 extern void machine_halt_smp(void);
 extern void machine_power_off_smp(void);
 
-#define NO_PROC_ID             0xFF            /* No processor magic marker */
-
-/*
- *     This magic constant controls our willingness to transfer
- *     a process across CPUs. Such a transfer incurs misses on the L1
- *     cache, and on a P6 or P5 with multiple L2 caches L2 hits. My
- *     gut feeling is this will vary by board in value. For a board
- *     with separate L2 cache it probably depends also on the RSS, and
- *     for a board with shared L2 cache it ought to decay fast as other
- *     processes are run.
- */
-#define PROC_CHANGE_PENALTY    20              /* Schedule penalty */
-
 #define raw_smp_processor_id() (S390_lowcore.cpu_nr)
-#define cpu_logical_map(cpu)   (cpu)
 
 extern int __cpu_disable (void);
 extern void __cpu_die (unsigned int cpu);
@@ -64,7 +29,9 @@ extern int smp_cpu_polarization[];
 extern void arch_send_call_function_single_ipi(int cpu);
 extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
 
-#endif
+extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
+
+#endif /* CONFIG_SMP */
 
 #ifdef CONFIG_HOTPLUG_CPU
 extern int smp_rescan_cpus(void);
@@ -72,5 +39,4 @@ extern int smp_rescan_cpus(void);
 static inline int smp_rescan_cpus(void) { return 0; }
 #endif
 
-extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
-#endif
+#endif /* __ASM_SMP_H */
index f4fc16c..6f60eee 100644 (file)
@@ -1,21 +1,6 @@
-/*
- *  include/asm-s390/sockios.h
- *
- *  S390 version
- *
- *  Derived from "include/asm-i386/sockios.h"
- */
+#ifndef _ASM_S390_SOCKIOS_H
+#define _ASM_S390_SOCKIOS_H
 
-#ifndef __ARCH_S390_SOCKIOS__
-#define __ARCH_S390_SOCKIOS__
-
-/* Socket-level I/O control calls. */
-#define FIOSETOWN      0x8901
-#define SIOCSPGRP      0x8902
-#define FIOGETOWN      0x8903
-#define SIOCGPGRP      0x8904
-#define SIOCATMARK     0x8905
-#define SIOCGSTAMP     0x8906          /* Get stamp (timeval) */
-#define SIOCGSTAMPNS   0x8907          /* Get stamp (timespec) */
+#include <asm-generic/sockios.h>
 
 #endif
index 5873185..71bf6ac 100644 (file)
@@ -1,206 +1,6 @@
-/*
- *  include/asm-s390/termbits.h
- *
- *  S390 version
- *
- *  Derived from "include/asm-i386/termbits.h"
- */
+#ifndef _ASM_S390_TERMBITS_H
+#define _ASM_S390_TERMBITS_H
 
-#ifndef __ARCH_S390_TERMBITS_H__
-#define __ARCH_S390_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
+#include <asm-generic/termbits.h>
 
 #endif
diff --git a/arch/s390/include/asm/todclk.h b/arch/s390/include/asm/todclk.h
deleted file mode 100644 (file)
index c7f6205..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * File...........: linux/include/asm/todclk.h
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
- *
- * History of changes (starts July 2000)
- */
-
-#ifndef __ASM_TODCLK_H
-#define __ASM_TODCLK_H
-
-#ifdef __KERNEL__
-
-#define TOD_uSEC (0x1000ULL)
-#define TOD_mSEC (1000 * TOD_uSEC)
-#define TOD_SEC (1000 * TOD_mSEC)
-#define TOD_MIN (60 * TOD_SEC)
-#define TOD_HOUR (60 * TOD_MIN)
-
-#endif
-
-#endif
index 8377e91..cbf0a87 100644 (file)
@@ -93,6 +93,8 @@ extern struct uaccess_ops uaccess_mvcos;
 extern struct uaccess_ops uaccess_mvcos_switch;
 extern struct uaccess_ops uaccess_pt;
 
+extern int __handle_fault(unsigned long, unsigned long, int);
+
 static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
 {
        size = uaccess.copy_to_user_small(size, ptr, x);
index c7be8e1..683f638 100644 (file)
@@ -44,6 +44,7 @@ obj-$(CONFIG_KPROBES)         += kprobes.o
 obj-$(CONFIG_FUNCTION_TRACER)  += $(if $(CONFIG_64BIT),mcount64.o,mcount.o)
 obj-$(CONFIG_DYNAMIC_FTRACE)   += ftrace.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS)  += ftrace.o
 
 # Kexec part
 S390_KEXEC_OBJS := machine_kexec.o crash.o
index fda1a81..25c31d6 100644 (file)
 #include <linux/shm.h>
 #include <linux/slab.h>
 #include <linux/uio.h>
-#include <linux/nfs_fs.h>
 #include <linux/quota.h>
 #include <linux/module.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/nfsd/nfsd.h>
-#include <linux/nfsd/cache.h>
-#include <linux/nfsd/xdr.h>
-#include <linux/nfsd/syscall.h>
 #include <linux/poll.h>
 #include <linux/personality.h>
 #include <linux/stat.h>
index 45e9092..cb97afc 100644 (file)
@@ -4,10 +4,6 @@
 #include <linux/compat.h>
 #include <linux/socket.h>
 #include <linux/syscalls.h>
-#include <linux/nfs_fs.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/nfsd/nfsd.h>
-#include <linux/nfsd/export.h>
 
 /* Macro that masks the high order bit of an 32 bit pointer and converts it*/
 /*       to a 64 bit pointer */
index 6a25080..d984a2a 100644 (file)
@@ -83,6 +83,8 @@ startup_continue:
        slr     %r0,%r0                 # set cpuid to zero
        sigp    %r1,%r0,0x12            # switch to esame mode
        sam64                           # switch to 64 bit mode
+       llgfr   %r13,%r13               # clear high-order half of base reg
+       lmh     %r0,%r15,.Lzero64-.LPG1(%r13)   # clear high-order half
        lctlg   %c0,%c15,.Lctl-.LPG1(%r13)      # load control registers
        lg      %r12,.Lparmaddr-.LPG1(%r13)     # pointer to parameter area
                                        # move IPL device to lowcore
@@ -127,6 +129,7 @@ startup_continue:
 .L4malign:.quad 0xffffffffffc00000
 .Lscan2g:.quad 0x80000000 + 0x20000 - 8        # 2GB + 128K - 8
 .Lnop: .long   0x07000700
+.Lzero64:.fill 16,4,0x0
 #ifdef CONFIG_ZFCPDUMP
 .Lcurrent_cpu:
        .long 0x0
index 061479f..0663287 100644 (file)
@@ -305,9 +305,8 @@ static int __init early_parse_mem(char *p)
 }
 early_param("mem", early_parse_mem);
 
-#ifdef CONFIG_S390_SWITCH_AMODE
-unsigned int switch_amode = 0;
-EXPORT_SYMBOL_GPL(switch_amode);
+unsigned int user_mode = HOME_SPACE_MODE;
+EXPORT_SYMBOL_GPL(user_mode);
 
 static int set_amode_and_uaccess(unsigned long user_amode,
                                 unsigned long user32_amode)
@@ -340,23 +339,29 @@ static int set_amode_and_uaccess(unsigned long user_amode,
  */
 static int __init early_parse_switch_amode(char *p)
 {
-       switch_amode = 1;
+       if (user_mode != SECONDARY_SPACE_MODE)
+               user_mode = PRIMARY_SPACE_MODE;
        return 0;
 }
 early_param("switch_amode", early_parse_switch_amode);
 
-#else /* CONFIG_S390_SWITCH_AMODE */
-static inline int set_amode_and_uaccess(unsigned long user_amode,
-                                       unsigned long user32_amode)
+static int __init early_parse_user_mode(char *p)
 {
+       if (p && strcmp(p, "primary") == 0)
+               user_mode = PRIMARY_SPACE_MODE;
+#ifdef CONFIG_S390_EXEC_PROTECT
+       else if (p && strcmp(p, "secondary") == 0)
+               user_mode = SECONDARY_SPACE_MODE;
+#endif
+       else if (!p || strcmp(p, "home") == 0)
+               user_mode = HOME_SPACE_MODE;
+       else
+               return 1;
        return 0;
 }
-#endif /* CONFIG_S390_SWITCH_AMODE */
+early_param("user_mode", early_parse_user_mode);
 
 #ifdef CONFIG_S390_EXEC_PROTECT
-unsigned int s390_noexec = 0;
-EXPORT_SYMBOL_GPL(s390_noexec);
-
 /*
  * Enable execute protection?
  */
@@ -364,8 +369,7 @@ static int __init early_parse_noexec(char *p)
 {
        if (!strncmp(p, "off", 3))
                return 0;
-       switch_amode = 1;
-       s390_noexec = 1;
+       user_mode = SECONDARY_SPACE_MODE;
        return 0;
 }
 early_param("noexec", early_parse_noexec);
@@ -373,7 +377,7 @@ early_param("noexec", early_parse_noexec);
 
 static void setup_addressing_mode(void)
 {
-       if (s390_noexec) {
+       if (user_mode == SECONDARY_SPACE_MODE) {
                if (set_amode_and_uaccess(PSW_ASC_SECONDARY,
                                          PSW32_ASC_SECONDARY))
                        pr_info("Execute protection active, "
@@ -381,7 +385,7 @@ static void setup_addressing_mode(void)
                else
                        pr_info("Execute protection active, "
                                "mvcos not available\n");
-       } else if (switch_amode) {
+       } else if (user_mode == PRIMARY_SPACE_MODE) {
                if (set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY))
                        pr_info("Address spaces switched, "
                                "mvcos available\n");
@@ -411,7 +415,7 @@ setup_lowcore(void)
        lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
        lc->restart_psw.addr =
                PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
-       if (switch_amode)
+       if (user_mode != HOME_SPACE_MODE)
                lc->restart_psw.mask |= PSW_ASC_HOME;
        lc->external_new_psw.mask = psw_kernel_bits;
        lc->external_new_psw.addr =
index 68e1ecf..65065ac 100644 (file)
@@ -335,7 +335,7 @@ int get_sync_clock(unsigned long long *clock)
        sw0 = atomic_read(sw_ptr);
        *clock = get_clock();
        sw1 = atomic_read(sw_ptr);
-       put_cpu_var(clock_sync_sync);
+       put_cpu_var(clock_sync_word);
        if (sw0 == sw1 && (sw0 & 0x80000000U))
                /* Success: time is in sync. */
                return 0;
@@ -385,7 +385,7 @@ static inline int check_sync_clock(void)
 
        sw_ptr = &get_cpu_var(clock_sync_word);
        rc = (atomic_read(sw_ptr) & 0x80000000U) != 0;
-       put_cpu_var(clock_sync_sync);
+       put_cpu_var(clock_sync_word);
        return rc;
 }
 
index adfb32a..5f99e66 100644 (file)
@@ -86,7 +86,8 @@ static void vdso_init_data(struct vdso_data *vd)
        unsigned int facility_list;
 
        facility_list = stfl();
-       vd->ectg_available = switch_amode && (facility_list & 1);
+       vd->ectg_available =
+               user_mode != HOME_SPACE_MODE && (facility_list & 1);
 }
 
 #ifdef CONFIG_64BIT
@@ -114,7 +115,7 @@ int vdso_alloc_per_cpu(int cpu, struct _lowcore *lowcore)
 
        lowcore->vdso_per_cpu_data = __LC_PASTE;
 
-       if (!switch_amode || !vdso_enabled)
+       if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
                return 0;
 
        segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
@@ -160,7 +161,7 @@ void vdso_free_per_cpu(int cpu, struct _lowcore *lowcore)
        unsigned long segment_table, page_table, page_frame;
        u32 *psal, *aste;
 
-       if (!switch_amode || !vdso_enabled)
+       if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
                return;
 
        psal = (u32 *)(addr_t) lowcore->paste[4];
@@ -184,7 +185,7 @@ static void __vdso_init_cr5(void *dummy)
 
 static void vdso_init_cr5(void)
 {
-       if (switch_amode && vdso_enabled)
+       if (user_mode != HOME_SPACE_MODE && vdso_enabled)
                on_each_cpu(__vdso_init_cr5, NULL, 1);
 }
 #endif /* CONFIG_64BIT */
index bf164fc..6ee55ae 100644 (file)
@@ -20,7 +20,6 @@ config KVM
        depends on HAVE_KVM && EXPERIMENTAL
        select PREEMPT_NOTIFIERS
        select ANON_INODES
-       select S390_SWITCH_AMODE
        ---help---
          Support hosting paravirtualized guest machines using the SIE
          virtualization capability on the mainframe. This should work
index 58da3f4..60455f1 100644 (file)
@@ -162,7 +162,6 @@ static size_t clear_user_mvcos(size_t size, void __user *to)
        return size;
 }
 
-#ifdef CONFIG_S390_SWITCH_AMODE
 static size_t strnlen_user_mvcos(size_t count, const char __user *src)
 {
        char buf[256];
@@ -200,7 +199,6 @@ static size_t strncpy_from_user_mvcos(size_t count, const char __user *src,
        } while ((len_str == len) && (done < count));
        return done;
 }
-#endif /* CONFIG_S390_SWITCH_AMODE */
 
 struct uaccess_ops uaccess_mvcos = {
        .copy_from_user = copy_from_user_mvcos_check,
@@ -215,7 +213,6 @@ struct uaccess_ops uaccess_mvcos = {
        .futex_atomic_cmpxchg = futex_atomic_cmpxchg_std,
 };
 
-#ifdef CONFIG_S390_SWITCH_AMODE
 struct uaccess_ops uaccess_mvcos_switch = {
        .copy_from_user = copy_from_user_mvcos,
        .copy_from_user_small = copy_from_user_mvcos,
@@ -228,4 +225,3 @@ struct uaccess_ops uaccess_mvcos_switch = {
        .futex_atomic_op = futex_atomic_op_pt,
        .futex_atomic_cmpxchg = futex_atomic_cmpxchg_pt,
 };
-#endif
index cb5d59e..404f2de 100644 (file)
@@ -23,86 +23,21 @@ static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr)
 
        pgd = pgd_offset(mm, addr);
        if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
-               return NULL;
+               return (pte_t *) 0x3a;
 
        pud = pud_offset(pgd, addr);
        if (pud_none(*pud) || unlikely(pud_bad(*pud)))
-               return NULL;
+               return (pte_t *) 0x3b;
 
        pmd = pmd_offset(pud, addr);
        if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
-               return NULL;
+               return (pte_t *) 0x10;
 
        return pte_offset_map(pmd, addr);
 }
 
-static int __handle_fault(struct mm_struct *mm, unsigned long address,
-                         int write_access)
-{
-       struct vm_area_struct *vma;
-       int ret = -EFAULT;
-       int fault;
-
-       if (in_atomic())
-               return ret;
-       down_read(&mm->mmap_sem);
-       vma = find_vma(mm, address);
-       if (unlikely(!vma))
-               goto out;
-       if (unlikely(vma->vm_start > address)) {
-               if (!(vma->vm_flags & VM_GROWSDOWN))
-                       goto out;
-               if (expand_stack(vma, address))
-                       goto out;
-       }
-
-       if (!write_access) {
-               /* page not present, check vm flags */
-               if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
-                       goto out;
-       } else {
-               if (!(vma->vm_flags & VM_WRITE))
-                       goto out;
-       }
-
-survive:
-       fault = handle_mm_fault(mm, vma, address, write_access ? FAULT_FLAG_WRITE : 0);
-       if (unlikely(fault & VM_FAULT_ERROR)) {
-               if (fault & VM_FAULT_OOM)
-                       goto out_of_memory;
-               else if (fault & VM_FAULT_SIGBUS)
-                       goto out_sigbus;
-               BUG();
-       }
-       if (fault & VM_FAULT_MAJOR)
-               current->maj_flt++;
-       else
-               current->min_flt++;
-       ret = 0;
-out:
-       up_read(&mm->mmap_sem);
-       return ret;
-
-out_of_memory:
-       up_read(&mm->mmap_sem);
-       if (is_global_init(current)) {
-               yield();
-               down_read(&mm->mmap_sem);
-               goto survive;
-       }
-       printk("VM: killing process %s\n", current->comm);
-       return ret;
-
-out_sigbus:
-       up_read(&mm->mmap_sem);
-       current->thread.prot_addr = address;
-       current->thread.trap_no = 0x11;
-       force_sig(SIGBUS, current);
-       return ret;
-}
-
-static size_t __user_copy_pt(unsigned long uaddr, void *kptr,
-                            size_t n, int write_user)
+static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
+                                            size_t n, int write_user)
 {
        struct mm_struct *mm = current->mm;
        unsigned long offset, pfn, done, size;
@@ -114,12 +49,17 @@ retry:
        spin_lock(&mm->page_table_lock);
        do {
                pte = follow_table(mm, uaddr);
-               if (!pte || !pte_present(*pte) ||
-                   (write_user && !pte_write(*pte)))
+               if ((unsigned long) pte < 0x1000)
                        goto fault;
+               if (!pte_present(*pte)) {
+                       pte = (pte_t *) 0x11;
+                       goto fault;
+               } else if (write_user && !pte_write(*pte)) {
+                       pte = (pte_t *) 0x04;
+                       goto fault;
+               }
 
                pfn = pte_pfn(*pte);
-
                offset = uaddr & (PAGE_SIZE - 1);
                size = min(n - done, PAGE_SIZE - offset);
                if (write_user) {
@@ -137,7 +77,7 @@ retry:
        return n - done;
 fault:
        spin_unlock(&mm->page_table_lock);
-       if (__handle_fault(mm, uaddr, write_user))
+       if (__handle_fault(uaddr, (unsigned long) pte, write_user))
                return n - done;
        goto retry;
 }
@@ -146,30 +86,31 @@ fault:
  * Do DAT for user address by page table walk, return kernel address.
  * This function needs to be called with current->mm->page_table_lock held.
  */
-static unsigned long __dat_user_addr(unsigned long uaddr)
+static __always_inline unsigned long __dat_user_addr(unsigned long uaddr)
 {
        struct mm_struct *mm = current->mm;
-       unsigned long pfn, ret;
+       unsigned long pfn;
        pte_t *pte;
        int rc;
 
-       ret = 0;
 retry:
        pte = follow_table(mm, uaddr);
-       if (!pte || !pte_present(*pte))
+       if ((unsigned long) pte < 0x1000)
                goto fault;
+       if (!pte_present(*pte)) {
+               pte = (pte_t *) 0x11;
+               goto fault;
+       }
 
        pfn = pte_pfn(*pte);
-       ret = (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
-out:
-       return ret;
+       return (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
 fault:
        spin_unlock(&mm->page_table_lock);
-       rc = __handle_fault(mm, uaddr, 0);
+       rc = __handle_fault(uaddr, (unsigned long) pte, 0);
        spin_lock(&mm->page_table_lock);
-       if (rc)
-               goto out;
-       goto retry;
+       if (!rc)
+               goto retry;
+       return 0;
 }
 
 size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
@@ -234,8 +175,12 @@ retry:
        spin_lock(&mm->page_table_lock);
        do {
                pte = follow_table(mm, uaddr);
-               if (!pte || !pte_present(*pte))
+               if ((unsigned long) pte < 0x1000)
+                       goto fault;
+               if (!pte_present(*pte)) {
+                       pte = (pte_t *) 0x11;
                        goto fault;
+               }
 
                pfn = pte_pfn(*pte);
                offset = uaddr & (PAGE_SIZE-1);
@@ -249,9 +194,8 @@ retry:
        return done + 1;
 fault:
        spin_unlock(&mm->page_table_lock);
-       if (__handle_fault(mm, uaddr, 0)) {
+       if (__handle_fault(uaddr, (unsigned long) pte, 0))
                return 0;
-       }
        goto retry;
 }
 
@@ -284,7 +228,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
 {
        struct mm_struct *mm = current->mm;
        unsigned long offset_from, offset_to, offset_max, pfn_from, pfn_to,
-                     uaddr, done, size;
+                     uaddr, done, size, error_code;
        unsigned long uaddr_from = (unsigned long) from;
        unsigned long uaddr_to = (unsigned long) to;
        pte_t *pte_from, *pte_to;
@@ -298,17 +242,28 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
 retry:
        spin_lock(&mm->page_table_lock);
        do {
+               write_user = 0;
+               uaddr = uaddr_from;
                pte_from = follow_table(mm, uaddr_from);
-               if (!pte_from || !pte_present(*pte_from)) {
-                       uaddr = uaddr_from;
-                       write_user = 0;
+               error_code = (unsigned long) pte_from;
+               if (error_code < 0x1000)
+                       goto fault;
+               if (!pte_present(*pte_from)) {
+                       error_code = 0x11;
                        goto fault;
                }
 
+               write_user = 1;
+               uaddr = uaddr_to;
                pte_to = follow_table(mm, uaddr_to);
-               if (!pte_to || !pte_present(*pte_to) || !pte_write(*pte_to)) {
-                       uaddr = uaddr_to;
-                       write_user = 1;
+               error_code = (unsigned long) pte_to;
+               if (error_code < 0x1000)
+                       goto fault;
+               if (!pte_present(*pte_to)) {
+                       error_code = 0x11;
+                       goto fault;
+               } else if (!pte_write(*pte_to)) {
+                       error_code = 0x04;
                        goto fault;
                }
 
@@ -329,7 +284,7 @@ retry:
        return n - done;
 fault:
        spin_unlock(&mm->page_table_lock);
-       if (__handle_fault(mm, uaddr, write_user))
+       if (__handle_fault(uaddr, error_code, write_user))
                return n - done;
        goto retry;
 }
index 3ee78cc..cd4e9c1 100644 (file)
@@ -2088,7 +2088,7 @@ int math_emu_ldr(__u8 *opcode) {
         __u16 opc = *((__u16 *) opcode);
 
         if ((opc & 0x90) == 0) {           /* test if rx in {0,2,4,6} */
-                /* we got an exception therfore ry can't be in {0,2,4,6} */
+                /* we got an exception therefore ry can't be in {0,2,4,6} */
                asm volatile(           /* load rx from fp_regs.fprs[ry] */
                        "       bras    1,0f\n"
                        "       ld      0,0(%1)\n"
@@ -2118,7 +2118,7 @@ int math_emu_ler(__u8 *opcode) {
         __u16 opc = *((__u16 *) opcode);
 
         if ((opc & 0x90) == 0) {           /* test if rx in {0,2,4,6} */
-                /* we got an exception therfore ry can't be in {0,2,4,6} */
+                /* we got an exception therefore ry can't be in {0,2,4,6} */
                asm volatile(           /* load rx from fp_regs.fprs[ry] */
                        "       bras    1,0f\n"
                        "       le      0,0(%1)\n"
index ff58779..76a3637 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/swap.h>
 #include <linux/kthread.h>
 #include <linux/oom.h>
+#include <linux/suspend.h>
 
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
@@ -44,6 +45,7 @@ static volatile long cmm_pages_target;
 static volatile long cmm_timed_pages_target;
 static long cmm_timeout_pages;
 static long cmm_timeout_seconds;
+static int cmm_suspended;
 
 static struct cmm_page_array *cmm_page_list;
 static struct cmm_page_array *cmm_timed_page_list;
@@ -147,9 +149,9 @@ cmm_thread(void *dummy)
 
        while (1) {
                rc = wait_event_interruptible(cmm_thread_wait,
-                       (cmm_pages != cmm_pages_target ||
-                        cmm_timed_pages != cmm_timed_pages_target ||
-                        kthread_should_stop()));
+                       (!cmm_suspended && (cmm_pages != cmm_pages_target ||
+                        cmm_timed_pages != cmm_timed_pages_target)) ||
+                        kthread_should_stop());
                if (kthread_should_stop() || rc == -ERESTARTSYS) {
                        cmm_pages_target = cmm_pages;
                        cmm_timed_pages_target = cmm_timed_pages;
@@ -410,6 +412,38 @@ cmm_smsg_target(char *from, char *msg)
 
 static struct ctl_table_header *cmm_sysctl_header;
 
+static int cmm_suspend(void)
+{
+       cmm_suspended = 1;
+       cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
+       cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
+       return 0;
+}
+
+static int cmm_resume(void)
+{
+       cmm_suspended = 0;
+       cmm_kick_thread();
+       return 0;
+}
+
+static int cmm_power_event(struct notifier_block *this,
+                          unsigned long event, void *ptr)
+{
+       switch (event) {
+       case PM_POST_HIBERNATION:
+               return cmm_resume();
+       case PM_HIBERNATION_PREPARE:
+               return cmm_suspend();
+       default:
+               return NOTIFY_DONE;
+       }
+}
+
+static struct notifier_block cmm_power_notifier = {
+       .notifier_call = cmm_power_event,
+};
+
 static int
 cmm_init (void)
 {
@@ -418,7 +452,7 @@ cmm_init (void)
 #ifdef CONFIG_CMM_PROC
        cmm_sysctl_header = register_sysctl_table(cmm_dir_table);
        if (!cmm_sysctl_header)
-               goto out;
+               goto out_sysctl;
 #endif
 #ifdef CONFIG_CMM_IUCV
        rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
@@ -428,17 +462,21 @@ cmm_init (void)
        rc = register_oom_notifier(&cmm_oom_nb);
        if (rc < 0)
                goto out_oom_notify;
+       rc = register_pm_notifier(&cmm_power_notifier);
+       if (rc)
+               goto out_pm;
        init_waitqueue_head(&cmm_thread_wait);
        init_timer(&cmm_timer);
        cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
        rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0;
-       if (!rc)
-               goto out;
-       /*
-        * kthread_create failed. undo all the stuff from above again.
-        */
-       unregister_oom_notifier(&cmm_oom_nb);
+       if (rc)
+               goto out_kthread;
+       return 0;
 
+out_kthread:
+       unregister_pm_notifier(&cmm_power_notifier);
+out_pm:
+       unregister_oom_notifier(&cmm_oom_nb);
 out_oom_notify:
 #ifdef CONFIG_CMM_IUCV
        smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
@@ -446,8 +484,8 @@ out_smsg:
 #endif
 #ifdef CONFIG_CMM_PROC
        unregister_sysctl_table(cmm_sysctl_header);
+out_sysctl:
 #endif
-out:
        return rc;
 }
 
@@ -455,6 +493,7 @@ static void
 cmm_exit(void)
 {
        kthread_stop(cmm_thread_ptr);
+       unregister_pm_notifier(&cmm_power_notifier);
        unregister_oom_notifier(&cmm_oom_nb);
        cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
        cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
index 6d50746..fc102e7 100644 (file)
 #include <asm/pgtable.h>
 #include <asm/s390_ext.h>
 #include <asm/mmu_context.h>
+#include <asm/compat.h>
 #include "../kernel/entry.h"
 
 #ifndef CONFIG_64BIT
 #define __FAIL_ADDR_MASK 0x7ffff000
-#define __FIXUP_MASK 0x7fffffff
 #define __SUBCODE_MASK 0x0200
 #define __PF_RES_FIELD 0ULL
 #else /* CONFIG_64BIT */
 #define __FAIL_ADDR_MASK -4096L
-#define __FIXUP_MASK ~0L
 #define __SUBCODE_MASK 0x0600
 #define __PF_RES_FIELD 0x8000000000000000ULL
 #endif /* CONFIG_64BIT */
 extern int sysctl_userprocess_debug;
 #endif
 
-#ifdef CONFIG_KPROBES
-static inline int notify_page_fault(struct pt_regs *regs, long err)
+#define VM_FAULT_BADCONTEXT    0x010000
+#define VM_FAULT_BADMAP                0x020000
+#define VM_FAULT_BADACCESS     0x040000
+
+static inline int notify_page_fault(struct pt_regs *regs)
 {
        int ret = 0;
 
+#ifdef CONFIG_KPROBES
        /* kprobe_running() needs smp_processor_id() */
        if (!user_mode(regs)) {
                preempt_disable();
@@ -64,15 +67,9 @@ static inline int notify_page_fault(struct pt_regs *regs, long err)
                        ret = 1;
                preempt_enable();
        }
-
+#endif
        return ret;
 }
-#else
-static inline int notify_page_fault(struct pt_regs *regs, long err)
-{
-       return 0;
-}
-#endif
 
 
 /*
@@ -100,57 +97,50 @@ void bust_spinlocks(int yes)
 
 /*
  * Returns the address space associated with the fault.
- * Returns 0 for kernel space, 1 for user space and
- * 2 for code execution in user space with noexec=on.
+ * Returns 0 for kernel space and 1 for user space.
  */
-static inline int check_space(struct task_struct *tsk)
+static inline int user_space_fault(unsigned long trans_exc_code)
 {
        /*
-        * The lowest two bits of S390_lowcore.trans_exc_code
-        * indicate which paging table was used.
+        * The lowest two bits of the translation exception
+        * identification indicate which paging table was used.
         */
-       int desc = S390_lowcore.trans_exc_code & 3;
-
-       if (desc == 3)  /* Home Segment Table Descriptor */
-               return switch_amode == 0;
-       if (desc == 2)  /* Secondary Segment Table Descriptor */
-               return tsk->thread.mm_segment.ar4;
-#ifdef CONFIG_S390_SWITCH_AMODE
-       if (unlikely(desc == 1)) { /* STD determined via access register */
-               /* %a0 always indicates primary space. */
-               if (S390_lowcore.exc_access_id != 0) {
-                       save_access_regs(tsk->thread.acrs);
-                       /*
-                        * An alet of 0 indicates primary space.
-                        * An alet of 1 indicates secondary space.
-                        * Any other alet values generate an
-                        * alen-translation exception.
-                        */
-                       if (tsk->thread.acrs[S390_lowcore.exc_access_id])
-                               return tsk->thread.mm_segment.ar4;
-               }
-       }
-#endif
-       /* Primary Segment Table Descriptor */
-       return switch_amode << s390_noexec;
+       trans_exc_code &= 3;
+       if (trans_exc_code == 2)
+               /* Access via secondary space, set_fs setting decides */
+               return current->thread.mm_segment.ar4;
+       if (user_mode == HOME_SPACE_MODE)
+               /* User space if the access has been done via home space. */
+               return trans_exc_code == 3;
+       /*
+        * If the user space is not the home space the kernel runs in home
+        * space. Access via secondary space has already been covered,
+        * access via primary space or access register is from user space
+        * and access via home space is from the kernel.
+        */
+       return trans_exc_code != 3;
 }
 
 /*
  * Send SIGSEGV to task.  This is an external routine
  * to keep the stack usage of do_page_fault small.
  */
-static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
-                      int si_code, unsigned long address)
+static noinline void do_sigsegv(struct pt_regs *regs, long int_code,
+                               int si_code, unsigned long trans_exc_code)
 {
        struct siginfo si;
+       unsigned long address;
 
+       address = trans_exc_code & __FAIL_ADDR_MASK;
+       current->thread.prot_addr = address;
+       current->thread.trap_no = int_code;
 #if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG)
 #if defined(CONFIG_SYSCTL)
        if (sysctl_userprocess_debug)
 #endif
        {
                printk("User process fault: interruption code 0x%lX\n",
-                      error_code);
+                      int_code);
                printk("failing address: %lX\n", address);
                show_regs(regs);
        }
@@ -161,13 +151,14 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
        force_sig_info(SIGSEGV, &si, current);
 }
 
-static void do_no_context(struct pt_regs *regs, unsigned long error_code,
-                         unsigned long address)
+static noinline void do_no_context(struct pt_regs *regs, long int_code,
+                                  unsigned long trans_exc_code)
 {
        const struct exception_table_entry *fixup;
+       unsigned long address;
 
        /* Are we prepared to handle this kernel fault?  */
-       fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK);
+       fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
        if (fixup) {
                regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
                return;
@@ -177,129 +168,149 @@ static void do_no_context(struct pt_regs *regs, unsigned long error_code,
         * Oops. The kernel tried to access some bad page. We'll have to
         * terminate things with extreme prejudice.
         */
-       if (check_space(current) == 0)
+       address = trans_exc_code & __FAIL_ADDR_MASK;
+       if (!user_space_fault(trans_exc_code))
                printk(KERN_ALERT "Unable to handle kernel pointer dereference"
                       " at virtual kernel address %p\n", (void *)address);
        else
                printk(KERN_ALERT "Unable to handle kernel paging request"
                       " at virtual user address %p\n", (void *)address);
 
-       die("Oops", regs, error_code);
+       die("Oops", regs, int_code);
        do_exit(SIGKILL);
 }
 
-static void do_low_address(struct pt_regs *regs, unsigned long error_code)
+static noinline void do_low_address(struct pt_regs *regs, long int_code,
+                                   unsigned long trans_exc_code)
 {
        /* Low-address protection hit in kernel mode means
           NULL pointer write access in kernel mode.  */
        if (regs->psw.mask & PSW_MASK_PSTATE) {
                /* Low-address protection hit in user mode 'cannot happen'. */
-               die ("Low-address protection", regs, error_code);
+               die ("Low-address protection", regs, int_code);
                do_exit(SIGKILL);
        }
 
-       do_no_context(regs, error_code, 0);
+       do_no_context(regs, int_code, trans_exc_code);
 }
 
-static void do_sigbus(struct pt_regs *regs, unsigned long error_code,
-                     unsigned long address)
+static noinline void do_sigbus(struct pt_regs *regs, long int_code,
+                              unsigned long trans_exc_code)
 {
        struct task_struct *tsk = current;
-       struct mm_struct *mm = tsk->mm;
 
-       up_read(&mm->mmap_sem);
        /*
         * Send a sigbus, regardless of whether we were in kernel
         * or user mode.
         */
-       tsk->thread.prot_addr = address;
-       tsk->thread.trap_no = error_code;
+       tsk->thread.prot_addr = trans_exc_code & __FAIL_ADDR_MASK;
+       tsk->thread.trap_no = int_code;
        force_sig(SIGBUS, tsk);
-
-       /* Kernel mode? Handle exceptions or die */
-       if (!(regs->psw.mask & PSW_MASK_PSTATE))
-               do_no_context(regs, error_code, address);
 }
 
 #ifdef CONFIG_S390_EXEC_PROTECT
-static int signal_return(struct mm_struct *mm, struct pt_regs *regs,
-                        unsigned long address, unsigned long error_code)
+static noinline int signal_return(struct pt_regs *regs, long int_code,
+                                 unsigned long trans_exc_code)
 {
        u16 instruction;
        int rc;
-#ifdef CONFIG_COMPAT
-       int compat;
-#endif
 
-       pagefault_disable();
        rc = __get_user(instruction, (u16 __user *) regs->psw.addr);
-       pagefault_enable();
-       if (rc)
-               return -EFAULT;
 
-       up_read(&mm->mmap_sem);
-       clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
-#ifdef CONFIG_COMPAT
-       compat = is_compat_task();
-       if (compat && instruction == 0x0a77)
-               sys32_sigreturn();
-       else if (compat && instruction == 0x0aad)
-               sys32_rt_sigreturn();
-       else
-#endif
-       if (instruction == 0x0a77)
-               sys_sigreturn();
-       else if (instruction == 0x0aad)
-               sys_rt_sigreturn();
-       else {
-               current->thread.prot_addr = address;
-               current->thread.trap_no = error_code;
-               do_sigsegv(regs, error_code, SEGV_MAPERR, address);
-       }
+       if (!rc && instruction == 0x0a77) {
+               clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
+               if (is_compat_task())
+                       sys32_sigreturn();
+               else
+                       sys_sigreturn();
+       } else if (!rc && instruction == 0x0aad) {
+               clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
+               if (is_compat_task())
+                       sys32_rt_sigreturn();
+               else
+                       sys_rt_sigreturn();
+       } else
+               do_sigsegv(regs, int_code, SEGV_MAPERR, trans_exc_code);
        return 0;
 }
 #endif /* CONFIG_S390_EXEC_PROTECT */
 
+static noinline void do_fault_error(struct pt_regs *regs, long int_code,
+                                   unsigned long trans_exc_code, int fault)
+{
+       int si_code;
+
+       switch (fault) {
+       case VM_FAULT_BADACCESS:
+#ifdef CONFIG_S390_EXEC_PROTECT
+               if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
+                   (trans_exc_code & 3) == 0) {
+                       signal_return(regs, int_code, trans_exc_code);
+                       break;
+               }
+#endif /* CONFIG_S390_EXEC_PROTECT */
+       case VM_FAULT_BADMAP:
+               /* Bad memory access. Check if it is kernel or user space. */
+               if (regs->psw.mask & PSW_MASK_PSTATE) {
+                       /* User mode accesses just cause a SIGSEGV */
+                       si_code = (fault == VM_FAULT_BADMAP) ?
+                               SEGV_MAPERR : SEGV_ACCERR;
+                       do_sigsegv(regs, int_code, si_code, trans_exc_code);
+                       return;
+               }
+       case VM_FAULT_BADCONTEXT:
+               do_no_context(regs, int_code, trans_exc_code);
+               break;
+       default: /* fault & VM_FAULT_ERROR */
+               if (fault & VM_FAULT_OOM)
+                       pagefault_out_of_memory();
+               else if (fault & VM_FAULT_SIGBUS) {
+                       do_sigbus(regs, int_code, trans_exc_code);
+                       /* Kernel mode? Handle exceptions or die */
+                       if (!(regs->psw.mask & PSW_MASK_PSTATE))
+                               do_no_context(regs, int_code, trans_exc_code);
+               } else
+                       BUG();
+               break;
+       }
+}
+
 /*
  * This routine handles page faults.  It determines the address,
  * and the problem, and then passes it off to one of the appropriate
  * routines.
  *
- * error_code:
+ * interruption code (int_code):
  *   04       Protection           ->  Write-Protection  (suprression)
  *   10       Segment translation  ->  Not present       (nullification)
  *   11       Page translation     ->  Not present       (nullification)
  *   3b       Region third trans.  ->  Not present       (nullification)
  */
-static inline void
-do_exception(struct pt_regs *regs, unsigned long error_code, int write)
+static inline int do_exception(struct pt_regs *regs, int access,
+                              unsigned long trans_exc_code)
 {
        struct task_struct *tsk;
        struct mm_struct *mm;
        struct vm_area_struct *vma;
        unsigned long address;
-       int space;
-       int si_code;
        int fault;
 
-       if (notify_page_fault(regs, error_code))
-               return;
+       if (notify_page_fault(regs))
+               return 0;
 
        tsk = current;
        mm = tsk->mm;
 
-       /* get the failing address and the affected space */
-       address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
-       space = check_space(tsk);
-
        /*
         * Verify that the fault happened in user space, that
         * we are not in an interrupt and that there is a 
         * user context.
         */
-       if (unlikely(space == 0 || in_atomic() || !mm))
-               goto no_context;
+       fault = VM_FAULT_BADCONTEXT;
+       if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
+               goto out;
 
+       address = trans_exc_code & __FAIL_ADDR_MASK;
        /*
         * When we get here, the fault happened in the current
         * task's user address space, so we can switch on the
@@ -309,42 +320,26 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int write)
        perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
        down_read(&mm->mmap_sem);
 
-       si_code = SEGV_MAPERR;
+       fault = VM_FAULT_BADMAP;
        vma = find_vma(mm, address);
        if (!vma)
-               goto bad_area;
-
-#ifdef CONFIG_S390_EXEC_PROTECT
-       if (unlikely((space == 2) && !(vma->vm_flags & VM_EXEC)))
-               if (!signal_return(mm, regs, address, error_code))
-                       /*
-                        * signal_return() has done an up_read(&mm->mmap_sem)
-                        * if it returns 0.
-                        */
-                       return;
-#endif
+               goto out_up;
 
-       if (vma->vm_start <= address)
-               goto good_area;
-       if (!(vma->vm_flags & VM_GROWSDOWN))
-               goto bad_area;
-       if (expand_stack(vma, address))
-               goto bad_area;
-/*
- * Ok, we have a good vm_area for this memory access, so
- * we can handle it..
- */
-good_area:
-       si_code = SEGV_ACCERR;
-       if (!write) {
-               /* page not present, check vm flags */
-               if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
-                       goto bad_area;
-       } else {
-               if (!(vma->vm_flags & VM_WRITE))
-                       goto bad_area;
+       if (unlikely(vma->vm_start > address)) {
+               if (!(vma->vm_flags & VM_GROWSDOWN))
+                       goto out_up;
+               if (expand_stack(vma, address))
+                       goto out_up;
        }
 
+       /*
+        * Ok, we have a good vm_area for this memory access, so
+        * we can handle it..
+        */
+       fault = VM_FAULT_BADACCESS;
+       if (unlikely(!(vma->vm_flags & access)))
+               goto out_up;
+
        if (is_vm_hugetlb_page(vma))
                address &= HPAGE_MASK;
        /*
@@ -352,18 +347,11 @@ good_area:
         * make sure we exit gracefully rather than endlessly redo
         * the fault.
         */
-       fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
-       if (unlikely(fault & VM_FAULT_ERROR)) {
-               if (fault & VM_FAULT_OOM) {
-                       up_read(&mm->mmap_sem);
-                       pagefault_out_of_memory();
-                       return;
-               } else if (fault & VM_FAULT_SIGBUS) {
-                       do_sigbus(regs, error_code, address);
-                       return;
-               }
-               BUG();
-       }
+       fault = handle_mm_fault(mm, vma, address,
+                               (access == VM_WRITE) ? FAULT_FLAG_WRITE : 0);
+       if (unlikely(fault & VM_FAULT_ERROR))
+               goto out_up;
+
        if (fault & VM_FAULT_MAJOR) {
                tsk->maj_flt++;
                perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
@@ -373,74 +361,69 @@ good_area:
                perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
                                     regs, address);
        }
-        up_read(&mm->mmap_sem);
        /*
         * The instruction that caused the program check will
         * be repeated. Don't signal single step via SIGTRAP.
         */
        clear_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
-        return;
-
-/*
- * Something tried to access memory that isn't in our memory map..
- * Fix it, but check if it's kernel or user first..
- */
-bad_area:
+       fault = 0;
+out_up:
        up_read(&mm->mmap_sem);
-
-       /* User mode accesses just cause a SIGSEGV */
-       if (regs->psw.mask & PSW_MASK_PSTATE) {
-               tsk->thread.prot_addr = address;
-               tsk->thread.trap_no = error_code;
-               do_sigsegv(regs, error_code, si_code, address);
-               return;
-       }
-
-no_context:
-       do_no_context(regs, error_code, address);
+out:
+       return fault;
 }
 
-void __kprobes do_protection_exception(struct pt_regs *regs,
-                                      long error_code)
+void __kprobes do_protection_exception(struct pt_regs *regs, long int_code)
 {
+       unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
+       int fault;
+
        /* Protection exception is supressing, decrement psw address. */
-       regs->psw.addr -= (error_code >> 16);
+       regs->psw.addr -= (int_code >> 16);
        /*
         * Check for low-address protection.  This needs to be treated
         * as a special case because the translation exception code
         * field is not guaranteed to contain valid data in this case.
         */
-       if (unlikely(!(S390_lowcore.trans_exc_code & 4))) {
-               do_low_address(regs, error_code);
+       if (unlikely(!(trans_exc_code & 4))) {
+               do_low_address(regs, int_code, trans_exc_code);
                return;
        }
-       do_exception(regs, 4, 1);
+       fault = do_exception(regs, VM_WRITE, trans_exc_code);
+       if (unlikely(fault))
+               do_fault_error(regs, 4, trans_exc_code, fault);
 }
 
-void __kprobes do_dat_exception(struct pt_regs *regs, long error_code)
+void __kprobes do_dat_exception(struct pt_regs *regs, long int_code)
 {
-       do_exception(regs, error_code & 0xff, 0);
+       unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
+       int access, fault;
+
+       access = VM_READ | VM_EXEC | VM_WRITE;
+#ifdef CONFIG_S390_EXEC_PROTECT
+       if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
+           (trans_exc_code & 3) == 0)
+               access = VM_EXEC;
+#endif
+       fault = do_exception(regs, access, trans_exc_code);
+       if (unlikely(fault))
+               do_fault_error(regs, int_code & 255, trans_exc_code, fault);
 }
 
 #ifdef CONFIG_64BIT
-void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
+void __kprobes do_asce_exception(struct pt_regs *regs, long int_code)
 {
-       struct mm_struct *mm;
+       unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
+       struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
-       unsigned long address;
-       int space;
-
-       mm = current->mm;
-       address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
-       space = check_space(current);
 
-       if (unlikely(space == 0 || in_atomic() || !mm))
+       if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
                goto no_context;
 
        local_irq_enable();
 
        down_read(&mm->mmap_sem);
-       vma = find_vma(mm, address);
+       vma = find_vma(mm, trans_exc_code & __FAIL_ADDR_MASK);
        up_read(&mm->mmap_sem);
 
        if (vma) {
@@ -450,17 +433,38 @@ void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
 
        /* User mode accesses just cause a SIGSEGV */
        if (regs->psw.mask & PSW_MASK_PSTATE) {
-               current->thread.prot_addr = address;
-               current->thread.trap_no = error_code;
-               do_sigsegv(regs, error_code, SEGV_MAPERR, address);
+               do_sigsegv(regs, int_code, SEGV_MAPERR, trans_exc_code);
                return;
        }
 
 no_context:
-       do_no_context(regs, error_code, address);
+       do_no_context(regs, int_code, trans_exc_code);
 }
 #endif
 
+int __handle_fault(unsigned long uaddr, unsigned long int_code, int write_user)
+{
+       struct pt_regs regs;
+       int access, fault;
+
+       regs.psw.mask = psw_kernel_bits;
+       if (!irqs_disabled())
+               regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT;
+       regs.psw.addr = (unsigned long) __builtin_return_address(0);
+       regs.psw.addr |= PSW_ADDR_AMODE;
+       uaddr &= PAGE_MASK;
+       access = write_user ? VM_WRITE : VM_READ;
+       fault = do_exception(&regs, access, uaddr | 2);
+       if (unlikely(fault)) {
+               if (fault & VM_FAULT_OOM) {
+                       pagefault_out_of_memory();
+                       fault = 0;
+               } else if (fault & VM_FAULT_SIGBUS)
+                       do_sigbus(&regs, int_code, uaddr);
+       }
+       return fault ? -EFAULT : 0;
+}
+
 #ifdef CONFIG_PFAULT 
 /*
  * 'pfault' pseudo page faults routines.
@@ -522,7 +526,7 @@ void pfault_fini(void)
                : : "a" (&refbk), "m" (refbk) : "cc");
 }
 
-static void pfault_interrupt(__u16 error_code)
+static void pfault_interrupt(__u16 int_code)
 {
        struct task_struct *tsk;
        __u16 subcode;
index 2757c56..ad621e0 100644 (file)
@@ -269,7 +269,7 @@ int s390_enable_sie(void)
        struct mm_struct *mm, *old_mm;
 
        /* Do we have switched amode? If no, we cannot do sie */
-       if (!switch_amode)
+       if (user_mode == HOME_SPACE_MODE)
                return -EINVAL;
 
        /* Do we have pgstes? if yes, we are done */
index 5f91a38..300ab01 100644 (file)
@@ -70,8 +70,12 @@ static pte_t __ref *vmem_pte_alloc(void)
                pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t));
        if (!pte)
                return NULL;
-       clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
-                   PTRS_PER_PTE * sizeof(pte_t));
+       if (MACHINE_HAS_HPAGE)
+               clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY | _PAGE_CO,
+                           PTRS_PER_PTE * sizeof(pte_t));
+       else
+               clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
+                           PTRS_PER_PTE * sizeof(pte_t));
        return pte;
 }
 
@@ -112,7 +116,8 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
                if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
                    (address + HPAGE_SIZE <= start + size) &&
                    (address >= HPAGE_SIZE)) {
-                       pte_val(pte) |= _SEGMENT_ENTRY_LARGE;
+                       pte_val(pte) |= _SEGMENT_ENTRY_LARGE |
+                                       _SEGMENT_ENTRY_CO;
                        pmd_val(*pm_dir) = pte_val(pte);
                        address += HPAGE_SIZE - PAGE_SIZE;
                        continue;
index 88cdeb9..0031a69 100644 (file)
@@ -16,7 +16,9 @@ config SUPERH
        select HAVE_IOREMAP_PROT if MMU
        select HAVE_ARCH_TRACEHOOK
        select HAVE_DMA_API_DEBUG
+       select HAVE_DMA_ATTRS
        select HAVE_PERF_EVENTS
+       select PERF_USE_VMALLOC
        select HAVE_KERNEL_GZIP
        select HAVE_KERNEL_BZIP2
        select HAVE_KERNEL_LZMA
@@ -37,6 +39,7 @@ config SUPERH32
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_DYNAMIC_FTRACE
        select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+       select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE
        select HAVE_FUNCTION_GRAPH_TRACER
        select HAVE_ARCH_KGDB
        select ARCH_HIBERNATION_POSSIBLE if MMU
@@ -170,6 +173,12 @@ config ARCH_HAS_CPU_IDLE_WAIT
 config IO_TRAPPED
        bool
 
+config DMA_COHERENT
+       bool
+
+config DMA_NONCOHERENT
+       def_bool !DMA_COHERENT
+
 source "init/Kconfig"
 
 source "kernel/Kconfig.freezer"
@@ -220,6 +229,7 @@ config CPU_SHX2
 
 config CPU_SHX3
        bool
+       select DMA_COHERENT
 
 config ARCH_SHMOBILE
        bool
@@ -761,17 +771,6 @@ config ENTRY_OFFSET
        default "0x00010000" if PAGE_SIZE_64KB
        default "0x00000000"
 
-config UBC_WAKEUP
-       bool "Wakeup UBC on startup"
-       depends on CPU_SH4 && !CPU_SH4A
-       help
-         Selecting this option will wakeup the User Break Controller (UBC) on
-         startup. Although the UBC is left in an awake state when the processor
-         comes up, some boot loaders misbehave by putting the UBC to sleep in a
-         power saving state, which causes issues with things like ptrace().
-
-         If unsure, say N.
-
 choice
        prompt "Kernel command line"
        optional
@@ -818,7 +817,13 @@ config MAPLE
         Dreamcast with a serial line terminal or a remote network
         connection.
 
-source "arch/sh/drivers/pci/Kconfig"
+config PCI
+       bool "PCI support"
+       depends on SYS_SUPPORTS_PCI
+       help
+         Find out whether you have a PCI motherboard. PCI is the name of a
+         bus system, i.e. the way the CPU talks to the other stuff inside
+         your box. If you have PCI, say Y, otherwise N.
 
 source "drivers/pci/pcie/Kconfig"
 
index 66e40aa..ac17c5a 100644 (file)
@@ -78,6 +78,9 @@ defaultimage-$(CONFIG_SUPERH32)                       := zImage
 defaultimage-$(CONFIG_SH_SH7785LCR)            := uImage
 defaultimage-$(CONFIG_SH_RSK)                  := uImage
 defaultimage-$(CONFIG_SH_URQUELL)              := uImage
+defaultimage-$(CONFIG_SH_MIGOR)                        := uImage
+defaultimage-$(CONFIG_SH_AP325RXA)             := uImage
+defaultimage-$(CONFIG_SH_7724_SOLUTION_ENGINE) := uImage
 defaultimage-$(CONFIG_SH_7206_SOLUTION_ENGINE) := vmlinux
 defaultimage-$(CONFIG_SH_7619_SOLUTION_ENGINE) := vmlinux
 
@@ -136,6 +139,7 @@ machdir-$(CONFIG_SH_7751_SYSTEMH)           += mach-systemh
 machdir-$(CONFIG_SH_EDOSK7705)                 += mach-edosk7705
 machdir-$(CONFIG_SH_HIGHLANDER)                        += mach-highlander
 machdir-$(CONFIG_SH_MIGOR)                     += mach-migor
+machdir-$(CONFIG_SH_AP325RXA)                  += mach-ap325rxa
 machdir-$(CONFIG_SH_KFR2R09)                   += mach-kfr2r09
 machdir-$(CONFIG_SH_ECOVEC)                    += mach-ecovec24
 machdir-$(CONFIG_SH_SDK7780)                   += mach-sdk7780
index 7baa210..ce0f263 100644 (file)
@@ -1,7 +1,6 @@
 #
 # Specific board support, not covered by a mach group.
 #
-obj-$(CONFIG_SH_AP325RXA)      += board-ap325rxa.o
 obj-$(CONFIG_SH_MAGIC_PANEL_R2)        += board-magicpanelr2.o
 obj-$(CONFIG_SH_SH7785LCR)     += board-sh7785lcr.o
 obj-$(CONFIG_SH_URQUELL)       += board-urquell.o
diff --git a/arch/sh/boards/mach-ap325rxa/Makefile b/arch/sh/boards/mach-ap325rxa/Makefile
new file mode 100644 (file)
index 0000000..4cf1774
--- /dev/null
@@ -0,0 +1,2 @@
+obj-y   := setup.o sdram.o
+
diff --git a/arch/sh/boards/mach-ap325rxa/sdram.S b/arch/sh/boards/mach-ap325rxa/sdram.S
new file mode 100644 (file)
index 0000000..db24fbe
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * AP325RXA sdram self/auto-refresh setup code
+ *
+ *  Copyright (C) 2009 Magnus Damm
+ *
+ * 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/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/suspend.h>
+#include <asm/romimage-macros.h>
+
+/* code to enter and leave self-refresh. must be self-contained.
+ * this code will be copied to on-chip memory and executed from there.
+ */
+       .balign 4
+ENTRY(ap325rxa_sdram_enter_start)
+
+       /* SBSC: disable power down and put in self-refresh mode */
+       mov.l   1f, r4
+       mov.l   2f, r1
+       mov.l   @r4, r2
+       or      r1, r2
+       mov.l   3f, r3
+       and     r3, r2
+       mov.l   r2, @r4
+
+       rts
+        nop
+
+       .balign 4
+1:     .long   0xfe400008 /* SDCR0 */
+2:     .long   0x00000400
+3:     .long   0xffff7fff
+ENTRY(ap325rxa_sdram_enter_end)
+
+       .balign 4
+ENTRY(ap325rxa_sdram_leave_start)
+
+       /* SBSC: set auto-refresh mode */
+       mov.l   1f, r4
+       mov.l   @r4, r0
+       mov.l   4f, r1
+       and     r1, r0
+       mov.l   r0, @r4
+       mov.l   6f, r4
+       mov.l   8f, r0
+       mov.l   @r4, r1
+       mov     #-1, r4
+       add     r4, r1
+       or      r1, r0
+       mov.l   7f, r1
+       mov.l   r0, @r1
+
+       rts
+        nop
+
+       .balign 4
+1:     .long   0xfe400008 /* SDCR0 */
+4:     .long   0xfffffbff
+6:     .long   0xfe40001c /* RTCOR */
+7:     .long   0xfe400018 /* RTCNT */
+8:     .long   0xa55a0000
+ENTRY(ap325rxa_sdram_leave_end)
similarity index 87%
rename from arch/sh/boards/board-ap325rxa.c
rename to arch/sh/boards/mach-ap325rxa/setup.c
index 2d08073..cf9dc12 100644 (file)
@@ -20,8 +20,6 @@
 #include <linux/i2c.h>
 #include <linux/smsc911x.h>
 #include <linux/gpio.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_gpio.h>
 #include <media/ov772x.h>
 #include <media/soc_camera.h>
 #include <media/soc_camera_platform.h>
@@ -29,6 +27,7 @@
 #include <video/sh_mobile_lcdc.h>
 #include <asm/io.h>
 #include <asm/clock.h>
+#include <asm/suspend.h>
 #include <cpu/sh7723.h>
 
 static struct smsc911x_platform_config smsc911x_config = {
@@ -409,17 +408,49 @@ static struct platform_device ceu_device = {
        },
 };
 
-struct spi_gpio_platform_data sdcard_cn3_platform_data = {
-       .sck = GPIO_PTD0,
-       .mosi = GPIO_PTD1,
-       .miso = GPIO_PTD2,
-       .num_chipselect = 1,
+static struct resource sdhi0_cn3_resources[] = {
+       [0] = {
+               .name   = "SDHI0",
+               .start  = 0x04ce0000,
+               .end    = 0x04ce01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 101,
+               .flags  = IORESOURCE_IRQ,
+       },
 };
 
-static struct platform_device sdcard_cn3_device = {
-       .name           = "spi_gpio",
-       .dev    = {
-               .platform_data  = &sdcard_cn3_platform_data,
+static struct platform_device sdhi0_cn3_device = {
+       .name           = "sh_mobile_sdhi",
+       .id             = 0, /* "sdhi0" clock */
+       .num_resources  = ARRAY_SIZE(sdhi0_cn3_resources),
+       .resource       = sdhi0_cn3_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI0,
+       },
+};
+
+static struct resource sdhi1_cn7_resources[] = {
+       [0] = {
+               .name   = "SDHI1",
+               .start  = 0x04cf0000,
+               .end    = 0x04cf01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 24,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device sdhi1_cn7_device = {
+       .name           = "sh_mobile_sdhi",
+       .id             = 1, /* "sdhi1" clock */
+       .num_resources  = ARRAY_SIZE(sdhi1_cn7_resources),
+       .resource       = sdhi1_cn7_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI1,
        },
 };
 
@@ -470,22 +501,26 @@ static struct platform_device *ap325rxa_devices[] __initdata = {
        &lcdc_device,
        &ceu_device,
        &nand_flash_device,
-       &sdcard_cn3_device,
+       &sdhi0_cn3_device,
+       &sdhi1_cn7_device,
        &ap325rxa_camera[0],
        &ap325rxa_camera[1],
 };
 
-static struct spi_board_info ap325rxa_spi_devices[] = {
-       {
-               .modalias = "mmc_spi",
-               .max_speed_hz = 5000000,
-               .chip_select = 0,
-               .controller_data = (void *) GPIO_PTD5,
-       },
-};
+extern char ap325rxa_sdram_enter_start;
+extern char ap325rxa_sdram_enter_end;
+extern char ap325rxa_sdram_leave_start;
+extern char ap325rxa_sdram_leave_end;
 
 static int __init ap325rxa_devices_setup(void)
 {
+       /* register board specific self-refresh code */
+       sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF,
+                                       &ap325rxa_sdram_enter_start,
+                                       &ap325rxa_sdram_enter_end,
+                                       &ap325rxa_sdram_leave_start,
+                                       &ap325rxa_sdram_leave_end);
+
        /* LD3 and LD4 LEDs */
        gpio_request(GPIO_PTX5, NULL); /* RUN */
        gpio_direction_output(GPIO_PTX5, 1);
@@ -578,12 +613,28 @@ static int __init ap325rxa_devices_setup(void)
 
        platform_resource_setup_memory(&ceu_device, "ceu", 4 << 20);
 
+       /* SDHI0 - CN3 - SD CARD */
+       gpio_request(GPIO_FN_SDHI0CD_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0WP_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0D3_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0D2_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0D1_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0D0_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0CMD_PTD, NULL);
+       gpio_request(GPIO_FN_SDHI0CLK_PTD, NULL);
+
+       /* SDHI1 - CN7 - MICRO SD CARD */
+       gpio_request(GPIO_FN_SDHI1CD, NULL);
+       gpio_request(GPIO_FN_SDHI1D3, NULL);
+       gpio_request(GPIO_FN_SDHI1D2, NULL);
+       gpio_request(GPIO_FN_SDHI1D1, NULL);
+       gpio_request(GPIO_FN_SDHI1D0, NULL);
+       gpio_request(GPIO_FN_SDHI1CMD, NULL);
+       gpio_request(GPIO_FN_SDHI1CLK, NULL);
+
        i2c_register_board_info(0, ap325rxa_i2c_devices,
                                ARRAY_SIZE(ap325rxa_i2c_devices));
 
-       spi_register_board_info(ap325rxa_spi_devices,
-                               ARRAY_SIZE(ap325rxa_spi_devices));
-
        return platform_add_devices(ap325rxa_devices,
                                ARRAY_SIZE(ap325rxa_devices));
 }
index 51f8521..e69bc82 100644 (file)
@@ -6,4 +6,4 @@
 # for more details.
 #
 
-obj-y   := setup.o
\ No newline at end of file
+obj-y   := setup.o sdram.o
\ No newline at end of file
diff --git a/arch/sh/boards/mach-ecovec24/sdram.S b/arch/sh/boards/mach-ecovec24/sdram.S
new file mode 100644 (file)
index 0000000..8334400
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Ecovec24 sdram self/auto-refresh setup code
+ *
+ *  Copyright (C) 2009 Magnus Damm
+ *
+ * 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/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/suspend.h>
+#include <asm/romimage-macros.h>
+
+/* code to enter and leave self-refresh. must be self-contained.
+ * this code will be copied to on-chip memory and executed from there.
+ */
+       .balign 4
+ENTRY(ecovec24_sdram_enter_start)
+
+       /* DBSC: put memory in self-refresh mode */
+
+       ED 0xFD000010, 0x00000000 /* DBEN */
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       ED 0xFD000014, 0x00000002 /* DBCMDCNT (PALL) */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000040, 0x00000001 /* DBRFPDN0 */
+
+       rts
+        nop
+
+ENTRY(ecovec24_sdram_enter_end)
+
+       .balign 4
+ENTRY(ecovec24_sdram_leave_start)
+
+       /* DBSC: put memory in auto-refresh mode */
+
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       WAIT 1
+       ED 0xFD000014, 0x00000002 /* DBCMDCNT (PALL) */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000010, 0x00000001 /* DBEN */
+       ED 0xFD000040, 0x00010000 /* DBRFPDN0 */
+
+       rts
+        nop
+
+ENTRY(ecovec24_sdram_leave_end)
index 3b1ceb4..826e623 100644 (file)
 #include <linux/i2c.h>
 #include <linux/i2c/tsc2007.h>
 #include <linux/input.h>
+#include <linux/input/sh_keysc.h>
+#include <linux/mfd/sh_mobile_sdhi.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
 #include <asm/heartbeat.h>
 #include <asm/sh_eth.h>
-#include <asm/sh_keysc.h>
 #include <asm/clock.h>
+#include <asm/suspend.h>
 #include <cpu/sh7724.h>
 
 /*
@@ -147,6 +149,9 @@ static struct platform_device sh_eth_device = {
        },
        .num_resources = ARRAY_SIZE(sh_eth_resources),
        .resource = sh_eth_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_ETHER,
+       },
 };
 
 /* USB0 host */
@@ -185,30 +190,18 @@ static struct platform_device usb0_host_device = {
        .resource       = usb0_host_resources,
 };
 
-/*
- * USB1
- *
- * CN5 can use both host/function,
- * and we can determine it by checking PTB[3]
- *
- * This time only USB1 host is supported.
- */
+/* USB1 host/function */
 void usb1_port_power(int port, int power)
 {
-       if (!gpio_get_value(GPIO_PTB3)) {
-               printk(KERN_ERR "USB1 function is not supported\n");
-               return;
-       }
-
        gpio_set_value(GPIO_PTB5, power);
 }
 
-static struct r8a66597_platdata usb1_host_data = {
+static struct r8a66597_platdata usb1_common_data = {
        .on_chip = 1,
        .port_power = usb1_port_power,
 };
 
-static struct resource usb1_host_resources[] = {
+static struct resource usb1_common_resources[] = {
        [0] = {
                .start  = 0xa4d90000,
                .end    = 0xa4d90124 - 1,
@@ -221,16 +214,16 @@ static struct resource usb1_host_resources[] = {
        },
 };
 
-static struct platform_device usb1_host_device = {
-       .name           = "r8a66597_hcd",
+static struct platform_device usb1_common_device = {
+       /* .name will be added in arch_setup */
        .id             = 1,
        .dev = {
                .dma_mask               = NULL,         /*  not use dma */
                .coherent_dma_mask      = 0xffffffff,
-               .platform_data          = &usb1_host_data,
+               .platform_data          = &usb1_common_data,
        },
-       .num_resources  = ARRAY_SIZE(usb1_host_resources),
-       .resource       = usb1_host_resources,
+       .num_resources  = ARRAY_SIZE(usb1_common_resources),
+       .resource       = usb1_common_resources,
 };
 
 /* LCDC */
@@ -428,16 +421,90 @@ static struct i2c_board_info ts_i2c_clients = {
        .irq            = IRQ0,
 };
 
+/* SHDI0 */
+static void sdhi0_set_pwr(struct platform_device *pdev, int state)
+{
+       gpio_set_value(GPIO_PTB6, state);
+}
+
+static struct sh_mobile_sdhi_info sdhi0_info = {
+       .set_pwr = sdhi0_set_pwr,
+};
+
+static struct resource sdhi0_resources[] = {
+       [0] = {
+               .name   = "SDHI0",
+               .start  = 0x04ce0000,
+               .end    = 0x04ce01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 101,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device sdhi0_device = {
+       .name           = "sh_mobile_sdhi",
+       .num_resources  = ARRAY_SIZE(sdhi0_resources),
+       .resource       = sdhi0_resources,
+       .id             = 0,
+       .dev    = {
+               .platform_data  = &sdhi0_info,
+       },
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI0,
+       },
+};
+
+/* SHDI1 */
+static void sdhi1_set_pwr(struct platform_device *pdev, int state)
+{
+       gpio_set_value(GPIO_PTB7, state);
+}
+
+static struct sh_mobile_sdhi_info sdhi1_info = {
+       .set_pwr = sdhi1_set_pwr,
+};
+
+static struct resource sdhi1_resources[] = {
+       [0] = {
+               .name   = "SDHI1",
+               .start  = 0x04cf0000,
+               .end    = 0x04cf01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 24,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device sdhi1_device = {
+       .name           = "sh_mobile_sdhi",
+       .num_resources  = ARRAY_SIZE(sdhi1_resources),
+       .resource       = sdhi1_resources,
+       .id             = 1,
+       .dev    = {
+               .platform_data  = &sdhi1_info,
+       },
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI1,
+       },
+};
+
 static struct platform_device *ecovec_devices[] __initdata = {
        &heartbeat_device,
        &nor_flash_device,
        &sh_eth_device,
        &usb0_host_device,
-       &usb1_host_device, /* USB1 host support */
+       &usb1_common_device,
        &lcdc_device,
        &ceu0_device,
        &ceu1_device,
        &keysc_device,
+       &sdhi0_device,
+       &sdhi1_device,
 };
 
 #define EEPROM_ADDR 0x50
@@ -466,12 +533,9 @@ static u8 mac_read(struct i2c_adapter *a, u8 command)
        return buf;
 }
 
-#define MAC_LEN 6
-static void __init sh_eth_init(void)
+static void __init sh_eth_init(struct sh_eth_plat_data *pd)
 {
        struct i2c_adapter *a = i2c_get_adapter(1);
-       struct clk *eth_clk;
-       u8 mac[MAC_LEN];
        int i;
 
        if (!a) {
@@ -479,39 +543,30 @@ static void __init sh_eth_init(void)
                return;
        }
 
-       eth_clk = clk_get(NULL, "eth0");
-       if (!eth_clk) {
-               pr_err("can not get eth0 clk\n");
-               return;
-       }
-
        /* read MAC address frome EEPROM */
-       for (i = 0; i < MAC_LEN; i++) {
-               mac[i] = mac_read(a, 0x10 + i);
+       for (i = 0; i < sizeof(pd->mac_addr); i++) {
+               pd->mac_addr[i] = mac_read(a, 0x10 + i);
                msleep(10);
        }
-
-       /* clock enable */
-       clk_enable(eth_clk);
-
-       /* reset sh-eth */
-       ctrl_outl(0x1, SH_ETH_ADDR + 0x0);
-
-       /* set MAC addr */
-       ctrl_outl((mac[0] << 24) |
-                 (mac[1] << 16) |
-                 (mac[2] <<  8) |
-                 (mac[3] <<  0), SH_ETH_MAHR);
-       ctrl_outl((mac[4] <<  8) |
-                 (mac[5] <<  0), SH_ETH_MALR);
-
-       clk_put(eth_clk);
 }
 
 #define PORT_HIZA 0xA4050158
 #define IODRIVEA  0xA405018A
+
+extern char ecovec24_sdram_enter_start;
+extern char ecovec24_sdram_enter_end;
+extern char ecovec24_sdram_leave_start;
+extern char ecovec24_sdram_leave_end;
+
 static int __init arch_setup(void)
 {
+       /* register board specific self-refresh code */
+       sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF,
+                                       &ecovec24_sdram_enter_start,
+                                       &ecovec24_sdram_enter_end,
+                                       &ecovec24_sdram_leave_start,
+                                       &ecovec24_sdram_leave_end);
+
        /* enable STATUS0, STATUS2 and PDSTATUS */
        gpio_request(GPIO_FN_STATUS0, NULL);
        gpio_request(GPIO_FN_STATUS2, NULL);
@@ -561,6 +616,14 @@ static int __init arch_setup(void)
        ctrl_outw(0x0600, 0xa40501d4);
        ctrl_outw(0x0600, 0xa4050192);
 
+       if (gpio_get_value(GPIO_PTB3)) {
+               printk(KERN_INFO "USB1 function is selected\n");
+               usb1_common_device.name = "r8a66597_udc";
+       } else {
+               printk(KERN_INFO "USB1 host is selected\n");
+               usb1_common_device.name = "r8a66597_hcd";
+       }
+
        /* enable LCDC */
        gpio_request(GPIO_FN_LCDD23,   NULL);
        gpio_request(GPIO_FN_LCDD22,   NULL);
@@ -603,8 +666,8 @@ static int __init arch_setup(void)
        gpio_direction_output(GPIO_PTR1, 0);
        gpio_direction_output(GPIO_PTA2, 0);
 
-       /* I/O buffer drive ability is low */
-       ctrl_outw((ctrl_inw(IODRIVEA) & ~0x00c0) | 0x0040 , IODRIVEA);
+       /* I/O buffer drive ability is high */
+       ctrl_outw((ctrl_inw(IODRIVEA) & ~0x00c0) | 0x0080 , IODRIVEA);
 
        if (gpio_get_value(GPIO_PTE6)) {
                /* DVI */
@@ -710,6 +773,33 @@ static int __init arch_setup(void)
        gpio_direction_input(GPIO_PTR5);
        gpio_direction_input(GPIO_PTR6);
 
+       /* enable SDHI0 (needs DS2.4 set to ON) */
+       gpio_request(GPIO_FN_SDHI0CD,  NULL);
+       gpio_request(GPIO_FN_SDHI0WP,  NULL);
+       gpio_request(GPIO_FN_SDHI0CMD, NULL);
+       gpio_request(GPIO_FN_SDHI0CLK, NULL);
+       gpio_request(GPIO_FN_SDHI0D3,  NULL);
+       gpio_request(GPIO_FN_SDHI0D2,  NULL);
+       gpio_request(GPIO_FN_SDHI0D1,  NULL);
+       gpio_request(GPIO_FN_SDHI0D0,  NULL);
+       gpio_request(GPIO_PTB6, NULL);
+       gpio_direction_output(GPIO_PTB6, 0);
+
+       /* enable SDHI1 (needs DS2.6,7 set to ON,OFF) */
+       gpio_request(GPIO_FN_SDHI1CD,  NULL);
+       gpio_request(GPIO_FN_SDHI1WP,  NULL);
+       gpio_request(GPIO_FN_SDHI1CMD, NULL);
+       gpio_request(GPIO_FN_SDHI1CLK, NULL);
+       gpio_request(GPIO_FN_SDHI1D3,  NULL);
+       gpio_request(GPIO_FN_SDHI1D2,  NULL);
+       gpio_request(GPIO_FN_SDHI1D1,  NULL);
+       gpio_request(GPIO_FN_SDHI1D0,  NULL);
+       gpio_request(GPIO_PTB7, NULL);
+       gpio_direction_output(GPIO_PTB7, 0);
+
+       /* I/O buffer drive ability is high for SDHI1 */
+       ctrl_outw((ctrl_inw(IODRIVEA) & ~0x3000) | 0x2000 , IODRIVEA);
+
        /* enable I2C device */
        i2c_register_board_info(1, i2c1_devices,
                                ARRAY_SIZE(i2c1_devices));
@@ -721,12 +811,11 @@ arch_initcall(arch_setup);
 
 static int __init devices_setup(void)
 {
-       sh_eth_init();
+       sh_eth_init(&sh_eth_plat);
        return 0;
 }
 device_initcall(devices_setup);
 
-
 static struct sh_machine_vector mv_ecovec __initmv = {
        .mv_name        = "R0P7724 (EcoVec)",
 };
index 566e69d..f663c14 100644 (file)
@@ -384,7 +384,7 @@ static unsigned char irl2irq[HL_NR_IRL];
 
 static int highlander_irq_demux(int irq)
 {
-       if (irq >= HL_NR_IRL || !irl2irq[irq])
+       if (irq >= HL_NR_IRL || irq < 0 || !irl2irq[irq])
                return irq;
 
        return irl2irq[irq];
index 5d58678..4e577a3 100644 (file)
@@ -1,2 +1,2 @@
-obj-y   := setup.o
+obj-y   := setup.o sdram.o
 obj-$(CONFIG_FB_SH_MOBILE_LCDC)        +=  lcd_wqvga.o
diff --git a/arch/sh/boards/mach-kfr2r09/sdram.S b/arch/sh/boards/mach-kfr2r09/sdram.S
new file mode 100644 (file)
index 0000000..0c9f55b
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * KFR2R09 sdram self/auto-refresh setup code
+ *
+ *  Copyright (C) 2009 Magnus Damm
+ *
+ * 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/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/suspend.h>
+#include <asm/romimage-macros.h>
+
+/* code to enter and leave self-refresh. must be self-contained.
+ * this code will be copied to on-chip memory and executed from there.
+ */
+       .balign 4
+ENTRY(kfr2r09_sdram_enter_start)
+
+       /* DBSC: put memory in self-refresh mode */
+
+       ED 0xFD000010, 0x00000000 /* DBEN */
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       ED 0xFD000014, 0x00000002 /* DBCMDCNT (PALL) */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000040, 0x00000001 /* DBRFPDN0 */
+
+       rts
+        nop
+
+ENTRY(kfr2r09_sdram_enter_end)
+
+       .balign 4
+ENTRY(kfr2r09_sdram_leave_start)
+
+       /* DBSC: put memory in auto-refresh mode */
+
+       mov.l   @(SH_SLEEP_MODE, r5), r0
+       tst     #SUSP_SH_RSTANDBY, r0
+       bf      resume_rstandby
+
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       WAIT 1
+       ED 0xFD000014, 0x00000002 /* DBCMDCNT (PALL) */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000010, 0x00000001 /* DBEN */
+       ED 0xFD000040, 0x00010000 /* DBRFPDN0 */
+
+       rts
+        nop
+
+resume_rstandby:
+
+       /* DBSC: re-initialize and put in auto-refresh */
+
+       ED 0xFD000108, 0x40000301 /* DBPDCNT0 */
+       ED 0xFD000020, 0x011B0002 /* DBCONF */
+       ED 0xFD000030, 0x03060E02 /* DBTR0 */
+       ED 0xFD000034, 0x01020102 /* DBTR1 */
+       ED 0xFD000038, 0x01090406 /* DBTR2 */
+       ED 0xFD000008, 0x00000004 /* DBKIND */
+       ED 0xFD000040, 0x00000001 /* DBRFPDN0 */
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       ED 0xFD000018, 0x00000001 /* DBCKECNT */
+       WAIT 1
+       ED 0xFD000010, 0x00000001 /* DBEN */
+       ED 0xFD000044, 0x000004AF /* DBRFPDN1 */
+       ED 0xFD000048, 0x20CF0037 /* DBRFPDN2 */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000108, 0x40000300 /* DBPDCNT0 */
+       ED 0xFD000040, 0x00010000 /* DBRFPDN0 */
+
+       rts
+        nop
+
+ENTRY(kfr2r09_sdram_leave_end)
index c08d33f..87438d6 100644 (file)
 #include <linux/clk.h>
 #include <linux/gpio.h>
 #include <linux/input.h>
+#include <linux/input/sh_keysc.h>
 #include <linux/i2c.h>
 #include <linux/usb/r8a66597.h>
+#include <media/soc_camera.h>
+#include <media/sh_mobile_ceu.h>
 #include <video/sh_mobile_lcdc.h>
+#include <asm/suspend.h>
 #include <asm/clock.h>
 #include <asm/machvec.h>
 #include <asm/io.h>
-#include <asm/sh_keysc.h>
 #include <cpu/sh7724.h>
 #include <mach/kfr2r09.h>
 
@@ -212,11 +215,154 @@ static struct platform_device kfr2r09_usb0_gadget_device = {
        .resource       = kfr2r09_usb0_gadget_resources,
 };
 
+static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
+       .flags = SH_CEU_FLAG_USE_8BIT_BUS,
+};
+
+static struct resource kfr2r09_ceu_resources[] = {
+       [0] = {
+               .name   = "CEU",
+               .start  = 0xfe910000,
+               .end    = 0xfe91009f,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 52,
+               .end  = 52,
+               .flags  = IORESOURCE_IRQ,
+       },
+       [2] = {
+               /* place holder for contiguous memory */
+       },
+};
+
+static struct platform_device kfr2r09_ceu_device = {
+       .name           = "sh_mobile_ceu",
+       .id             = 0, /* "ceu0" clock */
+       .num_resources  = ARRAY_SIZE(kfr2r09_ceu_resources),
+       .resource       = kfr2r09_ceu_resources,
+       .dev    = {
+               .platform_data  = &sh_mobile_ceu_info,
+       },
+       .archdata = {
+               .hwblk_id = HWBLK_CEU0,
+       },
+};
+
+static struct i2c_board_info kfr2r09_i2c_camera = {
+       I2C_BOARD_INFO("rj54n1cb0c", 0x50),
+};
+
+static struct clk *camera_clk;
+
+#define DRVCRB 0xA405018C
+static int camera_power(struct device *dev, int mode)
+{
+       int ret;
+
+       if (mode) {
+               long rate;
+
+               camera_clk = clk_get(NULL, "video_clk");
+               if (IS_ERR(camera_clk))
+                       return PTR_ERR(camera_clk);
+
+               /* set VIO_CKO clock to 25MHz */
+               rate = clk_round_rate(camera_clk, 25000000);
+               ret = clk_set_rate(camera_clk, rate);
+               if (ret < 0)
+                       goto eclkrate;
+
+               /* set DRVCRB
+                *
+                * use 1.8 V for VccQ_VIO
+                * use 2.85V for VccQ_SR
+                */
+               ctrl_outw((ctrl_inw(DRVCRB) & ~0x0003) | 0x0001, DRVCRB);
+
+               /* reset clear */
+               ret = gpio_request(GPIO_PTB4, NULL);
+               if (ret < 0)
+                       goto eptb4;
+               ret = gpio_request(GPIO_PTB7, NULL);
+               if (ret < 0)
+                       goto eptb7;
+
+               ret = gpio_direction_output(GPIO_PTB4, 1);
+               if (!ret)
+                       ret = gpio_direction_output(GPIO_PTB7, 1);
+               if (ret < 0)
+                       goto egpioout;
+               msleep(1);
+
+               ret = clk_enable(camera_clk);   /* start VIO_CKO */
+               if (ret < 0)
+                       goto eclkon;
+
+               return 0;
+       }
+
+       ret = 0;
+
+       clk_disable(camera_clk);
+eclkon:
+       gpio_set_value(GPIO_PTB7, 0);
+egpioout:
+       gpio_set_value(GPIO_PTB4, 0);
+       gpio_free(GPIO_PTB7);
+eptb7:
+       gpio_free(GPIO_PTB4);
+eptb4:
+eclkrate:
+       clk_put(camera_clk);
+       return ret;
+}
+
+static struct soc_camera_link rj54n1_link = {
+       .power          = camera_power,
+       .board_info     = &kfr2r09_i2c_camera,
+       .i2c_adapter_id = 1,
+       .module_name    = "rj54n1cb0c",
+};
+
+static struct platform_device kfr2r09_camera = {
+       .name   = "soc-camera-pdrv",
+       .id     = 0,
+       .dev    = {
+               .platform_data = &rj54n1_link,
+       },
+};
+
+static struct resource kfr2r09_sh_sdhi0_resources[] = {
+       [0] = {
+               .name   = "SDHI0",
+               .start  = 0x04ce0000,
+               .end    = 0x04ce01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 101,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device kfr2r09_sh_sdhi0_device = {
+       .name           = "sh_mobile_sdhi",
+       .num_resources  = ARRAY_SIZE(kfr2r09_sh_sdhi0_resources),
+       .resource       = kfr2r09_sh_sdhi0_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI0,
+       },
+};
+
 static struct platform_device *kfr2r09_devices[] __initdata = {
        &kfr2r09_nor_flash_device,
        &kfr2r09_nand_flash_device,
        &kfr2r09_sh_keysc_device,
        &kfr2r09_sh_lcdc_device,
+       &kfr2r09_ceu_device,
+       &kfr2r09_camera,
+       &kfr2r09_sh_sdhi0_device,
 };
 
 #define BSC_CS0BCR 0xfec10004
@@ -268,11 +414,59 @@ static int kfr2r09_usb0_gadget_i2c_setup(void)
 
        return 0;
 }
+
+static int kfr2r09_serial_i2c_setup(void)
+{
+       struct i2c_adapter *a;
+       struct i2c_msg msg;
+       unsigned char buf[2];
+       int ret;
+
+       a = i2c_get_adapter(0);
+       if (!a)
+               return -ENODEV;
+
+       /* set bit 6 (the 7th bit) of chip at 0x09, register 0x13 */
+       buf[0] = 0x13;
+       msg.addr = 0x09;
+       msg.buf = buf;
+       msg.len = 1;
+       msg.flags = 0;
+       ret = i2c_transfer(a, &msg, 1);
+       if (ret != 1)
+               return -ENODEV;
+
+       buf[0] = 0;
+       msg.addr = 0x09;
+       msg.buf = buf;
+       msg.len = 1;
+       msg.flags = I2C_M_RD;
+       ret = i2c_transfer(a, &msg, 1);
+       if (ret != 1)
+               return -ENODEV;
+
+       buf[1] = buf[0] | (1 << 6);
+       buf[0] = 0x13;
+       msg.addr = 0x09;
+       msg.buf = buf;
+       msg.len = 2;
+       msg.flags = 0;
+       ret = i2c_transfer(a, &msg, 1);
+       if (ret != 1)
+               return -ENODEV;
+
+       return 0;
+}
 #else
 static int kfr2r09_usb0_gadget_i2c_setup(void)
 {
        return -ENODEV;
 }
+
+static int kfr2r09_serial_i2c_setup(void)
+{
+       return -ENODEV;
+}
 #endif
 
 static int kfr2r09_usb0_gadget_setup(void)
@@ -299,11 +493,27 @@ static int kfr2r09_usb0_gadget_setup(void)
        return 0;
 }
 
+extern char kfr2r09_sdram_enter_start;
+extern char kfr2r09_sdram_enter_end;
+extern char kfr2r09_sdram_leave_start;
+extern char kfr2r09_sdram_leave_end;
+
 static int __init kfr2r09_devices_setup(void)
 {
+       /* register board specific self-refresh code */
+       sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF |
+                                       SUSP_SH_RSTANDBY,
+                                       &kfr2r09_sdram_enter_start,
+                                       &kfr2r09_sdram_enter_end,
+                                       &kfr2r09_sdram_leave_start,
+                                       &kfr2r09_sdram_leave_end);
+
        /* enable SCIF1 serial port for YC401 console support */
        gpio_request(GPIO_FN_SCIF1_RXD, NULL);
        gpio_request(GPIO_FN_SCIF1_TXD, NULL);
+       kfr2r09_serial_i2c_setup(); /* ECONTMSK(bit6=L10ONEN) set 1 */
+       gpio_request(GPIO_PTG3, NULL); /* HPON_ON */
+       gpio_direction_output(GPIO_PTG3, 1); /* HPON_ON = H */
 
        /* setup NOR flash at CS0 */
        ctrl_outl(0x36db0400, BSC_CS0BCR);
@@ -361,6 +571,32 @@ static int __init kfr2r09_devices_setup(void)
        if (kfr2r09_usb0_gadget_setup() == 0)
                platform_device_register(&kfr2r09_usb0_gadget_device);
 
+       /* CEU */
+       gpio_request(GPIO_FN_VIO_CKO, NULL);
+       gpio_request(GPIO_FN_VIO0_CLK, NULL);
+       gpio_request(GPIO_FN_VIO0_VD, NULL);
+       gpio_request(GPIO_FN_VIO0_HD, NULL);
+       gpio_request(GPIO_FN_VIO0_FLD, NULL);
+       gpio_request(GPIO_FN_VIO0_D7, NULL);
+       gpio_request(GPIO_FN_VIO0_D6, NULL);
+       gpio_request(GPIO_FN_VIO0_D5, NULL);
+       gpio_request(GPIO_FN_VIO0_D4, NULL);
+       gpio_request(GPIO_FN_VIO0_D3, NULL);
+       gpio_request(GPIO_FN_VIO0_D2, NULL);
+       gpio_request(GPIO_FN_VIO0_D1, NULL);
+       gpio_request(GPIO_FN_VIO0_D0, NULL);
+
+       platform_resource_setup_memory(&kfr2r09_ceu_device, "ceu", 4 << 20);
+
+       /* SDHI0 connected to yc304 */
+       gpio_request(GPIO_FN_SDHI0CD, NULL);
+       gpio_request(GPIO_FN_SDHI0D3, NULL);
+       gpio_request(GPIO_FN_SDHI0D2, NULL);
+       gpio_request(GPIO_FN_SDHI0D1, NULL);
+       gpio_request(GPIO_FN_SDHI0D0, NULL);
+       gpio_request(GPIO_FN_SDHI0CMD, NULL);
+       gpio_request(GPIO_FN_SDHI0CLK, NULL);
+
        return platform_add_devices(kfr2r09_devices,
                                    ARRAY_SIZE(kfr2r09_devices));
 }
index 5f231dd..4601a89 100644 (file)
@@ -1,2 +1,2 @@
-obj-y   := setup.o
+obj-y   := setup.o sdram.o
 obj-$(CONFIG_SH_MIGOR_QVGA)    +=  lcd_qvga.o
diff --git a/arch/sh/boards/mach-migor/sdram.S b/arch/sh/boards/mach-migor/sdram.S
new file mode 100644 (file)
index 0000000..614aa3a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Migo-R sdram self/auto-refresh setup code
+ *
+ *  Copyright (C) 2009 Magnus Damm
+ *
+ * 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/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/suspend.h>
+#include <asm/romimage-macros.h>
+
+/* code to enter and leave self-refresh. must be self-contained.
+ * this code will be copied to on-chip memory and executed from there.
+ */
+       .balign 4
+ENTRY(migor_sdram_enter_start)
+
+       /* SBSC: disable power down and put in self-refresh mode */
+       mov.l   1f, r4
+       mov.l   2f, r1
+       mov.l   @r4, r2
+       or      r1, r2
+       mov.l   3f, r3
+       and     r3, r2
+       mov.l   r2, @r4
+
+       rts
+        nop
+
+       .balign 4
+1:     .long   0xfe400008 /* SDCR0 */
+2:     .long   0x00000400
+3:     .long   0xffff7fff
+ENTRY(migor_sdram_enter_end)
+
+       .balign 4
+ENTRY(migor_sdram_leave_start)
+
+       /* SBSC: set auto-refresh mode */
+       mov.l   1f, r4
+       mov.l   @r4, r0
+       mov.l   4f, r1
+       and     r1, r0
+       mov.l   r0, @r4
+       mov.l   6f, r4
+       mov.l   8f, r0
+       mov.l   @r4, r1
+       mov     #-1, r4
+       add     r4, r1
+       or      r1, r0
+       mov.l   7f, r1
+       mov.l   r0, @r1
+
+       rts
+        nop
+
+       .balign 4
+1:     .long   0xfe400008 /* SDCR0 */
+4:     .long   0xfffffbff
+6:     .long   0xfe40001c /* RTCOR */
+7:     .long   0xfe400018 /* RTCNT */
+8:     .long   0xa55a0000
+ENTRY(migor_sdram_leave_end)
index 6ed1fd3..9099b6d 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/input.h>
+#include <linux/input/sh_keysc.h>
 #include <linux/mtd/physmap.h>
 #include <linux/mtd/nand.h>
 #include <linux/i2c.h>
@@ -18,8 +19,6 @@
 #include <linux/delay.h>
 #include <linux/clk.h>
 #include <linux/gpio.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_gpio.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
 #include <media/ov772x.h>
@@ -27,7 +26,7 @@
 #include <asm/clock.h>
 #include <asm/machvec.h>
 #include <asm/io.h>
-#include <asm/sh_keysc.h>
+#include <asm/suspend.h>
 #include <mach/migor.h>
 #include <cpu/sh7722.h>
 
@@ -390,17 +389,25 @@ static struct platform_device migor_ceu_device = {
        },
 };
 
-struct spi_gpio_platform_data sdcard_cn9_platform_data = {
-       .sck = GPIO_PTD0,
-       .mosi = GPIO_PTD1,
-       .miso = GPIO_PTD2,
-       .num_chipselect = 1,
+static struct resource sdhi_cn9_resources[] = {
+       [0] = {
+               .name   = "SDHI",
+               .start  = 0x04ce0000,
+               .end    = 0x04ce01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 101,
+               .flags  = IORESOURCE_IRQ,
+       },
 };
 
-static struct platform_device sdcard_cn9_device = {
-       .name           = "spi_gpio",
-       .dev    = {
-               .platform_data  = &sdcard_cn9_platform_data,
+static struct platform_device sdhi_cn9_device = {
+       .name           = "sh_mobile_sdhi",
+       .num_resources  = ARRAY_SIZE(sdhi_cn9_resources),
+       .resource       = sdhi_cn9_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI,
        },
 };
 
@@ -467,23 +474,24 @@ static struct platform_device *migor_devices[] __initdata = {
        &migor_ceu_device,
        &migor_nor_flash_device,
        &migor_nand_flash_device,
-       &sdcard_cn9_device,
+       &sdhi_cn9_device,
        &migor_camera[0],
        &migor_camera[1],
 };
 
-static struct spi_board_info migor_spi_devices[] = {
-       {
-               .modalias = "mmc_spi",
-               .max_speed_hz = 5000000,
-               .chip_select = 0,
-               .controller_data = (void *) GPIO_PTD5,
-       },
-};
+extern char migor_sdram_enter_start;
+extern char migor_sdram_enter_end;
+extern char migor_sdram_leave_start;
+extern char migor_sdram_leave_end;
 
 static int __init migor_devices_setup(void)
 {
-
+       /* register board specific self-refresh code */
+       sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF,
+                                       &migor_sdram_enter_start,
+                                       &migor_sdram_enter_end,
+                                       &migor_sdram_leave_start,
+                                       &migor_sdram_leave_end);
 #ifdef CONFIG_PM
        /* Let D11 LED show STATUS0 */
        gpio_request(GPIO_FN_STATUS0, NULL);
@@ -525,6 +533,16 @@ static int __init migor_devices_setup(void)
        gpio_request(GPIO_PTA1, NULL);
        gpio_direction_input(GPIO_PTA1);
 
+       /* SDHI */
+       gpio_request(GPIO_FN_SDHICD, NULL);
+       gpio_request(GPIO_FN_SDHIWP, NULL);
+       gpio_request(GPIO_FN_SDHID3, NULL);
+       gpio_request(GPIO_FN_SDHID2, NULL);
+       gpio_request(GPIO_FN_SDHID1, NULL);
+       gpio_request(GPIO_FN_SDHID0, NULL);
+       gpio_request(GPIO_FN_SDHICMD, NULL);
+       gpio_request(GPIO_FN_SDHICLK, NULL);
+
        /* Touch Panel */
        gpio_request(GPIO_FN_IRQ6, NULL);
 
@@ -612,9 +630,6 @@ static int __init migor_devices_setup(void)
        i2c_register_board_info(0, migor_i2c_devices,
                                ARRAY_SIZE(migor_i2c_devices));
 
-       spi_register_board_info(migor_spi_devices,
-                               ARRAY_SIZE(migor_spi_devices));
-
        return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices));
 }
 arch_initcall(migor_devices_setup);
index c70fece..78d7b27 100644 (file)
@@ -116,7 +116,7 @@ static unsigned char irl2irq[R2D_NR_IRL];
 
 int rts7751r2d_irq_demux(int irq)
 {
-       if (irq >= R2D_NR_IRL || !irl2irq[irq])
+       if (irq >= R2D_NR_IRL || irq < 0 || !irl2irq[irq])
                return irq;
 
        return irl2irq[irq];
index 02d21a3..4eb31ac 100644 (file)
 #include <asm/io.h>
 #include <mach-se/mach/se7722.h>
 
+unsigned int se7722_fpga_irq[SE7722_FPGA_IRQ_NR] = { 0, };
+
 static void disable_se7722_irq(unsigned int irq)
 {
-       unsigned int bit = irq - SE7722_FPGA_IRQ_BASE;
+       unsigned int bit = (unsigned int)get_irq_chip_data(irq);
        ctrl_outw(ctrl_inw(IRQ01_MASK) | 1 << bit, IRQ01_MASK);
 }
 
 static void enable_se7722_irq(unsigned int irq)
 {
-       unsigned int bit = irq - SE7722_FPGA_IRQ_BASE;
+       unsigned int bit = (unsigned int)get_irq_chip_data(irq);
        ctrl_outw(ctrl_inw(IRQ01_MASK) & ~(1 << bit), IRQ01_MASK);
 }
 
@@ -38,18 +40,15 @@ static struct irq_chip se7722_irq_chip __read_mostly = {
 static void se7722_irq_demux(unsigned int irq, struct irq_desc *desc)
 {
        unsigned short intv = ctrl_inw(IRQ01_STS);
-       struct irq_desc *ext_desc;
-       unsigned int ext_irq = SE7722_FPGA_IRQ_BASE;
+       unsigned int ext_irq = 0;
 
        intv &= (1 << SE7722_FPGA_IRQ_NR) - 1;
 
-       while (intv) {
-               if (intv & 1) {
-                       ext_desc = irq_desc + ext_irq;
-                       handle_level_irq(ext_irq, ext_desc);
-               }
-               intv >>= 1;
-               ext_irq++;
+       for (; intv; intv >>= 1, ext_irq++) {
+               if (!(intv & 1))
+                       continue;
+
+               generic_handle_irq(se7722_fpga_irq[ext_irq]);
        }
 }
 
@@ -63,11 +62,18 @@ void __init init_se7722_IRQ(void)
        ctrl_outw(0, IRQ01_MASK);       /* disable all irqs */
        ctrl_outw(0x2000, 0xb03fffec);  /* mrshpc irq enable */
 
-       for (i = 0; i < SE7722_FPGA_IRQ_NR; i++)
-               set_irq_chip_and_handler_name(SE7722_FPGA_IRQ_BASE + i,
+       for (i = 0; i < SE7722_FPGA_IRQ_NR; i++) {
+               se7722_fpga_irq[i] = create_irq();
+               if (se7722_fpga_irq[i] < 0)
+                       return;
+
+               set_irq_chip_and_handler_name(se7722_fpga_irq[i],
                                              &se7722_irq_chip,
                                              handle_level_irq, "level");
 
+               set_irq_chip_data(se7722_fpga_irq[i], (void *)i);
+       }
+
        set_irq_chained_handler(IRQ0_IRQ, se7722_irq_demux);
        set_irq_type(IRQ0_IRQ, IRQ_TYPE_LEVEL_LOW);
 
index 3637407..b1cb942 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/ata_platform.h>
 #include <linux/input.h>
+#include <linux/input/sh_keysc.h>
 #include <linux/smc91x.h>
 #include <mach-se/mach/se7722.h>
 #include <mach-se/mach/mrshpc.h>
@@ -21,7 +22,6 @@
 #include <asm/clock.h>
 #include <asm/io.h>
 #include <asm/heartbeat.h>
-#include <asm/sh_keysc.h>
 #include <cpu/sh7722.h>
 
 /* Heartbeat */
@@ -60,8 +60,7 @@ static struct resource smc91x_eth_resources[] = {
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
-               .start  = SMC_IRQ,
-               .end    = SMC_IRQ,
+               /* Filled in later */
                .flags  = IORESOURCE_IRQ,
        },
 };
@@ -90,8 +89,7 @@ static struct resource cf_ide_resources[] = {
                .flags  = IORESOURCE_IO,
        },
        [2] = {
-               .start  = MRSHPC_IRQ0,
-               .end    = MRSHPC_IRQ0,
+               /* Filled in later */
                .flags  = IORESOURCE_IRQ,
        },
 };
@@ -153,6 +151,14 @@ static struct platform_device *se7722_devices[] __initdata = {
 static int __init se7722_devices_setup(void)
 {
        mrshpc_setup_windows();
+
+       /* Wire-up dynamic vectors */
+       cf_ide_resources[2].start = cf_ide_resources[2].end =
+               se7722_fpga_irq[SE7722_FPGA_IRQ_MRSHPC0];
+
+       smc91x_eth_resources[1].start = smc91x_eth_resources[1].end =
+               se7722_fpga_irq[SE7722_FPGA_IRQ_SMC];
+
        return platform_add_devices(se7722_devices, ARRAY_SIZE(se7722_devices));
 }
 device_initcall(se7722_devices_setup);
@@ -193,6 +199,5 @@ static void __init se7722_setup(char **cmdline_p)
 static struct sh_machine_vector mv_se7722 __initmv = {
        .mv_name                = "Solution Engine 7722" ,
        .mv_setup               = se7722_setup ,
-       .mv_nr_irqs             = SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_NR,
        .mv_init_irq            = init_se7722_IRQ,
 };
index 349cbd6..a08b368 100644 (file)
@@ -7,4 +7,4 @@
 #
 #
 
-obj-y   := setup.o irq.o
\ No newline at end of file
+obj-y   := setup.o irq.o sdram.o
diff --git a/arch/sh/boards/mach-se/7724/sdram.S b/arch/sh/boards/mach-se/7724/sdram.S
new file mode 100644 (file)
index 0000000..9040167
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * MS7724SE sdram self/auto-refresh setup code
+ *
+ *  Copyright (C) 2009 Magnus Damm
+ *
+ * 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/sys.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/suspend.h>
+#include <asm/romimage-macros.h>
+
+/* code to enter and leave self-refresh. must be self-contained.
+ * this code will be copied to on-chip memory and executed from there.
+ */
+       .balign 4
+ENTRY(ms7724se_sdram_enter_start)
+
+       /* DBSC: put memory in self-refresh mode */
+
+       ED 0xFD000010, 0x00000000 /* DBEN */
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       ED 0xFD000014, 0x00000002 /* DBCMDCNT (PALL) */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000040, 0x00000001 /* DBRFPDN0 */
+
+       rts
+        nop
+
+ENTRY(ms7724se_sdram_enter_end)
+
+       .balign 4
+ENTRY(ms7724se_sdram_leave_start)
+
+       /* DBSC: put memory in auto-refresh mode */
+
+       ED 0xFD000040, 0x00000000 /* DBRFPDN0 */
+       WAIT 1
+       ED 0xFD000014, 0x00000002 /* DBCMDCNT (PALL) */
+       ED 0xFD000014, 0x00000004 /* DBCMDCNT (REF) */
+       ED 0xFD000010, 0x00000001 /* DBEN */
+       ED 0xFD000040, 0x00010000 /* DBRFPDN0 */
+
+       rts
+        nop
+
+ENTRY(ms7724se_sdram_leave_end)
index 0894bba..4b0f0c0 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/smc91x.h>
 #include <linux/gpio.h>
 #include <linux/input.h>
+#include <linux/input/sh_keysc.h>
 #include <linux/usb/r8a66597.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
@@ -27,7 +28,7 @@
 #include <asm/heartbeat.h>
 #include <asm/sh_eth.h>
 #include <asm/clock.h>
-#include <asm/sh_keysc.h>
+#include <asm/suspend.h>
 #include <cpu/sh7724.h>
 #include <mach-se/mach/se7724.h>
 
@@ -451,6 +452,52 @@ static struct platform_device sh7724_usb1_gadget_device = {
        .resource       = sh7724_usb1_gadget_resources,
 };
 
+static struct resource sdhi0_cn7_resources[] = {
+       [0] = {
+               .name   = "SDHI0",
+               .start  = 0x04ce0000,
+               .end    = 0x04ce01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 101,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device sdhi0_cn7_device = {
+       .name           = "sh_mobile_sdhi",
+       .id             = 0,
+       .num_resources  = ARRAY_SIZE(sdhi0_cn7_resources),
+       .resource       = sdhi0_cn7_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI0,
+       },
+};
+
+static struct resource sdhi1_cn8_resources[] = {
+       [0] = {
+               .name   = "SDHI1",
+               .start  = 0x04cf0000,
+               .end    = 0x04cf01ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = 24,
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device sdhi1_cn8_device = {
+       .name           = "sh_mobile_sdhi",
+       .id             = 1,
+       .num_resources  = ARRAY_SIZE(sdhi1_cn8_resources),
+       .resource       = sdhi1_cn8_resources,
+       .archdata = {
+               .hwblk_id = HWBLK_SDHI1,
+       },
+};
+
 static struct platform_device *ms7724se_devices[] __initdata = {
        &heartbeat_device,
        &smc91x_eth_device,
@@ -463,6 +510,8 @@ static struct platform_device *ms7724se_devices[] __initdata = {
        &sh7724_usb0_host_device,
        &sh7724_usb1_gadget_device,
        &fsi_device,
+       &sdhi0_cn7_device,
+       &sdhi1_cn8_device,
 };
 
 #define EEPROM_OP   0xBA206000
@@ -487,7 +536,7 @@ static int __init sh_eth_is_eeprom_ready(void)
 static void __init sh_eth_init(void)
 {
        int i;
-       u16 mac[3];
+       u16 mac;
 
        /* check EEPROM status */
        if (!sh_eth_is_eeprom_ready())
@@ -501,16 +550,10 @@ static void __init sh_eth_init(void)
                if (!sh_eth_is_eeprom_ready())
                        return;
 
-               mac[i] = ctrl_inw(EEPROM_DATA);
-               mac[i] = ((mac[i] & 0xFF) << 8) | (mac[i] >> 8); /* swap */
+               mac = ctrl_inw(EEPROM_DATA);
+               sh_eth_plat.mac_addr[i << 1] = mac & 0xff;
+               sh_eth_plat.mac_addr[(i << 1) + 1] = mac >> 8;
        }
-
-       /* reset sh-eth */
-       ctrl_outl(0x1, SH_ETH_ADDR + 0x0);
-
-       /* set MAC addr */
-       ctrl_outl(((mac[0] << 16) | (mac[1])), SH_ETH_MAHR);
-       ctrl_outl((mac[2]), SH_ETH_MALR);
 }
 
 #define SW4140    0xBA201000
@@ -527,11 +570,22 @@ static void __init sh_eth_init(void)
 #define SW41_G    0x4000
 #define SW41_H    0x8000
 
+extern char ms7724se_sdram_enter_start;
+extern char ms7724se_sdram_enter_end;
+extern char ms7724se_sdram_leave_start;
+extern char ms7724se_sdram_leave_end;
+
 static int __init devices_setup(void)
 {
        u16 sw = ctrl_inw(SW4140); /* select camera, monitor */
        struct clk *fsia_clk;
 
+       /* register board specific self-refresh code */
+       sh_mobile_register_self_refresh(SUSP_SH_STANDBY | SUSP_SH_SF,
+                                       &ms7724se_sdram_enter_start,
+                                       &ms7724se_sdram_enter_end,
+                                       &ms7724se_sdram_leave_start,
+                                       &ms7724se_sdram_leave_end);
        /* Reset Release */
        ctrl_outw(ctrl_inw(FPGA_OUT) &
                  ~((1 << 1)  | /* LAN */
@@ -701,6 +755,26 @@ static int __init devices_setup(void)
        clk_set_rate(&fsimcka_clk, 11000);
        clk_put(fsia_clk);
 
+       /* SDHI0 connected to cn7 */
+       gpio_request(GPIO_FN_SDHI0CD, NULL);
+       gpio_request(GPIO_FN_SDHI0WP, NULL);
+       gpio_request(GPIO_FN_SDHI0D3, NULL);
+       gpio_request(GPIO_FN_SDHI0D2, NULL);
+       gpio_request(GPIO_FN_SDHI0D1, NULL);
+       gpio_request(GPIO_FN_SDHI0D0, NULL);
+       gpio_request(GPIO_FN_SDHI0CMD, NULL);
+       gpio_request(GPIO_FN_SDHI0CLK, NULL);
+
+       /* SDHI1 connected to cn8 */
+       gpio_request(GPIO_FN_SDHI1CD, NULL);
+       gpio_request(GPIO_FN_SDHI1WP, NULL);
+       gpio_request(GPIO_FN_SDHI1D3, NULL);
+       gpio_request(GPIO_FN_SDHI1D2, NULL);
+       gpio_request(GPIO_FN_SDHI1D1, NULL);
+       gpio_request(GPIO_FN_SDHI1D0, NULL);
+       gpio_request(GPIO_FN_SDHI1CMD, NULL);
+       gpio_request(GPIO_FN_SDHI1CLK, NULL);
+
        /*
         * enable SH-Eth
         *
index fd56a71..b51b1fc 100644 (file)
@@ -131,7 +131,7 @@ void decompress_kernel(void)
 #ifdef CONFIG_SUPERH64
        output_addr = (CONFIG_MEMORY_START + 0x2000);
 #else
-       output_addr = PHYSADDR((unsigned long)&_text+PAGE_SIZE);
+       output_addr = __pa((unsigned long)&_text+PAGE_SIZE);
 #ifdef CONFIG_29BIT
        output_addr |= P2SEG;
 #endif
index 5806eee..f473a24 100644 (file)
@@ -4,16 +4,22 @@
 # create an image suitable for burning to flash from zImage
 #
 
-targets                := vmlinux head.o
+targets                := vmlinux head.o zeropage.bin piggy.o
 
 OBJECTS = $(obj)/head.o
-LDFLAGS_vmlinux := --oformat $(ld-bfd) -Ttext 0 -e romstart
+LDFLAGS_vmlinux := --oformat $(ld-bfd) -Ttext 0 -e romstart \
+                  -T $(obj)/../../kernel/vmlinux.lds
 
 $(obj)/vmlinux: $(OBJECTS) $(obj)/piggy.o FORCE
        $(call if_changed,ld)
        @:
 
+OBJCOPYFLAGS += -j .empty_zero_page
+
+$(obj)/zeropage.bin: vmlinux FORCE
+       $(call if_changed,objcopy)
+
 LDFLAGS_piggy.o := -r --format binary --oformat $(ld-bfd) -T
 
-$(obj)/piggy.o: $(obj)/vmlinux.scr arch/sh/boot/zImage FORCE
+$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/zeropage.bin arch/sh/boot/zImage FORCE
        $(call if_changed,ld)
index 219bc62..93e779a 100644 (file)
@@ -5,6 +5,44 @@
  */
 
 .text
+       #include <asm/page.h>
+
        .global romstart
 romstart:
+       /* include board specific setup code */
 #include <mach/romimage.h>
+
+       /* copy the empty_zero_page contents to where vmlinux expects it */
+       mova    empty_zero_page_src, r0
+       mov.l   empty_zero_page_dst, r1
+       mov     #(PAGE_SHIFT - 4), r4
+       mov     #1, r3
+       shld    r4, r3 /* r3 = PAGE_SIZE / 16 */
+
+1:
+       mov.l   @r0, r4
+       mov.l   @(4, r0), r5
+       mov.l   @(8, r0), r6
+       mov.l   @(12, r0), r7
+       add     #16,r0
+       mov.l   r4, @r1
+       mov.l   r5, @(4, r1)
+       mov.l   r6, @(8, r1)
+       mov.l   r7, @(12, r1)
+       dt      r3
+       add     #16,r1
+       bf      1b
+
+       /* jump to the zImage entry point located after the zero page data */
+       mov     #PAGE_SHIFT, r4
+       mov     #1, r1
+       shld    r4, r1
+       mova    empty_zero_page_src, r0
+       add     r1, r0
+       jmp     @r0
+        nop
+
+       .align 2
+empty_zero_page_dst:
+       .long   _text
+empty_zero_page_src:
index 347ee11..1ee631d 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/init.h>
 #include <linux/sysdev.h>
 #include <linux/platform_device.h>
-#include <linux/module.h>
 #include <linux/err.h>
 #include <linux/string.h>
 #include <asm/dma.h>
@@ -21,7 +20,6 @@
 static struct sysdev_class dma_sysclass = {
        .name = "dma",
 };
-EXPORT_SYMBOL(dma_sysclass);
 
 static ssize_t dma_show_devices(struct sys_device *dev,
                                struct sysdev_attribute *attr, char *buf)
diff --git a/arch/sh/drivers/pci/Kconfig b/arch/sh/drivers/pci/Kconfig
deleted file mode 100644 (file)
index e8db585..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-config PCI
-       bool "PCI support"
-       depends on SYS_SUPPORTS_PCI
-       help
-         Find out whether you have a PCI motherboard. PCI is the name of a
-         bus system, i.e. the way the CPU talks to the other stuff inside
-         your box. If you have PCI, say Y, otherwise N.
-
-config SH_PCIDMA_NONCOHERENT
-       bool "Cache and PCI noncoherent"
-       depends on PCI
-       default y
-       help
-         Enable this option if your platform does not have a CPU cache which
-         remains coherent with PCI DMA. It is safest to say 'Y', although you
-         will see better performance if you can say 'N', because the PCI DMA
-         code will not have to flush the CPU's caches. If you have a PCI host
-         bridge integrated with your SH CPU, refer carefully to the chip specs
-         to see if you can say 'N' here. Otherwise, leave it as 'Y'.
index 80d4081..99d6b3e 100644 (file)
@@ -28,9 +28,6 @@
 /* Returns the privileged segment base of a given address  */
 #define PXSEG(a)       (((unsigned long)(a)) & 0xe0000000)
 
-/* Returns the physical address of a PnSEG (n=1,2) address   */
-#define PHYSADDR(a)    (((unsigned long)(a)) & 0x1fffffff)
-
 #if defined(CONFIG_29BIT) || defined(CONFIG_PMB_FIXED)
 /*
  * Map an address to a certain privileged segment
 #define P3_ADDR_MAX            P4SEG
 #endif
 
+#ifndef __ASSEMBLY__
+#ifdef CONFIG_PMB
+extern int __in_29bit_mode(void);
+#endif /* CONFIG_PMB */
+#endif /* __ASSEMBLY__ */
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_SH_ADDRSPACE_H */
index e8e7813..b16388d 100644 (file)
@@ -78,11 +78,10 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
 #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
 #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
 
-/* Atomic operations are already serializing on SH */
-#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()
+#define smp_mb__before_atomic_dec()    smp_mb()
+#define smp_mb__after_atomic_dec()     smp_mb()
+#define smp_mb__before_atomic_inc()    smp_mb()
+#define smp_mb__after_atomic_inc()     smp_mb()
 
 #include <asm-generic/atomic-long.h>
 #include <asm-generic/atomic64.h>
index ebe595b..98511e4 100644 (file)
@@ -26,8 +26,8 @@
 /*
  * clear_bit() doesn't provide any barrier for the compiler.
  */
-#define smp_mb__before_clear_bit()     barrier()
-#define smp_mb__after_clear_bit()      barrier()
+#define smp_mb__before_clear_bit()     smp_mb()
+#define smp_mb__after_clear_bit()      smp_mb()
 
 #ifdef CONFIG_SUPERH32
 static inline unsigned long ffz(unsigned long word)
index 46260fc..02a19a1 100644 (file)
 
 #include <asm/processor.h>
 
+extern void select_idle_routine(void);
+
 static void __init check_bugs(void)
 {
        extern unsigned long loops_per_jiffy;
        char *p = &init_utsname()->machine[2]; /* "sh" */
 
+       select_idle_routine();
+
        current_cpu_data.loops_per_jiffy = loops_per_jiffy;
 
        switch (current_cpu_data.family) {
index 69d56dd..87ced13 100644 (file)
 #ifndef __ASM_SH_DMA_MAPPING_H
 #define __ASM_SH_DMA_MAPPING_H
 
-#include <linux/mm.h>
-#include <linux/scatterlist.h>
-#include <linux/dma-debug.h>
-#include <asm/cacheflush.h>
-#include <asm/io.h>
+extern struct dma_map_ops *dma_ops;
+extern void no_iommu_init(void);
+
+static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+{
+       return dma_ops;
+}
+
 #include <asm-generic/dma-coherent.h>
+#include <asm-generic/dma-mapping-common.h>
+
+static inline int dma_supported(struct device *dev, u64 mask)
+{
+       struct dma_map_ops *ops = get_dma_ops(dev);
 
-extern struct bus_type pci_bus_type;
+       if (ops->dma_supported)
+               return ops->dma_supported(dev, mask);
 
-#define dma_supported(dev, mask)       (1)
+       return 1;
+}
 
 static inline int dma_set_mask(struct device *dev, u64 mask)
 {
+       struct dma_map_ops *ops = get_dma_ops(dev);
+
        if (!dev->dma_mask || !dma_supported(dev, mask))
                return -EIO;
+       if (ops->set_dma_mask)
+               return ops->set_dma_mask(dev, mask);
 
        *dev->dma_mask = mask;
 
        return 0;
 }
 
-void *dma_alloc_coherent(struct device *dev, size_t size,
-                        dma_addr_t *dma_handle, gfp_t flag);
-
-void dma_free_coherent(struct device *dev, size_t size,
-                      void *vaddr, dma_addr_t dma_handle);
-
 void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
                    enum dma_data_direction dir);
 
 #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)
-#define dma_is_consistent(d, h) (1)
-
-static inline dma_addr_t dma_map_single(struct device *dev,
-                                       void *ptr, size_t size,
-                                       enum dma_data_direction dir)
-{
-       dma_addr_t addr = virt_to_phys(ptr);
-
-#if defined(CONFIG_PCI) && !defined(CONFIG_SH_PCIDMA_NONCOHERENT)
-       if (dev->bus == &pci_bus_type)
-               return addr;
-#endif
-       dma_cache_sync(dev, ptr, size, dir);
-
-       debug_dma_map_page(dev, virt_to_page(ptr),
-                          (unsigned long)ptr & ~PAGE_MASK, size,
-                          dir, addr, true);
-
-       return addr;
-}
-
-static inline void dma_unmap_single(struct device *dev, dma_addr_t addr,
-                                   size_t size, enum dma_data_direction dir)
-{
-       debug_dma_unmap_page(dev, addr, size, dir, true);
-}
 
-static inline int dma_map_sg(struct device *dev, struct scatterlist *sg,
-                            int nents, enum dma_data_direction dir)
-{
-       int i;
-
-       for (i = 0; i < nents; i++) {
-#if !defined(CONFIG_PCI) || defined(CONFIG_SH_PCIDMA_NONCOHERENT)
-               dma_cache_sync(dev, sg_virt(&sg[i]), sg[i].length, dir);
+#ifdef CONFIG_DMA_COHERENT
+#define dma_is_consistent(d, h) (1)
+#else
+#define dma_is_consistent(d, h) (0)
 #endif
-               sg[i].dma_address = sg_phys(&sg[i]);
-               sg[i].dma_length = sg[i].length;
-       }
 
-       debug_dma_map_sg(dev, sg, nents, i, dir);
-
-       return nents;
-}
-
-static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
-                               int nents, enum dma_data_direction dir)
-{
-       debug_dma_unmap_sg(dev, sg, nents, dir);
-}
-
-static inline dma_addr_t dma_map_page(struct device *dev, struct page *page,
-                                     unsigned long offset, size_t size,
-                                     enum dma_data_direction dir)
-{
-       return dma_map_single(dev, page_address(page) + offset, size, dir);
-}
-
-static inline void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
-                                 size_t size, enum dma_data_direction dir)
-{
-       dma_unmap_single(dev, dma_address, size, dir);
-}
-
-static inline void __dma_sync_single(struct device *dev, dma_addr_t dma_handle,
-                                  size_t size, enum dma_data_direction dir)
+static inline int dma_get_cache_alignment(void)
 {
-#if defined(CONFIG_PCI) && !defined(CONFIG_SH_PCIDMA_NONCOHERENT)
-       if (dev->bus == &pci_bus_type)
-               return;
-#endif
-       dma_cache_sync(dev, phys_to_virt(dma_handle), size, dir);
+       /*
+        * Each processor family will define its own L1_CACHE_SHIFT,
+        * L1_CACHE_BYTES wraps to this, so this is always safe.
+        */
+       return L1_CACHE_BYTES;
 }
 
-static inline void dma_sync_single_range(struct device *dev,
-                                        dma_addr_t dma_handle,
-                                        unsigned long offset, size_t size,
-                                        enum dma_data_direction dir)
+static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
 {
-#if defined(CONFIG_PCI) && !defined(CONFIG_SH_PCIDMA_NONCOHERENT)
-       if (dev->bus == &pci_bus_type)
-               return;
-#endif
-       dma_cache_sync(dev, phys_to_virt(dma_handle) + offset, size, dir);
-}
+       struct dma_map_ops *ops = get_dma_ops(dev);
 
-static inline void __dma_sync_sg(struct device *dev, struct scatterlist *sg,
-                              int nelems, enum dma_data_direction dir)
-{
-       int i;
+       if (ops->mapping_error)
+               return ops->mapping_error(dev, dma_addr);
 
-       for (i = 0; i < nelems; i++) {
-#if !defined(CONFIG_PCI) || defined(CONFIG_SH_PCIDMA_NONCOHERENT)
-               dma_cache_sync(dev, sg_virt(&sg[i]), sg[i].length, dir);
-#endif
-               sg[i].dma_address = sg_phys(&sg[i]);
-               sg[i].dma_length = sg[i].length;
-       }
+       return dma_addr == 0;
 }
 
-static inline void dma_sync_single_for_cpu(struct device *dev,
-                                          dma_addr_t dma_handle, size_t size,
-                                          enum dma_data_direction dir)
+static inline void *dma_alloc_coherent(struct device *dev, size_t size,
+                                      dma_addr_t *dma_handle, gfp_t gfp)
 {
-       __dma_sync_single(dev, dma_handle, size, dir);
-       debug_dma_sync_single_for_cpu(dev, dma_handle, size, dir);
-}
+       struct dma_map_ops *ops = get_dma_ops(dev);
+       void *memory;
 
-static inline void dma_sync_single_for_device(struct device *dev,
-                                             dma_addr_t dma_handle,
-                                             size_t size,
-                                             enum dma_data_direction dir)
-{
-       __dma_sync_single(dev, dma_handle, size, dir);
-       debug_dma_sync_single_for_device(dev, dma_handle, size, dir);
-}
+       if (dma_alloc_from_coherent(dev, size, dma_handle, &memory))
+               return memory;
+       if (!ops->alloc_coherent)
+               return NULL;
 
-static inline void dma_sync_single_range_for_cpu(struct device *dev,
-                                                dma_addr_t dma_handle,
-                                                unsigned long offset,
-                                                size_t size,
-                                                enum dma_data_direction direction)
-{
-       dma_sync_single_for_cpu(dev, dma_handle+offset, size, direction);
-       debug_dma_sync_single_range_for_cpu(dev, dma_handle,
-                                           offset, size, direction);
-}
+       memory = ops->alloc_coherent(dev, size, dma_handle, gfp);
+       debug_dma_alloc_coherent(dev, size, *dma_handle, memory);
 
-static inline void dma_sync_single_range_for_device(struct device *dev,
-                                                   dma_addr_t dma_handle,
-                                                   unsigned long offset,
-                                                   size_t size,
-                                                   enum dma_data_direction direction)
-{
-       dma_sync_single_for_device(dev, dma_handle+offset, size, direction);
-       debug_dma_sync_single_range_for_device(dev, dma_handle,
-                                              offset, size, direction);
+       return memory;
 }
 
-
-static inline void dma_sync_sg_for_cpu(struct device *dev,
-                                      struct scatterlist *sg, int nelems,
-                                      enum dma_data_direction dir)
+static inline void dma_free_coherent(struct device *dev, size_t size,
+                                    void *vaddr, dma_addr_t dma_handle)
 {
-       __dma_sync_sg(dev, sg, nelems, dir);
-       debug_dma_sync_sg_for_cpu(dev, sg, nelems, dir);
-}
+       struct dma_map_ops *ops = get_dma_ops(dev);
 
-static inline void dma_sync_sg_for_device(struct device *dev,
-                                         struct scatterlist *sg, int nelems,
-                                         enum dma_data_direction dir)
-{
-       __dma_sync_sg(dev, sg, nelems, dir);
-       debug_dma_sync_sg_for_device(dev, sg, nelems, dir);
-}
+       WARN_ON(irqs_disabled());       /* for portability */
 
-static inline int dma_get_cache_alignment(void)
-{
-       /*
-        * Each processor family will define its own L1_CACHE_SHIFT,
-        * L1_CACHE_BYTES wraps to this, so this is always safe.
-        */
-       return L1_CACHE_BYTES;
-}
+       if (dma_release_from_coherent(dev, get_order(size), vaddr))
+               return;
 
-static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
-{
-       return dma_addr == 0;
+       debug_dma_free_coherent(dev, size, vaddr, dma_handle);
+       if (ops->free_coherent)
+               ops->free_coherent(dev, size, vaddr, dma_handle);
 }
 
-#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY
-
-extern int
-dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
-                           dma_addr_t device_addr, size_t size, int flags);
-
-extern void
-dma_release_declared_memory(struct device *dev);
-
-extern void *
-dma_mark_declared_memory_occupied(struct device *dev,
-                                 dma_addr_t device_addr, size_t size);
+/* arch/sh/mm/consistent.c */
+extern void *dma_generic_alloc_coherent(struct device *dev, size_t size,
+                                       dma_addr_t *dma_addr, gfp_t flag);
+extern void dma_generic_free_coherent(struct device *dev, size_t size,
+                                     void *vaddr, dma_addr_t dma_handle);
 
 #endif /* __ASM_SH_DMA_MAPPING_H */
index ced6795..bdccbbf 100644 (file)
 #define DWARF_ARCH_RA_REG      17
 
 #ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+#include <linux/bug.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
 /*
  * Read either the frame pointer (r14) or the stack pointer (r15).
  * NOTE: this MUST be inlined.
@@ -241,6 +247,12 @@ struct dwarf_cie {
 
        unsigned long flags;
 #define DWARF_CIE_Z_AUGMENTATION       (1 << 0)
+
+       /*
+        * 'mod' will be non-NULL if this CIE came from a module's
+        * .eh_frame section.
+        */
+       struct module *mod;
 };
 
 /**
@@ -255,6 +267,12 @@ struct dwarf_fde {
        unsigned char *instructions;
        unsigned char *end;
        struct list_head link;
+
+       /*
+        * 'mod' will be non-NULL if this FDE came from a module's
+        * .eh_frame section.
+        */
+       struct module *mod;
 };
 
 /**
@@ -364,6 +382,12 @@ static inline unsigned int DW_CFA_operand(unsigned long insn)
 
 extern struct dwarf_frame *dwarf_unwind_stack(unsigned long,
                                              struct dwarf_frame *);
+extern void dwarf_free_frame(struct dwarf_frame *);
+
+extern int module_dwarf_finalize(const Elf_Ehdr *, const Elf_Shdr *,
+                                struct module *);
+extern void module_dwarf_cleanup(struct module *);
+
 #endif /* !__ASSEMBLY__ */
 
 #define CFI_STARTPROC  .cfi_startproc
@@ -391,6 +415,10 @@ extern struct dwarf_frame *dwarf_unwind_stack(unsigned long,
 static inline void dwarf_unwinder_init(void)
 {
 }
+
+#define module_dwarf_finalize(hdr, sechdrs, me)        (0)
+#define module_dwarf_cleanup(mod)              do { } while (0)
+
 #endif
 
 #endif /* CONFIG_DWARF_UNWINDER */
index 721fcc4..5ac1e40 100644 (file)
@@ -14,9 +14,9 @@
 #define _ASM_FIXMAP_H
 
 #include <linux/kernel.h>
+#include <linux/threads.h>
 #include <asm/page.h>
 #ifdef CONFIG_HIGHMEM
-#include <linux/threads.h>
 #include <asm/kmap_types.h>
 #endif
 
  * fix-mapped?
  */
 enum fixed_addresses {
-#define FIX_N_COLOURS 16
+       /*
+        * The FIX_CMAP entries are used by kmap_coherent() to get virtual
+        * addresses which are of a known color, and so their values are
+        * important. __fix_to_virt(FIX_CMAP_END - n) must give an address
+        * which is the same color as a page (n<<PAGE_SHIFT).
+        */
+#define FIX_N_COLOURS 8
        FIX_CMAP_BEGIN,
-       FIX_CMAP_END = FIX_CMAP_BEGIN + FIX_N_COLOURS,
+       FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS) - 1,
        FIX_UNCACHED,
 #ifdef CONFIG_HIGHMEM
        FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
index 1d3aee0..fb6bbb9 100644 (file)
@@ -18,16 +18,15 @@ static inline void grab_fpu(struct pt_regs *regs)
 
 struct task_struct;
 
-extern void save_fpu(struct task_struct *__tsk, struct pt_regs *regs);
+extern void save_fpu(struct task_struct *__tsk);
+void fpu_state_restore(struct pt_regs *regs);
 #else
 
+#define save_fpu(tsk)          do { } while (0)
 #define release_fpu(regs)      do { } while (0)
 #define grab_fpu(regs)         do { } while (0)
+#define fpu_state_restore(regs)        do { } while (0)
 
-static inline void save_fpu(struct task_struct *tsk, struct pt_regs *regs)
-{
-       clear_tsk_thread_flag(tsk, TIF_USEDFPU);
-}
 #endif
 
 struct user_regset;
@@ -39,19 +38,28 @@ extern int fpregs_get(struct task_struct *target,
                      unsigned int pos, unsigned int count,
                      void *kbuf, void __user *ubuf);
 
+static inline void __unlazy_fpu(struct task_struct *tsk, struct pt_regs *regs)
+{
+       if (task_thread_info(tsk)->status & TS_USEDFPU) {
+               task_thread_info(tsk)->status &= ~TS_USEDFPU;
+               save_fpu(tsk);
+               release_fpu(regs);
+       } else
+               tsk->fpu_counter = 0;
+}
+
 static inline void unlazy_fpu(struct task_struct *tsk, struct pt_regs *regs)
 {
        preempt_disable();
-       if (test_tsk_thread_flag(tsk, TIF_USEDFPU))
-               save_fpu(tsk, regs);
+       __unlazy_fpu(tsk, regs);
        preempt_enable();
 }
 
 static inline void clear_fpu(struct task_struct *tsk, struct pt_regs *regs)
 {
        preempt_disable();
-       if (test_tsk_thread_flag(tsk, TIF_USEDFPU)) {
-               clear_tsk_thread_flag(tsk, TIF_USEDFPU);
+       if (task_thread_info(tsk)->status & TS_USEDFPU) {
+               task_thread_info(tsk)->status &= ~TS_USEDFPU;
                release_fpu(regs);
        }
        preempt_enable();
index 12f3a31..13e9966 100644 (file)
@@ -35,4 +35,21 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)
 #endif /* __ASSEMBLY__ */
 #endif /* CONFIG_FUNCTION_TRACER */
 
+#ifndef __ASSEMBLY__
+
+/* arch/sh/kernel/return_address.c */
+extern void *return_address(unsigned int);
+
+#define HAVE_ARCH_CALLER_ADDR
+
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+#define CALLER_ADDR1 ((unsigned long)return_address(1))
+#define CALLER_ADDR2 ((unsigned long)return_address(2))
+#define CALLER_ADDR3 ((unsigned long)return_address(3))
+#define CALLER_ADDR4 ((unsigned long)return_address(4))
+#define CALLER_ADDR5 ((unsigned long)return_address(5))
+#define CALLER_ADDR6 ((unsigned long)return_address(6))
+
+#endif /* __ASSEMBLY__ */
+
 #endif /* __ASM_SH_FTRACE_H */
index 61f93da..f8d9a73 100644 (file)
@@ -20,7 +20,7 @@
 #endif
 
 #define ARCH_NR_GPIOS 512
-#include <asm-generic/gpio.h>
+#include <linux/sh_pfc.h>
 
 #ifdef CONFIG_GPIOLIB
 
@@ -53,84 +53,4 @@ static inline int irq_to_gpio(unsigned int irq)
 
 #endif /* CONFIG_GPIOLIB */
 
-typedef unsigned short pinmux_enum_t;
-typedef unsigned short pinmux_flag_t;
-
-#define PINMUX_TYPE_NONE            0
-#define PINMUX_TYPE_FUNCTION        1
-#define PINMUX_TYPE_GPIO            2
-#define PINMUX_TYPE_OUTPUT          3
-#define PINMUX_TYPE_INPUT           4
-#define PINMUX_TYPE_INPUT_PULLUP    5
-#define PINMUX_TYPE_INPUT_PULLDOWN  6
-
-#define PINMUX_FLAG_TYPE            (0x7)
-#define PINMUX_FLAG_WANT_PULLUP     (1 << 3)
-#define PINMUX_FLAG_WANT_PULLDOWN   (1 << 4)
-
-#define PINMUX_FLAG_DBIT_SHIFT      5
-#define PINMUX_FLAG_DBIT            (0x1f << PINMUX_FLAG_DBIT_SHIFT)
-#define PINMUX_FLAG_DREG_SHIFT      10
-#define PINMUX_FLAG_DREG            (0x3f << PINMUX_FLAG_DREG_SHIFT)
-
-struct pinmux_gpio {
-       pinmux_enum_t enum_id;
-       pinmux_flag_t flags;
-};
-
-#define PINMUX_GPIO(gpio, data_or_mark) [gpio] = { data_or_mark }
-#define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0
-
-struct pinmux_cfg_reg {
-       unsigned long reg, reg_width, field_width;
-       unsigned long *cnt;
-       pinmux_enum_t *enum_ids;
-};
-
-#define PINMUX_CFG_REG(name, r, r_width, f_width) \
-       .reg = r, .reg_width = r_width, .field_width = f_width,         \
-       .cnt = (unsigned long [r_width / f_width]) {}, \
-       .enum_ids = (pinmux_enum_t [(r_width / f_width) * (1 << f_width)]) \
-
-struct pinmux_data_reg {
-       unsigned long reg, reg_width, reg_shadow;
-       pinmux_enum_t *enum_ids;
-};
-
-#define PINMUX_DATA_REG(name, r, r_width) \
-       .reg = r, .reg_width = r_width, \
-       .enum_ids = (pinmux_enum_t [r_width]) \
-
-struct pinmux_range {
-       pinmux_enum_t begin;
-       pinmux_enum_t end;
-       pinmux_enum_t force;
-};
-
-struct pinmux_info {
-       char *name;
-       pinmux_enum_t reserved_id;
-       struct pinmux_range data;
-       struct pinmux_range input;
-       struct pinmux_range input_pd;
-       struct pinmux_range input_pu;
-       struct pinmux_range output;
-       struct pinmux_range mark;
-       struct pinmux_range function;
-
-       unsigned first_gpio, last_gpio;
-
-       struct pinmux_gpio *gpios;
-       struct pinmux_cfg_reg *cfg_regs;
-       struct pinmux_data_reg *data_regs;
-
-       pinmux_enum_t *gpio_data;
-       unsigned int gpio_data_size;
-
-       unsigned long *gpio_in_use;
-       struct gpio_chip chip;
-};
-
-int register_pinmux(struct pinmux_info *pip);
-
 #endif /* __ASM_SH_GPIO_H */
index a5be4af..48b1913 100644 (file)
@@ -1,9 +1,16 @@
 #ifndef __ASM_SH_HARDIRQ_H
 #define __ASM_SH_HARDIRQ_H
 
-extern void ack_bad_irq(unsigned int irq);
-#define ack_bad_irq ack_bad_irq
+#include <linux/threads.h>
+#include <linux/irq.h>
+
+typedef struct {
+       unsigned int __softirq_pending;
+       unsigned int __nmi_count;               /* arch dependent */
+} ____cacheline_aligned irq_cpustat_t;
 
-#include <asm-generic/hardirq.h>
+#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
+
+extern void ack_bad_irq(unsigned int irq);
 
 #endif /* __ASM_SH_HARDIRQ_H */
index 5be45ea..512cd3e 100644 (file)
 #define ctrl_outl              __raw_writel
 #define ctrl_outq              __raw_writeq
 
+extern unsigned long generic_io_base;
+
 static inline void ctrl_delay(void)
 {
-#ifdef CONFIG_CPU_SH4
-       __raw_readw(CCN_PVR);
-#elif defined(P2SEG)
-       __raw_readw(P2SEG);
-#else
-#error "Need a dummy address for delay"
-#endif
+       __raw_readw(generic_io_base);
 }
 
 #define __BUILD_MEMORY_STRING(bwlq, type)                              \
@@ -186,8 +182,6 @@ __BUILD_MEMORY_STRING(q, u64)
 
 #define IO_SPACE_LIMIT 0xffffffff
 
-extern unsigned long generic_io_base;
-
 /*
  * This function provides a method for the generic case where a
  * board-specific ioport_map simply needs to return the port + some
@@ -246,7 +240,7 @@ void __iounmap(void __iomem *addr);
 static inline void __iomem *
 __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags)
 {
-#if defined(CONFIG_SUPERH32) && !defined(CONFIG_PMB_FIXED)
+#if defined(CONFIG_SUPERH32) && !defined(CONFIG_PMB_FIXED) && !defined(CONFIG_PMB)
        unsigned long last_addr = offset + size - 1;
 #endif
        void __iomem *ret;
@@ -255,7 +249,7 @@ __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags)
        if (ret)
                return ret;
 
-#if defined(CONFIG_SUPERH32) && !defined(CONFIG_PMB_FIXED)
+#if defined(CONFIG_SUPERH32) && !defined(CONFIG_PMB_FIXED) && !defined(CONFIG_PMB)
        /*
         * For P1 and P2 space this is trivial, as everything is already
         * mapped. Uncached access for P1 addresses are done through P2.
index 46e71da..a741153 100644 (file)
@@ -1,34 +1,9 @@
 #ifndef __ASM_SH_IRQFLAGS_H
 #define __ASM_SH_IRQFLAGS_H
 
-#ifdef CONFIG_SUPERH32
-#include "irqflags_32.h"
-#else
-#include "irqflags_64.h"
-#endif
+#define RAW_IRQ_DISABLED       0xf0
+#define RAW_IRQ_ENABLED                0x00
 
-#define raw_local_save_flags(flags) \
-               do { (flags) = __raw_local_save_flags(); } while (0)
-
-static inline int raw_irqs_disabled_flags(unsigned long flags)
-{
-       return (flags != 0);
-}
-
-static inline int raw_irqs_disabled(void)
-{
-       unsigned long flags = __raw_local_save_flags();
-
-       return raw_irqs_disabled_flags(flags);
-}
-
-#define raw_local_irq_save(flags) \
-               do { (flags) = __raw_local_irq_save(); } while (0)
-
-static inline void raw_local_irq_restore(unsigned long flags)
-{
-       if ((flags & 0xf0) != 0xf0)
-               raw_local_irq_enable();
-}
+#include <asm-generic/irqflags.h>
 
 #endif /* __ASM_SH_IRQFLAGS_H */
diff --git a/arch/sh/include/asm/irqflags_32.h b/arch/sh/include/asm/irqflags_32.h
deleted file mode 100644 (file)
index 60218f5..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef __ASM_SH_IRQFLAGS_32_H
-#define __ASM_SH_IRQFLAGS_32_H
-
-static inline void raw_local_irq_enable(void)
-{
-       unsigned long __dummy0, __dummy1;
-
-       __asm__ __volatile__ (
-               "stc    sr, %0\n\t"
-               "and    %1, %0\n\t"
-#ifdef CONFIG_CPU_HAS_SR_RB
-               "stc    r6_bank, %1\n\t"
-               "or     %1, %0\n\t"
-#endif
-               "ldc    %0, sr\n\t"
-               : "=&r" (__dummy0), "=r" (__dummy1)
-               : "1" (~0x000000f0)
-               : "memory"
-       );
-}
-
-static inline void raw_local_irq_disable(void)
-{
-       unsigned long flags;
-
-       __asm__ __volatile__ (
-               "stc    sr, %0\n\t"
-               "or     #0xf0, %0\n\t"
-               "ldc    %0, sr\n\t"
-               : "=&z" (flags)
-               : /* no inputs */
-               : "memory"
-       );
-}
-
-static inline void set_bl_bit(void)
-{
-       unsigned long __dummy0, __dummy1;
-
-       __asm__ __volatile__ (
-               "stc    sr, %0\n\t"
-               "or     %2, %0\n\t"
-               "and    %3, %0\n\t"
-               "ldc    %0, sr\n\t"
-               : "=&r" (__dummy0), "=r" (__dummy1)
-               : "r" (0x10000000), "r" (0xffffff0f)
-               : "memory"
-       );
-}
-
-static inline void clear_bl_bit(void)
-{
-       unsigned long __dummy0, __dummy1;
-
-       __asm__ __volatile__ (
-               "stc    sr, %0\n\t"
-               "and    %2, %0\n\t"
-               "ldc    %0, sr\n\t"
-               : "=&r" (__dummy0), "=r" (__dummy1)
-               : "1" (~0x10000000)
-               : "memory"
-       );
-}
-
-static inline unsigned long __raw_local_save_flags(void)
-{
-       unsigned long flags;
-
-       __asm__ __volatile__ (
-               "stc    sr, %0\n\t"
-               "and    #0xf0, %0\n\t"
-               : "=&z" (flags)
-               : /* no inputs */
-               : "memory"
-       );
-
-       return flags;
-}
-
-static inline unsigned long __raw_local_irq_save(void)
-{
-       unsigned long flags, __dummy;
-
-       __asm__ __volatile__ (
-               "stc    sr, %1\n\t"
-               "mov    %1, %0\n\t"
-               "or     #0xf0, %0\n\t"
-               "ldc    %0, sr\n\t"
-               "mov    %1, %0\n\t"
-               "and    #0xf0, %0\n\t"
-               : "=&z" (flags), "=&r" (__dummy)
-               : /* no inputs */
-               : "memory"
-       );
-
-       return flags;
-}
-
-#endif /* __ASM_SH_IRQFLAGS_32_H */
diff --git a/arch/sh/include/asm/irqflags_64.h b/arch/sh/include/asm/irqflags_64.h
deleted file mode 100644 (file)
index 88f6522..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef __ASM_SH_IRQFLAGS_64_H
-#define __ASM_SH_IRQFLAGS_64_H
-
-#include <cpu/registers.h>
-
-#define SR_MASK_LL     0x00000000000000f0LL
-#define SR_BL_LL       0x0000000010000000LL
-
-static inline void raw_local_irq_enable(void)
-{
-       unsigned long long __dummy0, __dummy1 = ~SR_MASK_LL;
-
-       __asm__ __volatile__("getcon    " __SR ", %0\n\t"
-                            "and       %0, %1, %0\n\t"
-                            "putcon    %0, " __SR "\n\t"
-                            : "=&r" (__dummy0)
-                            : "r" (__dummy1));
-}
-
-static inline void raw_local_irq_disable(void)
-{
-       unsigned long long __dummy0, __dummy1 = SR_MASK_LL;
-
-       __asm__ __volatile__("getcon    " __SR ", %0\n\t"
-                            "or        %0, %1, %0\n\t"
-                            "putcon    %0, " __SR "\n\t"
-                            : "=&r" (__dummy0)
-                            : "r" (__dummy1));
-}
-
-static inline void set_bl_bit(void)
-{
-       unsigned long long __dummy0, __dummy1 = SR_BL_LL;
-
-       __asm__ __volatile__("getcon    " __SR ", %0\n\t"
-                            "or        %0, %1, %0\n\t"
-                            "putcon    %0, " __SR "\n\t"
-                            : "=&r" (__dummy0)
-                            : "r" (__dummy1));
-
-}
-
-static inline void clear_bl_bit(void)
-{
-       unsigned long long __dummy0, __dummy1 = ~SR_BL_LL;
-
-       __asm__ __volatile__("getcon    " __SR ", %0\n\t"
-                            "and       %0, %1, %0\n\t"
-                            "putcon    %0, " __SR "\n\t"
-                            : "=&r" (__dummy0)
-                            : "r" (__dummy1));
-}
-
-static inline unsigned long __raw_local_save_flags(void)
-{
-       unsigned long long __dummy = SR_MASK_LL;
-       unsigned long flags;
-
-       __asm__ __volatile__ (
-               "getcon " __SR ", %0\n\t"
-               "and    %0, %1, %0"
-               : "=&r" (flags)
-               : "r" (__dummy));
-
-       return flags;
-}
-
-static inline unsigned long __raw_local_irq_save(void)
-{
-       unsigned long long __dummy0, __dummy1 = SR_MASK_LL;
-       unsigned long flags;
-
-       __asm__ __volatile__ (
-               "getcon " __SR ", %1\n\t"
-               "or     %1, r63, %0\n\t"
-               "or     %1, %2, %1\n\t"
-               "putcon %1, " __SR "\n\t"
-               "and    %0, %2, %0"
-               : "=&r" (flags), "=&r" (__dummy0)
-               : "r" (__dummy1));
-
-       return flags;
-}
-
-#endif /* __ASM_SH_IRQFLAGS_64_H */
index f596303..c7426ad 100644 (file)
@@ -7,12 +7,16 @@
 #define PMB_PASCR              0xff000070
 #define PMB_IRMCR              0xff000078
 
+#define PASCR_SE               0x80000000
+
 #define PMB_ADDR               0xf6100000
 #define PMB_DATA               0xf7100000
 #define PMB_ENTRY_MAX          16
 #define PMB_E_MASK             0x0000000f
 #define PMB_E_SHIFT            8
 
+#define PMB_PFN_MASK           0xff000000
+
 #define PMB_SZ_16M             0x00000000
 #define PMB_SZ_64M             0x00000010
 #define PMB_SZ_128M            0x00000080
@@ -62,17 +66,10 @@ struct pmb_entry {
 };
 
 /* arch/sh/mm/pmb.c */
-int __set_pmb_entry(unsigned long vpn, unsigned long ppn,
-                   unsigned long flags, int *entry);
-int set_pmb_entry(struct pmb_entry *pmbe);
-void clear_pmb_entry(struct pmb_entry *pmbe);
-struct pmb_entry *pmb_alloc(unsigned long vpn, unsigned long ppn,
-                           unsigned long flags);
-void pmb_free(struct pmb_entry *pmbe);
 long pmb_remap(unsigned long virt, unsigned long phys,
               unsigned long size, unsigned long flags);
 void pmb_unmap(unsigned long addr);
+int pmb_init(void);
 #endif /* __ASSEMBLY__ */
 
 #endif /* __MMU_H */
-
index 4163950..67f3999 100644 (file)
@@ -3,8 +3,6 @@
 
 #ifdef __KERNEL__
 
-#include <linux/dma-mapping.h>
-
 /* Can be used to override the logic in pci_scan_bus for skipping
    already-configured bus numbers - to be used for buggy BIOSes
    or architectures with incomplete PCI setup by the loader */
@@ -54,30 +52,18 @@ static inline void pcibios_penalize_isa_irq(int irq, int active)
  * address space.  The networking and block device layers use
  * this boolean for bounce buffer decisions.
  */
-#define PCI_DMA_BUS_IS_PHYS    (1)
-
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <asm/scatterlist.h>
-#include <linux/string.h>
-#include <asm/io.h>
+#define PCI_DMA_BUS_IS_PHYS    (dma_ops->is_phys)
 
 /* pci_unmap_{single,page} being a nop depends upon the
  * configuration.
  */
-#ifdef CONFIG_SH_PCIDMA_NONCOHERENT
-#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)      \
-       dma_addr_t ADDR_NAME;
-#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)                \
-       __u32 LEN_NAME;
-#define pci_unmap_addr(PTR, ADDR_NAME)                 \
-       ((PTR)->ADDR_NAME)
-#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)                \
-       (((PTR)->ADDR_NAME) = (VAL))
-#define pci_unmap_len(PTR, LEN_NAME)                   \
-       ((PTR)->LEN_NAME)
-#define pci_unmap_len_set(PTR, LEN_NAME, VAL)          \
-       (((PTR)->LEN_NAME) = (VAL))
+#ifdef CONFIG_DMA_NONCOHERENT
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)      dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)                __u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)         ((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)        (((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)           ((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)  (((PTR)->LEN_NAME) = (VAL))
 #else
 #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
 #define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
index 11a3022..3d0c9f3 100644 (file)
@@ -1,8 +1,35 @@
 #ifndef __ASM_SH_PERF_EVENT_H
 #define __ASM_SH_PERF_EVENT_H
 
-/* SH only supports software events through this interface. */
-static inline void set_perf_event_pending(void) {}
+struct hw_perf_event;
+
+#define MAX_HWEVENTS   2
+
+struct sh_pmu {
+       const char      *name;
+       unsigned int    num_events;
+       void            (*disable_all)(void);
+       void            (*enable_all)(void);
+       void            (*enable)(struct hw_perf_event *, int);
+       void            (*disable)(struct hw_perf_event *, int);
+       u64             (*read)(int);
+       int             (*event_map)(int);
+       unsigned int    max_events;
+       unsigned long   raw_event_mask;
+       const int       (*cache_events)[PERF_COUNT_HW_CACHE_MAX]
+                                      [PERF_COUNT_HW_CACHE_OP_MAX]
+                                      [PERF_COUNT_HW_CACHE_RESULT_MAX];
+};
+
+/* arch/sh/kernel/perf_event.c */
+extern int register_sh_pmu(struct sh_pmu *);
+extern int reserve_pmc_hardware(void);
+extern void release_pmc_hardware(void);
+
+static inline void set_perf_event_pending(void)
+{
+       /* Nothing to see here, move along. */
+}
 
 #define PERF_EVENT_INDEX_OFFSET        0
 
index 4f3efa7..ba3046e 100644 (file)
@@ -75,13 +75,31 @@ static inline unsigned long long neff_sign_extend(unsigned long val)
 #define USER_PTRS_PER_PGD      (TASK_SIZE/PGDIR_SIZE)
 #define FIRST_USER_ADDRESS     0
 
-#ifdef CONFIG_32BIT
-#define PHYS_ADDR_MASK         0xffffffff
+#define PHYS_ADDR_MASK29               0x1fffffff
+#define PHYS_ADDR_MASK32               0xffffffff
+
+#ifdef CONFIG_PMB
+static inline unsigned long phys_addr_mask(void)
+{
+       /* Is the MMU in 29bit mode? */
+       if (__in_29bit_mode())
+               return PHYS_ADDR_MASK29;
+
+       return PHYS_ADDR_MASK32;
+}
+#elif defined(CONFIG_32BIT)
+static inline unsigned long phys_addr_mask(void)
+{
+       return PHYS_ADDR_MASK32;
+}
 #else
-#define PHYS_ADDR_MASK         0x1fffffff
+static inline unsigned long phys_addr_mask(void)
+{
+       return PHYS_ADDR_MASK29;
+}
 #endif
 
-#define PTE_PHYS_MASK          (PHYS_ADDR_MASK & PAGE_MASK)
+#define PTE_PHYS_MASK          (phys_addr_mask() & PAGE_MASK)
 #define PTE_FLAGS_MASK         (~(PTE_PHYS_MASK) << PAGE_SHIFT)
 
 #ifdef CONFIG_SUPERH32
index c0d359c..b354355 100644 (file)
@@ -108,7 +108,7 @@ static inline unsigned long copy_ptea_attributes(unsigned long x)
 #define _PAGE_CLEAR_FLAGS      (_PAGE_PROTNONE | _PAGE_ACCESSED | _PAGE_FILE)
 #endif
 
-#define _PAGE_FLAGS_HARDWARE_MASK      (PHYS_ADDR_MASK & ~(_PAGE_CLEAR_FLAGS))
+#define _PAGE_FLAGS_HARDWARE_MASK      (phys_addr_mask() & ~(_PAGE_CLEAR_FLAGS))
 
 /* Hardware flags, page size encoding */
 #if !defined(CONFIG_MMU)
index 9a87149..1f3d6fa 100644 (file)
@@ -56,6 +56,7 @@ asmlinkage void __init sh_cpu_init(void);
 #define SR_DSP         0x00001000
 #define SR_IMASK       0x000000f0
 #define SR_FD          0x00008000
+#define SR_MD          0x40000000
 
 /*
  * DSP structure and data
@@ -136,7 +137,7 @@ struct mm_struct;
 extern void release_thread(struct task_struct *);
 
 /* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)   do { } while (0)
+void prepare_to_copy(struct task_struct *tsk);
 
 /*
  * create a kernel thread without removing it from tasklists
index 327cc2e..e38d1d4 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __ASM_SH_SCATTERLIST_H
 #define __ASM_SH_SCATTERLIST_H
 
-#define ISA_DMA_THRESHOLD      PHYS_ADDR_MASK
+#define ISA_DMA_THRESHOLD      phys_addr_mask()
 
 #include <asm-generic/scatterlist.h>
 
index 5c8ea28..fe9c2a1 100644 (file)
@@ -2,6 +2,7 @@
 #define _ASM_SH_SUSPEND_H
 
 #ifndef __ASSEMBLY__
+#include <linux/notifier.h>
 static inline int arch_prepare_suspend(void) { return 0; }
 
 #include <asm/ptrace.h>
@@ -19,6 +20,69 @@ void sh_mobile_setup_cpuidle(void);
 static inline void sh_mobile_setup_cpuidle(void) {}
 #endif
 
+/* notifier chains for pre/post sleep hooks */
+extern struct atomic_notifier_head sh_mobile_pre_sleep_notifier_list;
+extern struct atomic_notifier_head sh_mobile_post_sleep_notifier_list;
+
+/* priority levels for notifiers */
+#define SH_MOBILE_SLEEP_BOARD  0
+#define SH_MOBILE_SLEEP_CPU    1
+#define SH_MOBILE_PRE(x)       (x)
+#define SH_MOBILE_POST(x)      (-(x))
+
+/* board code registration function for self-refresh assembly snippets */
+void sh_mobile_register_self_refresh(unsigned long flags,
+                                    void *pre_start, void *pre_end,
+                                    void *post_start, void *post_end);
+
+/* register structure for address/data information */
+struct sh_sleep_regs {
+       unsigned long stbcr;
+       unsigned long bar;
+
+       /* MMU */
+       unsigned long pteh;
+       unsigned long ptel;
+       unsigned long ttb;
+       unsigned long tea;
+       unsigned long mmucr;
+       unsigned long ptea;
+       unsigned long pascr;
+       unsigned long irmcr;
+
+       /* Cache */
+       unsigned long ccr;
+       unsigned long ramcr;
+};
+
+/* data area for low-level sleep code */
+struct sh_sleep_data {
+       /* current sleep mode (SUSP_SH_...) */
+       unsigned long mode;
+
+       /* addresses of board specific self-refresh snippets */
+       unsigned long sf_pre;
+       unsigned long sf_post;
+
+       /* address of resume code */
+       unsigned long resume;
+
+       /* register state saved and restored by the assembly code */
+       unsigned long vbr;
+       unsigned long spc;
+       unsigned long sr;
+       unsigned long sp;
+
+       /* structure for keeping register addresses */
+       struct sh_sleep_regs addr;
+
+       /* structure for saving/restoring register state */
+       struct sh_sleep_regs data;
+};
+
+/* a bitmap of supported sleep modes (SUSP_SH..) */
+extern unsigned long sh_mobile_sleep_supported;
+
 #endif
 
 /* flags passed to assembly suspend code */
@@ -27,5 +91,6 @@ static inline void sh_mobile_setup_cpuidle(void) {}
 #define SUSP_SH_RSTANDBY       (1 << 2) /* SH-Mobile R-standby mode */
 #define SUSP_SH_USTANDBY       (1 << 3) /* SH-Mobile U-standby mode */
 #define SUSP_SH_SF             (1 << 4) /* Enable self-refresh */
+#define SUSP_SH_MMU            (1 << 5) /* Save/restore MMU and cache */
 
 #endif /* _ASM_SH_SUSPEND_H */
index b5c5acd..c15415b 100644 (file)
@@ -171,10 +171,6 @@ BUILD_TRAP_HANDLER(fpu_error);
 BUILD_TRAP_HANDLER(fpu_state_restore);
 BUILD_TRAP_HANDLER(nmi);
 
-#ifdef CONFIG_BUG
-extern void handle_BUG(struct pt_regs *);
-#endif
-
 #define arch_align_stack(x) (x)
 
 struct mem_access {
index 607d413..06814f5 100644 (file)
@@ -232,4 +232,33 @@ asmlinkage void do_exception_error(unsigned long r4, unsigned long r5,
                                   unsigned long r6, unsigned long r7,
                                   struct pt_regs __regs);
 
+static inline void set_bl_bit(void)
+{
+       unsigned long __dummy0, __dummy1;
+
+       __asm__ __volatile__ (
+               "stc    sr, %0\n\t"
+               "or     %2, %0\n\t"
+               "and    %3, %0\n\t"
+               "ldc    %0, sr\n\t"
+               : "=&r" (__dummy0), "=r" (__dummy1)
+               : "r" (0x10000000), "r" (0xffffff0f)
+               : "memory"
+       );
+}
+
+static inline void clear_bl_bit(void)
+{
+       unsigned long __dummy0, __dummy1;
+
+       __asm__ __volatile__ (
+               "stc    sr, %0\n\t"
+               "and    %2, %0\n\t"
+               "ldc    %0, sr\n\t"
+               : "=&r" (__dummy0), "=r" (__dummy1)
+               : "1" (~0x10000000)
+               : "memory"
+       );
+}
+
 #endif /* __ASM_SH_SYSTEM_32_H */
index 8e4a03e..ab1dd91 100644 (file)
@@ -12,6 +12,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
+#include <cpu/registers.h>
 #include <asm/processor.h>
 
 /*
@@ -47,4 +48,29 @@ static inline reg_size_t register_align(void *val)
        return (unsigned long long)(signed long long)(signed long)val;
 }
 
+#define SR_BL_LL       0x0000000010000000LL
+
+static inline void set_bl_bit(void)
+{
+       unsigned long long __dummy0, __dummy1 = SR_BL_LL;
+
+       __asm__ __volatile__("getcon    " __SR ", %0\n\t"
+                            "or        %0, %1, %0\n\t"
+                            "putcon    %0, " __SR "\n\t"
+                            : "=&r" (__dummy0)
+                            : "r" (__dummy1));
+
+}
+
+static inline void clear_bl_bit(void)
+{
+       unsigned long long __dummy0, __dummy1 = ~SR_BL_LL;
+
+       __asm__ __volatile__("getcon    " __SR ", %0\n\t"
+                            "and       %0, %1, %0\n\t"
+                            "putcon    %0, " __SR "\n\t"
+                            : "=&r" (__dummy0)
+                            : "r" (__dummy1));
+}
+
 #endif /* __ASM_SH_SYSTEM_64_H */
index bdeb9d4..1f3d927 100644 (file)
@@ -19,6 +19,7 @@ struct thread_info {
        struct task_struct      *task;          /* main task structure */
        struct exec_domain      *exec_domain;   /* execution domain */
        unsigned long           flags;          /* low level flags */
+       __u32                   status;         /* thread synchronous flags */
        __u32                   cpu;
        int                     preempt_count; /* 0 => preemptable, <0 => BUG */
        mm_segment_t            addr_limit;     /* thread address space */
@@ -50,6 +51,7 @@ struct thread_info {
        .task           = &tsk,                 \
        .exec_domain    = &default_exec_domain, \
        .flags          = 0,                    \
+       .status         = 0,                    \
        .cpu            = 0,                    \
        .preempt_count  = INIT_PREEMPT_COUNT,   \
        .addr_limit     = KERNEL_DS,            \
@@ -111,13 +113,11 @@ extern void free_thread_info(struct thread_info *ti);
 #define TIF_SYSCALL_TRACE      0       /* syscall trace active */
 #define TIF_SIGPENDING         1       /* signal pending */
 #define TIF_NEED_RESCHED       2       /* rescheduling necessary */
-#define TIF_RESTORE_SIGMASK    3       /* restore signal mask in do_signal() */
 #define TIF_SINGLESTEP         4       /* singlestepping active */
 #define TIF_SYSCALL_AUDIT      5       /* syscall auditing active */
 #define TIF_SECCOMP            6       /* secure computing */
 #define TIF_NOTIFY_RESUME      7       /* callback before returning to user */
 #define TIF_SYSCALL_TRACEPOINT 8       /* for ftrace syscall instrumentation */
-#define TIF_USEDFPU            16      /* FPU was used by this task this quantum (SMP) */
 #define TIF_POLLING_NRFLAG     17      /* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_MEMDIE             18
 #define TIF_FREEZE             19      /* Freezing for suspend */
@@ -125,13 +125,11 @@ extern void free_thread_info(struct thread_info *ti);
 #define _TIF_SYSCALL_TRACE     (1 << TIF_SYSCALL_TRACE)
 #define _TIF_SIGPENDING                (1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED      (1 << TIF_NEED_RESCHED)
-#define _TIF_RESTORE_SIGMASK   (1 << TIF_RESTORE_SIGMASK)
 #define _TIF_SINGLESTEP                (1 << TIF_SINGLESTEP)
 #define _TIF_SYSCALL_AUDIT     (1 << TIF_SYSCALL_AUDIT)
 #define _TIF_SECCOMP           (1 << TIF_SECCOMP)
 #define _TIF_NOTIFY_RESUME     (1 << TIF_NOTIFY_RESUME)
 #define _TIF_SYSCALL_TRACEPOINT        (1 << TIF_SYSCALL_TRACEPOINT)
-#define _TIF_USEDFPU           (1 << TIF_USEDFPU)
 #define _TIF_POLLING_NRFLAG    (1 << TIF_POLLING_NRFLAG)
 #define _TIF_FREEZE            (1 << TIF_FREEZE)
 
@@ -149,13 +147,33 @@ extern void free_thread_info(struct thread_info *ti);
 /* work to do on any return to u-space */
 #define _TIF_ALLWORK_MASK      (_TIF_SYSCALL_TRACE | _TIF_SIGPENDING      | \
                                 _TIF_NEED_RESCHED  | _TIF_SYSCALL_AUDIT   | \
-                                _TIF_SINGLESTEP    | _TIF_RESTORE_SIGMASK | \
-                                _TIF_NOTIFY_RESUME | _TIF_SYSCALL_TRACEPOINT)
+                                _TIF_SINGLESTEP    | _TIF_NOTIFY_RESUME   | \
+                                _TIF_SYSCALL_TRACEPOINT)
 
 /* work to do on interrupt/exception return */
 #define _TIF_WORK_MASK         (_TIF_ALLWORK_MASK & ~(_TIF_SYSCALL_TRACE | \
                                 _TIF_SYSCALL_AUDIT | _TIF_SINGLESTEP))
 
+/*
+ * Thread-synchronous status.
+ *
+ * This is different from the flags in that nobody else
+ * ever touches our thread-synchronous status, so we don't
+ * have to worry about atomic accesses.
+ */
+#define TS_RESTORE_SIGMASK     0x0001  /* restore signal mask in do_signal() */
+#define TS_USEDFPU             0x0002  /* FPU used by this task this quantum */
+
+#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 /* !__ASSEMBLY__ */
+
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_SH_THREAD_INFO_H */
index 65e7bd2..37cdadd 100644 (file)
 
 #endif
 
+#define mc_capable()    (1)
+
+const struct cpumask *cpu_coregroup_mask(unsigned int cpu);
+
+extern cpumask_t cpu_core_map[NR_CPUS];
+
+#define topology_core_cpumask(cpu)     (&cpu_core_map[cpu])
+
 #include <asm-generic/topology.h>
 
 #endif /* _ASM_SH_TOPOLOGY_H */
index 4ca4b77..9bf9616 100644 (file)
 #define BRCR_UBDE              (1 << 0)
 #endif
 
-#ifndef __ASSEMBLY__
-/* arch/sh/kernel/cpu/ubc.S */
-extern void ubc_sleep(void);
-
-#ifdef CONFIG_UBC_WAKEUP
-extern void ubc_wakeup(void);
-#else
-#define ubc_wakeup()   do { } while (0)
-#endif
-#endif
-
 #endif /* __KERNEL__ */
 #endif /* __ASM_SH_UBC_H */
index 2fe7cee..19dfff5 100644 (file)
@@ -2,6 +2,8 @@
  * include/asm-sh/watchdog.h
  *
  * Copyright (C) 2002, 2003 Paul Mundt
+ * Copyright (C) 2009 Siemens AG
+ * Copyright (C) 2009 Valentin Sitdikov
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
 #define WTCSR_CKS_2048 0x06
 #define WTCSR_CKS_4096 0x07
 
+#if defined(CONFIG_CPU_SUBTYPE_SH7785) || defined(CONFIG_CPU_SUBTYPE_SH7780)
+/**
+ *     sh_wdt_read_cnt - Read from Counter
+ *     Reads back the WTCNT value.
+ */
+static inline __u32 sh_wdt_read_cnt(void)
+{
+       return ctrl_inl(WTCNT_R);
+}
+
+/**
+ *     sh_wdt_write_cnt - Write to Counter
+ *     @val: Value to write
+ *
+ *     Writes the given value @val to the lower byte of the timer counter.
+ *     The upper byte is set manually on each write.
+ */
+static inline void sh_wdt_write_cnt(__u32 val)
+{
+       ctrl_outl((WTCNT_HIGH << 24) | (__u32)val, WTCNT);
+}
+
+/**
+ *     sh_wdt_write_bst - Write to Counter
+ *     @val: Value to write
+ *
+ *     Writes the given value @val to the lower byte of the timer counter.
+ *     The upper byte is set manually on each write.
+ */
+static inline void sh_wdt_write_bst(__u32 val)
+{
+       ctrl_outl((WTBST_HIGH << 24) | (__u32)val, WTBST);
+}
+/**
+ *     sh_wdt_read_csr - Read from Control/Status Register
+ *
+ *     Reads back the WTCSR value.
+ */
+static inline __u32 sh_wdt_read_csr(void)
+{
+       return ctrl_inl(WTCSR_R);
+}
+
+/**
+ *     sh_wdt_write_csr - Write to Control/Status Register
+ *     @val: Value to write
+ *
+ *     Writes the given value @val to the lower byte of the control/status
+ *     register. The upper byte is set manually on each write.
+ */
+static inline void sh_wdt_write_csr(__u32 val)
+{
+       ctrl_outl((WTCSR_HIGH << 24) | (__u32)val, WTCSR);
+}
+#else
 /**
  *     sh_wdt_read_cnt - Read from Counter
  *     Reads back the WTCNT value.
@@ -103,6 +160,6 @@ static inline void sh_wdt_write_csr(__u8 val)
 {
        ctrl_outw((WTCSR_HIGH << 8) | (__u16)val, WTCSR);
 }
-
+#endif /* CONFIG_CPU_SUBTYPE_SH7785 || CONFIG_CPU_SUBTYPE_SH7780 */
 #endif /* __KERNEL__ */
 #endif /* __ASM_SH_WATCHDOG_H */
index 259f6a0..7672301 100644 (file)
@@ -2,6 +2,8 @@
  * include/asm-sh/cpu-sh4/watchdog.h
  *
  * Copyright (C) 2002, 2003 Paul Mundt
+ * Copyright (C) 2009 Siemens AG
+ * Copyright (C) 2009 Sitdikov Valentin
  *
  * 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
 #ifndef __ASM_CPU_SH4_WATCHDOG_H
 #define __ASM_CPU_SH4_WATCHDOG_H
 
+#if defined(CONFIG_CPU_SUBTYPE_SH7785) || defined(CONFIG_CPU_SUBTYPE_SH7780)
+/* Prefix definition */
+#define WTBST_HIGH     0x55
+/* Register definitions */
+#define WTCNT_R                0xffcc0010 /*WDTCNT*/
+#define WTCSR          0xffcc0004 /*WDTCSR*/
+#define WTCNT          0xffcc0000 /*WDTST*/
+#define WTST           WTCNT
+#define WTBST          0xffcc0008 /*WDTBST*/
+#else
 /* Register definitions */
 #define WTCNT          0xffc00008
 #define WTCSR          0xffc0000c
+#endif
 
 /* Bit definitions */
 #define WTCSR_TME      0x80
index 03f3583..4a674d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Platform defintions for Titan
+ * Platform definitions for Titan
  */
 #ifndef _ASM_SH_TITAN_H
 #define _ASM_SH_TITAN_H
index 8b8e4fa..cc737b8 100644 (file)
@@ -22,13 +22,12 @@ ED 0xff000010, 0x00000004
 LIST "setup clocks"
 ED 0xa4150024, 0x00004000
 ED 0xa4150000, 0x8E003508
-ED 0xa4150004, 0x00000000
 
 WAIT 1
 
 LIST "BSC"
 ED 0xff800020, 0xa5a50000
-ED 0xfec10000, 0x00000013
+ED 0xfec10000, 0x00001013
 ED 0xfec10004, 0x11110400
 ED 0xfec10024, 0x00000440
 
index e971d9a..16505bf 100644 (file)
 #define SE7722_FPGA_IRQ_MRSHPC1        3 /* IRQ1 */
 #define SE7722_FPGA_IRQ_MRSHPC2        4 /* IRQ1 */
 #define SE7722_FPGA_IRQ_MRSHPC3        5 /* IRQ1 */
-
 #define SE7722_FPGA_IRQ_NR     6
-#define SE7722_FPGA_IRQ_BASE   110
-
-#define MRSHPC_IRQ3            (SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_MRSHPC3)
-#define MRSHPC_IRQ2            (SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_MRSHPC2)
-#define MRSHPC_IRQ1            (SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_MRSHPC1)
-#define MRSHPC_IRQ0            (SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_MRSHPC0)
-#define SMC_IRQ                (SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_SMC)
-#define USB_IRQ                (SE7722_FPGA_IRQ_BASE + SE7722_FPGA_IRQ_USB)
 
 /* arch/sh/boards/se/7722/irq.c */
+extern unsigned int se7722_fpga_irq[];
+
 void init_se7722_IRQ(void);
 
 #define __IO_PREFIX            se7722
index a2d0a40..0471a3e 100644 (file)
@@ -9,8 +9,12 @@ ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_ftrace.o = -pg
 endif
 
-obj-y  := debugtraps.o dumpstack.o idle.o io.o io_generic.o irq.o      \
-          machvec.o nmi_debug.o process_$(BITS).o ptrace_$(BITS).o     \
+CFLAGS_REMOVE_return_address.o = -pg
+
+obj-y  := debugtraps.o dma-nommu.o dumpstack.o                         \
+          idle.o io.o io_generic.o irq.o                               \
+          irq_$(BITS).o machvec.o nmi_debug.o process_$(BITS).o        \
+          ptrace_$(BITS).o return_address.o                            \
           setup.o signal_$(BITS).o sys_sh.o sys_sh$(BITS).o            \
           syscalls_$(BITS).o time.o topology.o traps.o                 \
           traps_$(BITS).o unwinder.o
@@ -28,13 +32,13 @@ obj-$(CONFIG_CRASH_DUMP)    += crash_dump.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_IO_TRAPPED)       += io_trapped.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
-obj-$(CONFIG_GENERIC_GPIO)     += gpio.o
 obj-$(CONFIG_DYNAMIC_FTRACE)   += ftrace.o
 obj-$(CONFIG_FTRACE_SYSCALLS)  += ftrace.o
 obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
 obj-$(CONFIG_DUMP_CODE)                += disassemble.o
 obj-$(CONFIG_HIBERNATION)      += swsusp.o
 obj-$(CONFIG_DWARF_UNWINDER)   += dwarf.o
+obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_callchain.o
 
 obj-$(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST)    += localtimer.o
 
index d218e80..08a2be7 100644 (file)
@@ -34,5 +34,28 @@ int main(void)
        DEFINE(PBE_NEXT, offsetof(struct pbe, next));
        DEFINE(SWSUSP_ARCH_REGS_SIZE, sizeof(struct swsusp_arch_regs));
 #endif
+
+       DEFINE(SH_SLEEP_MODE, offsetof(struct sh_sleep_data, mode));
+       DEFINE(SH_SLEEP_SF_PRE, offsetof(struct sh_sleep_data, sf_pre));
+       DEFINE(SH_SLEEP_SF_POST, offsetof(struct sh_sleep_data, sf_post));
+       DEFINE(SH_SLEEP_RESUME, offsetof(struct sh_sleep_data, resume));
+       DEFINE(SH_SLEEP_VBR, offsetof(struct sh_sleep_data, vbr));
+       DEFINE(SH_SLEEP_SPC, offsetof(struct sh_sleep_data, spc));
+       DEFINE(SH_SLEEP_SR, offsetof(struct sh_sleep_data, sr));
+       DEFINE(SH_SLEEP_SP, offsetof(struct sh_sleep_data, sp));
+       DEFINE(SH_SLEEP_BASE_ADDR, offsetof(struct sh_sleep_data, addr));
+       DEFINE(SH_SLEEP_BASE_DATA, offsetof(struct sh_sleep_data, data));
+       DEFINE(SH_SLEEP_REG_STBCR, offsetof(struct sh_sleep_regs, stbcr));
+       DEFINE(SH_SLEEP_REG_BAR, offsetof(struct sh_sleep_regs, bar));
+       DEFINE(SH_SLEEP_REG_PTEH, offsetof(struct sh_sleep_regs, pteh));
+       DEFINE(SH_SLEEP_REG_PTEL, offsetof(struct sh_sleep_regs, ptel));
+       DEFINE(SH_SLEEP_REG_TTB, offsetof(struct sh_sleep_regs, ttb));
+       DEFINE(SH_SLEEP_REG_TEA, offsetof(struct sh_sleep_regs, tea));
+       DEFINE(SH_SLEEP_REG_MMUCR, offsetof(struct sh_sleep_regs, mmucr));
+       DEFINE(SH_SLEEP_REG_PTEA, offsetof(struct sh_sleep_regs, ptea));
+       DEFINE(SH_SLEEP_REG_PASCR, offsetof(struct sh_sleep_regs, pascr));
+       DEFINE(SH_SLEEP_REG_IRMCR, offsetof(struct sh_sleep_regs, irmcr));
+       DEFINE(SH_SLEEP_REG_CCR, offsetof(struct sh_sleep_regs, ccr));
+       DEFINE(SH_SLEEP_REG_RAMCR, offsetof(struct sh_sleep_regs, ramcr));
        return 0;
 }
index 3d6b931..d97c803 100644 (file)
@@ -15,7 +15,6 @@ obj-$(CONFIG_ARCH_SHMOBILE)   += shmobile/
 
 # Common interfaces.
 
-obj-$(CONFIG_UBC_WAKEUP)       += ubc.o
 obj-$(CONFIG_SH_ADC)           += adc.o
 obj-$(CONFIG_SH_CLK_CPG)       += clock-cpg.o
 
index e932ebe..89b4b76 100644 (file)
@@ -75,16 +75,11 @@ static void __init expmask_init(void)
        /*
         * Future proofing.
         *
-        * Disable support for slottable sleep instruction
-        * and non-nop instructions in the rte delay slot.
+        * Disable support for slottable sleep instruction, non-nop
+        * instructions in the rte delay slot, and associative writes to
+        * the memory-mapped cache array.
         */
-       expmask &= ~(EXPMASK_RTEDS | EXPMASK_BRDSSLP);
-
-       /*
-        * Enable associative writes to the memory-mapped cache array
-        * until the cache flush ops have been rewritten.
-        */
-       expmask |= EXPMASK_MMCAW;
+       expmask &= ~(EXPMASK_RTEDS | EXPMASK_BRDSSLP | EXPMASK_MMCAW);
 
        __raw_writel(expmask, EXPMASK);
        ctrl_barrier();
@@ -311,12 +306,12 @@ asmlinkage void __init sh_cpu_init(void)
        if (fpu_disabled) {
                printk("FPU Disabled\n");
                current_cpu_data.flags &= ~CPU_HAS_FPU;
-               disable_fpu();
        }
 
        /* FPU initialization */
+       disable_fpu();
        if ((current_cpu_data.flags & CPU_HAS_FPU)) {
-               clear_thread_flag(TIF_USEDFPU);
+               current_thread_info()->status &= ~TS_USEDFPU;
                clear_used_math();
        }
 
@@ -338,17 +333,6 @@ asmlinkage void __init sh_cpu_init(void)
        }
 #endif
 
-       /*
-        * Some brain-damaged loaders decided it would be a good idea to put
-        * the UBC to sleep. This causes some issues when it comes to things
-        * like PTRACE_SINGLESTEP or doing hardware watchpoints in GDB.  So ..
-        * we wake it up and hope that all is well.
-        */
-#ifdef CONFIG_SUPERH32
-       if (raw_smp_processor_id() == 0)
-               ubc_wakeup();
-#endif
-
        speculative_execution_init();
        expmask_init();
 }
index 6df2fb9..d395ce5 100644 (file)
 
 /*
  * Save FPU registers onto task structure.
- * Assume called with FPU enabled (SR.FD=0).
  */
 void
-save_fpu(struct task_struct *tsk, struct pt_regs *regs)
+save_fpu(struct task_struct *tsk)
 {
        unsigned long dummy;
 
-       clear_tsk_thread_flag(tsk, TIF_USEDFPU);
        enable_fpu();
        asm volatile("sts.l     fpul, @-%0\n\t"
                     "sts.l     fpscr, @-%0\n\t"
@@ -60,7 +58,6 @@ save_fpu(struct task_struct *tsk, struct pt_regs *regs)
                     : "memory");
 
        disable_fpu();
-       release_fpu(regs);
 }
 
 static void
@@ -598,31 +595,31 @@ BUILD_TRAP_HANDLER(fpu_error)
        struct task_struct *tsk = current;
        TRAP_HANDLER_DECL;
 
-       save_fpu(tsk, regs);
+       __unlazy_fpu(tsk, regs);
        if (ieee_fpe_handler(regs)) {
                tsk->thread.fpu.hard.fpscr &=
                        ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
                grab_fpu(regs);
                restore_fpu(tsk);
-               set_tsk_thread_flag(tsk, TIF_USEDFPU);
+               task_thread_info(tsk)->status |= TS_USEDFPU;
                return;
        }
 
        force_sig(SIGFPE, tsk);
 }
 
-BUILD_TRAP_HANDLER(fpu_state_restore)
+void fpu_state_restore(struct pt_regs *regs)
 {
        struct task_struct *tsk = current;
-       TRAP_HANDLER_DECL;
 
        grab_fpu(regs);
-       if (!user_mode(regs)) {
+       if (unlikely(!user_mode(regs))) {
                printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
+               BUG();
                return;
        }
 
-       if (used_math()) {
+       if (likely(used_math())) {
                /* Using the FPU again.  */
                restore_fpu(tsk);
        } else  {
@@ -630,5 +627,13 @@ BUILD_TRAP_HANDLER(fpu_state_restore)
                fpu_init();
                set_used_math();
        }
-       set_tsk_thread_flag(tsk, TIF_USEDFPU);
+       task_thread_info(tsk)->status |= TS_USEDFPU;
+       tsk->fpu_counter++;
+}
+
+BUILD_TRAP_HANDLER(fpu_state_restore)
+{
+       TRAP_HANDLER_DECL;
+
+       fpu_state_restore(regs);
 }
index bb407ef..3f7e2a2 100644 (file)
@@ -297,41 +297,8 @@ ENTRY(vbr_base)
 !
        .balign         256,0,256
 general_exception:
-#ifndef CONFIG_CPU_SUBTYPE_SHX3
        bra     handle_exception
         sts    pr, k3          ! save original pr value in k3
-#else
-       mov.l   1f, k4
-       mov.l   @k4, k4
-
-       ! Is EXPEVT larger than 0x800?
-       mov     #0x8, k0
-       shll8   k0
-       cmp/hs  k0, k4
-       bf      0f
-
-       ! then add 0x580 (k2 is 0xd80 or 0xda0)
-       mov     #0x58, k0
-       shll2   k0
-       shll2   k0
-       add     k0, k4
-0:
-       ! Setup stack and save DSP context (k0 contains original r15 on return)
-       bsr     prepare_stack
-        nop
-
-       ! Save registers / Switch to bank 0
-       mov             k4, k2          ! keep vector in k2
-       mov.l   1f, k4          ! SR bits to clear in k4
-       bsr     save_regs       ! needs original pr value in k3
-        nop
-
-       bra     handle_exception_special
-        nop
-
-       .align  2
-1:     .long   EXPEVT
-#endif
 
 ! prepare_stack()
 ! - roll back gRB
index 203b183..3a1dbc7 100644 (file)
@@ -9,6 +9,11 @@ obj-$(CONFIG_HIBERNATION)              += $(addprefix ../sh3/, swsusp.o)
 obj-$(CONFIG_SH_FPU)                   += fpu.o softfloat.o
 obj-$(CONFIG_SH_STORE_QUEUES)          += sq.o
 
+# Perf events
+perf-$(CONFIG_CPU_SUBTYPE_SH7750)      := perf_event.o
+perf-$(CONFIG_CPU_SUBTYPE_SH7750S)     := perf_event.o
+perf-$(CONFIG_CPU_SUBTYPE_SH7091)      := perf_event.o
+
 # CPU subtype setup
 obj-$(CONFIG_CPU_SUBTYPE_SH7750)       += setup-sh7750.o
 obj-$(CONFIG_CPU_SUBTYPE_SH7750R)      += setup-sh7750.o
@@ -27,4 +32,5 @@ endif
 # Additional clocks by subtype
 clock-$(CONFIG_CPU_SUBTYPE_SH4_202)    += clock-sh4-202.o
 
-obj-y  += $(clock-y)
+obj-y                                  += $(clock-y)
+obj-$(CONFIG_PERF_EVENTS)              += $(perf-y)
index e3ea541..e97857a 100644 (file)
@@ -41,13 +41,11 @@ static unsigned int fpu_exception_flags;
 
 /*
  * Save FPU registers onto task structure.
- * Assume called with FPU enabled (SR.FD=0).
  */
-void save_fpu(struct task_struct *tsk, struct pt_regs *regs)
+void save_fpu(struct task_struct *tsk)
 {
        unsigned long dummy;
 
-       clear_tsk_thread_flag(tsk, TIF_USEDFPU);
        enable_fpu();
        asm volatile ("sts.l    fpul, @-%0\n\t"
                      "sts.l    fpscr, @-%0\n\t"
@@ -92,7 +90,6 @@ void save_fpu(struct task_struct *tsk, struct pt_regs *regs)
                      :"memory");
 
        disable_fpu();
-       release_fpu(regs);
 }
 
 static void restore_fpu(struct task_struct *tsk)
@@ -285,7 +282,6 @@ static int ieee_fpe_handler(struct pt_regs *regs)
                /* fcnvsd */
                struct task_struct *tsk = current;
 
-               save_fpu(tsk, regs);
                if ((tsk->thread.fpu.hard.fpscr & FPSCR_CAUSE_ERROR))
                        /* FPU error */
                        denormal_to_double(&tsk->thread.fpu.hard,
@@ -462,7 +458,7 @@ BUILD_TRAP_HANDLER(fpu_error)
        struct task_struct *tsk = current;
        TRAP_HANDLER_DECL;
 
-       save_fpu(tsk, regs);
+       __unlazy_fpu(tsk, regs);
        fpu_exception_flags = 0;
        if (ieee_fpe_handler(regs)) {
                tsk->thread.fpu.hard.fpscr &=
@@ -473,7 +469,7 @@ BUILD_TRAP_HANDLER(fpu_error)
                tsk->thread.fpu.hard.fpscr |= (fpu_exception_flags >> 10);
                grab_fpu(regs);
                restore_fpu(tsk);
-               set_tsk_thread_flag(tsk, TIF_USEDFPU);
+               task_thread_info(tsk)->status |= TS_USEDFPU;
                if ((((tsk->thread.fpu.hard.fpscr & FPSCR_ENABLE_MASK) >> 7) &
                     (fpu_exception_flags >> 2)) == 0) {
                        return;
@@ -483,18 +479,18 @@ BUILD_TRAP_HANDLER(fpu_error)
        force_sig(SIGFPE, tsk);
 }
 
-BUILD_TRAP_HANDLER(fpu_state_restore)
+void fpu_state_restore(struct pt_regs *regs)
 {
        struct task_struct *tsk = current;
-       TRAP_HANDLER_DECL;
 
        grab_fpu(regs);
-       if (!user_mode(regs)) {
+       if (unlikely(!user_mode(regs))) {
                printk(KERN_ERR "BUG: FPU is used in kernel mode.\n");
+               BUG();
                return;
        }
 
-       if (used_math()) {
+       if (likely(used_math())) {
                /* Using the FPU again.  */
                restore_fpu(tsk);
        } else {
@@ -502,5 +498,13 @@ BUILD_TRAP_HANDLER(fpu_state_restore)
                fpu_init();
                set_used_math();
        }
-       set_tsk_thread_flag(tsk, TIF_USEDFPU);
+       task_thread_info(tsk)->status |= TS_USEDFPU;
+       tsk->fpu_counter++;
+}
+
+BUILD_TRAP_HANDLER(fpu_state_restore)
+{
+       TRAP_HANDLER_DECL;
+
+       fpu_state_restore(regs);
 }
diff --git a/arch/sh/kernel/cpu/sh4/perf_event.c b/arch/sh/kernel/cpu/sh4/perf_event.c
new file mode 100644 (file)
index 0000000..7f9ecc9
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Performance events support for SH7750-style performance counters
+ *
+ *  Copyright (C) 2009  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/perf_event.h>
+#include <asm/processor.h>
+
+#define PM_CR_BASE     0xff000084      /* 16-bit */
+#define PM_CTR_BASE    0xff100004      /* 32-bit */
+
+#define PMCR(n)                (PM_CR_BASE + ((n) * 0x04))
+#define PMCTRH(n)      (PM_CTR_BASE + 0x00 + ((n) * 0x08))
+#define PMCTRL(n)      (PM_CTR_BASE + 0x04 + ((n) * 0x08))
+
+#define PMCR_PMM_MASK  0x0000003f
+
+#define PMCR_CLKF      0x00000100
+#define PMCR_PMCLR     0x00002000
+#define PMCR_PMST      0x00004000
+#define PMCR_PMEN      0x00008000
+
+static struct sh_pmu sh7750_pmu;
+
+/*
+ * There are a number of events supported by each counter (33 in total).
+ * Since we have 2 counters, each counter will take the event code as it
+ * corresponds to the PMCR PMM setting. Each counter can be configured
+ * independently.
+ *
+ *     Event Code      Description
+ *     ----------      -----------
+ *
+ *     0x01            Operand read access
+ *     0x02            Operand write access
+ *     0x03            UTLB miss
+ *     0x04            Operand cache read miss
+ *     0x05            Operand cache write miss
+ *     0x06            Instruction fetch (w/ cache)
+ *     0x07            Instruction TLB miss
+ *     0x08            Instruction cache miss
+ *     0x09            All operand accesses
+ *     0x0a            All instruction accesses
+ *     0x0b            OC RAM operand access
+ *     0x0d            On-chip I/O space access
+ *     0x0e            Operand access (r/w)
+ *     0x0f            Operand cache miss (r/w)
+ *     0x10            Branch instruction
+ *     0x11            Branch taken
+ *     0x12            BSR/BSRF/JSR
+ *     0x13            Instruction execution
+ *     0x14            Instruction execution in parallel
+ *     0x15            FPU Instruction execution
+ *     0x16            Interrupt
+ *     0x17            NMI
+ *     0x18            trapa instruction execution
+ *     0x19            UBCA match
+ *     0x1a            UBCB match
+ *     0x21            Instruction cache fill
+ *     0x22            Operand cache fill
+ *     0x23            Elapsed time
+ *     0x24            Pipeline freeze by I-cache miss
+ *     0x25            Pipeline freeze by D-cache miss
+ *     0x27            Pipeline freeze by branch instruction
+ *     0x28            Pipeline freeze by CPU register
+ *     0x29            Pipeline freeze by FPU
+ */
+
+static const int sh7750_general_events[] = {
+       [PERF_COUNT_HW_CPU_CYCLES]              = 0x0023,
+       [PERF_COUNT_HW_INSTRUCTIONS]            = 0x000a,
+       [PERF_COUNT_HW_CACHE_REFERENCES]        = 0x0006,       /* I-cache */
+       [PERF_COUNT_HW_CACHE_MISSES]            = 0x0008,       /* I-cache */
+       [PERF_COUNT_HW_BRANCH_INSTRUCTIONS]     = 0x0010,
+       [PERF_COUNT_HW_BRANCH_MISSES]           = -1,
+       [PERF_COUNT_HW_BUS_CYCLES]              = -1,
+};
+
+#define C(x)   PERF_COUNT_HW_CACHE_##x
+
+static const int sh7750_cache_events
+                       [PERF_COUNT_HW_CACHE_MAX]
+                       [PERF_COUNT_HW_CACHE_OP_MAX]
+                       [PERF_COUNT_HW_CACHE_RESULT_MAX] =
+{
+       [ C(L1D) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0001,
+                       [ C(RESULT_MISS)   ] = 0x0004,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0002,
+                       [ C(RESULT_MISS)   ] = 0x0005,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(L1I) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0006,
+                       [ C(RESULT_MISS)   ] = 0x0008,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(LL) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(DTLB) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0x0003,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(ITLB) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0x0007,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+       },
+
+       [ C(BPU) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+       },
+};
+
+static int sh7750_event_map(int event)
+{
+       return sh7750_general_events[event];
+}
+
+static u64 sh7750_pmu_read(int idx)
+{
+       return (u64)((u64)(__raw_readl(PMCTRH(idx)) & 0xffff) << 32) |
+                          __raw_readl(PMCTRL(idx));
+}
+
+static void sh7750_pmu_disable(struct hw_perf_event *hwc, int idx)
+{
+       unsigned int tmp;
+
+       tmp = __raw_readw(PMCR(idx));
+       tmp &= ~(PMCR_PMM_MASK | PMCR_PMEN);
+       __raw_writew(tmp, PMCR(idx));
+}
+
+static void sh7750_pmu_enable(struct hw_perf_event *hwc, int idx)
+{
+       __raw_writew(__raw_readw(PMCR(idx)) | PMCR_PMCLR, PMCR(idx));
+       __raw_writew(hwc->config | PMCR_PMEN | PMCR_PMST, PMCR(idx));
+}
+
+static void sh7750_pmu_disable_all(void)
+{
+       int i;
+
+       for (i = 0; i < sh7750_pmu.num_events; i++)
+               __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i));
+}
+
+static void sh7750_pmu_enable_all(void)
+{
+       int i;
+
+       for (i = 0; i < sh7750_pmu.num_events; i++)
+               __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMEN, PMCR(i));
+}
+
+static struct sh_pmu sh7750_pmu = {
+       .name           = "SH7750",
+       .num_events     = 2,
+       .event_map      = sh7750_event_map,
+       .max_events     = ARRAY_SIZE(sh7750_general_events),
+       .raw_event_mask = PMCR_PMM_MASK,
+       .cache_events   = &sh7750_cache_events,
+       .read           = sh7750_pmu_read,
+       .disable        = sh7750_pmu_disable,
+       .enable         = sh7750_pmu_enable,
+       .disable_all    = sh7750_pmu_disable_all,
+       .enable_all     = sh7750_pmu_enable_all,
+};
+
+static int __init sh7750_pmu_init(void)
+{
+       /*
+        * Make sure this CPU actually has perf counters.
+        */
+       if (!(boot_cpu_data.flags & CPU_HAS_PERF_COUNTER)) {
+               pr_notice("HW perf events unsupported, software events only.\n");
+               return -ENODEV;
+       }
+
+       return register_sh_pmu(&sh7750_pmu);
+}
+arch_initcall(sh7750_pmu_init);
index 490d5dc..33bab47 100644 (file)
@@ -44,3 +44,4 @@ pinmux-$(CONFIG_CPU_SUBTYPE_SH7786)   := pinmux-sh7786.o
 obj-y                          += $(clock-y)
 obj-$(CONFIG_SMP)              += $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)     += $(pinmux-y)
+obj-$(CONFIG_PERF_EVENTS)      += perf_event.o
index dfe9192..9db7438 100644 (file)
@@ -152,7 +152,7 @@ struct clk div6_clks[] = {
        SH_CLK_DIV6("fsia_clk", &div3_clk, FCLKACR, 0),
        SH_CLK_DIV6("fsib_clk", &div3_clk, FCLKBCR, 0),
        SH_CLK_DIV6("irda_clk", &div3_clk, IRDACLKCR, 0),
-       SH_CLK_DIV6("spu_clk", &div3_clk, SPUCLKCR, 0),
+       SH_CLK_DIV6("spu_clk", &div3_clk, SPUCLKCR, CLK_ENABLE_ON_INIT),
 };
 
 #define R_CLK (&r_clk)
diff --git a/arch/sh/kernel/cpu/sh4a/perf_event.c b/arch/sh/kernel/cpu/sh4a/perf_event.c
new file mode 100644 (file)
index 0000000..eddc219
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Performance events support for SH-4A performance counters
+ *
+ *  Copyright (C) 2009  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/perf_event.h>
+#include <asm/processor.h>
+
+#define PPC_CCBR(idx)  (0xff200800 + (sizeof(u32) * idx))
+#define PPC_PMCTR(idx) (0xfc100000 + (sizeof(u32) * idx))
+
+#define CCBR_CIT_MASK  (0x7ff << 6)
+#define CCBR_DUC       (1 << 3)
+#define CCBR_CMDS      (1 << 1)
+#define CCBR_PPCE      (1 << 0)
+
+#define PPC_PMCAT      0xfc100080
+
+#define PMCAT_OVF3     (1 << 27)
+#define PMCAT_CNN3     (1 << 26)
+#define PMCAT_CLR3     (1 << 25)
+#define PMCAT_OVF2     (1 << 19)
+#define PMCAT_CLR2     (1 << 17)
+#define PMCAT_OVF1     (1 << 11)
+#define PMCAT_CNN1     (1 << 10)
+#define PMCAT_CLR1     (1 << 9)
+#define PMCAT_OVF0     (1 << 3)
+#define PMCAT_CLR0     (1 << 1)
+
+static struct sh_pmu sh4a_pmu;
+
+/*
+ * Supported raw event codes:
+ *
+ *     Event Code      Description
+ *     ----------      -----------
+ *
+ *     0x0000          number of elapsed cycles
+ *     0x0200          number of elapsed cycles in privileged mode
+ *     0x0280          number of elapsed cycles while SR.BL is asserted
+ *     0x0202          instruction execution
+ *     0x0203          instruction execution in parallel
+ *     0x0204          number of unconditional branches
+ *     0x0208          number of exceptions
+ *     0x0209          number of interrupts
+ *     0x0220          UTLB miss caused by instruction fetch
+ *     0x0222          UTLB miss caused by operand access
+ *     0x02a0          number of ITLB misses
+ *     0x0028          number of accesses to instruction memories
+ *     0x0029          number of accesses to instruction cache
+ *     0x002a          instruction cache miss
+ *     0x022e          number of access to instruction X/Y memory
+ *     0x0030          number of reads to operand memories
+ *     0x0038          number of writes to operand memories
+ *     0x0031          number of operand cache read accesses
+ *     0x0039          number of operand cache write accesses
+ *     0x0032          operand cache read miss
+ *     0x003a          operand cache write miss
+ *     0x0236          number of reads to operand X/Y memory
+ *     0x023e          number of writes to operand X/Y memory
+ *     0x0237          number of reads to operand U memory
+ *     0x023f          number of writes to operand U memory
+ *     0x0337          number of U memory read buffer misses
+ *     0x02b4          number of wait cycles due to operand read access
+ *     0x02bc          number of wait cycles due to operand write access
+ *     0x0033          number of wait cycles due to operand cache read miss
+ *     0x003b          number of wait cycles due to operand cache write miss
+ */
+
+/*
+ * Special reserved bits used by hardware emulators, read values will
+ * vary, but writes must always be 0.
+ */
+#define PMCAT_EMU_CLR_MASK     ((1 << 24) | (1 << 16) | (1 << 8) | (1 << 0))
+
+static const int sh4a_general_events[] = {
+       [PERF_COUNT_HW_CPU_CYCLES]              = 0x0000,
+       [PERF_COUNT_HW_INSTRUCTIONS]            = 0x0202,
+       [PERF_COUNT_HW_CACHE_REFERENCES]        = 0x0029,       /* I-cache */
+       [PERF_COUNT_HW_CACHE_MISSES]            = 0x002a,       /* I-cache */
+       [PERF_COUNT_HW_BRANCH_INSTRUCTIONS]     = 0x0204,
+       [PERF_COUNT_HW_BRANCH_MISSES]           = -1,
+       [PERF_COUNT_HW_BUS_CYCLES]              = -1,
+};
+
+#define C(x)   PERF_COUNT_HW_CACHE_##x
+
+static const int sh4a_cache_events
+                       [PERF_COUNT_HW_CACHE_MAX]
+                       [PERF_COUNT_HW_CACHE_OP_MAX]
+                       [PERF_COUNT_HW_CACHE_RESULT_MAX] =
+{
+       [ C(L1D) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0031,
+                       [ C(RESULT_MISS)   ] = 0x0032,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0039,
+                       [ C(RESULT_MISS)   ] = 0x003a,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(L1I) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0029,
+                       [ C(RESULT_MISS)   ] = 0x002a,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(LL) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0030,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0038,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(DTLB) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0x0222,
+                       [ C(RESULT_MISS)   ] = 0x0220,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0,
+               },
+       },
+
+       [ C(ITLB) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = 0,
+                       [ C(RESULT_MISS)   ] = 0x02a0,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+       },
+
+       [ C(BPU) ] = {
+               [ C(OP_READ) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_WRITE) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+               [ C(OP_PREFETCH) ] = {
+                       [ C(RESULT_ACCESS) ] = -1,
+                       [ C(RESULT_MISS)   ] = -1,
+               },
+       },
+};
+
+static int sh4a_event_map(int event)
+{
+       return sh4a_general_events[event];
+}
+
+static u64 sh4a_pmu_read(int idx)
+{
+       return __raw_readl(PPC_PMCTR(idx));
+}
+
+static void sh4a_pmu_disable(struct hw_perf_event *hwc, int idx)
+{
+       unsigned int tmp;
+
+       tmp = __raw_readl(PPC_CCBR(idx));
+       tmp &= ~(CCBR_CIT_MASK | CCBR_DUC);
+       __raw_writel(tmp, PPC_CCBR(idx));
+}
+
+static void sh4a_pmu_enable(struct hw_perf_event *hwc, int idx)
+{
+       unsigned int tmp;
+
+       tmp = __raw_readl(PPC_PMCAT);
+       tmp &= ~PMCAT_EMU_CLR_MASK;
+       tmp |= idx ? PMCAT_CLR1 : PMCAT_CLR0;
+       __raw_writel(tmp, PPC_PMCAT);
+
+       tmp = __raw_readl(PPC_CCBR(idx));
+       tmp |= (hwc->config << 6) | CCBR_CMDS | CCBR_PPCE;
+       __raw_writel(tmp, PPC_CCBR(idx));
+
+       __raw_writel(__raw_readl(PPC_CCBR(idx)) | CCBR_DUC, PPC_CCBR(idx));
+}
+
+static void sh4a_pmu_disable_all(void)
+{
+       int i;
+
+       for (i = 0; i < sh4a_pmu.num_events; i++)
+               __raw_writel(__raw_readl(PPC_CCBR(i)) & ~CCBR_DUC, PPC_CCBR(i));
+}
+
+static void sh4a_pmu_enable_all(void)
+{
+       int i;
+
+       for (i = 0; i < sh4a_pmu.num_events; i++)
+               __raw_writel(__raw_readl(PPC_CCBR(i)) | CCBR_DUC, PPC_CCBR(i));
+}
+
+static struct sh_pmu sh4a_pmu = {
+       .name           = "SH-4A",
+       .num_events     = 2,
+       .event_map      = sh4a_event_map,
+       .max_events     = ARRAY_SIZE(sh4a_general_events),
+       .raw_event_mask = 0x3ff,
+       .cache_events   = &sh4a_cache_events,
+       .read           = sh4a_pmu_read,
+       .disable        = sh4a_pmu_disable,
+       .enable         = sh4a_pmu_enable,
+       .disable_all    = sh4a_pmu_disable_all,
+       .enable_all     = sh4a_pmu_enable_all,
+};
+
+static int __init sh4a_pmu_init(void)
+{
+       /*
+        * Make sure this CPU actually has perf counters.
+        */
+       if (!(boot_cpu_data.flags & CPU_HAS_PERF_COUNTER)) {
+               pr_notice("HW perf events unsupported, software events only.\n");
+               return -ENODEV;
+       }
+
+       return register_sh_pmu(&sh4a_pmu);
+}
+arch_initcall(sh4a_pmu_init);
index f3851fd..845e89c 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/uio_driver.h>
 #include <linux/sh_timer.h>
 #include <linux/io.h>
+#include <linux/notifier.h>
+#include <asm/suspend.h>
 #include <asm/clock.h>
 #include <asm/mmzone.h>
 #include <cpu/sh7724.h>
@@ -202,7 +204,7 @@ static struct resource veu0_resources[] = {
        [0] = {
                .name   = "VEU3F0",
                .start  = 0xfe920000,
-               .end    = 0xfe9200cb - 1,
+               .end    = 0xfe9200cb,
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
@@ -234,7 +236,7 @@ static struct resource veu1_resources[] = {
        [0] = {
                .name   = "VEU3F1",
                .start  = 0xfe924000,
-               .end    = 0xfe9240cb - 1,
+               .end    = 0xfe9240cb,
                .flags  = IORESOURCE_MEM,
        },
        [1] = {
@@ -523,6 +525,70 @@ static struct platform_device jpu_device = {
        },
 };
 
+/* SPU2DSP0 */
+static struct uio_info spu0_platform_data = {
+       .name = "SPU2DSP0",
+       .version = "0",
+       .irq = 86,
+};
+
+static struct resource spu0_resources[] = {
+       [0] = {
+               .name   = "SPU2DSP0",
+               .start  = 0xFE200000,
+               .end    = 0xFE2FFFFF,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* place holder for contiguous memory */
+       },
+};
+
+static struct platform_device spu0_device = {
+       .name           = "uio_pdrv_genirq",
+       .id             = 4,
+       .dev = {
+               .platform_data  = &spu0_platform_data,
+       },
+       .resource       = spu0_resources,
+       .num_resources  = ARRAY_SIZE(spu0_resources),
+       .archdata = {
+               .hwblk_id = HWBLK_SPU,
+       },
+};
+
+/* SPU2DSP1 */
+static struct uio_info spu1_platform_data = {
+       .name = "SPU2DSP1",
+       .version = "0",
+       .irq = 87,
+};
+
+static struct resource spu1_resources[] = {
+       [0] = {
+               .name   = "SPU2DSP1",
+               .start  = 0xFE300000,
+               .end    = 0xFE3FFFFF,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* place holder for contiguous memory */
+       },
+};
+
+static struct platform_device spu1_device = {
+       .name           = "uio_pdrv_genirq",
+       .id             = 5,
+       .dev = {
+               .platform_data  = &spu1_platform_data,
+       },
+       .resource       = spu1_resources,
+       .num_resources  = ARRAY_SIZE(spu1_resources),
+       .archdata = {
+               .hwblk_id = HWBLK_SPU,
+       },
+};
+
 static struct platform_device *sh7724_devices[] __initdata = {
        &cmt_device,
        &tmu0_device,
@@ -539,6 +605,8 @@ static struct platform_device *sh7724_devices[] __initdata = {
        &veu0_device,
        &veu1_device,
        &jpu_device,
+       &spu0_device,
+       &spu1_device,
 };
 
 static int __init sh7724_devices_setup(void)
@@ -547,6 +615,8 @@ static int __init sh7724_devices_setup(void)
        platform_resource_setup_memory(&veu0_device, "veu0", 2 << 20);
        platform_resource_setup_memory(&veu1_device, "veu1", 2 << 20);
        platform_resource_setup_memory(&jpu_device,  "jpu",  2 << 20);
+       platform_resource_setup_memory(&spu0_device, "spu0", 2 << 20);
+       platform_resource_setup_memory(&spu1_device, "spu1", 2 << 20);
 
        return platform_add_devices(sh7724_devices,
                                    ARRAY_SIZE(sh7724_devices));
@@ -827,3 +897,193 @@ void __init plat_irq_setup(void)
 {
        register_intc_controller(&intc_desc);
 }
+
+static struct {
+       /* BSC */
+       unsigned long mmselr;
+       unsigned long cs0bcr;
+       unsigned long cs4bcr;
+       unsigned long cs5abcr;
+       unsigned long cs5bbcr;
+       unsigned long cs6abcr;
+       unsigned long cs6bbcr;
+       unsigned long cs4wcr;
+       unsigned long cs5awcr;
+       unsigned long cs5bwcr;
+       unsigned long cs6awcr;
+       unsigned long cs6bwcr;
+       /* INTC */
+       unsigned short ipra;
+       unsigned short iprb;
+       unsigned short iprc;
+       unsigned short iprd;
+       unsigned short ipre;
+       unsigned short iprf;
+       unsigned short iprg;
+       unsigned short iprh;
+       unsigned short ipri;
+       unsigned short iprj;
+       unsigned short iprk;
+       unsigned short iprl;
+       unsigned char imr0;
+       unsigned char imr1;
+       unsigned char imr2;
+       unsigned char imr3;
+       unsigned char imr4;
+       unsigned char imr5;
+       unsigned char imr6;
+       unsigned char imr7;
+       unsigned char imr8;
+       unsigned char imr9;
+       unsigned char imr10;
+       unsigned char imr11;
+       unsigned char imr12;
+       /* RWDT */
+       unsigned short rwtcnt;
+       unsigned short rwtcsr;
+       /* CPG */
+       unsigned long irdaclk;
+       unsigned long spuclk;
+} sh7724_rstandby_state;
+
+static int sh7724_pre_sleep_notifier_call(struct notifier_block *nb,
+                                         unsigned long flags, void *unused)
+{
+       if (!(flags & SUSP_SH_RSTANDBY))
+               return NOTIFY_DONE;
+
+       /* BCR */
+       sh7724_rstandby_state.mmselr = __raw_readl(0xff800020); /* MMSELR */
+       sh7724_rstandby_state.mmselr |= 0xa5a50000;
+       sh7724_rstandby_state.cs0bcr = __raw_readl(0xfec10004); /* CS0BCR */
+       sh7724_rstandby_state.cs4bcr = __raw_readl(0xfec10010); /* CS4BCR */
+       sh7724_rstandby_state.cs5abcr = __raw_readl(0xfec10014); /* CS5ABCR */
+       sh7724_rstandby_state.cs5bbcr = __raw_readl(0xfec10018); /* CS5BBCR */
+       sh7724_rstandby_state.cs6abcr = __raw_readl(0xfec1001c); /* CS6ABCR */
+       sh7724_rstandby_state.cs6bbcr = __raw_readl(0xfec10020); /* CS6BBCR */
+       sh7724_rstandby_state.cs4wcr = __raw_readl(0xfec10030); /* CS4WCR */
+       sh7724_rstandby_state.cs5awcr = __raw_readl(0xfec10034); /* CS5AWCR */
+       sh7724_rstandby_state.cs5bwcr = __raw_readl(0xfec10038); /* CS5BWCR */
+       sh7724_rstandby_state.cs6awcr = __raw_readl(0xfec1003c); /* CS6AWCR */
+       sh7724_rstandby_state.cs6bwcr = __raw_readl(0xfec10040); /* CS6BWCR */
+
+       /* INTC */
+       sh7724_rstandby_state.ipra = __raw_readw(0xa4080000); /* IPRA */
+       sh7724_rstandby_state.iprb = __raw_readw(0xa4080004); /* IPRB */
+       sh7724_rstandby_state.iprc = __raw_readw(0xa4080008); /* IPRC */
+       sh7724_rstandby_state.iprd = __raw_readw(0xa408000c); /* IPRD */
+       sh7724_rstandby_state.ipre = __raw_readw(0xa4080010); /* IPRE */
+       sh7724_rstandby_state.iprf = __raw_readw(0xa4080014); /* IPRF */
+       sh7724_rstandby_state.iprg = __raw_readw(0xa4080018); /* IPRG */
+       sh7724_rstandby_state.iprh = __raw_readw(0xa408001c); /* IPRH */
+       sh7724_rstandby_state.ipri = __raw_readw(0xa4080020); /* IPRI */
+       sh7724_rstandby_state.iprj = __raw_readw(0xa4080024); /* IPRJ */
+       sh7724_rstandby_state.iprk = __raw_readw(0xa4080028); /* IPRK */
+       sh7724_rstandby_state.iprl = __raw_readw(0xa408002c); /* IPRL */
+       sh7724_rstandby_state.imr0 = __raw_readb(0xa4080080); /* IMR0 */
+       sh7724_rstandby_state.imr1 = __raw_readb(0xa4080084); /* IMR1 */
+       sh7724_rstandby_state.imr2 = __raw_readb(0xa4080088); /* IMR2 */
+       sh7724_rstandby_state.imr3 = __raw_readb(0xa408008c); /* IMR3 */
+       sh7724_rstandby_state.imr4 = __raw_readb(0xa4080090); /* IMR4 */
+       sh7724_rstandby_state.imr5 = __raw_readb(0xa4080094); /* IMR5 */
+       sh7724_rstandby_state.imr6 = __raw_readb(0xa4080098); /* IMR6 */
+       sh7724_rstandby_state.imr7 = __raw_readb(0xa408009c); /* IMR7 */
+       sh7724_rstandby_state.imr8 = __raw_readb(0xa40800a0); /* IMR8 */
+       sh7724_rstandby_state.imr9 = __raw_readb(0xa40800a4); /* IMR9 */
+       sh7724_rstandby_state.imr10 = __raw_readb(0xa40800a8); /* IMR10 */
+       sh7724_rstandby_state.imr11 = __raw_readb(0xa40800ac); /* IMR11 */
+       sh7724_rstandby_state.imr12 = __raw_readb(0xa40800b0); /* IMR12 */
+
+       /* RWDT */
+       sh7724_rstandby_state.rwtcnt = __raw_readb(0xa4520000); /* RWTCNT */
+       sh7724_rstandby_state.rwtcnt |= 0x5a00;
+       sh7724_rstandby_state.rwtcsr = __raw_readb(0xa4520004); /* RWTCSR */
+       sh7724_rstandby_state.rwtcsr |= 0xa500;
+       __raw_writew(sh7724_rstandby_state.rwtcsr & 0x07, 0xa4520004);
+
+       /* CPG */
+       sh7724_rstandby_state.irdaclk = __raw_readl(0xa4150018); /* IRDACLKCR */
+       sh7724_rstandby_state.spuclk = __raw_readl(0xa415003c); /* SPUCLKCR */
+
+       return NOTIFY_DONE;
+}
+
+static int sh7724_post_sleep_notifier_call(struct notifier_block *nb,
+                                          unsigned long flags, void *unused)
+{
+       if (!(flags & SUSP_SH_RSTANDBY))
+               return NOTIFY_DONE;
+
+       /* BCR */
+       __raw_writel(sh7724_rstandby_state.mmselr, 0xff800020); /* MMSELR */
+       __raw_writel(sh7724_rstandby_state.cs0bcr, 0xfec10004); /* CS0BCR */
+       __raw_writel(sh7724_rstandby_state.cs4bcr, 0xfec10010); /* CS4BCR */
+       __raw_writel(sh7724_rstandby_state.cs5abcr, 0xfec10014); /* CS5ABCR */
+       __raw_writel(sh7724_rstandby_state.cs5bbcr, 0xfec10018); /* CS5BBCR */
+       __raw_writel(sh7724_rstandby_state.cs6abcr, 0xfec1001c); /* CS6ABCR */
+       __raw_writel(sh7724_rstandby_state.cs6bbcr, 0xfec10020); /* CS6BBCR */
+       __raw_writel(sh7724_rstandby_state.cs4wcr, 0xfec10030); /* CS4WCR */
+       __raw_writel(sh7724_rstandby_state.cs5awcr, 0xfec10034); /* CS5AWCR */
+       __raw_writel(sh7724_rstandby_state.cs5bwcr, 0xfec10038); /* CS5BWCR */
+       __raw_writel(sh7724_rstandby_state.cs6awcr, 0xfec1003c); /* CS6AWCR */
+       __raw_writel(sh7724_rstandby_state.cs6bwcr, 0xfec10040); /* CS6BWCR */
+
+       /* INTC */
+       __raw_writew(sh7724_rstandby_state.ipra, 0xa4080000); /* IPRA */
+       __raw_writew(sh7724_rstandby_state.iprb, 0xa4080004); /* IPRB */
+       __raw_writew(sh7724_rstandby_state.iprc, 0xa4080008); /* IPRC */
+       __raw_writew(sh7724_rstandby_state.iprd, 0xa408000c); /* IPRD */
+       __raw_writew(sh7724_rstandby_state.ipre, 0xa4080010); /* IPRE */
+       __raw_writew(sh7724_rstandby_state.iprf, 0xa4080014); /* IPRF */
+       __raw_writew(sh7724_rstandby_state.iprg, 0xa4080018); /* IPRG */
+       __raw_writew(sh7724_rstandby_state.iprh, 0xa408001c); /* IPRH */
+       __raw_writew(sh7724_rstandby_state.ipri, 0xa4080020); /* IPRI */
+       __raw_writew(sh7724_rstandby_state.iprj, 0xa4080024); /* IPRJ */
+       __raw_writew(sh7724_rstandby_state.iprk, 0xa4080028); /* IPRK */
+       __raw_writew(sh7724_rstandby_state.iprl, 0xa408002c); /* IPRL */
+       __raw_writeb(sh7724_rstandby_state.imr0, 0xa4080080); /* IMR0 */
+       __raw_writeb(sh7724_rstandby_state.imr1, 0xa4080084); /* IMR1 */
+       __raw_writeb(sh7724_rstandby_state.imr2, 0xa4080088); /* IMR2 */
+       __raw_writeb(sh7724_rstandby_state.imr3, 0xa408008c); /* IMR3 */
+       __raw_writeb(sh7724_rstandby_state.imr4, 0xa4080090); /* IMR4 */
+       __raw_writeb(sh7724_rstandby_state.imr5, 0xa4080094); /* IMR5 */
+       __raw_writeb(sh7724_rstandby_state.imr6, 0xa4080098); /* IMR6 */
+       __raw_writeb(sh7724_rstandby_state.imr7, 0xa408009c); /* IMR7 */
+       __raw_writeb(sh7724_rstandby_state.imr8, 0xa40800a0); /* IMR8 */
+       __raw_writeb(sh7724_rstandby_state.imr9, 0xa40800a4); /* IMR9 */
+       __raw_writeb(sh7724_rstandby_state.imr10, 0xa40800a8); /* IMR10 */
+       __raw_writeb(sh7724_rstandby_state.imr11, 0xa40800ac); /* IMR11 */
+       __raw_writeb(sh7724_rstandby_state.imr12, 0xa40800b0); /* IMR12 */
+
+       /* RWDT */
+       __raw_writew(sh7724_rstandby_state.rwtcnt, 0xa4520000); /* RWTCNT */
+       __raw_writew(sh7724_rstandby_state.rwtcsr, 0xa4520004); /* RWTCSR */
+
+       /* CPG */
+       __raw_writel(sh7724_rstandby_state.irdaclk, 0xa4150018); /* IRDACLKCR */
+       __raw_writel(sh7724_rstandby_state.spuclk, 0xa415003c); /* SPUCLKCR */
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block sh7724_pre_sleep_notifier = {
+       .notifier_call = sh7724_pre_sleep_notifier_call,
+       .priority = SH_MOBILE_PRE(SH_MOBILE_SLEEP_CPU),
+};
+
+static struct notifier_block sh7724_post_sleep_notifier = {
+       .notifier_call = sh7724_post_sleep_notifier_call,
+       .priority = SH_MOBILE_POST(SH_MOBILE_SLEEP_CPU),
+};
+
+static int __init sh7724_sleep_setup(void)
+{
+       atomic_notifier_chain_register(&sh_mobile_pre_sleep_notifier_list,
+                                      &sh7724_pre_sleep_notifier);
+
+       atomic_notifier_chain_register(&sh_mobile_post_sleep_notifier_list,
+                                      &sh7724_post_sleep_notifier);
+       return 0;
+}
+arch_initcall(sh7724_sleep_setup);
+
index e848443..c7ba916 100644 (file)
 #include <linux/sh_timer.h>
 #include <asm/mmzone.h>
 
+/*
+ * This intentionally only registers SCIF ports 0, 1, and 3. SCIF 2
+ * INTEVT values overlap with the FPU EXPEVT ones, requiring special
+ * demuxing in the exception dispatch path.
+ *
+ * As this overlap is something that never should have made it in to
+ * silicon in the first place, we just refuse to deal with the port at
+ * all rather than adding infrastructure to hack around it.
+ */
 static struct plat_sci_port sci_platform_data[] = {
        {
                .mapbase        = 0xffc30000,
@@ -26,11 +35,6 @@ static struct plat_sci_port sci_platform_data[] = {
                .flags          = UPF_BOOT_AUTOCONF,
                .type           = PORT_SCIF,
                .irqs           = { 44, 45, 47, 46 },
-       }, {
-               .mapbase        = 0xffc50000,
-               .flags          = UPF_BOOT_AUTOCONF,
-               .type           = PORT_SCIF,
-               .irqs           = { 48, 49, 51, 50 },
        }, {
                .mapbase        = 0xffc60000,
                .flags          = UPF_BOOT_AUTOCONF,
@@ -268,7 +272,11 @@ enum {
        UNUSED = 0,
 
        /* interrupt sources */
-       IRL, IRQ0, IRQ1, IRQ2, IRQ3,
+       IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH,
+       IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH,
+       IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH,
+       IRL_HHLL, IRL_HHLH, IRL_HHHL,
+       IRQ0, IRQ1, IRQ2, IRQ3,
        HUDII,
        TMU0, TMU1, TMU2, TMU3, TMU4, TMU5,
        PCII0, PCII1, PCII2, PCII3, PCII4,
@@ -291,7 +299,7 @@ enum {
        INTICI4, INTICI5, INTICI6, INTICI7,
 
        /* interrupt groups */
-       PCII56789, SCIF0, SCIF1, SCIF2, SCIF3,
+       IRL, PCII56789, SCIF0, SCIF1, SCIF2, SCIF3,
        DMAC0, DMAC1,
 };
 
@@ -309,8 +317,6 @@ static struct intc_vect vectors[] __initdata = {
        INTC_VECT(SCIF0_BRI, 0x740), INTC_VECT(SCIF0_TXI, 0x760),
        INTC_VECT(SCIF1_ERI, 0x780), INTC_VECT(SCIF1_RXI, 0x7a0),
        INTC_VECT(SCIF1_BRI, 0x7c0), INTC_VECT(SCIF1_TXI, 0x7e0),
-       INTC_VECT(SCIF2_ERI, 0x800), INTC_VECT(SCIF2_RXI, 0x820),
-       INTC_VECT(SCIF2_BRI, 0x840), INTC_VECT(SCIF2_TXI, 0x860),
        INTC_VECT(SCIF3_ERI, 0x880), INTC_VECT(SCIF3_RXI, 0x8a0),
        INTC_VECT(SCIF3_BRI, 0x8c0), INTC_VECT(SCIF3_TXI, 0x8e0),
        INTC_VECT(DMAC0_DMINT0, 0x900), INTC_VECT(DMAC0_DMINT1, 0x920),
@@ -344,10 +350,13 @@ static struct intc_vect vectors[] __initdata = {
 };
 
 static struct intc_group groups[] __initdata = {
+       INTC_GROUP(IRL, IRL_LLLL, IRL_LLLH, IRL_LLHL, IRL_LLHH,
+                  IRL_LHLL, IRL_LHLH, IRL_LHHL, IRL_LHHH,
+                  IRL_HLLL, IRL_HLLH, IRL_HLHL, IRL_HLHH,
+                  IRL_HHLL, IRL_HHLH, IRL_HHHL),
        INTC_GROUP(PCII56789, PCII5, PCII6, PCII7, PCII8, PCII9),
        INTC_GROUP(SCIF0, SCIF0_ERI, SCIF0_RXI, SCIF0_BRI, SCIF0_TXI),
        INTC_GROUP(SCIF1, SCIF1_ERI, SCIF1_RXI, SCIF1_BRI, SCIF1_TXI),
-       INTC_GROUP(SCIF2, SCIF2_ERI, SCIF2_RXI, SCIF2_BRI, SCIF2_TXI),
        INTC_GROUP(SCIF3, SCIF3_ERI, SCIF3_RXI, SCIF3_BRI, SCIF3_TXI),
        INTC_GROUP(DMAC0, DMAC0_DMINT0, DMAC0_DMINT1, DMAC0_DMINT2,
                   DMAC0_DMINT3, DMAC0_DMINT4, DMAC0_DMINT5, DMAC0_DMAE),
@@ -419,14 +428,14 @@ static DECLARE_INTC_DESC(intc_desc_irq, "shx3-irq", vectors_irq, groups,
 
 /* External interrupt pins in IRL mode */
 static struct intc_vect vectors_irl[] __initdata = {
-       INTC_VECT(IRL, 0x200), INTC_VECT(IRL, 0x220),
-       INTC_VECT(IRL, 0x240), INTC_VECT(IRL, 0x260),
-       INTC_VECT(IRL, 0x280), INTC_VECT(IRL, 0x2a0),
-       INTC_VECT(IRL, 0x2c0), INTC_VECT(IRL, 0x2e0),
-       INTC_VECT(IRL, 0x300), INTC_VECT(IRL, 0x320),
-       INTC_VECT(IRL, 0x340), INTC_VECT(IRL, 0x360),
-       INTC_VECT(IRL, 0x380), INTC_VECT(IRL, 0x3a0),
-       INTC_VECT(IRL, 0x3c0),
+       INTC_VECT(IRL_LLLL, 0x200), INTC_VECT(IRL_LLLH, 0x220),
+       INTC_VECT(IRL_LLHL, 0x240), INTC_VECT(IRL_LLHH, 0x260),
+       INTC_VECT(IRL_LHLL, 0x280), INTC_VECT(IRL_LHLH, 0x2a0),
+       INTC_VECT(IRL_LHHL, 0x2c0), INTC_VECT(IRL_LHHH, 0x2e0),
+       INTC_VECT(IRL_HLLL, 0x300), INTC_VECT(IRL_HLLH, 0x320),
+       INTC_VECT(IRL_HLHL, 0x340), INTC_VECT(IRL_HLHH, 0x360),
+       INTC_VECT(IRL_HHLL, 0x380), INTC_VECT(IRL_HHLH, 0x3a0),
+       INTC_VECT(IRL_HHHL, 0x3c0),
 };
 
 static DECLARE_INTC_DESC(intc_desc_irl, "shx3-irl", vectors_irl, groups,
index 185ec39..5863e0c 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/io.h>
 
+#define STBCR_REG(phys_id) (0xfe400004 | (phys_id << 12))
+#define RESET_REG(phys_id) (0xfe400008 | (phys_id << 12))
+
+#define STBCR_MSTP     0x00000001
+#define STBCR_RESET    0x00000002
+#define STBCR_LTSLP    0x80000000
+
 static irqreturn_t ipi_interrupt_handler(int irq, void *arg)
 {
        unsigned int message = (unsigned int)(long)arg;
@@ -21,9 +28,9 @@ static irqreturn_t ipi_interrupt_handler(int irq, void *arg)
        unsigned int offs = 4 * cpu;
        unsigned int x;
 
-       x = ctrl_inl(0xfe410070 + offs); /* C0INITICI..CnINTICI */
+       x = __raw_readl(0xfe410070 + offs); /* C0INITICI..CnINTICI */
        x &= (1 << (message << 2));
-       ctrl_outl(x, 0xfe410080 + offs); /* C0INTICICLR..CnINTICICLR */
+       __raw_writel(x, 0xfe410080 + offs); /* C0INTICICLR..CnINTICICLR */
 
        smp_message_recv(message);
 
@@ -37,6 +44,9 @@ void __init plat_smp_setup(void)
 
        init_cpu_possible(cpumask_of(cpu));
 
+       /* Enable light sleep for the boot CPU */
+       __raw_writel(__raw_readl(STBCR_REG(cpu)) | STBCR_LTSLP, STBCR_REG(cpu));
+
        __cpu_number_map[0] = 0;
        __cpu_logical_map[0] = 0;
 
@@ -66,32 +76,23 @@ void __init plat_prepare_cpus(unsigned int max_cpus)
                            "IPI", (void *)(long)i);
 }
 
-#define STBCR_REG(phys_id) (0xfe400004 | (phys_id << 12))
-#define RESET_REG(phys_id) (0xfe400008 | (phys_id << 12))
-
-#define STBCR_MSTP     0x00000001
-#define STBCR_RESET    0x00000002
-#define STBCR_LTSLP    0x80000000
-
-#define STBCR_AP_VAL   (STBCR_RESET | STBCR_LTSLP)
-
 void plat_start_cpu(unsigned int cpu, unsigned long entry_point)
 {
-       ctrl_outl(entry_point, RESET_REG(cpu));
+       __raw_writel(entry_point, RESET_REG(cpu));
 
-       if (!(ctrl_inl(STBCR_REG(cpu)) & STBCR_MSTP))
-               ctrl_outl(STBCR_MSTP, STBCR_REG(cpu));
+       if (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP))
+               __raw_writel(STBCR_MSTP, STBCR_REG(cpu));
 
-       while (!(ctrl_inl(STBCR_REG(cpu)) & STBCR_MSTP))
+       while (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP))
                cpu_relax();
 
        /* Start up secondary processor by sending a reset */
-       ctrl_outl(STBCR_AP_VAL, STBCR_REG(cpu));
+       __raw_writel(STBCR_RESET | STBCR_LTSLP, STBCR_REG(cpu));
 }
 
 int plat_smp_processor_id(void)
 {
-       return ctrl_inl(0xff000048); /* CPIDR */
+       return __raw_readl(0xff000048); /* CPIDR */
 }
 
 void plat_send_ipi(unsigned int cpu, unsigned int message)
@@ -100,5 +101,5 @@ void plat_send_ipi(unsigned int cpu, unsigned int message)
 
        BUG_ON(cpu >= 4);
 
-       ctrl_outl(1 << (message << 2), addr); /* C0INTICI..CnINTICI */
+       __raw_writel(1 << (message << 2), addr); /* C0INTICI..CnINTICI */
 }
index b0aacf6..8f13f73 100644 (file)
@@ -933,7 +933,7 @@ ret_with_reschedule:
 
        pta     restore_all, tr1
 
-       movi    (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r8
+       movi    _TIF_SIGPENDING, r8
        and     r8, r7, r8
        pta     work_notifysig, tr0
        bne     r8, ZERO, tr0
index 1c504bd..83972aa 100644 (file)
@@ -87,25 +87,31 @@ void sh_mobile_setup_cpuidle(void)
 
        dev->safe_state = state;
 
-       state = &dev->states[i++];
-       snprintf(state->name, CPUIDLE_NAME_LEN, "C1");
-       strncpy(state->desc, "SuperH Sleep Mode [SF]", CPUIDLE_DESC_LEN);
-       state->exit_latency = 100;
-       state->target_residency = 1 * 2;
-       state->power_usage = 1;
-       state->flags = 0;
-       state->flags |= CPUIDLE_FLAG_TIME_VALID;
-       state->enter = cpuidle_sleep_enter;
+       if (sh_mobile_sleep_supported & SUSP_SH_SF) {
+               state = &dev->states[i++];
+               snprintf(state->name, CPUIDLE_NAME_LEN, "C1");
+               strncpy(state->desc, "SuperH Sleep Mode [SF]",
+                       CPUIDLE_DESC_LEN);
+               state->exit_latency = 100;
+               state->target_residency = 1 * 2;
+               state->power_usage = 1;
+               state->flags = 0;
+               state->flags |= CPUIDLE_FLAG_TIME_VALID;
+               state->enter = cpuidle_sleep_enter;
+       }
 
-       state = &dev->states[i++];
-       snprintf(state->name, CPUIDLE_NAME_LEN, "C2");
-       strncpy(state->desc, "SuperH Mobile Standby Mode [SF]", CPUIDLE_DESC_LEN);
-       state->exit_latency = 2300;
-       state->target_residency = 1 * 2;
-       state->power_usage = 1;
-       state->flags = 0;
-       state->flags |= CPUIDLE_FLAG_TIME_VALID;
-       state->enter = cpuidle_sleep_enter;
+       if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) {
+               state = &dev->states[i++];
+               snprintf(state->name, CPUIDLE_NAME_LEN, "C2");
+               strncpy(state->desc, "SuperH Mobile Standby Mode [SF]",
+                       CPUIDLE_DESC_LEN);
+               state->exit_latency = 2300;
+               state->target_residency = 1 * 2;
+               state->power_usage = 1;
+               state->flags = 0;
+               state->flags |= CPUIDLE_FLAG_TIME_VALID;
+               state->enter = cpuidle_sleep_enter;
+       }
 
        dev->state_count = i;
 
index ee3c2aa..ca029a4 100644 (file)
 #include <linux/suspend.h>
 #include <asm/suspend.h>
 #include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+
+/*
+ * Notifier lists for pre/post sleep notification
+ */
+ATOMIC_NOTIFIER_HEAD(sh_mobile_pre_sleep_notifier_list);
+ATOMIC_NOTIFIER_HEAD(sh_mobile_post_sleep_notifier_list);
 
 /*
  * Sleep modes available on SuperH Mobile:
 #define SUSP_MODE_SLEEP                (SUSP_SH_SLEEP)
 #define SUSP_MODE_SLEEP_SF     (SUSP_SH_SLEEP | SUSP_SH_SF)
 #define SUSP_MODE_STANDBY_SF   (SUSP_SH_STANDBY | SUSP_SH_SF)
+#define SUSP_MODE_RSTANDBY     (SUSP_SH_RSTANDBY | SUSP_SH_MMU | SUSP_SH_SF)
+ /*
+  * U-standby mode is unsupported since it needs bootloader hacks
+  */
 
-/*
- * The following modes are not there yet:
- *
- * R-standby mode is unsupported, but will be added in the future
- * U-standby mode is low priority since it needs bootloader hacks
- */
-
-#define ILRAM_BASE 0xe5200000
-
-extern const unsigned char sh_mobile_standby[];
-extern const unsigned int sh_mobile_standby_size;
+#ifdef CONFIG_CPU_SUBTYPE_SH7724
+#define RAM_BASE 0xfd800000 /* RSMEM */
+#else
+#define RAM_BASE 0xe5200000 /* ILRAM */
+#endif
 
 void sh_mobile_call_standby(unsigned long mode)
 {
-       void *onchip_mem = (void *)ILRAM_BASE;
-       void (*standby_onchip_mem)(unsigned long, unsigned long) = onchip_mem;
+       void *onchip_mem = (void *)RAM_BASE;
+       struct sh_sleep_data *sdp = onchip_mem;
+       void (*standby_onchip_mem)(unsigned long, unsigned long);
+
+       /* code located directly after data structure */
+       standby_onchip_mem = (void *)(sdp + 1);
+
+       atomic_notifier_call_chain(&sh_mobile_pre_sleep_notifier_list,
+                                  mode, NULL);
+
+       /* flush the caches if MMU flag is set */
+       if (mode & SUSP_SH_MMU)
+               flush_cache_all();
 
        /* Let assembly snippet in on-chip memory handle the rest */
-       standby_onchip_mem(mode, ILRAM_BASE);
+       standby_onchip_mem(mode, RAM_BASE);
+
+       atomic_notifier_call_chain(&sh_mobile_post_sleep_notifier_list,
+                                  mode, NULL);
+}
+
+extern char sh_mobile_sleep_enter_start;
+extern char sh_mobile_sleep_enter_end;
+
+extern char sh_mobile_sleep_resume_start;
+extern char sh_mobile_sleep_resume_end;
+
+unsigned long sh_mobile_sleep_supported = SUSP_SH_SLEEP;
+
+void sh_mobile_register_self_refresh(unsigned long flags,
+                                    void *pre_start, void *pre_end,
+                                    void *post_start, void *post_end)
+{
+       void *onchip_mem = (void *)RAM_BASE;
+       void *vp;
+       struct sh_sleep_data *sdp;
+       int n;
+
+       /* part 0: data area */
+       sdp = onchip_mem;
+       sdp->addr.stbcr = 0xa4150020; /* STBCR */
+       sdp->addr.bar = 0xa4150040; /* BAR */
+       sdp->addr.pteh = 0xff000000; /* PTEH */
+       sdp->addr.ptel = 0xff000004; /* PTEL */
+       sdp->addr.ttb = 0xff000008; /* TTB */
+       sdp->addr.tea = 0xff00000c; /* TEA */
+       sdp->addr.mmucr = 0xff000010; /* MMUCR */
+       sdp->addr.ptea = 0xff000034; /* PTEA */
+       sdp->addr.pascr = 0xff000070; /* PASCR */
+       sdp->addr.irmcr = 0xff000078; /* IRMCR */
+       sdp->addr.ccr = 0xff00001c; /* CCR */
+       sdp->addr.ramcr = 0xff000074; /* RAMCR */
+       vp = sdp + 1;
+
+       /* part 1: common code to enter sleep mode */
+       n = &sh_mobile_sleep_enter_end - &sh_mobile_sleep_enter_start;
+       memcpy(vp, &sh_mobile_sleep_enter_start, n);
+       vp += roundup(n, 4);
+
+       /* part 2: board specific code to enter self-refresh mode */
+       n = pre_end - pre_start;
+       memcpy(vp, pre_start, n);
+       sdp->sf_pre = (unsigned long)vp;
+       vp += roundup(n, 4);
+
+       /* part 3: board specific code to resume from self-refresh mode */
+       n = post_end - post_start;
+       memcpy(vp, post_start, n);
+       sdp->sf_post = (unsigned long)vp;
+       vp += roundup(n, 4);
+
+       /* part 4: common code to resume from sleep mode */
+       WARN_ON(vp > (onchip_mem + 0x600));
+       vp = onchip_mem + 0x600; /* located at interrupt vector */
+       n = &sh_mobile_sleep_resume_end - &sh_mobile_sleep_resume_start;
+       memcpy(vp, &sh_mobile_sleep_resume_start, n);
+       sdp->resume = (unsigned long)vp;
+
+       sh_mobile_sleep_supported |= flags;
 }
 
 static int sh_pm_enter(suspend_state_t state)
 {
+       if (!(sh_mobile_sleep_supported & SUSP_MODE_STANDBY_SF))
+               return -ENXIO;
+
        local_irq_disable();
        set_bl_bit();
        sh_mobile_call_standby(SUSP_MODE_STANDBY_SF);
@@ -65,13 +147,6 @@ static struct platform_suspend_ops sh_pm_ops = {
 
 static int __init sh_pm_init(void)
 {
-       void *onchip_mem = (void *)ILRAM_BASE;
-
-       /* Copy the assembly snippet to the otherwise ununsed ILRAM */
-       memcpy(onchip_mem, sh_mobile_standby, sh_mobile_standby_size);
-       wmb();
-       ctrl_barrier();
-
        suspend_set_ops(&sh_pm_ops);
        sh_mobile_setup_cpuidle();
        return 0;
index 7c615b1..6dcb816 100644 (file)
@@ -45,12 +45,14 @@ static int __platform_pm_runtime_resume(struct platform_device *pdev)
 
        dev_dbg(d, "__platform_pm_runtime_resume() [%d]\n", hwblk);
 
-       if (d->driver && d->driver->pm && d->driver->pm->runtime_resume) {
+       if (d->driver) {
                hwblk_enable(hwblk_info, hwblk);
                ret = 0;
 
                if (test_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags)) {
-                       ret = d->driver->pm->runtime_resume(d);
+                       if (d->driver->pm && d->driver->pm->runtime_resume)
+                               ret = d->driver->pm->runtime_resume(d);
+
                        if (!ret)
                                clear_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags);
                        else
@@ -73,12 +75,15 @@ static int __platform_pm_runtime_suspend(struct platform_device *pdev)
 
        dev_dbg(d, "__platform_pm_runtime_suspend() [%d]\n", hwblk);
 
-       if (d->driver && d->driver->pm && d->driver->pm->runtime_suspend) {
+       if (d->driver) {
                BUG_ON(!test_bit(PDEV_ARCHDATA_FLAG_IDLE, &ad->flags));
+               ret = 0;
 
-               hwblk_enable(hwblk_info, hwblk);
-               ret = d->driver->pm->runtime_suspend(d);
-               hwblk_disable(hwblk_info, hwblk);
+               if (d->driver->pm && d->driver->pm->runtime_suspend) {
+                       hwblk_enable(hwblk_info, hwblk);
+                       ret = d->driver->pm->runtime_suspend(d);
+                       hwblk_disable(hwblk_info, hwblk);
+               }
 
                if (!ret) {
                        set_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags);
index a439e6c..e9dd7fa 100644 (file)
  * Kernel mode register usage, see entry.S:
  *     k0      scratch
  *     k1      scratch
- *     k4      scratch
  */
 #define k0     r0
 #define k1     r1
-#define k4     r4
 
-/* manage self-refresh and enter standby mode.
+/* manage self-refresh and enter standby mode. must be self-contained.
  * this code will be copied to on-chip memory and executed from there.
  */
+       .balign 4
+ENTRY(sh_mobile_sleep_enter_start)
 
-       .balign         4096,0,4096
-ENTRY(sh_mobile_standby)
+       /* save mode flags */
+       mov.l   r4, @(SH_SLEEP_MODE, r5)
 
        /* save original vbr */
-       stc     vbr, r1
-       mova    saved_vbr, r0
-       mov.l   r1, @r0
+       stc     vbr, r0
+       mov.l   r0, @(SH_SLEEP_VBR, r5)
 
        /* point vbr to our on-chip memory page */
        ldc     r5, vbr
 
        /* save return address */
-       mova    saved_spc, r0
-       sts     pr, r5
-       mov.l   r5, @r0
+       sts     pr, r0
+       mov.l   r0, @(SH_SLEEP_SPC, r5)
 
        /* save sr */
-       mova    saved_sr, r0
-       stc     sr, r5
-       mov.l   r5, @r0
+       stc     sr, r0
+       mov.l   r0, @(SH_SLEEP_SR, r5)
 
-       /* save mode flags */
-       mova    saved_mode, r0
-       mov.l   r4, @r0
+       /* save sp */
+       mov.l   r15, @(SH_SLEEP_SP, r5)
+
+       /* save stbcr */
+       bsr     save_register
+        mov    #SH_SLEEP_REG_STBCR, r0
+
+       /* save mmu and cache context if needed */
+       mov.l   @(SH_SLEEP_MODE, r5), r0
+       tst     #SUSP_SH_MMU, r0
+       bt      skip_mmu_save_disable
+
+       /* save mmu state */
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PTEH, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PTEL, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_TTB, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_TEA, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_MMUCR, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PTEA, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_PASCR, r0
 
-       /* put mode flags in r0 */
-       mov     r4, r0
+       bsr     save_register
+        mov    #SH_SLEEP_REG_IRMCR, r0
 
+       /* invalidate TLBs and disable the MMU */
+       bsr     get_register
+        mov    #SH_SLEEP_REG_MMUCR, r0
+       mov     #4, r1
+       mov.l   r1, @r0
+       icbi    @r0
+
+       /* save cache registers and disable caches */
+       bsr     save_register
+        mov    #SH_SLEEP_REG_CCR, r0
+
+       bsr     save_register
+        mov    #SH_SLEEP_REG_RAMCR, r0
+
+       bsr     get_register
+        mov    #SH_SLEEP_REG_CCR, r0
+       mov     #0, r1
+       mov.l   r1, @r0
+       icbi    @r0
+
+skip_mmu_save_disable:
+       /* call self-refresh entering code if needed */
+       mov.l   @(SH_SLEEP_MODE, r5), r0
        tst     #SUSP_SH_SF, r0
        bt      skip_set_sf
-#ifdef CONFIG_CPU_SUBTYPE_SH7724
-       /* DBSC: put memory in self-refresh mode */
-       mov.l   dben_reg, r4
-       mov.l   dben_data0, r1
-       mov.l   r1, @r4
-
-       mov.l   dbrfpdn0_reg, r4
-       mov.l   dbrfpdn0_data0, r1
-       mov.l   r1, @r4
-
-       mov.l   dbcmdcnt_reg, r4
-       mov.l   dbcmdcnt_data0, r1
-       mov.l   r1, @r4
-
-       mov.l   dbcmdcnt_reg, r4
-       mov.l   dbcmdcnt_data1, r1
-       mov.l   r1, @r4
-
-       mov.l   dbrfpdn0_reg, r4
-       mov.l   dbrfpdn0_data1, r1
-       mov.l   r1, @r4
-#else
-       /* SBSC: disable power down and put in self-refresh mode */
-       mov.l   1f, r4
-       mov.l   2f, r1
-       mov.l   @r4, r2
-       or      r1, r2
-       mov.l   3f, r3
-       and     r3, r2
-       mov.l   r2, @r4
-#endif
+
+       mov.l   @(SH_SLEEP_SF_PRE, r5), r0
+       jsr     @r0
+        nop
 
 skip_set_sf:
+       mov.l   @(SH_SLEEP_MODE, r5), r0
        tst     #SUSP_SH_STANDBY, r0
        bt      test_rstandby
 
@@ -104,6 +128,12 @@ test_rstandby:
        tst     #SUSP_SH_RSTANDBY, r0
        bt      test_ustandby
 
+       /* setup BAR register */
+       bsr     get_register
+        mov    #SH_SLEEP_REG_BAR, r0
+       mov.l   @(SH_SLEEP_RESUME, r5), r1
+       mov.l   r1, @r0
+
        /* set mode to "r-standby mode" */
        bra     do_sleep
         mov    #0x20, r1
@@ -123,124 +153,136 @@ force_sleep:
 
 do_sleep:
        /* setup and enter selected standby mode */
-       mov.l   5f, r4
-       mov.l   r1, @r4
+       bsr     get_register
+        mov    #SH_SLEEP_REG_STBCR, r0
+       mov.l   r1, @r0
 again:
        sleep
        bra     again
         nop
 
-restore_jump_vbr:
+save_register:
+       add     #SH_SLEEP_BASE_ADDR, r0
+       mov.l   @(r0, r5), r1
+       add     #-SH_SLEEP_BASE_ADDR, r0
+       mov.l   @r1, r1
+       add     #SH_SLEEP_BASE_DATA, r0
+       mov.l   r1, @(r0, r5)
+       add     #-SH_SLEEP_BASE_DATA, r0
+       rts
+        nop
+
+get_register:
+       add     #SH_SLEEP_BASE_ADDR, r0
+       mov.l   @(r0, r5), r0
+       rts
+        nop
+ENTRY(sh_mobile_sleep_enter_end)
+
+       .balign 4
+ENTRY(sh_mobile_sleep_resume_start)
+
+       /* figure out start address */
+       bsr     0f
+        nop
+0:
+       sts     pr, k1
+       mov.l   1f, k0
+       and     k0, k1
+
+       /* store pointer to data area in VBR */
+       ldc     k1, vbr
+
+       /* setup sr with saved sr */
+       mov.l   @(SH_SLEEP_SR, k1), k0
+       ldc     k0, sr
+
+       /* now: user register set! */
+       stc     vbr, r5
+
        /* setup spc with return address to c code */
-       mov.l   saved_spc, k0
-       ldc     k0, spc
+       mov.l   @(SH_SLEEP_SPC, r5), r0
+       ldc     r0, spc
 
        /* restore vbr */
-       mov.l   saved_vbr, k0
-       ldc     k0, vbr
+       mov.l   @(SH_SLEEP_VBR, r5), r0
+       ldc     r0, vbr
 
        /* setup ssr with saved sr */
-       mov.l   saved_sr, k0
-       ldc     k0, ssr
+       mov.l   @(SH_SLEEP_SR, r5), r0
+       ldc     r0, ssr
 
-       /* get mode flags */
-       mov.l   saved_mode, k0
+       /* restore sp */
+       mov.l   @(SH_SLEEP_SP, r5), r15
 
-done_sleep:
-       /* reset standby mode to sleep mode */
-       mov.l   5f, k4
-       mov     #0x00, k1
-       mov.l   k1, @k4
+       /* restore sleep mode register */
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_STBCR, r0
 
-       tst     #SUSP_SH_SF, k0
+       /* call self-refresh resume code if needed */
+       mov.l   @(SH_SLEEP_MODE, r5), r0
+       tst     #SUSP_SH_SF, r0
        bt      skip_restore_sf
 
-#ifdef CONFIG_CPU_SUBTYPE_SH7724
-       /* DBSC: put memory in auto-refresh mode */
-       mov.l   dbrfpdn0_reg, k4
-       mov.l   dbrfpdn0_data0, k1
-       mov.l   k1, @k4
-
-       nop /* sleep 140 ns */
-       nop
-       nop
-       nop
-
-       mov.l   dbcmdcnt_reg, k4
-       mov.l   dbcmdcnt_data0, k1
-       mov.l   k1, @k4
-
-       mov.l   dbcmdcnt_reg, k4
-       mov.l   dbcmdcnt_data1, k1
-       mov.l   k1, @k4
-
-       mov.l   dben_reg, k4
-       mov.l   dben_data1, k1
-       mov.l   k1, @k4
-
-       mov.l   dbrfpdn0_reg, k4
-       mov.l   dbrfpdn0_data2, k1
-       mov.l   k1, @k4
-#else
-       /* SBSC: set auto-refresh mode */
-       mov.l   1f, k4
-       mov.l   @k4, k0
-       mov.l   4f, k1
-       and     k1, k0
-       mov.l   k0, @k4
-       mov.l   6f, k4
-       mov.l   8f, k0
-       mov.l   @k4, k1
-       mov     #-1, k4
-       add     k4, k1
-       or      k1, k0
-       mov.l   7f, k1
-       mov.l   k0, @k1
-#endif
+       mov.l   @(SH_SLEEP_SF_POST, r5), r0
+       jsr     @r0
+        nop
+
 skip_restore_sf:
-       /* jump to vbr vector */
-       mov.l   saved_vbr, k0
-       mov.l   offset_vbr, k4
-       add     k4, k0
-       jmp     @k0
+       /* restore mmu and cache state if needed */
+       mov.l   @(SH_SLEEP_MODE, r5), r0
+       tst     #SUSP_SH_MMU, r0
+       bt      skip_restore_mmu
+
+       /* restore mmu state */
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PTEH, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PTEL, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_TTB, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_TEA, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PTEA, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_PASCR, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_IRMCR, r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_MMUCR, r0
+       icbi    @r0
+
+       /* restore cache settings */
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_RAMCR, r0
+       icbi    @r0
+
+       bsr     restore_register
+        mov    #SH_SLEEP_REG_CCR, r0
+       icbi    @r0
+
+skip_restore_mmu:
+       rte
         nop
 
-       .balign 4
-saved_mode:    .long   0
-saved_spc:     .long   0
-saved_sr:      .long   0
-saved_vbr:     .long   0
-offset_vbr:    .long   0x600
-#ifdef CONFIG_CPU_SUBTYPE_SH7724
-dben_reg:      .long   0xfd000010 /* DBEN */
-dben_data0:    .long   0
-dben_data1:    .long   1
-dbrfpdn0_reg:  .long   0xfd000040 /* DBRFPDN0 */
-dbrfpdn0_data0:        .long   0
-dbrfpdn0_data1:        .long   1
-dbrfpdn0_data2:        .long   0x00010000
-dbcmdcnt_reg:  .long   0xfd000014 /* DBCMDCNT */
-dbcmdcnt_data0:        .long   2
-dbcmdcnt_data1:        .long   4
-#else
-1:     .long   0xfe400008 /* SDCR0 */
-2:     .long   0x00000400
-3:     .long   0xffff7fff
-4:     .long   0xfffffbff
-#endif
-5:     .long   0xa4150020 /* STBCR */
-6:     .long   0xfe40001c /* RTCOR */
-7:     .long   0xfe400018 /* RTCNT */
-8:     .long   0xa55a0000
-
-
-/* interrupt vector @ 0x600 */
-       .balign         0x400,0,0x400
-       .long   0xdeadbeef
-       .balign         0x200,0,0x200
-       bra     restore_jump_vbr
+restore_register:
+       add     #SH_SLEEP_BASE_DATA, r0
+       mov.l   @(r0, r5), r1
+       add     #-SH_SLEEP_BASE_DATA, r0
+       add     #SH_SLEEP_BASE_ADDR, r0
+       mov.l   @(r0, r5), r0
+       mov.l   r1, @r0
+       rts
         nop
-sh_mobile_standby_end:
 
-ENTRY(sh_mobile_standby_size)
-       .long sh_mobile_standby_end - sh_mobile_standby
+       .balign 4
+1:     .long   ~0x7ff
+ENTRY(sh_mobile_sleep_resume_end)
diff --git a/arch/sh/kernel/cpu/ubc.S b/arch/sh/kernel/cpu/ubc.S
deleted file mode 100644 (file)
index 8192307..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * arch/sh/kernel/cpu/ubc.S
- *
- * Set of management routines for the User Break Controller (UBC)
- *
- * Copyright (C) 2002 Paul Mundt
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-#include <linux/linkage.h>
-#include <asm/ubc.h>
-
-#define STBCR2         0xffc00010
-
-ENTRY(ubc_sleep)
-       mov     #0, r0
-
-       mov.l   1f, r1          ! Zero out UBC_BBRA ..
-       mov.w   r0, @r1
-
-       mov.l   2f, r1          ! .. same for BBRB ..
-       mov.w   r0, @r1
-
-       mov.l   3f, r1          ! .. and again for BRCR.
-       mov.w   r0, @r1
-
-       mov.w   @r1, r0         ! Dummy read BRCR
-
-       mov.l   4f, r1          ! Set MSTP5 in STBCR2
-       mov.b   @r1, r0
-       or      #0x01, r0
-       mov.b   r0, @r1
-
-       mov.b   @r1, r0         ! Two dummy reads ..
-       mov.b   @r1, r0
-
-       rts
-       nop
-
-ENTRY(ubc_wakeup)
-       mov.l   4f, r1          ! Clear MSTP5
-       mov.b   @r1, r0
-       and     #0xfe, r0
-       mov.b   r0, @r1
-
-       mov.b   @r1, r0         ! Two more dummy reads ..
-       mov.b   @r1, r0
-
-       rts
-       nop
-
-1:     .long   UBC_BBRA
-2:     .long   UBC_BBRB
-3:     .long   UBC_BRCR
-4:     .long   STBCR2
-
diff --git a/arch/sh/kernel/dma-nommu.c b/arch/sh/kernel/dma-nommu.c
new file mode 100644 (file)
index 0000000..3c55b87
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * DMA mapping support for platforms lacking IOMMUs.
+ *
+ * Copyright (C) 2009  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+
+static dma_addr_t nommu_map_page(struct device *dev, struct page *page,
+                                unsigned long offset, size_t size,
+                                enum dma_data_direction dir,
+                                struct dma_attrs *attrs)
+{
+       dma_addr_t addr = page_to_phys(page) + offset;
+
+       WARN_ON(size == 0);
+       dma_cache_sync(dev, page_address(page) + offset, size, dir);
+
+       return addr;
+}
+
+static int nommu_map_sg(struct device *dev, struct scatterlist *sg,
+                       int nents, enum dma_data_direction dir,
+                       struct dma_attrs *attrs)
+{
+       struct scatterlist *s;
+       int i;
+
+       WARN_ON(nents == 0 || sg[0].length == 0);
+
+       for_each_sg(sg, s, nents, i) {
+               BUG_ON(!sg_page(s));
+
+               dma_cache_sync(dev, sg_virt(s), s->length, dir);
+
+               s->dma_address = sg_phys(s);
+               s->dma_length = s->length;
+       }
+
+       return nents;
+}
+
+#ifdef CONFIG_DMA_NONCOHERENT
+static void nommu_sync_single(struct device *dev, dma_addr_t addr,
+                             size_t size, enum dma_data_direction dir)
+{
+       dma_cache_sync(dev, phys_to_virt(addr), size, dir);
+}
+
+static void nommu_sync_sg(struct device *dev, struct scatterlist *sg,
+                         int nelems, enum dma_data_direction dir)
+{
+       struct scatterlist *s;
+       int i;
+
+       for_each_sg(sg, s, nelems, i)
+               dma_cache_sync(dev, sg_virt(s), s->length, dir);
+}
+#endif
+
+struct dma_map_ops nommu_dma_ops = {
+       .alloc_coherent         = dma_generic_alloc_coherent,
+       .free_coherent          = dma_generic_free_coherent,
+       .map_page               = nommu_map_page,
+       .map_sg                 = nommu_map_sg,
+#ifdef CONFIG_DMA_NONCOHERENT
+       .sync_single_for_device = nommu_sync_single,
+       .sync_sg_for_device     = nommu_sync_sg,
+#endif
+       .is_phys                = 1,
+};
+
+void __init no_iommu_init(void)
+{
+       if (dma_ops)
+               return;
+       dma_ops = &nommu_dma_ops;
+}
index d76a231..3576b70 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/list.h>
 #include <linux/mempool.h>
 #include <linux/mm.h>
+#include <linux/elf.h>
 #include <linux/ftrace.h>
 #include <asm/dwarf.h>
 #include <asm/unwinder.h>
@@ -530,7 +531,18 @@ static int dwarf_cfa_execute_insns(unsigned char *insn_start,
 }
 
 /**
- *     dwarf_unwind_stack - recursively unwind the stack
+ *     dwarf_free_frame - free the memory allocated for @frame
+ *     @frame: the frame to free
+ */
+void dwarf_free_frame(struct dwarf_frame *frame)
+{
+       dwarf_frame_free_regs(frame);
+       mempool_free(frame, dwarf_frame_pool);
+}
+
+/**
+ *     dwarf_unwind_stack - unwind the stack
+ *
  *     @pc: address of the function to unwind
  *     @prev: struct dwarf_frame of the previous stackframe on the callstack
  *
@@ -548,9 +560,9 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
        unsigned long addr;
 
        /*
-        * If this is the first invocation of this recursive function we
-        * need get the contents of a physical register to get the CFA
-        * in order to begin the virtual unwinding of the stack.
+        * If we're starting at the top of the stack we need get the
+        * contents of a physical register to get the CFA in order to
+        * begin the virtual unwinding of the stack.
         *
         * NOTE: the return address is guaranteed to be setup by the
         * time this function makes its first function call.
@@ -593,9 +605,8 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
        fde = dwarf_lookup_fde(pc);
        if (!fde) {
                /*
-                * This is our normal exit path - the one that stops the
-                * recursion. There's two reasons why we might exit
-                * here,
+                * This is our normal exit path. There are two reasons
+                * why we might exit here,
                 *
                 *      a) pc has no asscociated DWARF frame info and so
                 *      we don't know how to unwind this frame. This is
@@ -637,10 +648,10 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
 
                } else {
                        /*
-                        * Again, this is the first invocation of this
-                        * recurisve function. We need to physically
-                        * read the contents of a register in order to
-                        * get the Canonical Frame Address for this
+                        * Again, we're starting from the top of the
+                        * stack. We need to physically read
+                        * the contents of a register in order to get
+                        * the Canonical Frame Address for this
                         * function.
                         */
                        frame->cfa = dwarf_read_arch_reg(frame->cfa_register);
@@ -670,13 +681,12 @@ struct dwarf_frame * dwarf_unwind_stack(unsigned long pc,
        return frame;
 
 bail:
-       dwarf_frame_free_regs(frame);
-       mempool_free(frame, dwarf_frame_pool);
+       dwarf_free_frame(frame);
        return NULL;
 }
 
 static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
-                          unsigned char *end)
+                          unsigned char *end, struct module *mod)
 {
        struct dwarf_cie *cie;
        unsigned long flags;
@@ -772,6 +782,8 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
        cie->initial_instructions = p;
        cie->instructions_end = end;
 
+       cie->mod = mod;
+
        /* Add to list */
        spin_lock_irqsave(&dwarf_cie_lock, flags);
        list_add_tail(&cie->link, &dwarf_cie_list);
@@ -782,7 +794,7 @@ static int dwarf_parse_cie(void *entry, void *p, unsigned long len,
 
 static int dwarf_parse_fde(void *entry, u32 entry_type,
                           void *start, unsigned long len,
-                          unsigned char *end)
+                          unsigned char *end, struct module *mod)
 {
        struct dwarf_fde *fde;
        struct dwarf_cie *cie;
@@ -831,6 +843,8 @@ static int dwarf_parse_fde(void *entry, u32 entry_type,
        fde->instructions = p;
        fde->end = end;
 
+       fde->mod = mod;
+
        /* Add to list. */
        spin_lock_irqsave(&dwarf_fde_lock, flags);
        list_add_tail(&fde->link, &dwarf_fde_list);
@@ -854,10 +868,8 @@ static void dwarf_unwinder_dump(struct task_struct *task,
        while (1) {
                frame = dwarf_unwind_stack(return_addr, _frame);
 
-               if (_frame) {
-                       dwarf_frame_free_regs(_frame);
-                       mempool_free(_frame, dwarf_frame_pool);
-               }
+               if (_frame)
+                       dwarf_free_frame(_frame);
 
                _frame = frame;
 
@@ -867,6 +879,9 @@ static void dwarf_unwinder_dump(struct task_struct *task,
                return_addr = frame->return_addr;
                ops->address(data, return_addr, 1);
        }
+
+       if (frame)
+               dwarf_free_frame(frame);
 }
 
 static struct unwinder dwarf_unwinder = {
@@ -896,48 +911,28 @@ static void dwarf_unwinder_cleanup(void)
 }
 
 /**
- *     dwarf_unwinder_init - initialise the dwarf unwinder
+ *     dwarf_parse_section - parse DWARF section
+ *     @eh_frame_start: start address of the .eh_frame section
+ *     @eh_frame_end: end address of the .eh_frame section
+ *     @mod: the kernel module containing the .eh_frame section
  *
- *     Build the data structures describing the .dwarf_frame section to
- *     make it easier to lookup CIE and FDE entries. Because the
- *     .eh_frame section is packed as tightly as possible it is not
- *     easy to lookup the FDE for a given PC, so we build a list of FDE
- *     and CIE entries that make it easier.
+ *     Parse the information in a .eh_frame section.
  */
-static int __init dwarf_unwinder_init(void)
+static int dwarf_parse_section(char *eh_frame_start, char *eh_frame_end,
+                              struct module *mod)
 {
        u32 entry_type;
        void *p, *entry;
        int count, err = 0;
-       unsigned long len;
+       unsigned long len = 0;
        unsigned int c_entries, f_entries;
        unsigned char *end;
-       INIT_LIST_HEAD(&dwarf_cie_list);
-       INIT_LIST_HEAD(&dwarf_fde_list);
 
        c_entries = 0;
        f_entries = 0;
-       entry = &__start_eh_frame;
-
-       dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
-                       sizeof(struct dwarf_frame), 0,
-                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
-
-       dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
-                       sizeof(struct dwarf_reg), 0,
-                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
+       entry = eh_frame_start;
 
-       dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ,
-                                         mempool_alloc_slab,
-                                         mempool_free_slab,
-                                         dwarf_frame_cachep);
-
-       dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ,
-                                        mempool_alloc_slab,
-                                        mempool_free_slab,
-                                        dwarf_reg_cachep);
-
-       while ((char *)entry < __stop_eh_frame) {
+       while ((char *)entry < eh_frame_end) {
                p = entry;
 
                count = dwarf_entry_len(p, &len);
@@ -949,6 +944,7 @@ static int __init dwarf_unwinder_init(void)
                         * entry and move to the next one because 'len'
                         * tells us where our next entry is.
                         */
+                       err = -EINVAL;
                        goto out;
                } else
                        p += count;
@@ -960,13 +956,14 @@ static int __init dwarf_unwinder_init(void)
                p += 4;
 
                if (entry_type == DW_EH_FRAME_CIE) {
-                       err = dwarf_parse_cie(entry, p, len, end);
+                       err = dwarf_parse_cie(entry, p, len, end, mod);
                        if (err < 0)
                                goto out;
                        else
                                c_entries++;
                } else {
-                       err = dwarf_parse_fde(entry, entry_type, p, len, end);
+                       err = dwarf_parse_fde(entry, entry_type, p, len,
+                                             end, mod);
                        if (err < 0)
                                goto out;
                        else
@@ -979,6 +976,129 @@ static int __init dwarf_unwinder_init(void)
        printk(KERN_INFO "DWARF unwinder initialised: read %u CIEs, %u FDEs\n",
               c_entries, f_entries);
 
+       return 0;
+
+out:
+       return err;
+}
+
+#ifdef CONFIG_MODULES
+int module_dwarf_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+                         struct module *me)
+{
+       unsigned int i, err;
+       unsigned long start, end;
+       char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+       start = end = 0;
+
+       for (i = 1; i < hdr->e_shnum; i++) {
+               /* Alloc bit cleared means "ignore it." */
+               if ((sechdrs[i].sh_flags & SHF_ALLOC)
+                   && !strcmp(secstrings+sechdrs[i].sh_name, ".eh_frame")) {
+                       start = sechdrs[i].sh_addr;
+                       end = start + sechdrs[i].sh_size;
+                       break;
+               }
+       }
+
+       /* Did we find the .eh_frame section? */
+       if (i != hdr->e_shnum) {
+               err = dwarf_parse_section((char *)start, (char *)end, me);
+               if (err) {
+                       printk(KERN_WARNING "%s: failed to parse DWARF info\n",
+                              me->name);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ *     module_dwarf_cleanup - remove FDE/CIEs associated with @mod
+ *     @mod: the module that is being unloaded
+ *
+ *     Remove any FDEs and CIEs from the global lists that came from
+ *     @mod's .eh_frame section because @mod is being unloaded.
+ */
+void module_dwarf_cleanup(struct module *mod)
+{
+       struct dwarf_fde *fde;
+       struct dwarf_cie *cie;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dwarf_cie_lock, flags);
+
+again_cie:
+       list_for_each_entry(cie, &dwarf_cie_list, link) {
+               if (cie->mod == mod)
+                       break;
+       }
+
+       if (&cie->link != &dwarf_cie_list) {
+               list_del(&cie->link);
+               kfree(cie);
+               goto again_cie;
+       }
+
+       spin_unlock_irqrestore(&dwarf_cie_lock, flags);
+
+       spin_lock_irqsave(&dwarf_fde_lock, flags);
+
+again_fde:
+       list_for_each_entry(fde, &dwarf_fde_list, link) {
+               if (fde->mod == mod)
+                       break;
+       }
+
+       if (&fde->link != &dwarf_fde_list) {
+               list_del(&fde->link);
+               kfree(fde);
+               goto again_fde;
+       }
+
+       spin_unlock_irqrestore(&dwarf_fde_lock, flags);
+}
+#endif /* CONFIG_MODULES */
+
+/**
+ *     dwarf_unwinder_init - initialise the dwarf unwinder
+ *
+ *     Build the data structures describing the .dwarf_frame section to
+ *     make it easier to lookup CIE and FDE entries. Because the
+ *     .eh_frame section is packed as tightly as possible it is not
+ *     easy to lookup the FDE for a given PC, so we build a list of FDE
+ *     and CIE entries that make it easier.
+ */
+static int __init dwarf_unwinder_init(void)
+{
+       int err;
+       INIT_LIST_HEAD(&dwarf_cie_list);
+       INIT_LIST_HEAD(&dwarf_fde_list);
+
+       dwarf_frame_cachep = kmem_cache_create("dwarf_frames",
+                       sizeof(struct dwarf_frame), 0,
+                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
+
+       dwarf_reg_cachep = kmem_cache_create("dwarf_regs",
+                       sizeof(struct dwarf_reg), 0,
+                       SLAB_PANIC | SLAB_HWCACHE_ALIGN | SLAB_NOTRACK, NULL);
+
+       dwarf_frame_pool = mempool_create(DWARF_FRAME_MIN_REQ,
+                                         mempool_alloc_slab,
+                                         mempool_free_slab,
+                                         dwarf_frame_cachep);
+
+       dwarf_reg_pool = mempool_create(DWARF_REG_MIN_REQ,
+                                        mempool_alloc_slab,
+                                        mempool_free_slab,
+                                        dwarf_reg_cachep);
+
+       err = dwarf_parse_section(__start_eh_frame, __stop_eh_frame, NULL);
+       if (err)
+               goto out;
+
        err = unwinder_register(&dwarf_unwinder);
        if (err)
                goto out;
index 3eb8493..f0abd58 100644 (file)
@@ -133,7 +133,7 @@ work_pending:
        ! r8: current_thread_info
        ! t:  result of "tst    #_TIF_NEED_RESCHED, r0"
        bf/s    work_resched
-        tst    #(_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK), r0
+        tst    #_TIF_SIGPENDING, r0
 work_notifysig:
        bt/s    __restore_all
         mov    r15, r4
index 2c48e26..b6f41c1 100644 (file)
@@ -62,6 +62,150 @@ static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
        return ftrace_replaced_code;
 }
 
+/*
+ * Modifying code must take extra care. On an SMP machine, if
+ * the code being modified is also being executed on another CPU
+ * that CPU will have undefined results and possibly take a GPF.
+ * We use kstop_machine to stop other CPUS from exectuing code.
+ * But this does not stop NMIs from happening. We still need
+ * to protect against that. We separate out the modification of
+ * the code to take care of this.
+ *
+ * Two buffers are added: An IP buffer and a "code" buffer.
+ *
+ * 1) Put the instruction pointer into the IP buffer
+ *    and the new code into the "code" buffer.
+ * 2) Wait for any running NMIs to finish and set a flag that says
+ *    we are modifying code, it is done in an atomic operation.
+ * 3) Write the code
+ * 4) clear the flag.
+ * 5) Wait for any running NMIs to finish.
+ *
+ * If an NMI is executed, the first thing it does is to call
+ * "ftrace_nmi_enter". This will check if the flag is set to write
+ * and if it is, it will write what is in the IP and "code" buffers.
+ *
+ * The trick is, it does not matter if everyone is writing the same
+ * content to the code location. Also, if a CPU is executing code
+ * it is OK to write to that code location if the contents being written
+ * are the same as what exists.
+ */
+#define MOD_CODE_WRITE_FLAG (1 << 31)  /* set when NMI should do the write */
+static atomic_t nmi_running = ATOMIC_INIT(0);
+static int mod_code_status;            /* holds return value of text write */
+static void *mod_code_ip;              /* holds the IP to write to */
+static void *mod_code_newcode;         /* holds the text to write to the IP */
+
+static unsigned nmi_wait_count;
+static atomic_t nmi_update_count = ATOMIC_INIT(0);
+
+int ftrace_arch_read_dyn_info(char *buf, int size)
+{
+       int r;
+
+       r = snprintf(buf, size, "%u %u",
+                    nmi_wait_count,
+                    atomic_read(&nmi_update_count));
+       return r;
+}
+
+static void clear_mod_flag(void)
+{
+       int old = atomic_read(&nmi_running);
+
+       for (;;) {
+               int new = old & ~MOD_CODE_WRITE_FLAG;
+
+               if (old == new)
+                       break;
+
+               old = atomic_cmpxchg(&nmi_running, old, new);
+       }
+}
+
+static void ftrace_mod_code(void)
+{
+       /*
+        * Yes, more than one CPU process can be writing to mod_code_status.
+        *    (and the code itself)
+        * But if one were to fail, then they all should, and if one were
+        * to succeed, then they all should.
+        */
+       mod_code_status = probe_kernel_write(mod_code_ip, mod_code_newcode,
+                                            MCOUNT_INSN_SIZE);
+
+       /* if we fail, then kill any new writers */
+       if (mod_code_status)
+               clear_mod_flag();
+}
+
+void ftrace_nmi_enter(void)
+{
+       if (atomic_inc_return(&nmi_running) & MOD_CODE_WRITE_FLAG) {
+               smp_rmb();
+               ftrace_mod_code();
+               atomic_inc(&nmi_update_count);
+       }
+       /* Must have previous changes seen before executions */
+       smp_mb();
+}
+
+void ftrace_nmi_exit(void)
+{
+       /* Finish all executions before clearing nmi_running */
+       smp_mb();
+       atomic_dec(&nmi_running);
+}
+
+static void wait_for_nmi_and_set_mod_flag(void)
+{
+       if (!atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG))
+               return;
+
+       do {
+               cpu_relax();
+       } while (atomic_cmpxchg(&nmi_running, 0, MOD_CODE_WRITE_FLAG));
+
+       nmi_wait_count++;
+}
+
+static void wait_for_nmi(void)
+{
+       if (!atomic_read(&nmi_running))
+               return;
+
+       do {
+               cpu_relax();
+       } while (atomic_read(&nmi_running));
+
+       nmi_wait_count++;
+}
+
+static int
+do_ftrace_mod_code(unsigned long ip, void *new_code)
+{
+       mod_code_ip = (void *)ip;
+       mod_code_newcode = new_code;
+
+       /* The buffers need to be visible before we let NMIs write them */
+       smp_mb();
+
+       wait_for_nmi_and_set_mod_flag();
+
+       /* Make sure all running NMIs have finished before we write the code */
+       smp_mb();
+
+       ftrace_mod_code();
+
+       /* Make sure the write happens before clearing the bit */
+       smp_mb();
+
+       clear_mod_flag();
+       wait_for_nmi();
+
+       return mod_code_status;
+}
+
 static int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
                       unsigned char *new_code)
 {
@@ -86,7 +230,7 @@ static int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
                return -EINVAL;
 
        /* replace the text with the new text */
-       if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE))
+       if (do_ftrace_mod_code(ip, new_code))
                return -EPERM;
 
        flush_icache_range(ip, ip + MCOUNT_INSN_SIZE);
index a78be74..1151ecd 100644 (file)
@@ -33,7 +33,7 @@ ENTRY(empty_zero_page)
        .long   1               /* LOADER_TYPE */
        .long   0x00000000      /* INITRD_START */
        .long   0x00000000      /* INITRD_SIZE */
-#ifdef CONFIG_32BIT
+#if defined(CONFIG_32BIT) && defined(CONFIG_PMB_FIXED)
        .long   0x53453f00 + 32 /* "SE?" = 32 bit */
 #else
        .long   0x53453f00 + 29 /* "SE?" = 29 bit */
index 27ff2dc..aaff003 100644 (file)
@@ -21,7 +21,7 @@
 #include <asm/atomic.h>
 
 static int hlt_counter;
-void (*pm_idle)(void);
+void (*pm_idle)(void) = NULL;
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
 
@@ -39,48 +39,92 @@ static int __init hlt_setup(char *__unused)
 }
 __setup("hlt", hlt_setup);
 
+static inline int hlt_works(void)
+{
+       return !hlt_counter;
+}
+
+/*
+ * On SMP it's slightly faster (but much more power-consuming!)
+ * to poll the ->work.need_resched flag instead of waiting for the
+ * cross-CPU IPI to arrive. Use this option with caution.
+ */
+static void poll_idle(void)
+{
+       local_irq_enable();
+       while (!need_resched())
+               cpu_relax();
+}
+
 void default_idle(void)
 {
-       if (!hlt_counter) {
+       if (hlt_works()) {
                clear_thread_flag(TIF_POLLING_NRFLAG);
                smp_mb__after_clear_bit();
-               set_bl_bit();
-               stop_critical_timings();
 
-               while (!need_resched())
+               if (!need_resched()) {
+                       local_irq_enable();
                        cpu_sleep();
+               } else
+                       local_irq_enable();
 
-               start_critical_timings();
-               clear_bl_bit();
                set_thread_flag(TIF_POLLING_NRFLAG);
        } else
-               while (!need_resched())
-                       cpu_relax();
+               poll_idle();
 }
 
+/*
+ * The idle thread. There's no useful work to be done, so just try to conserve
+ * power and have a low exit latency (ie sit in a loop waiting for somebody to
+ * say that they'd like to reschedule)
+ */
 void cpu_idle(void)
 {
+       unsigned int cpu = smp_processor_id();
+
        set_thread_flag(TIF_POLLING_NRFLAG);
 
        /* endless idle loop with no priority at all */
        while (1) {
-               void (*idle)(void) = pm_idle;
+               tick_nohz_stop_sched_tick(1);
 
-               if (!idle)
-                       idle = default_idle;
+               while (!need_resched() && cpu_online(cpu)) {
+                       check_pgt_cache();
+                       rmb();
 
-               tick_nohz_stop_sched_tick(1);
-               while (!need_resched())
-                       idle();
-               tick_nohz_restart_sched_tick();
+                       local_irq_disable();
+                       /* Don't trace irqs off for idle */
+                       stop_critical_timings();
+                       pm_idle();
+                       /*
+                        * Sanity check to ensure that pm_idle() returns
+                        * with IRQs enabled
+                        */
+                       WARN_ON(irqs_disabled());
+                       start_critical_timings();
+               }
 
+               tick_nohz_restart_sched_tick();
                preempt_enable_no_resched();
                schedule();
                preempt_disable();
-               check_pgt_cache();
        }
 }
 
+void __cpuinit select_idle_routine(void)
+{
+       /*
+        * If a platform has set its own idle routine, leave it alone.
+        */
+       if (pm_idle)
+               return;
+
+       if (hlt_works())
+               pm_idle = default_idle;
+       else
+               pm_idle = poll_idle;
+}
+
 static void do_nothing(void *unused)
 {
 }
index b8fa652..e1e1dbd 100644 (file)
@@ -24,7 +24,7 @@
 #define dummy_read()
 #endif
 
-unsigned long generic_io_base;
+unsigned long generic_io_base = 0;
 
 u8 generic_inb(unsigned long port)
 {
@@ -147,8 +147,10 @@ void generic_outsl(unsigned long port, const void *src, unsigned long count)
 
 void __iomem *generic_ioport_map(unsigned long addr, unsigned int size)
 {
+#ifdef P1SEG
        if (PXSEG(addr) >= P1SEG)
                return (void __iomem *)addr;
+#endif
 
        return (void __iomem *)(addr + generic_io_base);
 }
index eac7da7..e1913f2 100644 (file)
@@ -37,7 +37,15 @@ void ack_bad_irq(unsigned int irq)
  */
 static int show_other_interrupts(struct seq_file *p, int prec)
 {
+       int j;
+
+       seq_printf(p, "%*s: ", prec, "NMI");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", irq_stat[j].__nmi_count);
+       seq_printf(p, "  Non-maskable interrupts\n");
+
        seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
+
        return 0;
 }
 
@@ -255,6 +263,12 @@ void __init init_IRQ(void)
 {
        plat_irq_setup();
 
+       /*
+        * Pin any of the legacy IRQ vectors that haven't already been
+        * grabbed by the platform
+        */
+       reserve_irq_legacy();
+
        /* Perform the machine specific initialisation */
        if (sh_mv.mv_init_irq)
                sh_mv.mv_init_irq();
diff --git a/arch/sh/kernel/irq_32.c b/arch/sh/kernel/irq_32.c
new file mode 100644 (file)
index 0000000..e33ab15
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SHcompact irqflags support
+ *
+ * Copyright (C) 2006 - 2009 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/irqflags.h>
+#include <linux/module.h>
+
+void notrace raw_local_irq_restore(unsigned long flags)
+{
+       unsigned long __dummy0, __dummy1;
+
+       if (flags == RAW_IRQ_DISABLED) {
+               __asm__ __volatile__ (
+                       "stc    sr, %0\n\t"
+                       "or     #0xf0, %0\n\t"
+                       "ldc    %0, sr\n\t"
+                       : "=&z" (__dummy0)
+                       : /* no inputs */
+                       : "memory"
+               );
+       } else {
+               __asm__ __volatile__ (
+                       "stc    sr, %0\n\t"
+                       "and    %1, %0\n\t"
+#ifdef CONFIG_CPU_HAS_SR_RB
+                       "stc    r6_bank, %1\n\t"
+                       "or     %1, %0\n\t"
+#endif
+                       "ldc    %0, sr\n\t"
+                       : "=&r" (__dummy0), "=r" (__dummy1)
+                       : "1" (~RAW_IRQ_DISABLED)
+                       : "memory"
+               );
+       }
+}
+EXPORT_SYMBOL(raw_local_irq_restore);
+
+unsigned long notrace __raw_local_save_flags(void)
+{
+       unsigned long flags;
+
+       __asm__ __volatile__ (
+               "stc    sr, %0\n\t"
+               "and    #0xf0, %0\n\t"
+               : "=&z" (flags)
+               : /* no inputs */
+               : "memory"
+       );
+
+       return flags;
+}
+EXPORT_SYMBOL(__raw_local_save_flags);
diff --git a/arch/sh/kernel/irq_64.c b/arch/sh/kernel/irq_64.c
new file mode 100644 (file)
index 0000000..32365ba
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SHmedia irqflags support
+ *
+ * Copyright (C) 2006 - 2009 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/irqflags.h>
+#include <linux/module.h>
+#include <cpu/registers.h>
+
+void notrace raw_local_irq_restore(unsigned long flags)
+{
+       unsigned long long __dummy;
+
+       if (flags == RAW_IRQ_DISABLED) {
+               __asm__ __volatile__ (
+                       "getcon " __SR ", %0\n\t"
+                       "or     %0, %1, %0\n\t"
+                       "putcon %0, " __SR "\n\t"
+                       : "=&r" (__dummy)
+                       : "r" (RAW_IRQ_DISABLED)
+               );
+       } else {
+               __asm__ __volatile__ (
+                       "getcon " __SR ", %0\n\t"
+                       "and    %0, %1, %0\n\t"
+                       "putcon %0, " __SR "\n\t"
+                       : "=&r" (__dummy)
+                       : "r" (~RAW_IRQ_DISABLED)
+               );
+       }
+}
+EXPORT_SYMBOL(raw_local_irq_restore);
+
+unsigned long notrace __raw_local_save_flags(void)
+{
+       unsigned long flags;
+
+       __asm__ __volatile__ (
+               "getcon " __SR ", %0\n\t"
+               "and    %0, %1, %0"
+               : "=&r" (flags)
+               : "r" (RAW_IRQ_DISABLED)
+       );
+
+       return flags;
+}
+EXPORT_SYMBOL(__raw_local_save_flags);
index 7ea2704..76f2802 100644 (file)
@@ -46,12 +46,6 @@ void machine_crash_shutdown(struct pt_regs *regs)
  */
 int machine_kexec_prepare(struct kimage *image)
 {
-       /* older versions of kexec-tools are passing
-        * the zImage entry point as a virtual address.
-        */
-       if (image->start != PHYSADDR(image->start))
-               return -EINVAL; /* upgrade your kexec-tools */
-
        return 0;
 }
 
index cbce639..1652340 100644 (file)
@@ -135,5 +135,9 @@ void __init sh_mv_setup(void)
        if (!sh_mv.mv_nr_irqs)
                sh_mv.mv_nr_irqs = NR_IRQS;
 
+#ifdef P2SEG
        __set_io_port_base(P2SEG);
+#else
+       __set_io_port_base(0);
+#endif
 }
index c2efdcd..43adddf 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/string.h>
 #include <linux/kernel.h>
 #include <asm/unaligned.h>
+#include <asm/dwarf.h>
 
 void *module_alloc(unsigned long size)
 {
@@ -145,10 +146,16 @@ int module_finalize(const Elf_Ehdr *hdr,
                    const Elf_Shdr *sechdrs,
                    struct module *me)
 {
-       return module_bug_finalize(hdr, sechdrs, me);
+       int ret = 0;
+
+       ret |= module_dwarf_finalize(hdr, sechdrs, me);
+       ret |= module_bug_finalize(hdr, sechdrs, me);
+
+       return ret;
 }
 
 void module_arch_cleanup(struct module *mod)
 {
        module_bug_cleanup(mod);
+       module_dwarf_cleanup(mod);
 }
diff --git a/arch/sh/kernel/perf_callchain.c b/arch/sh/kernel/perf_callchain.c
new file mode 100644 (file)
index 0000000..24ea837
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Performance event callchain support - SuperH architecture code
+ *
+ * Copyright (C) 2009  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/perf_event.h>
+#include <linux/percpu.h>
+#include <asm/unwinder.h>
+#include <asm/ptrace.h>
+
+static inline void callchain_store(struct perf_callchain_entry *entry, u64 ip)
+{
+       if (entry->nr < PERF_MAX_STACK_DEPTH)
+               entry->ip[entry->nr++] = ip;
+}
+
+static void callchain_warning(void *data, char *msg)
+{
+}
+
+static void
+callchain_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+}
+
+static int callchain_stack(void *data, char *name)
+{
+       return 0;
+}
+
+static void callchain_address(void *data, unsigned long addr, int reliable)
+{
+       struct perf_callchain_entry *entry = data;
+
+       if (reliable)
+               callchain_store(entry, addr);
+}
+
+static const struct stacktrace_ops callchain_ops = {
+       .warning        = callchain_warning,
+       .warning_symbol = callchain_warning_symbol,
+       .stack          = callchain_stack,
+       .address        = callchain_address,
+};
+
+static void
+perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry)
+{
+       callchain_store(entry, PERF_CONTEXT_KERNEL);
+       callchain_store(entry, regs->pc);
+
+       unwind_stack(NULL, regs, NULL, &callchain_ops, entry);
+}
+
+static void
+perf_do_callchain(struct pt_regs *regs, struct perf_callchain_entry *entry)
+{
+       int is_user;
+
+       if (!regs)
+               return;
+
+       is_user = user_mode(regs);
+
+       if (!current || current->pid == 0)
+               return;
+
+       if (is_user && current->state != TASK_RUNNING)
+               return;
+
+       /*
+        * Only the kernel side is implemented for now.
+        */
+       if (!is_user)
+               perf_callchain_kernel(regs, entry);
+}
+
+/*
+ * No need for separate IRQ and NMI entries.
+ */
+static DEFINE_PER_CPU(struct perf_callchain_entry, callchain);
+
+struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
+{
+       struct perf_callchain_entry *entry = &__get_cpu_var(callchain);
+
+       entry->nr = 0;
+
+       perf_do_callchain(regs, entry);
+
+       return entry;
+}
diff --git a/arch/sh/kernel/perf_event.c b/arch/sh/kernel/perf_event.c
new file mode 100644 (file)
index 0000000..7ff0943
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * Performance event support framework for SuperH hardware counters.
+ *
+ *  Copyright (C) 2009  Paul Mundt
+ *
+ * Heavily based on the x86 and PowerPC implementations.
+ *
+ * x86:
+ *  Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
+ *  Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
+ *  Copyright (C) 2009 Jaswinder Singh Rajput
+ *  Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
+ *  Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ *  Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com>
+ *
+ * ppc:
+ *  Copyright 2008-2009 Paul Mackerras, IBM Corporation.
+ *
+ * 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/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/perf_event.h>
+#include <asm/processor.h>
+
+struct cpu_hw_events {
+       struct perf_event       *events[MAX_HWEVENTS];
+       unsigned long           used_mask[BITS_TO_LONGS(MAX_HWEVENTS)];
+       unsigned long           active_mask[BITS_TO_LONGS(MAX_HWEVENTS)];
+};
+
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
+
+static struct sh_pmu *sh_pmu __read_mostly;
+
+/* Number of perf_events counting hardware events */
+static atomic_t num_events;
+/* Used to avoid races in calling reserve/release_pmc_hardware */
+static DEFINE_MUTEX(pmc_reserve_mutex);
+
+/*
+ * Stub these out for now, do something more profound later.
+ */
+int reserve_pmc_hardware(void)
+{
+       return 0;
+}
+
+void release_pmc_hardware(void)
+{
+}
+
+static inline int sh_pmu_initialized(void)
+{
+       return !!sh_pmu;
+}
+
+/*
+ * Release the PMU if this is the last perf_event.
+ */
+static void hw_perf_event_destroy(struct perf_event *event)
+{
+       if (!atomic_add_unless(&num_events, -1, 1)) {
+               mutex_lock(&pmc_reserve_mutex);
+               if (atomic_dec_return(&num_events) == 0)
+                       release_pmc_hardware();
+               mutex_unlock(&pmc_reserve_mutex);
+       }
+}
+
+static int hw_perf_cache_event(int config, int *evp)
+{
+       unsigned long type, op, result;
+       int ev;
+
+       if (!sh_pmu->cache_events)
+               return -EINVAL;
+
+       /* unpack config */
+       type = config & 0xff;
+       op = (config >> 8) & 0xff;
+       result = (config >> 16) & 0xff;
+
+       if (type >= PERF_COUNT_HW_CACHE_MAX ||
+           op >= PERF_COUNT_HW_CACHE_OP_MAX ||
+           result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+               return -EINVAL;
+
+       ev = (*sh_pmu->cache_events)[type][op][result];
+       if (ev == 0)
+               return -EOPNOTSUPP;
+       if (ev == -1)
+               return -EINVAL;
+       *evp = ev;
+       return 0;
+}
+
+static int __hw_perf_event_init(struct perf_event *event)
+{
+       struct perf_event_attr *attr = &event->attr;
+       struct hw_perf_event *hwc = &event->hw;
+       int config = -1;
+       int err;
+
+       if (!sh_pmu_initialized())
+               return -ENODEV;
+
+       /*
+        * All of the on-chip counters are "limited", in that they have
+        * no interrupts, and are therefore unable to do sampling without
+        * further work and timer assistance.
+        */
+       if (hwc->sample_period)
+               return -EINVAL;
+
+       /*
+        * See if we need to reserve the counter.
+        *
+        * If no events are currently in use, then we have to take a
+        * mutex to ensure that we don't race with another task doing
+        * reserve_pmc_hardware or release_pmc_hardware.
+        */
+       err = 0;
+       if (!atomic_inc_not_zero(&num_events)) {
+               mutex_lock(&pmc_reserve_mutex);
+               if (atomic_read(&num_events) == 0 &&
+                   reserve_pmc_hardware())
+                       err = -EBUSY;
+               else
+                       atomic_inc(&num_events);
+               mutex_unlock(&pmc_reserve_mutex);
+       }
+
+       if (err)
+               return err;
+
+       event->destroy = hw_perf_event_destroy;
+
+       switch (attr->type) {
+       case PERF_TYPE_RAW:
+               config = attr->config & sh_pmu->raw_event_mask;
+               break;
+       case PERF_TYPE_HW_CACHE:
+               err = hw_perf_cache_event(attr->config, &config);
+               if (err)
+                       return err;
+               break;
+       case PERF_TYPE_HARDWARE:
+               if (attr->config >= sh_pmu->max_events)
+                       return -EINVAL;
+
+               config = sh_pmu->event_map(attr->config);
+               break;
+       }
+
+       if (config == -1)
+               return -EINVAL;
+
+       hwc->config |= config;
+
+       return 0;
+}
+
+static void sh_perf_event_update(struct perf_event *event,
+                                  struct hw_perf_event *hwc, int idx)
+{
+       u64 prev_raw_count, new_raw_count;
+       s64 delta;
+       int shift = 0;
+
+       /*
+        * Depending on the counter configuration, they may or may not
+        * be chained, in which case the previous counter value can be
+        * updated underneath us if the lower-half overflows.
+        *
+        * Our tactic to handle this is to first atomically read and
+        * exchange a new raw count - then add that new-prev delta
+        * count to the generic counter atomically.
+        *
+        * As there is no interrupt associated with the overflow events,
+        * this is the simplest approach for maintaining consistency.
+        */
+again:
+       prev_raw_count = atomic64_read(&hwc->prev_count);
+       new_raw_count = sh_pmu->read(idx);
+
+       if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count,
+                            new_raw_count) != prev_raw_count)
+               goto again;
+
+       /*
+        * Now we have the new raw value and have updated the prev
+        * timestamp already. We can now calculate the elapsed delta
+        * (counter-)time and add that to the generic counter.
+        *
+        * Careful, not all hw sign-extends above the physical width
+        * of the count.
+        */
+       delta = (new_raw_count << shift) - (prev_raw_count << shift);
+       delta >>= shift;
+
+       atomic64_add(delta, &event->count);
+}
+
+static void sh_pmu_disable(struct perf_event *event)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       clear_bit(idx, cpuc->active_mask);
+       sh_pmu->disable(hwc, idx);
+
+       barrier();
+
+       sh_perf_event_update(event, &event->hw, idx);
+
+       cpuc->events[idx] = NULL;
+       clear_bit(idx, cpuc->used_mask);
+
+       perf_event_update_userpage(event);
+}
+
+static int sh_pmu_enable(struct perf_event *event)
+{
+       struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+       struct hw_perf_event *hwc = &event->hw;
+       int idx = hwc->idx;
+
+       if (test_and_set_bit(idx, cpuc->used_mask)) {
+               idx = find_first_zero_bit(cpuc->used_mask, sh_pmu->num_events);
+               if (idx == sh_pmu->num_events)
+                       return -EAGAIN;
+
+               set_bit(idx, cpuc->used_mask);
+               hwc->idx = idx;
+       }
+
+       sh_pmu->disable(hwc, idx);
+
+       cpuc->events[idx] = event;
+       set_bit(idx, cpuc->active_mask);
+
+       sh_pmu->enable(hwc, idx);
+
+       perf_event_update_userpage(event);
+
+       return 0;
+}
+
+static void sh_pmu_read(struct perf_event *event)
+{
+       sh_perf_event_update(event, &event->hw, event->hw.idx);
+}
+
+static const struct pmu pmu = {
+       .enable         = sh_pmu_enable,
+       .disable        = sh_pmu_disable,
+       .read           = sh_pmu_read,
+};
+
+const struct pmu *hw_perf_event_init(struct perf_event *event)
+{
+       int err = __hw_perf_event_init(event);
+       if (unlikely(err)) {
+               if (event->destroy)
+                       event->destroy(event);
+               return ERR_PTR(err);
+       }
+
+       return &pmu;
+}
+
+void hw_perf_event_setup(int cpu)
+{
+       struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu);
+
+       memset(cpuhw, 0, sizeof(struct cpu_hw_events));
+}
+
+void hw_perf_enable(void)
+{
+       if (!sh_pmu_initialized())
+               return;
+
+       sh_pmu->enable_all();
+}
+
+void hw_perf_disable(void)
+{
+       if (!sh_pmu_initialized())
+               return;
+
+       sh_pmu->disable_all();
+}
+
+int register_sh_pmu(struct sh_pmu *pmu)
+{
+       if (sh_pmu)
+               return -EBUSY;
+       sh_pmu = pmu;
+
+       pr_info("Performance Events: %s support registered\n", pmu->name);
+
+       WARN_ON(pmu->num_events > MAX_HWEVENTS);
+
+       return 0;
+}
index 0673c47..d8af889 100644 (file)
@@ -134,7 +134,10 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
        regs.regs[5] = (unsigned long)fn;
 
        regs.pc = (unsigned long)kernel_thread_helper;
-       regs.sr = (1 << 30);
+       regs.sr = SR_MD;
+#if defined(CONFIG_SH_FPU)
+       regs.sr |= SR_FD;
+#endif
 
        /* Ok, create the new process.. */
        pid = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0,
@@ -142,6 +145,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 
        return pid;
 }
+EXPORT_SYMBOL(kernel_thread);
 
 /*
  * Free current thread data structures etc..
@@ -186,6 +190,16 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
 
        return fpvalid;
 }
+EXPORT_SYMBOL(dump_fpu);
+
+/*
+ * This gets called before we allocate a new thread and copy
+ * the current task into it.
+ */
+void prepare_to_copy(struct task_struct *tsk)
+{
+       unlazy_fpu(tsk, task_pt_regs(tsk));
+}
 
 asmlinkage void ret_from_fork(void);
 
@@ -195,16 +209,10 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
 {
        struct thread_info *ti = task_thread_info(p);
        struct pt_regs *childregs;
-#if defined(CONFIG_SH_FPU) || defined(CONFIG_SH_DSP)
+#if defined(CONFIG_SH_DSP)
        struct task_struct *tsk = current;
 #endif
 
-#if defined(CONFIG_SH_FPU)
-       unlazy_fpu(tsk, regs);
-       p->thread.fpu = tsk->thread.fpu;
-       copy_to_stopped_child_used_math(p);
-#endif
-
 #if defined(CONFIG_SH_DSP)
        if (is_dsp_enabled(tsk)) {
                /* We can use the __save_dsp or just copy the struct:
@@ -224,6 +232,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
        } else {
                childregs->regs[15] = (unsigned long)childregs;
                ti->addr_limit = KERNEL_DS;
+               ti->status &= ~TS_USEDFPU;
+               p->fpu_counter = 0;
        }
 
        if (clone_flags & CLONE_SETTLS)
@@ -288,9 +298,13 @@ static void ubc_set_tracing(int asid, unsigned long pc)
 __notrace_funcgraph struct task_struct *
 __switch_to(struct task_struct *prev, struct task_struct *next)
 {
-#if defined(CONFIG_SH_FPU)
+       struct thread_struct *next_t = &next->thread;
+
        unlazy_fpu(prev, task_pt_regs(prev));
-#endif
+
+       /* we're going to use this soon, after a few expensive things */
+       if (next->fpu_counter > 5)
+               prefetch(&next_t->fpu.hard);
 
 #ifdef CONFIG_MMU
        /*
@@ -321,6 +335,14 @@ __switch_to(struct task_struct *prev, struct task_struct *next)
 #endif
        }
 
+       /*
+        * If the task has used fpu the last 5 timeslices, just do a full
+        * restore of the math state immediately to avoid the trap; the
+        * chances of needing FPU soon are obviously high now
+        */
+       if (next->fpu_counter > 5)
+               fpu_state_restore(task_pt_regs(next));
+
        return prev;
 }
 
index 1192398..359b8a2 100644 (file)
@@ -335,6 +335,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(kernel_thread);
 
 /*
  * Free current thread data structures etc..
@@ -417,6 +418,7 @@ int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
        return 0; /* Task didn't use the fpu at all. */
 #endif
 }
+EXPORT_SYMBOL(dump_fpu);
 
 asmlinkage void ret_from_fork(void);
 
diff --git a/arch/sh/kernel/return_address.c b/arch/sh/kernel/return_address.c
new file mode 100644 (file)
index 0000000..df3ab58
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * arch/sh/kernel/return_address.c
+ *
+ * Copyright (C) 2009  Matt Fleming
+ * Copyright (C) 2009  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/kernel.h>
+#include <asm/dwarf.h>
+
+#ifdef CONFIG_DWARF_UNWINDER
+
+void *return_address(unsigned int depth)
+{
+       struct dwarf_frame *frame;
+       unsigned long ra;
+       int i;
+
+       for (i = 0, frame = NULL, ra = 0; i <= depth; i++) {
+               struct dwarf_frame *tmp;
+
+               tmp = dwarf_unwind_stack(ra, frame);
+
+               if (frame)
+                       dwarf_free_frame(frame);
+
+               frame = tmp;
+
+               if (!frame || !frame->return_addr)
+                       break;
+
+               ra = frame->return_addr;
+       }
+
+       /* Failed to unwind the stack to the specified depth. */
+       WARN_ON(i != depth + 1);
+
+       if (frame)
+               dwarf_free_frame(frame);
+
+       return (void *)ra;
+}
+
+#else
+
+void *return_address(unsigned int depth)
+{
+       return NULL;
+}
+
+#endif
index 99b4fb5..5a947a2 100644 (file)
@@ -453,6 +453,10 @@ void __init setup_arch(char **cmdline_p)
 
        paging_init();
 
+#ifdef CONFIG_PMB_ENABLE
+       pmb_init();
+#endif
+
 #ifdef CONFIG_SMP
        plat_smp_setup();
 #endif
index 444cce3..3896f26 100644 (file)
@@ -1,37 +1,11 @@
 #include <linux/module.h>
-#include <linux/smp.h>
-#include <linux/user.h>
-#include <linux/elfcore.h>
-#include <linux/sched.h>
-#include <linux/in6.h>
-#include <linux/interrupt.h>
-#include <linux/vmalloc.h>
-#include <linux/pci.h>
-#include <linux/irq.h>
-#include <asm/sections.h>
-#include <asm/processor.h>
-#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
 #include <asm/checksum.h>
-#include <asm/io.h>
-#include <asm/delay.h>
-#include <asm/tlbflush.h>
-#include <asm/cacheflush.h>
-#include <asm/ftrace.h>
-
-extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
-
-/* platform dependent support */
-EXPORT_SYMBOL(dump_fpu);
-EXPORT_SYMBOL(kernel_thread);
-EXPORT_SYMBOL(strlen);
-
-/* PCI exports */
-#ifdef CONFIG_PCI
-EXPORT_SYMBOL(pci_alloc_consistent);
-EXPORT_SYMBOL(pci_free_consistent);
-#endif
+#include <asm/sections.h>
 
-/* mem exports */
 EXPORT_SYMBOL(memchr);
 EXPORT_SYMBOL(memcpy);
 EXPORT_SYMBOL(memset);
@@ -40,6 +14,13 @@ EXPORT_SYMBOL(__copy_user);
 EXPORT_SYMBOL(__udelay);
 EXPORT_SYMBOL(__ndelay);
 EXPORT_SYMBOL(__const_udelay);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(csum_partial_copy_generic);
+EXPORT_SYMBOL(copy_page);
+EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(_ebss);
+EXPORT_SYMBOL(empty_zero_page);
 
 #define DECLARE_EXPORT(name)           \
        extern void name(void);EXPORT_SYMBOL(name)
@@ -107,30 +88,6 @@ DECLARE_EXPORT(__sdivsi3_i4);
 DECLARE_EXPORT(__udivsi3_i4);
 DECLARE_EXPORT(__sdivsi3_i4i);
 DECLARE_EXPORT(__udivsi3_i4i);
-
-#if !defined(CONFIG_CACHE_OFF) && (defined(CONFIG_CPU_SH4) || \
-       defined(CONFIG_SH7705_CACHE_32KB))
-/* needed by some modules */
-EXPORT_SYMBOL(flush_cache_all);
-EXPORT_SYMBOL(flush_cache_range);
-EXPORT_SYMBOL(flush_dcache_page);
-#endif
-
 #ifdef CONFIG_MCOUNT
 DECLARE_EXPORT(mcount);
 #endif
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_generic);
-#ifdef CONFIG_IPV6
-EXPORT_SYMBOL(csum_ipv6_magic);
-#endif
-EXPORT_SYMBOL(copy_page);
-EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(_ebss);
-EXPORT_SYMBOL(empty_zero_page);
-
-#ifndef CONFIG_CACHE_OFF
-EXPORT_SYMBOL(__flush_purge_region);
-EXPORT_SYMBOL(__flush_wback_region);
-EXPORT_SYMBOL(__flush_invalidate_region);
-#endif
index d008e17..45afa5c 100644 (file)
 #include <asm/delay.h>
 #include <asm/irq.h>
 
-extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
-
-/* platform dependent support */
-EXPORT_SYMBOL(dump_fpu);
-EXPORT_SYMBOL(kernel_thread);
-
-#ifdef CONFIG_VT
-EXPORT_SYMBOL(screen_info);
-#endif
-
 EXPORT_SYMBOL(__put_user_asm_b);
 EXPORT_SYMBOL(__put_user_asm_w);
 EXPORT_SYMBOL(__put_user_asm_l);
index 3db3742..12815ce 100644 (file)
@@ -67,7 +67,8 @@ sys_sigsuspend(old_sigset_t mask,
 
        current->state = TASK_INTERRUPTIBLE;
        schedule();
-       set_thread_flag(TIF_RESTORE_SIGMASK);
+       set_restore_sigmask();
+
        return -ERESTARTNOHAND;
 }
 
@@ -590,7 +591,7 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0)
        if (try_to_freeze())
                goto no_signal;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
                oldset = &current->saved_sigmask;
        else
                oldset = &current->blocked;
@@ -602,12 +603,13 @@ static void do_signal(struct pt_regs *regs, unsigned int save_r0)
                /* Whee!  Actually deliver the signal.  */
                if (handle_signal(signr, &ka, &info, oldset,
                                  regs, save_r0) == 0) {
-                       /* a signal was successfully delivered; the saved
+                       /*
+                        * A signal was successfully delivered; the saved
                         * sigmask will have been stored in the signal frame,
                         * and will be restored by sigreturn, so we can simply
-                        * clear the TIF_RESTORE_SIGMASK flag */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
+                        * clear the TS_RESTORE_SIGMASK flag
+                        */
+                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
 
                        tracehook_signal_handler(signr, &info, &ka, regs,
                                        test_thread_flag(TIF_SINGLESTEP));
@@ -631,10 +633,12 @@ no_signal:
                }
        }
 
-       /* if there's no signal to deliver, we just put the saved sigmask
-        * back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
+       /*
+        * If there's no signal to deliver, we just put the saved sigmask
+        * back.
+        */
+       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
+               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
                sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
        }
 }
index 74793c8..feb3ddd 100644 (file)
@@ -101,7 +101,7 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset)
        if (try_to_freeze())
                goto no_signal;
 
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+       if (current_thread_info()->status & TS_RESTORE_SIGMASK)
                oldset = &current->saved_sigmask;
        else if (!oldset)
                oldset = &current->blocked;
@@ -115,11 +115,9 @@ static int do_signal(struct pt_regs *regs, sigset_t *oldset)
                        /*
                         * If a signal was successfully delivered, the
                         * saved sigmask is in its frame, and we can
-                        * clear the TIF_RESTORE_SIGMASK flag.
+                        * clear the TS_RESTORE_SIGMASK flag.
                         */
-                       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-                               clear_thread_flag(TIF_RESTORE_SIGMASK);
-
+                       current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
                        tracehook_signal_handler(signr, &info, &ka, regs, 0);
                        return 1;
                }
@@ -146,8 +144,8 @@ no_signal:
        }
 
        /* No signal to deliver -- put the saved sigmask back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
+       if (current_thread_info()->status & TS_RESTORE_SIGMASK) {
+               current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
                sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
        }
 
@@ -176,6 +174,7 @@ sys_sigsuspend(old_sigset_t mask,
        while (1) {
                current->state = TASK_INTERRUPTIBLE;
                schedule();
+               set_restore_sigmask();
                regs->pc += 4;    /* because sys_sigreturn decrements the pc */
                if (do_signal(regs, &saveset)) {
                        /* pc now points at signal handler. Need to decrement
index 160db10..983e079 100644 (file)
@@ -122,7 +122,9 @@ int __cpuinit __cpu_up(unsigned int cpu)
        stack_start.bss_start = 0; /* don't clear bss for secondary cpus */
        stack_start.start_kernel_fn = start_secondary;
 
-       flush_cache_all();
+       flush_icache_range((unsigned long)&stack_start,
+                          (unsigned long)&stack_start + sizeof(stack_start));
+       wmb();
 
        plat_start_cpu(cpu, (unsigned long)_stext);
 
index 0838942..9b0b633 100644 (file)
 
 static DEFINE_PER_CPU(struct cpu, cpu_devices);
 
+cpumask_t cpu_core_map[NR_CPUS];
+
+static cpumask_t cpu_coregroup_map(unsigned int cpu)
+{
+       /*
+        * Presently all SH-X3 SMP cores are multi-cores, so just keep it
+        * simple until we have a method for determining topology..
+        */
+       return cpu_possible_map;
+}
+
+const struct cpumask *cpu_coregroup_mask(unsigned int cpu)
+{
+       return &cpu_core_map[cpu];
+}
+
+int arch_update_cpu_topology(void)
+{
+       unsigned int cpu;
+
+       for_each_possible_cpu(cpu)
+               cpu_core_map[cpu] = cpu_coregroup_map(cpu);
+
+       return 0;
+}
+
 static int __init topology_init(void)
 {
        int i, ret;
index a8396f3..7b03633 100644 (file)
@@ -9,8 +9,8 @@
 #include <asm/unwinder.h>
 #include <asm/system.h>
 
-#ifdef CONFIG_BUG
-void handle_BUG(struct pt_regs *regs)
+#ifdef CONFIG_GENERIC_BUG
+static void handle_BUG(struct pt_regs *regs)
 {
        const struct bug_entry *bug;
        unsigned long bugaddr = regs->pc;
@@ -81,7 +81,7 @@ BUILD_TRAP_HANDLER(bug)
                       SIGTRAP) == NOTIFY_STOP)
                return;
 
-#ifdef CONFIG_BUG
+#ifdef CONFIG_GENERIC_BUG
        if (__kernel_text_address(instruction_pointer(regs))) {
                insn_size_t insn = *(insn_size_t *)instruction_pointer(regs);
                if (insn == TRAPA_BUG_OPCODE)
@@ -95,9 +95,11 @@ BUILD_TRAP_HANDLER(bug)
 
 BUILD_TRAP_HANDLER(nmi)
 {
+       unsigned int cpu = smp_processor_id();
        TRAP_HANDLER_DECL;
 
        nmi_enter();
+       nmi_count(cpu)++;
 
        switch (notify_die(DIE_NMI, "NMI", regs, 0, vec & 0xff, SIGINT)) {
        case NOTIFY_OK:
index 7a2ee3a..3da5a12 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/kexec.h>
 #include <linux/limits.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/sysfs.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -68,61 +69,49 @@ static const char *se_usermode_action[] = {
        "signal+warn"
 };
 
-static int
-proc_alignment_read(char *page, char **start, off_t off, int count, int *eof,
-                   void *data)
+static int alignment_proc_show(struct seq_file *m, void *v)
 {
-       char *p = page;
-       int len;
-
-       p += sprintf(p, "User:\t\t%lu\n", se_user);
-       p += sprintf(p, "System:\t\t%lu\n", se_sys);
-       p += sprintf(p, "Half:\t\t%lu\n", se_half);
-       p += sprintf(p, "Word:\t\t%lu\n", se_word);
-       p += sprintf(p, "DWord:\t\t%lu\n", se_dword);
-       p += sprintf(p, "Multi:\t\t%lu\n", se_multi);
-       p += sprintf(p, "User faults:\t%i (%s)\n", se_usermode,
+       seq_printf(m, "User:\t\t%lu\n", se_user);
+       seq_printf(m, "System:\t\t%lu\n", se_sys);
+       seq_printf(m, "Half:\t\t%lu\n", se_half);
+       seq_printf(m, "Word:\t\t%lu\n", se_word);
+       seq_printf(m, "DWord:\t\t%lu\n", se_dword);
+       seq_printf(m, "Multi:\t\t%lu\n", se_multi);
+       seq_printf(m, "User faults:\t%i (%s)\n", se_usermode,
                        se_usermode_action[se_usermode]);
-       p += sprintf(p, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn,
+       seq_printf(m, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn,
                        se_kernmode_warn ? "+warn" : "");
-
-       len = (p - page) - off;
-       if (len < 0)
-               len = 0;
-
-       *eof = (len <= count) ? 1 : 0;
-       *start = page + off;
-
-       return len;
+       return 0;
 }
 
-static int proc_alignment_write(struct file *file, const char __user *buffer,
-                               unsigned long count, void *data)
+static int alignment_proc_open(struct inode *inode, struct file *file)
 {
-       char mode;
-
-       if (count > 0) {
-               if (get_user(mode, buffer))
-                       return -EFAULT;
-               if (mode >= '0' && mode <= '5')
-                       se_usermode = mode - '0';
-       }
-       return count;
+       return single_open(file, alignment_proc_show, NULL);
 }
 
-static int proc_alignment_kern_write(struct file *file, const char __user *buffer,
-                                    unsigned long count, void *data)
+static ssize_t alignment_proc_write(struct file *file,
+               const char __user *buffer, size_t count, loff_t *pos)
 {
+       int *data = PDE(file->f_path.dentry->d_inode)->data;
        char mode;
 
        if (count > 0) {
                if (get_user(mode, buffer))
                        return -EFAULT;
-               if (mode >= '0' && mode <= '1')
-                       se_kernmode_warn = mode - '0';
+               if (mode >= '0' && mode <= '5')
+                       *data = mode - '0';
        }
        return count;
 }
+
+static const struct file_operations alignment_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = alignment_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = alignment_proc_write,
+};
 #endif
 
 static void dump_mem(const char *str, unsigned long bottom, unsigned long top)
@@ -945,14 +934,9 @@ void __init trap_init(void)
        set_exception_table_evt(0x800, do_reserved_inst);
        set_exception_table_evt(0x820, do_illegal_slot_inst);
 #elif defined(CONFIG_SH_FPU)
-#ifdef CONFIG_CPU_SUBTYPE_SHX3
-       set_exception_table_evt(0xd80, fpu_state_restore_trap_handler);
-       set_exception_table_evt(0xda0, fpu_state_restore_trap_handler);
-#else
        set_exception_table_evt(0x800, fpu_state_restore_trap_handler);
        set_exception_table_evt(0x820, fpu_state_restore_trap_handler);
 #endif
-#endif
 
 #ifdef CONFIG_CPU_SH2
        set_exception_table_vec(TRAP_ADDRESS_ERROR, address_error_trap_handler);
@@ -1011,20 +995,16 @@ static int __init alignment_init(void)
        if (!dir)
                return -ENOMEM;
 
-       res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, dir);
+       res = proc_create_data("alignment", S_IWUSR | S_IRUGO, dir,
+                              &alignment_proc_fops, &se_usermode);
        if (!res)
                return -ENOMEM;
 
-       res->read_proc = proc_alignment_read;
-       res->write_proc = proc_alignment_write;
-
-        res = create_proc_entry("kernel_alignment", S_IWUSR | S_IRUGO, dir);
+        res = proc_create_data("kernel_alignment", S_IWUSR | S_IRUGO, dir,
+                              &alignment_proc_fops, &se_kernmode_warn);
         if (!res)
                 return -ENOMEM;
 
-        res->read_proc = proc_alignment_read;
-        res->write_proc = proc_alignment_kern_write;
-
        return 0;
 }
 
index a969b47..dab4d21 100644 (file)
@@ -2,7 +2,7 @@
 # Makefile for SuperH-specific library files..
 #
 
-lib-y  = delay.o memset.o memmove.o memchr.o \
+lib-y  = delay.o memmove.o memchr.o \
         checksum.o strlen.o div64.o div64-generic.o
 
 # Extracted from libgcc
@@ -23,8 +23,11 @@ obj-y                                += io.o
 memcpy-y                       := memcpy.o
 memcpy-$(CONFIG_CPU_SH4)       := memcpy-sh4.o
 
+memset-y                       := memset.o
+memset-$(CONFIG_CPU_SH4)       := memset-sh4.o
+
 lib-$(CONFIG_MMU)              += copy_page.o __clear_user.o
 lib-$(CONFIG_MCOUNT)           += mcount.o
-lib-y                          += $(memcpy-y) $(udivsi3-y)
+lib-y                          += $(memcpy-y) $(memset-y) $(udivsi3-y)
 
 EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/lib/memset-sh4.S b/arch/sh/lib/memset-sh4.S
new file mode 100644 (file)
index 0000000..1a6e32c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * "memset" implementation for SH4
+ *
+ * Copyright (C) 1999  Niibe Yutaka
+ * Copyright (c) 2009  STMicroelectronics Limited
+ * Author: Stuart Menefy <stuart.menefy:st.com>
+ */
+
+/*
+ *            void *memset(void *s, int c, size_t n);
+ */
+
+#include <linux/linkage.h>
+
+ENTRY(memset)
+       mov     #12,r0
+       add     r6,r4
+       cmp/gt  r6,r0
+       bt/s    40f             ! if it's too small, set a byte at once
+        mov    r4,r0
+       and     #3,r0
+       cmp/eq  #0,r0
+       bt/s    2f              ! It's aligned
+        sub    r0,r6
+1:
+       dt      r0
+       bf/s    1b
+        mov.b  r5,@-r4
+2:                             ! make VVVV
+       extu.b  r5,r5
+       swap.b  r5,r0           !   V0
+       or      r0,r5           !   VV
+       swap.w  r5,r0           ! VV00
+       or      r0,r5           ! VVVV
+
+       ! Check if enough bytes need to be copied to be worth the big loop
+       mov     #0x40, r0       ! (MT)
+       cmp/gt  r6,r0           ! (MT)  64 > len => slow loop
+
+       bt/s    22f
+        mov    r6,r0
+
+       ! align the dst to the cache block size if necessary
+       mov     r4, r3
+       mov     #~(0x1f), r1
+
+       and     r3, r1
+       cmp/eq  r3, r1
+
+       bt/s    11f             ! dst is already aligned
+        sub    r1, r3          ! r3-r1 -> r3
+       shlr2   r3              ! number of loops
+
+10:    mov.l   r5,@-r4
+       dt      r3
+       bf/s    10b
+        add    #-4, r6
+
+11:    ! dst is 32byte aligned
+       mov     r6,r2
+       mov     #-5,r0
+       shld    r0,r2           ! number of loops
+
+       add     #-32, r4
+       mov     r5, r0
+12:
+       movca.l r0,@r4
+       mov.l   r5,@(4, r4)
+       mov.l   r5,@(8, r4)
+       mov.l   r5,@(12,r4)
+       mov.l   r5,@(16,r4)
+       mov.l   r5,@(20,r4)
+       add     #-0x20, r6
+       mov.l   r5,@(24,r4)
+       dt      r2
+       mov.l   r5,@(28,r4)
+       bf/s    12b
+        add    #-32, r4
+
+       add     #32, r4
+       mov     #8, r0
+       cmp/ge  r0, r6
+       bf      40f
+
+       mov     r6,r0
+22:
+       shlr2   r0
+       shlr    r0              ! r0 = r6 >> 3
+3:
+       dt      r0
+       mov.l   r5,@-r4         ! set 8-byte at once
+       bf/s    3b
+        mov.l  r5,@-r4
+       !
+       mov     #7,r0
+       and     r0,r6
+
+       ! fill bytes (length may be zero)
+40:    tst     r6,r6
+       bt      5f
+4:
+       dt      r6
+       bf/s    4b
+        mov.b  r5,@-r4
+5:
+       rts
+        mov    r4,r0
index ac2d7ab..d6c15ca 100644 (file)
@@ -558,7 +558,7 @@ static int ieee_fpe_handler(struct pt_regs *regs)
                                            (finsn >> 8) & 0xf);
                        tsk->thread.fpu.hard.fpscr &=
                                ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
-                       set_tsk_thread_flag(tsk, TIF_USEDFPU);
+                       task_thread_info(tsk)->status |= TS_USEDFPU;
                } else {
                        info.si_signo = SIGFPE;
                        info.si_errno = 0;
@@ -619,10 +619,10 @@ int do_fpu_inst(unsigned short inst, struct pt_regs *regs)
        struct task_struct *tsk = current;
        struct sh_fpu_soft_struct *fpu = &(tsk->thread.fpu.soft);
 
-       if (!test_tsk_thread_flag(tsk, TIF_USEDFPU)) {
+       if (!(task_thread_info(tsk)->status & TS_USEDFPU)) {
                /* initialize once. */
                fpu_init(fpu);
-               set_tsk_thread_flag(tsk, TIF_USEDFPU);
+               task_thread_info(tsk)->status |= TS_USEDFPU;
        }
 
        return fpu_emulate(inst, fpu, regs);
index 7f7b52f..0e7ba8e 100644 (file)
@@ -82,8 +82,7 @@ config 32BIT
 
 config PMB_ENABLE
        bool "Support 32-bit physical addressing through PMB"
-       depends on MMU && EXPERIMENTAL && (CPU_SUBTYPE_SH7757 || CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785)
-       select 32BIT
+       depends on MMU && EXPERIMENTAL && CPU_SH4A
        default y
        help
          If you say Y here, physical addressing will be extended to
@@ -97,8 +96,7 @@ choice
 
 config PMB
        bool "PMB"
-       depends on MMU && EXPERIMENTAL && (CPU_SUBTYPE_SH7757 || CPU_SUBTYPE_SH7780 || CPU_SUBTYPE_SH7785)
-       select 32BIT
+       depends on MMU && EXPERIMENTAL && CPU_SH4A
        help
          If you say Y here, physical addressing will be extended to
          32-bits through the SH-4A PMB. If this is not set, legacy
@@ -106,9 +104,7 @@ config PMB
 
 config PMB_FIXED
        bool "fixed PMB"
-       depends on MMU && EXPERIMENTAL && (CPU_SUBTYPE_SH7757 || \
-                                          CPU_SUBTYPE_SH7780 || \
-                                          CPU_SUBTYPE_SH7785)
+       depends on MMU && EXPERIMENTAL && CPU_SH4A
        select 32BIT
        help
          If this option is enabled, fixed PMB mappings are inherited
@@ -258,6 +254,15 @@ endchoice
 
 source "mm/Kconfig"
 
+config SCHED_MC
+       bool "Multi-core scheduler support"
+       depends on SMP
+       default y
+       help
+         Multi-core scheduler support improves the CPU scheduler's decision
+         making when dealing with multi-core CPU chips at a cost of slightly
+         increased overhead in some places. If unsure say N here.
+
 endmenu
 
 menu "Cache configuration"
index 3759bf8..8a70535 100644 (file)
@@ -33,8 +33,7 @@ obj-y                         += $(tlb-y)
 endif
 
 obj-$(CONFIG_HUGETLB_PAGE)     += hugetlbpage.o
-obj-$(CONFIG_PMB)              += pmb.o
-obj-$(CONFIG_PMB_FIXED)                += pmb-fixed.o
+obj-$(CONFIG_PMB_ENABLE)       += pmb.o
 obj-$(CONFIG_NUMA)             += numa.o
 
 # Special flags for fault_64.o.  This puts restrictions on the number of
index b7f235c..f36a08b 100644 (file)
@@ -2,7 +2,7 @@
  * arch/sh/mm/cache-sh4.c
  *
  * Copyright (C) 1999, 2000, 2002  Niibe Yutaka
- * Copyright (C) 2001 - 2007  Paul Mundt
+ * Copyright (C) 2001 - 2009  Paul Mundt
  * Copyright (C) 2003  Richard Curnow
  * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
  *
@@ -15,6 +15,8 @@
 #include <linux/io.h>
 #include <linux/mutex.h>
 #include <linux/fs.h>
+#include <linux/highmem.h>
+#include <asm/pgtable.h>
 #include <asm/mmu_context.h>
 #include <asm/cacheflush.h>
 
  * flushing. Anything exceeding this will simply flush the dcache in its
  * entirety.
  */
-#define MAX_DCACHE_PAGES       64      /* XXX: Tune for ways */
 #define MAX_ICACHE_PAGES       32
 
 static void __flush_cache_one(unsigned long addr, unsigned long phys,
                               unsigned long exec_offset);
 
-/*
- * This is initialised here to ensure that it is not placed in the BSS.  If
- * that were to happen, note that cache_init gets called before the BSS is
- * cleared, so this would get nulled out which would be hopeless.
- */
-static void (*__flush_dcache_segment_fn)(unsigned long, unsigned long) =
-       (void (*)(unsigned long, unsigned long))0xdeadbeef;
-
 /*
  * Write back the range of D-cache, and purge the I-cache.
  *
@@ -97,15 +90,15 @@ static inline void flush_cache_one(unsigned long start, unsigned long phys)
        unsigned long flags, exec_offset = 0;
 
        /*
-        * All types of SH-4 require PC to be in P2 to operate on the I-cache.
-        * Some types of SH-4 require PC to be in P2 to operate on the D-cache.
+        * All types of SH-4 require PC to be uncached to operate on the I-cache.
+        * Some types of SH-4 require PC to be uncached to operate on the D-cache.
         */
        if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) ||
            (start < CACHE_OC_ADDRESS_ARRAY))
-               exec_offset = 0x20000000;
+               exec_offset = cached_to_uncached;
 
        local_irq_save(flags);
-       __flush_cache_one(start | SH_CACHE_ASSOC, P1SEGADDR(phys), exec_offset);
+       __flush_cache_one(start, phys, exec_offset);
        local_irq_restore(flags);
 }
 
@@ -124,7 +117,7 @@ static void sh4_flush_dcache_page(void *arg)
        else
 #endif
        {
-               unsigned long phys = PHYSADDR(page_address(page));
+               unsigned long phys = page_to_phys(page);
                unsigned long addr = CACHE_OC_ADDRESS_ARRAY;
                int i, n;
 
@@ -159,10 +152,27 @@ static void __uses_jump_to_uncached flush_icache_all(void)
        local_irq_restore(flags);
 }
 
-static inline void flush_dcache_all(void)
+static void flush_dcache_all(void)
 {
-       (*__flush_dcache_segment_fn)(0UL, boot_cpu_data.dcache.way_size);
-       wmb();
+       unsigned long addr, end_addr, entry_offset;
+
+       end_addr = CACHE_OC_ADDRESS_ARRAY +
+               (current_cpu_data.dcache.sets <<
+                current_cpu_data.dcache.entry_shift) *
+                       current_cpu_data.dcache.ways;
+
+       entry_offset = 1 << current_cpu_data.dcache.entry_shift;
+
+       for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; ) {
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+               __raw_writel(0, addr); addr += entry_offset;
+       }
 }
 
 static void sh4_flush_cache_all(void *unused)
@@ -171,89 +181,13 @@ static void sh4_flush_cache_all(void *unused)
        flush_icache_all();
 }
 
-static void __flush_cache_mm(struct mm_struct *mm, unsigned long start,
-                            unsigned long end)
-{
-       unsigned long d = 0, p = start & PAGE_MASK;
-       unsigned long alias_mask = boot_cpu_data.dcache.alias_mask;
-       unsigned long n_aliases = boot_cpu_data.dcache.n_aliases;
-       unsigned long select_bit;
-       unsigned long all_aliases_mask;
-       unsigned long addr_offset;
-       pgd_t *dir;
-       pmd_t *pmd;
-       pud_t *pud;
-       pte_t *pte;
-       int i;
-
-       dir = pgd_offset(mm, p);
-       pud = pud_offset(dir, p);
-       pmd = pmd_offset(pud, p);
-       end = PAGE_ALIGN(end);
-
-       all_aliases_mask = (1 << n_aliases) - 1;
-
-       do {
-               if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) {
-                       p &= PMD_MASK;
-                       p += PMD_SIZE;
-                       pmd++;
-
-                       continue;
-               }
-
-               pte = pte_offset_kernel(pmd, p);
-
-               do {
-                       unsigned long phys;
-                       pte_t entry = *pte;
-
-                       if (!(pte_val(entry) & _PAGE_PRESENT)) {
-                               pte++;
-                               p += PAGE_SIZE;
-                               continue;
-                       }
-
-                       phys = pte_val(entry) & PTE_PHYS_MASK;
-
-                       if ((p ^ phys) & alias_mask) {
-                               d |= 1 << ((p & alias_mask) >> PAGE_SHIFT);
-                               d |= 1 << ((phys & alias_mask) >> PAGE_SHIFT);
-
-                               if (d == all_aliases_mask)
-                                       goto loop_exit;
-                       }
-
-                       pte++;
-                       p += PAGE_SIZE;
-               } while (p < end && ((unsigned long)pte & ~PAGE_MASK));
-               pmd++;
-       } while (p < end);
-
-loop_exit:
-       addr_offset = 0;
-       select_bit = 1;
-
-       for (i = 0; i < n_aliases; i++) {
-               if (d & select_bit) {
-                       (*__flush_dcache_segment_fn)(addr_offset, PAGE_SIZE);
-                       wmb();
-               }
-
-               select_bit <<= 1;
-               addr_offset += PAGE_SIZE;
-       }
-}
-
 /*
  * Note : (RPC) since the caches are physically tagged, the only point
  * of flush_cache_mm for SH-4 is to get rid of aliases from the
  * D-cache.  The assumption elsewhere, e.g. flush_cache_range, is that
  * lines can stay resident so long as the virtual address they were
  * accessed with (hence cache set) is in accord with the physical
- * address (i.e. tag).  It's no different here.  So I reckon we don't
- * need to flush the I-cache, since aliases don't matter for that.  We
- * should try that.
+ * address (i.e. tag).  It's no different here.
  *
  * Caller takes mm->mmap_sem.
  */
@@ -264,33 +198,7 @@ static void sh4_flush_cache_mm(void *arg)
        if (cpu_context(smp_processor_id(), mm) == NO_CONTEXT)
                return;
 
-       /*
-        * If cache is only 4k-per-way, there are never any 'aliases'.  Since
-        * the cache is physically tagged, the data can just be left in there.
-        */
-       if (boot_cpu_data.dcache.n_aliases == 0)
-               return;
-
-       /*
-        * Don't bother groveling around the dcache for the VMA ranges
-        * if there are too many PTEs to make it worthwhile.
-        */
-       if (mm->nr_ptes >= MAX_DCACHE_PAGES)
-               flush_dcache_all();
-       else {
-               struct vm_area_struct *vma;
-
-               /*
-                * In this case there are reasonably sized ranges to flush,
-                * iterate through the VMA list and take care of any aliases.
-                */
-               for (vma = mm->mmap; vma; vma = vma->vm_next)
-                       __flush_cache_mm(mm, vma->vm_start, vma->vm_end);
-       }
-
-       /* Only touch the icache if one of the VMAs has VM_EXEC set. */
-       if (mm->exec_vm)
-               flush_icache_all();
+       flush_dcache_all();
 }
 
 /*
@@ -303,44 +211,63 @@ static void sh4_flush_cache_page(void *args)
 {
        struct flusher_data *data = args;
        struct vm_area_struct *vma;
+       struct page *page;
        unsigned long address, pfn, phys;
-       unsigned int alias_mask;
+       int map_coherent = 0;
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *pte;
+       void *vaddr;
 
        vma = data->vma;
-       address = data->addr1;
+       address = data->addr1 & PAGE_MASK;
        pfn = data->addr2;
        phys = pfn << PAGE_SHIFT;
+       page = pfn_to_page(pfn);
 
        if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT)
                return;
 
-       alias_mask = boot_cpu_data.dcache.alias_mask;
-
-       /* We only need to flush D-cache when we have alias */
-       if ((address^phys) & alias_mask) {
-               /* Loop 4K of the D-cache */
-               flush_cache_one(
-                       CACHE_OC_ADDRESS_ARRAY | (address & alias_mask),
-                       phys);
-               /* Loop another 4K of the D-cache */
-               flush_cache_one(
-                       CACHE_OC_ADDRESS_ARRAY | (phys & alias_mask),
-                       phys);
-       }
+       pgd = pgd_offset(vma->vm_mm, address);
+       pud = pud_offset(pgd, address);
+       pmd = pmd_offset(pud, address);
+       pte = pte_offset_kernel(pmd, address);
+
+       /* If the page isn't present, there is nothing to do here. */
+       if (!(pte_val(*pte) & _PAGE_PRESENT))
+               return;
 
-       alias_mask = boot_cpu_data.icache.alias_mask;
-       if (vma->vm_flags & VM_EXEC) {
+       if ((vma->vm_mm == current->active_mm))
+               vaddr = NULL;
+       else {
                /*
-                * Evict entries from the portion of the cache from which code
-                * may have been executed at this address (virtual).  There's
-                * no need to evict from the portion corresponding to the
-                * physical address as for the D-cache, because we know the
-                * kernel has never executed the code through its identity
-                * translation.
+                * Use kmap_coherent or kmap_atomic to do flushes for
+                * another ASID than the current one.
                 */
-               flush_cache_one(
-                       CACHE_IC_ADDRESS_ARRAY | (address & alias_mask),
-                       phys);
+               map_coherent = (current_cpu_data.dcache.n_aliases &&
+                       !test_bit(PG_dcache_dirty, &page->flags) &&
+                       page_mapped(page));
+               if (map_coherent)
+                       vaddr = kmap_coherent(page, address);
+               else
+                       vaddr = kmap_atomic(page, KM_USER0);
+
+               address = (unsigned long)vaddr;
+       }
+
+       if (pages_do_alias(address, phys))
+               flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
+                       (address & shm_align_mask), phys);
+
+       if (vma->vm_flags & VM_EXEC)
+               flush_icache_all();
+
+       if (vaddr) {
+               if (map_coherent)
+                       kunmap_coherent(vaddr);
+               else
+                       kunmap_atomic(vaddr, KM_USER0);
        }
 }
 
@@ -373,24 +300,10 @@ static void sh4_flush_cache_range(void *args)
        if (boot_cpu_data.dcache.n_aliases == 0)
                return;
 
-       /*
-        * Don't bother with the lookup and alias check if we have a
-        * wide range to cover, just blow away the dcache in its
-        * entirety instead. -- PFM.
-        */
-       if (((end - start) >> PAGE_SHIFT) >= MAX_DCACHE_PAGES)
-               flush_dcache_all();
-       else
-               __flush_cache_mm(vma->vm_mm, start, end);
+       flush_dcache_all();
 
-       if (vma->vm_flags & VM_EXEC) {
-               /*
-                * TODO: Is this required???  Need to look at how I-cache
-                * coherency is assured when new programs are loaded to see if
-                * this matters.
-                */
+       if (vma->vm_flags & VM_EXEC)
                flush_icache_all();
-       }
 }
 
 /**
@@ -464,245 +377,6 @@ static void __flush_cache_one(unsigned long addr, unsigned long phys,
        } while (--way_count != 0);
 }
 
-/*
- * Break the 1, 2 and 4 way variants of this out into separate functions to
- * avoid nearly all the overhead of having the conditional stuff in the function
- * bodies (+ the 1 and 2 way cases avoid saving any registers too).
- *
- * We want to eliminate unnecessary bus transactions, so this code uses
- * a non-obvious technique.
- *
- * Loop over a cache way sized block of, one cache line at a time. For each
- * line, use movca.a to cause the current cache line contents to be written
- * back, but without reading anything from main memory. However this has the
- * side effect that the cache is now caching that memory location. So follow
- * this with a cache invalidate to mark the cache line invalid. And do all
- * this with interrupts disabled, to avoid the cache line being accidently
- * evicted while it is holding garbage.
- *
- * This also breaks in a number of circumstances:
- * - if there are modifications to the region of memory just above
- *   empty_zero_page (for example because a breakpoint has been placed
- *   there), then these can be lost.
- *
- *   This is because the the memory address which the cache temporarily
- *   caches in the above description is empty_zero_page. So the
- *   movca.l hits the cache (it is assumed that it misses, or at least
- *   isn't dirty), modifies the line and then invalidates it, losing the
- *   required change.
- *
- * - If caches are disabled or configured in write-through mode, then
- *   the movca.l writes garbage directly into memory.
- */
-static void __flush_dcache_segment_writethrough(unsigned long start,
-                                               unsigned long extent_per_way)
-{
-       unsigned long addr;
-       int i;
-
-       addr = CACHE_OC_ADDRESS_ARRAY | (start & cpu_data->dcache.entry_mask);
-
-       while (extent_per_way) {
-               for (i = 0; i < cpu_data->dcache.ways; i++)
-                       __raw_writel(0, addr + cpu_data->dcache.way_incr * i);
-
-               addr += cpu_data->dcache.linesz;
-               extent_per_way -= cpu_data->dcache.linesz;
-       }
-}
-
-static void __flush_dcache_segment_1way(unsigned long start,
-                                       unsigned long extent_per_way)
-{
-       unsigned long orig_sr, sr_with_bl;
-       unsigned long base_addr;
-       unsigned long way_incr, linesz, way_size;
-       struct cache_info *dcache;
-       register unsigned long a0, a0e;
-
-       asm volatile("stc sr, %0" : "=r" (orig_sr));
-       sr_with_bl = orig_sr | (1<<28);
-       base_addr = ((unsigned long)&empty_zero_page[0]);
-
-       /*
-        * The previous code aligned base_addr to 16k, i.e. the way_size of all
-        * existing SH-4 D-caches.  Whilst I don't see a need to have this
-        * aligned to any better than the cache line size (which it will be
-        * anyway by construction), let's align it to at least the way_size of
-        * any existing or conceivable SH-4 D-cache.  -- RPC
-        */
-       base_addr = ((base_addr >> 16) << 16);
-       base_addr |= start;
-
-       dcache = &boot_cpu_data.dcache;
-       linesz = dcache->linesz;
-       way_incr = dcache->way_incr;
-       way_size = dcache->way_size;
-
-       a0 = base_addr;
-       a0e = base_addr + extent_per_way;
-       do {
-               asm volatile("ldc %0, sr" : : "r" (sr_with_bl));
-               asm volatile("movca.l r0, @%0\n\t"
-                            "ocbi @%0" : : "r" (a0));
-               a0 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "ocbi @%0" : : "r" (a0));
-               a0 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "ocbi @%0" : : "r" (a0));
-               a0 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "ocbi @%0" : : "r" (a0));
-               asm volatile("ldc %0, sr" : : "r" (orig_sr));
-               a0 += linesz;
-       } while (a0 < a0e);
-}
-
-static void __flush_dcache_segment_2way(unsigned long start,
-                                       unsigned long extent_per_way)
-{
-       unsigned long orig_sr, sr_with_bl;
-       unsigned long base_addr;
-       unsigned long way_incr, linesz, way_size;
-       struct cache_info *dcache;
-       register unsigned long a0, a1, a0e;
-
-       asm volatile("stc sr, %0" : "=r" (orig_sr));
-       sr_with_bl = orig_sr | (1<<28);
-       base_addr = ((unsigned long)&empty_zero_page[0]);
-
-       /* See comment under 1-way above */
-       base_addr = ((base_addr >> 16) << 16);
-       base_addr |= start;
-
-       dcache = &boot_cpu_data.dcache;
-       linesz = dcache->linesz;
-       way_incr = dcache->way_incr;
-       way_size = dcache->way_size;
-
-       a0 = base_addr;
-       a1 = a0 + way_incr;
-       a0e = base_addr + extent_per_way;
-       do {
-               asm volatile("ldc %0, sr" : : "r" (sr_with_bl));
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1" : :
-                            "r" (a0), "r" (a1));
-               a0 += linesz;
-               a1 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1" : :
-                            "r" (a0), "r" (a1));
-               a0 += linesz;
-               a1 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1" : :
-                            "r" (a0), "r" (a1));
-               a0 += linesz;
-               a1 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1" : :
-                            "r" (a0), "r" (a1));
-               asm volatile("ldc %0, sr" : : "r" (orig_sr));
-               a0 += linesz;
-               a1 += linesz;
-       } while (a0 < a0e);
-}
-
-static void __flush_dcache_segment_4way(unsigned long start,
-                                       unsigned long extent_per_way)
-{
-       unsigned long orig_sr, sr_with_bl;
-       unsigned long base_addr;
-       unsigned long way_incr, linesz, way_size;
-       struct cache_info *dcache;
-       register unsigned long a0, a1, a2, a3, a0e;
-
-       asm volatile("stc sr, %0" : "=r" (orig_sr));
-       sr_with_bl = orig_sr | (1<<28);
-       base_addr = ((unsigned long)&empty_zero_page[0]);
-
-       /* See comment under 1-way above */
-       base_addr = ((base_addr >> 16) << 16);
-       base_addr |= start;
-
-       dcache = &boot_cpu_data.dcache;
-       linesz = dcache->linesz;
-       way_incr = dcache->way_incr;
-       way_size = dcache->way_size;
-
-       a0 = base_addr;
-       a1 = a0 + way_incr;
-       a2 = a1 + way_incr;
-       a3 = a2 + way_incr;
-       a0e = base_addr + extent_per_way;
-       do {
-               asm volatile("ldc %0, sr" : : "r" (sr_with_bl));
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "movca.l r0, @%2\n\t"
-                            "movca.l r0, @%3\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1\n\t"
-                            "ocbi @%2\n\t"
-                            "ocbi @%3\n\t" : :
-                            "r" (a0), "r" (a1), "r" (a2), "r" (a3));
-               a0 += linesz;
-               a1 += linesz;
-               a2 += linesz;
-               a3 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "movca.l r0, @%2\n\t"
-                            "movca.l r0, @%3\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1\n\t"
-                            "ocbi @%2\n\t"
-                            "ocbi @%3\n\t" : :
-                            "r" (a0), "r" (a1), "r" (a2), "r" (a3));
-               a0 += linesz;
-               a1 += linesz;
-               a2 += linesz;
-               a3 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "movca.l r0, @%2\n\t"
-                            "movca.l r0, @%3\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1\n\t"
-                            "ocbi @%2\n\t"
-                            "ocbi @%3\n\t" : :
-                            "r" (a0), "r" (a1), "r" (a2), "r" (a3));
-               a0 += linesz;
-               a1 += linesz;
-               a2 += linesz;
-               a3 += linesz;
-               asm volatile("movca.l r0, @%0\n\t"
-                            "movca.l r0, @%1\n\t"
-                            "movca.l r0, @%2\n\t"
-                            "movca.l r0, @%3\n\t"
-                            "ocbi @%0\n\t"
-                            "ocbi @%1\n\t"
-                            "ocbi @%2\n\t"
-                            "ocbi @%3\n\t" : :
-                            "r" (a0), "r" (a1), "r" (a2), "r" (a3));
-               asm volatile("ldc %0, sr" : : "r" (orig_sr));
-               a0 += linesz;
-               a1 += linesz;
-               a2 += linesz;
-               a3 += linesz;
-       } while (a0 < a0e);
-}
-
 extern void __weak sh4__flush_region_init(void);
 
 /*
@@ -710,32 +384,11 @@ extern void __weak sh4__flush_region_init(void);
  */
 void __init sh4_cache_init(void)
 {
-       unsigned int wt_enabled = !!(__raw_readl(CCR) & CCR_CACHE_WT);
-
        printk("PVR=%08x CVR=%08x PRR=%08x\n",
                ctrl_inl(CCN_PVR),
                ctrl_inl(CCN_CVR),
                ctrl_inl(CCN_PRR));
 
-       if (wt_enabled)
-               __flush_dcache_segment_fn = __flush_dcache_segment_writethrough;
-       else {
-               switch (boot_cpu_data.dcache.ways) {
-               case 1:
-                       __flush_dcache_segment_fn = __flush_dcache_segment_1way;
-                       break;
-               case 2:
-                       __flush_dcache_segment_fn = __flush_dcache_segment_2way;
-                       break;
-               case 4:
-                       __flush_dcache_segment_fn = __flush_dcache_segment_4way;
-                       break;
-               default:
-                       panic("unknown number of cache ways\n");
-                       break;
-               }
-       }
-
        local_flush_icache_range        = sh4_flush_icache_range;
        local_flush_dcache_page         = sh4_flush_dcache_page;
        local_flush_cache_all           = sh4_flush_cache_all;
index 467ff8e..eb4cc4e 100644 (file)
@@ -563,7 +563,7 @@ static void sh5_flush_cache_page(void *args)
 
 static void sh5_flush_dcache_page(void *page)
 {
-       sh64_dcache_purge_phy_page(page_to_phys(page));
+       sh64_dcache_purge_phy_page(page_to_phys((struct page *)page));
        wmb();
 }
 
index 2601935..f527fb7 100644 (file)
@@ -141,7 +141,7 @@ static void sh7705_flush_dcache_page(void *arg)
        if (mapping && !mapping_mapped(mapping))
                set_bit(PG_dcache_dirty, &page->flags);
        else
-               __flush_dcache_page(PHYSADDR(page_address(page)));
+               __flush_dcache_page(__pa(page_address(page)));
 }
 
 static void __uses_jump_to_uncached sh7705_flush_cache_all(void *args)
index a2dc7f9..e9415d3 100644 (file)
@@ -27,8 +27,11 @@ void (*local_flush_icache_page)(void *args) = cache_noop;
 void (*local_flush_cache_sigtramp)(void *args) = cache_noop;
 
 void (*__flush_wback_region)(void *start, int size);
+EXPORT_SYMBOL(__flush_wback_region);
 void (*__flush_purge_region)(void *start, int size);
+EXPORT_SYMBOL(__flush_purge_region);
 void (*__flush_invalidate_region)(void *start, int size);
+EXPORT_SYMBOL(__flush_invalidate_region);
 
 static inline void noop__flush_region(void *start, int size)
 {
@@ -161,14 +164,21 @@ void flush_cache_all(void)
 {
        cacheop_on_each_cpu(local_flush_cache_all, NULL, 1);
 }
+EXPORT_SYMBOL(flush_cache_all);
 
 void flush_cache_mm(struct mm_struct *mm)
 {
+       if (boot_cpu_data.dcache.n_aliases == 0)
+               return;
+
        cacheop_on_each_cpu(local_flush_cache_mm, mm, 1);
 }
 
 void flush_cache_dup_mm(struct mm_struct *mm)
 {
+       if (boot_cpu_data.dcache.n_aliases == 0)
+               return;
+
        cacheop_on_each_cpu(local_flush_cache_dup_mm, mm, 1);
 }
 
@@ -195,11 +205,13 @@ void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
 
        cacheop_on_each_cpu(local_flush_cache_range, (void *)&data, 1);
 }
+EXPORT_SYMBOL(flush_cache_range);
 
 void flush_dcache_page(struct page *page)
 {
        cacheop_on_each_cpu(local_flush_dcache_page, page, 1);
 }
+EXPORT_SYMBOL(flush_dcache_page);
 
 void flush_icache_range(unsigned long start, unsigned long end)
 {
@@ -265,7 +277,11 @@ static void __init emit_cache_params(void)
 
 void __init cpu_cache_init(void)
 {
-       unsigned int cache_disabled = !(__raw_readl(CCR) & CCR_CACHE_ENABLE);
+       unsigned int cache_disabled = 0;
+
+#ifdef CCR
+       cache_disabled = !(__raw_readl(CCR) & CCR_CACHE_ENABLE);
+#endif
 
        compute_alias(&boot_cpu_data.icache);
        compute_alias(&boot_cpu_data.dcache);
index e098ec1..902967e 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/dma-debug.h>
 #include <linux/io.h>
+#include <linux/module.h>
 #include <asm/cacheflush.h>
 #include <asm/addrspace.h>
 
 #define PREALLOC_DMA_DEBUG_ENTRIES     4096
 
+struct dma_map_ops *dma_ops;
+EXPORT_SYMBOL(dma_ops);
+
 static int __init dma_init(void)
 {
        dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
@@ -27,15 +31,12 @@ static int __init dma_init(void)
 }
 fs_initcall(dma_init);
 
-void *dma_alloc_coherent(struct device *dev, size_t size,
-                          dma_addr_t *dma_handle, gfp_t gfp)
+void *dma_generic_alloc_coherent(struct device *dev, size_t size,
+                                dma_addr_t *dma_handle, gfp_t gfp)
 {
        void *ret, *ret_nocache;
        int order = get_order(size);
 
-       if (dma_alloc_from_coherent(dev, size, dma_handle, &ret))
-               return ret;
-
        ret = (void *)__get_free_pages(gfp, order);
        if (!ret)
                return NULL;
@@ -57,35 +58,26 @@ void *dma_alloc_coherent(struct device *dev, size_t size,
 
        *dma_handle = virt_to_phys(ret);
 
-       debug_dma_alloc_coherent(dev, size, *dma_handle, ret_nocache);
-
        return ret_nocache;
 }
-EXPORT_SYMBOL(dma_alloc_coherent);
 
-void dma_free_coherent(struct device *dev, size_t size,
-                        void *vaddr, dma_addr_t dma_handle)
+void dma_generic_free_coherent(struct device *dev, size_t size,
+                              void *vaddr, dma_addr_t dma_handle)
 {
        int order = get_order(size);
        unsigned long pfn = dma_handle >> PAGE_SHIFT;
        int k;
 
-       WARN_ON(irqs_disabled());       /* for portability */
-
-       if (dma_release_from_coherent(dev, order, vaddr))
-               return;
-
-       debug_dma_free_coherent(dev, size, vaddr, dma_handle);
        for (k = 0; k < (1 << order); k++)
                __free_pages(pfn_to_page(pfn + k), 0);
+
        iounmap(vaddr);
 }
-EXPORT_SYMBOL(dma_free_coherent);
 
 void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
                    enum dma_data_direction direction)
 {
-#ifdef CONFIG_CPU_SH5
+#if defined(CONFIG_CPU_SH5) || defined(CONFIG_PMB)
        void *p1addr = vaddr;
 #else
        void *p1addr = (void*) P1SEGADDR((unsigned long)vaddr);
index 8173e38..432acd0 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/pagemap.h>
 #include <linux/percpu.h>
 #include <linux/io.h>
+#include <linux/dma-mapping.h>
 #include <asm/mmu_context.h>
 #include <asm/tlb.h>
 #include <asm/cacheflush.h>
@@ -186,11 +187,21 @@ void __init paging_init(void)
        set_fixmap_nocache(FIX_UNCACHED, __pa(&__uncached_start));
 }
 
+/*
+ * Early initialization for any I/O MMUs we might have.
+ */
+static void __init iommu_init(void)
+{
+       no_iommu_init();
+}
+
 void __init mem_init(void)
 {
        int codesize, datasize, initsize;
        int nid;
 
+       iommu_init();
+
        num_physpages = 0;
        high_memory = NULL;
 
@@ -323,4 +334,12 @@ int memory_add_physaddr_to_nid(u64 addr)
 }
 EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
 #endif
+
 #endif /* CONFIG_MEMORY_HOTPLUG */
+
+#ifdef CONFIG_PMB
+int __in_29bit_mode(void)
+{
+       return !(ctrl_inl(PMB_PASCR) & PASCR_SE);
+}
+#endif /* CONFIG_PMB */
index 16e01b5..15d74ea 100644 (file)
@@ -39,7 +39,9 @@ void *kmap_coherent(struct page *page, unsigned long addr)
        pagefault_disable();
 
        idx = FIX_CMAP_END -
-               ((addr & current_cpu_data.dcache.alias_mask) >> PAGE_SHIFT);
+               (((addr >> PAGE_SHIFT) & (FIX_N_COLOURS - 1)) +
+                (FIX_N_COLOURS * smp_processor_id()));
+
        vaddr = __fix_to_virt(idx);
 
        BUG_ON(!pte_none(*(kmap_coherent_pte - idx)));
index 9b784fd..6c52444 100644 (file)
@@ -60,7 +60,7 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end)
        unsigned long bootmem_paddr;
 
        /* Don't allow bogus node assignment */
-       BUG_ON(nid > MAX_NUMNODES || nid == 0);
+       BUG_ON(nid > MAX_NUMNODES || nid <= 0);
 
        start_pfn = start >> PAGE_SHIFT;
        end_pfn = end >> PAGE_SHIFT;
diff --git a/arch/sh/mm/pmb-fixed.c b/arch/sh/mm/pmb-fixed.c
deleted file mode 100644 (file)
index 43c8eac..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * arch/sh/mm/fixed_pmb.c
- *
- * Copyright (C) 2009  Renesas Solutions Corp.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/io.h>
-#include <asm/mmu.h>
-#include <asm/mmu_context.h>
-
-static int __uses_jump_to_uncached fixed_pmb_init(void)
-{
-       int i;
-       unsigned long addr, data;
-
-       jump_to_uncached();
-
-       for (i = 0; i < PMB_ENTRY_MAX; i++) {
-               addr = PMB_DATA + (i << PMB_E_SHIFT);
-               data = ctrl_inl(addr);
-               if (!(data & PMB_V))
-                       continue;
-
-               if (data & PMB_C) {
-#if defined(CONFIG_CACHE_WRITETHROUGH)
-                       data |= PMB_WT;
-#elif defined(CONFIG_CACHE_WRITEBACK)
-                       data &= ~PMB_WT;
-#else
-                       data &= ~(PMB_C | PMB_WT);
-#endif
-               }
-               ctrl_outl(data, addr);
-       }
-
-       back_to_cached();
-
-       return 0;
-}
-arch_initcall(fixed_pmb_init);
index aade311..280f6a1 100644 (file)
 
 static void __pmb_unmap(struct pmb_entry *);
 
-static struct kmem_cache *pmb_cache;
+static struct pmb_entry pmb_entry_list[NR_PMB_ENTRIES];
 static unsigned long pmb_map;
 
-static struct pmb_entry pmb_init_map[] = {
-       /* vpn         ppn         flags (ub/sz/c/wt) */
-
-       /* P1 Section Mappings */
-       { 0x80000000, 0x00000000, PMB_SZ_64M  | PMB_C, },
-       { 0x84000000, 0x04000000, PMB_SZ_64M  | PMB_C, },
-       { 0x88000000, 0x08000000, PMB_SZ_128M | PMB_C, },
-       { 0x90000000, 0x10000000, PMB_SZ_64M  | PMB_C, },
-       { 0x94000000, 0x14000000, PMB_SZ_64M  | PMB_C, },
-       { 0x98000000, 0x18000000, PMB_SZ_64M  | PMB_C, },
-
-       /* P2 Section Mappings */
-       { 0xa0000000, 0x00000000, PMB_UB | PMB_SZ_64M  | PMB_WT, },
-       { 0xa4000000, 0x04000000, PMB_UB | PMB_SZ_64M  | PMB_WT, },
-       { 0xa8000000, 0x08000000, PMB_UB | PMB_SZ_128M | PMB_WT, },
-       { 0xb0000000, 0x10000000, PMB_UB | PMB_SZ_64M  | PMB_WT, },
-       { 0xb4000000, 0x14000000, PMB_UB | PMB_SZ_64M  | PMB_WT, },
-       { 0xb8000000, 0x18000000, PMB_UB | PMB_SZ_64M  | PMB_WT, },
-};
-
 static inline unsigned long mk_pmb_entry(unsigned int entry)
 {
        return (entry & PMB_E_MASK) << PMB_E_SHIFT;
@@ -73,81 +53,68 @@ static inline unsigned long mk_pmb_data(unsigned int entry)
        return mk_pmb_entry(entry) | PMB_DATA;
 }
 
-static DEFINE_SPINLOCK(pmb_list_lock);
-static struct pmb_entry *pmb_list;
-
-static inline void pmb_list_add(struct pmb_entry *pmbe)
+static int pmb_alloc_entry(void)
 {
-       struct pmb_entry **p, *tmp;
+       unsigned int pos;
 
-       p = &pmb_list;
-       while ((tmp = *p) != NULL)
-               p = &tmp->next;
+repeat:
+       pos = find_first_zero_bit(&pmb_map, NR_PMB_ENTRIES);
 
-       pmbe->next = tmp;
-       *p = pmbe;
-}
+       if (unlikely(pos > NR_PMB_ENTRIES))
+               return -ENOSPC;
 
-static inline void pmb_list_del(struct pmb_entry *pmbe)
-{
-       struct pmb_entry **p, *tmp;
+       if (test_and_set_bit(pos, &pmb_map))
+               goto repeat;
 
-       for (p = &pmb_list; (tmp = *p); p = &tmp->next)
-               if (tmp == pmbe) {
-                       *p = tmp->next;
-                       return;
-               }
+       return pos;
 }
 
-struct pmb_entry *pmb_alloc(unsigned long vpn, unsigned long ppn,
-                           unsigned long flags)
+static struct pmb_entry *pmb_alloc(unsigned long vpn, unsigned long ppn,
+                                  unsigned long flags, int entry)
 {
        struct pmb_entry *pmbe;
+       int pos;
+
+       if (entry == PMB_NO_ENTRY) {
+               pos = pmb_alloc_entry();
+               if (pos < 0)
+                       return ERR_PTR(pos);
+       } else {
+               if (test_bit(entry, &pmb_map))
+                       return ERR_PTR(-ENOSPC);
+               pos = entry;
+       }
 
-       pmbe = kmem_cache_alloc(pmb_cache, GFP_KERNEL);
+       pmbe = &pmb_entry_list[pos];
        if (!pmbe)
                return ERR_PTR(-ENOMEM);
 
        pmbe->vpn       = vpn;
        pmbe->ppn       = ppn;
        pmbe->flags     = flags;
-
-       spin_lock_irq(&pmb_list_lock);
-       pmb_list_add(pmbe);
-       spin_unlock_irq(&pmb_list_lock);
+       pmbe->entry     = pos;
 
        return pmbe;
 }
 
-void pmb_free(struct pmb_entry *pmbe)
+static void pmb_free(struct pmb_entry *pmbe)
 {
-       spin_lock_irq(&pmb_list_lock);
-       pmb_list_del(pmbe);
-       spin_unlock_irq(&pmb_list_lock);
+       int pos = pmbe->entry;
 
-       kmem_cache_free(pmb_cache, pmbe);
+       pmbe->vpn       = 0;
+       pmbe->ppn       = 0;
+       pmbe->flags     = 0;
+       pmbe->entry     = 0;
+
+       clear_bit(pos, &pmb_map);
 }
 
 /*
  * Must be in P2 for __set_pmb_entry()
  */
-int __set_pmb_entry(unsigned long vpn, unsigned long ppn,
-                   unsigned long flags, int *entry)
+static void __set_pmb_entry(unsigned long vpn, unsigned long ppn,
+                           unsigned long flags, int pos)
 {
-       unsigned int pos = *entry;
-
-       if (unlikely(pos == PMB_NO_ENTRY))
-               pos = find_first_zero_bit(&pmb_map, NR_PMB_ENTRIES);
-
-repeat:
-       if (unlikely(pos > NR_PMB_ENTRIES))
-               return -ENOSPC;
-
-       if (test_and_set_bit(pos, &pmb_map)) {
-               pos = find_first_zero_bit(&pmb_map, NR_PMB_ENTRIES);
-               goto repeat;
-       }
-
        ctrl_outl(vpn | PMB_V, mk_pmb_addr(pos));
 
 #ifdef CONFIG_CACHE_WRITETHROUGH
@@ -161,35 +128,21 @@ repeat:
 #endif
 
        ctrl_outl(ppn | flags | PMB_V, mk_pmb_data(pos));
-
-       *entry = pos;
-
-       return 0;
 }
 
-int __uses_jump_to_uncached set_pmb_entry(struct pmb_entry *pmbe)
+static void __uses_jump_to_uncached set_pmb_entry(struct pmb_entry *pmbe)
 {
-       int ret;
-
        jump_to_uncached();
-       ret = __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &pmbe->entry);
+       __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, pmbe->entry);
        back_to_cached();
-
-       return ret;
 }
 
-void __uses_jump_to_uncached clear_pmb_entry(struct pmb_entry *pmbe)
+static void __uses_jump_to_uncached clear_pmb_entry(struct pmb_entry *pmbe)
 {
        unsigned int entry = pmbe->entry;
        unsigned long addr;
 
-       /*
-        * Don't allow clearing of wired init entries, P1 or P2 access
-        * without a corresponding mapping in the PMB will lead to reset
-        * by the TLB.
-        */
-       if (unlikely(entry < ARRAY_SIZE(pmb_init_map) ||
-                    entry >= NR_PMB_ENTRIES))
+       if (unlikely(entry >= NR_PMB_ENTRIES))
                return;
 
        jump_to_uncached();
@@ -202,8 +155,6 @@ void __uses_jump_to_uncached clear_pmb_entry(struct pmb_entry *pmbe)
        ctrl_outl(ctrl_inl(addr) & ~PMB_V, addr);
 
        back_to_cached();
-
-       clear_bit(entry, &pmb_map);
 }
 
 
@@ -239,23 +190,17 @@ long pmb_remap(unsigned long vaddr, unsigned long phys,
 
 again:
        for (i = 0; i < ARRAY_SIZE(pmb_sizes); i++) {
-               int ret;
-
                if (size < pmb_sizes[i].size)
                        continue;
 
-               pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag);
+               pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag,
+                                PMB_NO_ENTRY);
                if (IS_ERR(pmbe)) {
                        err = PTR_ERR(pmbe);
                        goto out;
                }
 
-               ret = set_pmb_entry(pmbe);
-               if (ret != 0) {
-                       pmb_free(pmbe);
-                       err = -EBUSY;
-                       goto out;
-               }
+               set_pmb_entry(pmbe);
 
                phys    += pmb_sizes[i].size;
                vaddr   += pmb_sizes[i].size;
@@ -292,11 +237,16 @@ out:
 
 void pmb_unmap(unsigned long addr)
 {
-       struct pmb_entry **p, *pmbe;
+       struct pmb_entry *pmbe = NULL;
+       int i;
 
-       for (p = &pmb_list; (pmbe = *p); p = &pmbe->next)
-               if (pmbe->vpn == addr)
-                       break;
+       for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) {
+               if (test_bit(i, &pmb_map)) {
+                       pmbe = &pmb_entry_list[i];
+                       if (pmbe->vpn == addr)
+                               break;
+               }
+       }
 
        if (unlikely(!pmbe))
                return;
@@ -306,13 +256,22 @@ void pmb_unmap(unsigned long addr)
 
 static void __pmb_unmap(struct pmb_entry *pmbe)
 {
-       WARN_ON(!test_bit(pmbe->entry, &pmb_map));
+       BUG_ON(!test_bit(pmbe->entry, &pmb_map));
 
        do {
                struct pmb_entry *pmblink = pmbe;
 
-               if (pmbe->entry != PMB_NO_ENTRY)
-                       clear_pmb_entry(pmbe);
+               /*
+                * We may be called before this pmb_entry has been
+                * entered into the PMB table via set_pmb_entry(), but
+                * that's OK because we've allocated a unique slot for
+                * this entry in pmb_alloc() (even if we haven't filled
+                * it yet).
+                *
+                * Therefore, calling clear_pmb_entry() is safe as no
+                * other mapping can be using that slot.
+                */
+               clear_pmb_entry(pmbe);
 
                pmbe = pmblink->link;
 
@@ -320,42 +279,34 @@ static void __pmb_unmap(struct pmb_entry *pmbe)
        } while (pmbe);
 }
 
-static void pmb_cache_ctor(void *pmb)
+#ifdef CONFIG_PMB
+int __uses_jump_to_uncached pmb_init(void)
 {
-       struct pmb_entry *pmbe = pmb;
-
-       memset(pmb, 0, sizeof(struct pmb_entry));
-
-       pmbe->entry = PMB_NO_ENTRY;
-}
-
-static int __uses_jump_to_uncached pmb_init(void)
-{
-       unsigned int nr_entries = ARRAY_SIZE(pmb_init_map);
-       unsigned int entry, i;
-
-       BUG_ON(unlikely(nr_entries >= NR_PMB_ENTRIES));
-
-       pmb_cache = kmem_cache_create("pmb", sizeof(struct pmb_entry), 0,
-                                     SLAB_PANIC, pmb_cache_ctor);
+       unsigned int i;
+       long size, ret;
 
        jump_to_uncached();
 
        /*
-        * Ordering is important, P2 must be mapped in the PMB before we
-        * can set PMB.SE, and P1 must be mapped before we jump back to
-        * P1 space.
+        * Insert PMB entries for the P1 and P2 areas so that, after
+        * we've switched the MMU to 32-bit mode, the semantics of P1
+        * and P2 are the same as in 29-bit mode, e.g.
+        *
+        *      P1 - provides a cached window onto physical memory
+        *      P2 - provides an uncached window onto physical memory
         */
-       for (entry = 0; entry < nr_entries; entry++) {
-               struct pmb_entry *pmbe = pmb_init_map + entry;
+       size = __MEMORY_START + __MEMORY_SIZE;
 
-               __set_pmb_entry(pmbe->vpn, pmbe->ppn, pmbe->flags, &entry);
-       }
+       ret = pmb_remap(P1SEG, 0x00000000, size, PMB_C);
+       BUG_ON(ret != size);
+
+       ret = pmb_remap(P2SEG, 0x00000000, size, PMB_WT | PMB_UB);
+       BUG_ON(ret != size);
 
        ctrl_outl(0, PMB_IRMCR);
 
        /* PMB.SE and UB[7] */
-       ctrl_outl((1 << 31) | (1 << 7), PMB_PASCR);
+       ctrl_outl(PASCR_SE | (1 << 7), PMB_PASCR);
 
        /* Flush out the TLB */
        i =  ctrl_inl(MMUCR);
@@ -366,7 +317,53 @@ static int __uses_jump_to_uncached pmb_init(void)
 
        return 0;
 }
-arch_initcall(pmb_init);
+#else
+int __uses_jump_to_uncached pmb_init(void)
+{
+       int i;
+       unsigned long addr, data;
+
+       jump_to_uncached();
+
+       for (i = 0; i < PMB_ENTRY_MAX; i++) {
+               struct pmb_entry *pmbe;
+               unsigned long vpn, ppn, flags;
+
+               addr = PMB_DATA + (i << PMB_E_SHIFT);
+               data = ctrl_inl(addr);
+               if (!(data & PMB_V))
+                       continue;
+
+               if (data & PMB_C) {
+#if defined(CONFIG_CACHE_WRITETHROUGH)
+                       data |= PMB_WT;
+#elif defined(CONFIG_CACHE_WRITEBACK)
+                       data &= ~PMB_WT;
+#else
+                       data &= ~(PMB_C | PMB_WT);
+#endif
+               }
+               ctrl_outl(data, addr);
+
+               ppn = data & PMB_PFN_MASK;
+
+               flags = data & (PMB_C | PMB_WT | PMB_UB);
+               flags |= data & PMB_SZ_MASK;
+
+               addr = PMB_ADDR + (i << PMB_E_SHIFT);
+               data = ctrl_inl(addr);
+
+               vpn = data & PMB_PFN_MASK;
+
+               pmbe = pmb_alloc(vpn, ppn, flags, i);
+               WARN_ON(IS_ERR(pmbe));
+       }
+
+       back_to_cached();
+
+       return 0;
+}
+#endif /* CONFIG_PMB */
 
 static int pmb_seq_show(struct seq_file *file, void *iter)
 {
@@ -434,15 +431,18 @@ postcore_initcall(pmb_debugfs_init);
 static int pmb_sysdev_suspend(struct sys_device *dev, pm_message_t state)
 {
        static pm_message_t prev_state;
+       int i;
 
        /* Restore the PMB after a resume from hibernation */
        if (state.event == PM_EVENT_ON &&
            prev_state.event == PM_EVENT_FREEZE) {
                struct pmb_entry *pmbe;
-               spin_lock_irq(&pmb_list_lock);
-               for (pmbe = pmb_list; pmbe; pmbe = pmbe->next)
-                       set_pmb_entry(pmbe);
-               spin_unlock_irq(&pmb_list_lock);
+               for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) {
+                       if (test_bit(i, &pmb_map)) {
+                               pmbe = &pmb_entry_list[i];
+                               set_pmb_entry(pmbe);
+                       }
+               }
        }
        prev_state = state;
        return 0;
index 8e6eec9..4886c5c 100644 (file)
@@ -7,7 +7,3 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
                timer_int.o )
 
 oprofile-y     := $(DRIVER_OBJS) common.o backtrace.o
-
-oprofile-$(CONFIG_CPU_SUBTYPE_SH7750S) += op_model_sh7750.o
-oprofile-$(CONFIG_CPU_SUBTYPE_SH7750)  += op_model_sh7750.o
-oprofile-$(CONFIG_CPU_SUBTYPE_SH7091)  += op_model_sh7750.o
index 44f4e31..ac60493 100644 (file)
@@ -20,9 +20,6 @@
 #include <asm/processor.h>
 #include "op_impl.h"
 
-extern struct op_sh_model op_model_sh7750_ops __weak;
-extern struct op_sh_model op_model_sh4a_ops __weak;
-
 static struct op_sh_model *model;
 
 static struct op_counter_config ctr[20];
@@ -94,33 +91,14 @@ int __init oprofile_arch_init(struct oprofile_operations *ops)
         */
        ops->backtrace = sh_backtrace;
 
-       switch (current_cpu_data.type) {
-       /* SH-4 types */
-       case CPU_SH7750:
-       case CPU_SH7750S:
-               lmodel = &op_model_sh7750_ops;
-               break;
-
-        /* SH-4A types */
-       case CPU_SH7763:
-       case CPU_SH7770:
-       case CPU_SH7780:
-       case CPU_SH7781:
-       case CPU_SH7785:
-       case CPU_SH7786:
-       case CPU_SH7723:
-       case CPU_SH7724:
-       case CPU_SHX3:
-               lmodel = &op_model_sh4a_ops;
-               break;
-
-       /* SH4AL-DSP types */
-       case CPU_SH7343:
-       case CPU_SH7722:
-       case CPU_SH7366:
-               lmodel = &op_model_sh4a_ops;
-               break;
-       }
+       /*
+        * XXX
+        *
+        * All of the SH7750/SH-4A counters have been converted to perf,
+        * this infrastructure hook is left for other users until they've
+        * had a chance to convert over, at which point all of this
+        * will be deleted.
+        */
 
        if (!lmodel)
                return -ENODEV;
index 4d50997..1244479 100644 (file)
@@ -6,7 +6,7 @@ struct op_counter_config {
        unsigned long enabled;
        unsigned long event;
 
-       unsigned long long count;
+       unsigned long count;
 
        /* Dummy values for userspace tool compliance */
        unsigned long kernel;
diff --git a/arch/sh/oprofile/op_model_sh7750.c b/arch/sh/oprofile/op_model_sh7750.c
deleted file mode 100644 (file)
index c892c7c..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * arch/sh/oprofile/op_model_sh7750.c
- *
- * OProfile support for SH7750/SH7750S Performance Counters
- *
- * Copyright (C) 2003 - 2008  Paul Mundt
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/kernel.h>
-#include <linux/oprofile.h>
-#include <linux/profile.h>
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/fs.h>
-#include "op_impl.h"
-
-#define PM_CR_BASE     0xff000084      /* 16-bit */
-#define PM_CTR_BASE    0xff100004      /* 32-bit */
-
-#define PMCR(n)                (PM_CR_BASE + ((n) * 0x04))
-#define PMCTRH(n)      (PM_CTR_BASE + 0x00 + ((n) * 0x08))
-#define PMCTRL(n)      (PM_CTR_BASE + 0x04 + ((n) * 0x08))
-
-#define PMCR_PMM_MASK  0x0000003f
-
-#define PMCR_CLKF      0x00000100
-#define PMCR_PMCLR     0x00002000
-#define PMCR_PMST      0x00004000
-#define PMCR_PMEN      0x00008000
-
-struct op_sh_model op_model_sh7750_ops;
-
-#define NR_CNTRS       2
-
-static struct sh7750_ppc_register_config {
-       unsigned int ctrl;
-       unsigned long cnt_hi;
-       unsigned long cnt_lo;
-} regcache[NR_CNTRS];
-
-/*
- * There are a number of events supported by each counter (33 in total).
- * Since we have 2 counters, each counter will take the event code as it
- * corresponds to the PMCR PMM setting. Each counter can be configured
- * independently.
- *
- *     Event Code      Description
- *     ----------      -----------
- *
- *     0x01            Operand read access
- *     0x02            Operand write access
- *     0x03            UTLB miss
- *     0x04            Operand cache read miss
- *     0x05            Operand cache write miss
- *     0x06            Instruction fetch (w/ cache)
- *     0x07            Instruction TLB miss
- *     0x08            Instruction cache miss
- *     0x09            All operand accesses
- *     0x0a            All instruction accesses
- *     0x0b            OC RAM operand access
- *     0x0d            On-chip I/O space access
- *     0x0e            Operand access (r/w)
- *     0x0f            Operand cache miss (r/w)
- *     0x10            Branch instruction
- *     0x11            Branch taken
- *     0x12            BSR/BSRF/JSR
- *     0x13            Instruction execution
- *     0x14            Instruction execution in parallel
- *     0x15            FPU Instruction execution
- *     0x16            Interrupt
- *     0x17            NMI
- *     0x18            trapa instruction execution
- *     0x19            UBCA match
- *     0x1a            UBCB match
- *     0x21            Instruction cache fill
- *     0x22            Operand cache fill
- *     0x23            Elapsed time
- *     0x24            Pipeline freeze by I-cache miss
- *     0x25            Pipeline freeze by D-cache miss
- *     0x27            Pipeline freeze by branch instruction
- *     0x28            Pipeline freeze by CPU register
- *     0x29            Pipeline freeze by FPU
- *
- * Unfortunately we don't have a native exception or interrupt for counter
- * overflow (although since these counters can run for 16.3 days without
- * overflowing, it's not really necessary).
- *
- * OProfile on the other hand likes to have samples taken periodically, so
- * for now we just piggyback the timer interrupt to get the expected
- * behavior.
- */
-
-static int sh7750_timer_notify(struct pt_regs *regs)
-{
-       oprofile_add_sample(regs, 0);
-       return 0;
-}
-
-static u64 sh7750_read_counter(int counter)
-{
-       return (u64)((u64)(__raw_readl(PMCTRH(counter)) & 0xffff) << 32) |
-                          __raw_readl(PMCTRL(counter));
-}
-
-/*
- * Files will be in a path like:
- *
- *  /<oprofilefs mount point>/<counter number>/<file>
- *
- * So when dealing with <file>, we look to the parent dentry for the counter
- * number.
- */
-static inline int to_counter(struct file *file)
-{
-       const unsigned char *name = file->f_path.dentry->d_parent->d_name.name;
-
-       return (int)simple_strtol(name, NULL, 10);
-}
-
-/*
- * XXX: We have 48-bit counters, so we're probably going to want something
- * more along the lines of oprofilefs_ullong_to_user().. Truncating to
- * unsigned long works fine for now though, as long as we don't attempt to
- * profile for too horribly long.
- */
-static ssize_t sh7750_read_count(struct file *file, char __user *buf,
-                                size_t count, loff_t *ppos)
-{
-       int counter = to_counter(file);
-       u64 val = sh7750_read_counter(counter);
-
-       return oprofilefs_ulong_to_user((unsigned long)val, buf, count, ppos);
-}
-
-static ssize_t sh7750_write_count(struct file *file, const char __user *buf,
-                                 size_t count, loff_t *ppos)
-{
-       int counter = to_counter(file);
-       unsigned long val;
-
-       if (oprofilefs_ulong_from_user(&val, buf, count))
-               return -EFAULT;
-
-       /*
-        * Any write will clear the counter, although only 0 should be
-        * written for this purpose, as we do not support setting the
-        * counter to an arbitrary value.
-        */
-       WARN_ON(val != 0);
-
-       __raw_writew(__raw_readw(PMCR(counter)) | PMCR_PMCLR, PMCR(counter));
-
-       return count;
-}
-
-static const struct file_operations count_fops = {
-       .read           = sh7750_read_count,
-       .write          = sh7750_write_count,
-};
-
-static int sh7750_ppc_create_files(struct super_block *sb, struct dentry *dir)
-{
-       return oprofilefs_create_file(sb, dir, "count", &count_fops);
-}
-
-static void sh7750_ppc_reg_setup(struct op_counter_config *ctr)
-{
-       unsigned int counters = op_model_sh7750_ops.num_counters;
-       int i;
-
-       for (i = 0; i < counters; i++) {
-               regcache[i].ctrl        = 0;
-               regcache[i].cnt_hi      = 0;
-               regcache[i].cnt_lo      = 0;
-
-               if (!ctr[i].enabled)
-                       continue;
-
-               regcache[i].ctrl |= ctr[i].event | PMCR_PMEN | PMCR_PMST;
-               regcache[i].cnt_hi = (unsigned long)((ctr->count >> 32) & 0xffff);
-               regcache[i].cnt_lo = (unsigned long)(ctr->count & 0xffffffff);
-       }
-}
-
-static void sh7750_ppc_cpu_setup(void *args)
-{
-       unsigned int counters = op_model_sh7750_ops.num_counters;
-       int i;
-
-       for (i = 0; i < counters; i++) {
-               __raw_writew(0, PMCR(i));
-               __raw_writel(regcache[i].cnt_hi, PMCTRH(i));
-               __raw_writel(regcache[i].cnt_lo, PMCTRL(i));
-       }
-}
-
-static void sh7750_ppc_cpu_start(void *args)
-{
-       unsigned int counters = op_model_sh7750_ops.num_counters;
-       int i;
-
-       for (i = 0; i < counters; i++)
-               __raw_writew(regcache[i].ctrl, PMCR(i));
-}
-
-static void sh7750_ppc_cpu_stop(void *args)
-{
-       unsigned int counters = op_model_sh7750_ops.num_counters;
-       int i;
-
-       /* Disable the counters */
-       for (i = 0; i < counters; i++)
-               __raw_writew(__raw_readw(PMCR(i)) & ~PMCR_PMEN, PMCR(i));
-}
-
-static inline void sh7750_ppc_reset(void)
-{
-       unsigned int counters = op_model_sh7750_ops.num_counters;
-       int i;
-
-       /* Clear the counters */
-       for (i = 0; i < counters; i++)
-               __raw_writew(__raw_readw(PMCR(i)) | PMCR_PMCLR, PMCR(i));
-}
-
-static int sh7750_ppc_init(void)
-{
-       sh7750_ppc_reset();
-
-       return register_timer_hook(sh7750_timer_notify);
-}
-
-static void sh7750_ppc_exit(void)
-{
-       unregister_timer_hook(sh7750_timer_notify);
-
-       sh7750_ppc_reset();
-}
-
-struct op_sh_model op_model_sh7750_ops = {
-       .cpu_type       = "sh/sh7750",
-       .num_counters   = NR_CNTRS,
-       .reg_setup      = sh7750_ppc_reg_setup,
-       .cpu_setup      = sh7750_ppc_cpu_setup,
-       .cpu_start      = sh7750_ppc_cpu_start,
-       .cpu_stop       = sh7750_ppc_cpu_stop,
-       .init           = sh7750_ppc_init,
-       .exit           = sh7750_ppc_exit,
-       .create_files   = sh7750_ppc_create_files,
-};
index bbeb0c3..89bbf4e 100644 (file)
@@ -9,6 +9,9 @@
 #include <byteswap.h>
 #define USE_BSD
 #include <endian.h>
+#include <regex.h>
+
+static void die(char *fmt, ...);
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 static Elf32_Ehdr ehdr;
@@ -30,25 +33,47 @@ static struct section *secs;
  * the address for which it has been compiled. Don't warn user about
  * absolute relocations present w.r.t these symbols.
  */
-static const char* safe_abs_relocs[] = {
-               "xen_irq_disable_direct_reloc",
-               "xen_save_fl_direct_reloc",
-};
+static const char abs_sym_regex[] =
+       "^(xen_irq_disable_direct_reloc$|"
+       "xen_save_fl_direct_reloc$|"
+       "VDSO|"
+       "__crc_)";
+static regex_t abs_sym_regex_c;
+static int is_abs_reloc(const char *sym_name)
+{
+       return !regexec(&abs_sym_regex_c, sym_name, 0, NULL, 0);
+}
 
-static int is_safe_abs_reloc(const char* sym_name)
+/*
+ * These symbols are known to be relative, even if the linker marks them
+ * as absolute (typically defined outside any section in the linker script.)
+ */
+static const char rel_sym_regex[] =
+       "^_end$";
+static regex_t rel_sym_regex_c;
+static int is_rel_reloc(const char *sym_name)
 {
-       int i;
+       return !regexec(&rel_sym_regex_c, sym_name, 0, NULL, 0);
+}
 
-       for (i = 0; i < ARRAY_SIZE(safe_abs_relocs); i++) {
-               if (!strcmp(sym_name, safe_abs_relocs[i]))
-                       /* Match found */
-                       return 1;
-       }
-       if (strncmp(sym_name, "VDSO", 4) == 0)
-               return 1;
-       if (strncmp(sym_name, "__crc_", 6) == 0)
-               return 1;
-       return 0;
+static void regex_init(void)
+{
+        char errbuf[128];
+        int err;
+       
+        err = regcomp(&abs_sym_regex_c, abs_sym_regex,
+                      REG_EXTENDED|REG_NOSUB);
+        if (err) {
+                regerror(err, &abs_sym_regex_c, errbuf, sizeof errbuf);
+                die("%s", errbuf);
+        }
+
+        err = regcomp(&rel_sym_regex_c, rel_sym_regex,
+                      REG_EXTENDED|REG_NOSUB);
+        if (err) {
+                regerror(err, &rel_sym_regex_c, errbuf, sizeof errbuf);
+                die("%s", errbuf);
+        }
 }
 
 static void die(char *fmt, ...)
@@ -131,7 +156,7 @@ static const char *rel_type(unsigned type)
 #undef REL_TYPE
        };
        const char *name = "unknown type rel type name";
-       if (type < ARRAY_SIZE(type_name)) {
+       if (type < ARRAY_SIZE(type_name) && type_name[type]) {
                name = type_name[type];
        }
        return name;
@@ -448,7 +473,7 @@ static void print_absolute_relocs(void)
                         * Before warning check if this absolute symbol
                         * relocation is harmless.
                         */
-                       if (is_safe_abs_reloc(name))
+                       if (is_abs_reloc(name) || is_rel_reloc(name))
                                continue;
 
                        if (!printed) {
@@ -501,21 +526,26 @@ static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym))
                        sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
                        r_type = ELF32_R_TYPE(rel->r_info);
                        /* Don't visit relocations to absolute symbols */
-                       if (sym->st_shndx == SHN_ABS) {
+                       if (sym->st_shndx == SHN_ABS &&
+                           !is_rel_reloc(sym_name(sym_strtab, sym))) {
                                continue;
                        }
-                       if (r_type == R_386_NONE || r_type == R_386_PC32) {
+                       switch (r_type) {
+                       case R_386_NONE:
+                       case R_386_PC32:
                                /*
                                 * NONE can be ignored and and PC relative
                                 * relocations don't need to be adjusted.
                                 */
-                       }
-                       else if (r_type == R_386_32) {
+                               break;
+                       case R_386_32:
                                /* Visit relocations that need to be adjusted */
                                visit(rel, sym);
-                       }
-                       else {
-                               die("Unsupported relocation type: %d\n", r_type);
+                               break;
+                       default:
+                               die("Unsupported relocation type: %s (%d)\n",
+                                   rel_type(r_type), r_type);
+                               break;
                        }
                }
        }
@@ -571,16 +601,15 @@ static void emit_relocs(int as_text)
        }
        else {
                unsigned char buf[4];
-               buf[0] = buf[1] = buf[2] = buf[3] = 0;
                /* Print a stop */
-               printf("%c%c%c%c", buf[0], buf[1], buf[2], buf[3]);
+               fwrite("\0\0\0\0", 4, 1, stdout);
                /* Now print each relocation */
                for (i = 0; i < reloc_count; i++) {
                        buf[0] = (relocs[i] >>  0) & 0xff;
                        buf[1] = (relocs[i] >>  8) & 0xff;
                        buf[2] = (relocs[i] >> 16) & 0xff;
                        buf[3] = (relocs[i] >> 24) & 0xff;
-                       printf("%c%c%c%c", buf[0], buf[1], buf[2], buf[3]);
+                       fwrite(buf, 4, 1, stdout);
                }
        }
 }
@@ -598,6 +627,8 @@ int main(int argc, char **argv)
        FILE *fp;
        int i;
 
+       regex_init();
+
        show_absolute_syms = 0;
        show_absolute_relocs = 0;
        as_text = 0;
index 84786fb..4d817f9 100644 (file)
@@ -28,7 +28,9 @@ extern void amd_iommu_flush_all_domains(void);
 extern void amd_iommu_flush_all_devices(void);
 extern void amd_iommu_apply_erratum_63(u16 devid);
 extern void amd_iommu_reset_cmd_buffer(struct amd_iommu *iommu);
-
+extern int amd_iommu_init_devices(void);
+extern void amd_iommu_uninit_devices(void);
+extern void amd_iommu_init_notifier(void);
 #ifndef CONFIG_AMD_IOMMU_STATS
 
 static inline void amd_iommu_stats_init(void) { }
index 9d66848..278441f 100644 (file)
@@ -12,9 +12,9 @@
 #include <linux/types.h>
 
 /*
- * FIXME: Acessing the desc_struct through its fields is more elegant,
+ * FIXME: Accessing the desc_struct through its fields is more elegant,
  * and should be the one valid thing to do. However, a lot of open code
- * still touches the a and b acessors, and doing this allow us to do it
+ * still touches the a and b accessors, and doing this allow us to do it
  * incrementally. We keep the signature as a struct, rather than an union,
  * so we can get rid of it transparently in the future -- glommer
  */
index 6a635bd..4611f08 100644 (file)
  */
 #define LOCAL_PENDING_VECTOR           0xec
 
-#define UV_BAU_MESSAGE                 0xec
+#define UV_BAU_MESSAGE                 0xea
 
 /*
  * Self IPI vector for machine checks
index ede6998..91df7c5 100644 (file)
@@ -47,7 +47,7 @@ static inline void resume_map_numa_kva(pgd_t *pgd) {}
 /*
  * generic node memory support, the following assumptions apply:
  *
- * 1) memory comes in 64Mb contigious chunks which are either present or not
+ * 1) memory comes in 64Mb contiguous chunks which are either present or not
  * 2) we will not have more than 64Gb in total
  *
  * for now assume that 64Gb is max amount of RAM for whole system
index 5bef931..2d228fc 100644 (file)
@@ -244,6 +244,9 @@ do {                                                            \
 
 #define write_rdtscp_aux(val) wrmsr(0xc0000103, (val), 0)
 
+struct msr *msrs_alloc(void);
+void msrs_free(struct msr *msrs);
+
 #ifdef CONFIG_SMP
 int rdmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 *l, u32 *h);
 int wrmsr_on_cpu(unsigned int cpu, u32 msr_no, u32 l, u32 h);
index 72e5a44..04459d2 100644 (file)
@@ -124,7 +124,7 @@ struct sigcontext {
         * fpstate is really (struct _fpstate *) or (struct _xstate *)
         * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
         * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
-        * of extended memory layout. See comments at the defintion of
+        * of extended memory layout. See comments at the definition of
         * (struct _fpx_sw_bytes)
         */
        void __user *fpstate;           /* zero when no FPU/extended context */
@@ -219,7 +219,7 @@ struct sigcontext {
         * fpstate is really (struct _fpstate *) or (struct _xstate *)
         * depending on the FP_XSTATE_MAGIC1 encoded in the SW reserved
         * bytes of (struct _fpstate) and FP_XSTATE_MAGIC2 present at the end
-        * of extended memory layout. See comments at the defintion of
+        * of extended memory layout. See comments at the definition of
         * (struct _fpx_sw_bytes)
         */
        void __user *fpstate;           /* zero when no FPU/extended context */
index 87ffcb1..8085277 100644 (file)
@@ -5,13 +5,17 @@
 
 #ifdef CONFIG_SWIOTLB
 extern int swiotlb;
-extern int pci_swiotlb_init(void);
+extern int __init pci_swiotlb_detect(void);
+extern void __init pci_swiotlb_init(void);
 #else
 #define swiotlb 0
-static inline int pci_swiotlb_init(void)
+static inline int pci_swiotlb_detect(void)
 {
        return 0;
 }
+static inline void pci_swiotlb_init(void)
+{
+}
 #endif
 
 static inline void dma_mark_clean(void *addr, size_t size) {}
index 022a843..ecb544e 100644 (file)
@@ -23,6 +23,7 @@ struct task_struct *__switch_to(struct task_struct *prev,
 struct tss_struct;
 void __switch_to_xtra(struct task_struct *prev_p, struct task_struct *next_p,
                      struct tss_struct *tss);
+extern void show_regs_common(void);
 
 #ifdef CONFIG_X86_32
 
index 90f06c2..cb507bb 100644 (file)
@@ -16,7 +16,6 @@ extern unsigned long initial_code;
 extern unsigned long initial_gs;
 
 #define TRAMPOLINE_SIZE roundup(trampoline_end - trampoline_data, PAGE_SIZE)
-#define TRAMPOLINE_BASE 0x6000
 
 extern unsigned long setup_trampoline(void);
 extern void __init reserve_trampoline_memory(void);
index 80e2984..b414d2b 100644 (file)
@@ -55,7 +55,7 @@
 #define DESC_STATUS_SOURCE_TIMEOUT     3
 
 /*
- * source side threshholds at which message retries print a warning
+ * source side thresholds at which message retries print a warning
  */
 #define SOURCE_TIMEOUT_LIMIT           20
 #define DESTINATION_TIMEOUT_LIMIT      20
index 87eee07..fb1035c 100644 (file)
@@ -1123,7 +1123,7 @@ static int __init acpi_parse_madt_ioapic_entries(void)
        if (!acpi_sci_override_gsi)
                acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0);
 
-       /* Fill in identity legacy mapings where no override */
+       /* Fill in identity legacy mappings where no override */
        mp_config_acpi_legacy_irqs();
 
        count =
index 32fb091..b990b5c 100644 (file)
@@ -166,6 +166,43 @@ static void iommu_uninit_device(struct device *dev)
 {
        kfree(dev->archdata.iommu);
 }
+
+void __init amd_iommu_uninit_devices(void)
+{
+       struct pci_dev *pdev = NULL;
+
+       for_each_pci_dev(pdev) {
+
+               if (!check_device(&pdev->dev))
+                       continue;
+
+               iommu_uninit_device(&pdev->dev);
+       }
+}
+
+int __init amd_iommu_init_devices(void)
+{
+       struct pci_dev *pdev = NULL;
+       int ret = 0;
+
+       for_each_pci_dev(pdev) {
+
+               if (!check_device(&pdev->dev))
+                       continue;
+
+               ret = iommu_init_device(&pdev->dev);
+               if (ret)
+                       goto out_free;
+       }
+
+       return 0;
+
+out_free:
+
+       amd_iommu_uninit_devices();
+
+       return ret;
+}
 #ifdef CONFIG_AMD_IOMMU_STATS
 
 /*
@@ -1587,6 +1624,11 @@ static struct notifier_block device_nb = {
        .notifier_call = device_change_notifier,
 };
 
+void amd_iommu_init_notifier(void)
+{
+       bus_register_notifier(&pci_bus_type, &device_nb);
+}
+
 /*****************************************************************************
  *
  * The next functions belong to the dma_ops mapping/unmapping code.
@@ -1783,7 +1825,7 @@ retry:
                        goto out;
 
                /*
-                * aperture was sucessfully enlarged by 128 MB, try
+                * aperture was successfully enlarged by 128 MB, try
                 * allocation again
                 */
                goto retry;
@@ -2145,8 +2187,6 @@ static void prealloc_protection_domains(void)
                if (!check_device(&dev->dev))
                        continue;
 
-               iommu_init_device(&dev->dev);
-
                /* Is there already any domain for it? */
                if (domain_for_device(&dev->dev))
                        continue;
@@ -2215,8 +2255,6 @@ int __init amd_iommu_init_dma_ops(void)
 
        register_iommu(&amd_iommu_ops);
 
-       bus_register_notifier(&pci_bus_type, &device_nb);
-
        amd_iommu_stats_init();
 
        return 0;
@@ -2490,7 +2528,7 @@ int __init amd_iommu_init_passthrough(void)
        struct pci_dev *dev = NULL;
        u16 devid;
 
-       /* allocate passthroug domain */
+       /* allocate passthrough domain */
        pt_domain = protection_domain_alloc();
        if (!pt_domain)
                return -ENOMEM;
index 7ffc399..309a52f 100644 (file)
@@ -1274,6 +1274,10 @@ static int __init amd_iommu_init(void)
        if (ret)
                goto free;
 
+       ret = amd_iommu_init_devices();
+       if (ret)
+               goto free;
+
        if (iommu_pass_through)
                ret = amd_iommu_init_passthrough();
        else
@@ -1281,6 +1285,8 @@ static int __init amd_iommu_init(void)
        if (ret)
                goto free;
 
+       amd_iommu_init_notifier();
+
        enable_iommus();
 
        if (iommu_pass_through)
@@ -1296,6 +1302,9 @@ out:
        return ret;
 
 free:
+
+       amd_iommu_uninit_devices();
+
        free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
                   get_order(MAX_DOMAIN_ID/8));
 
index e0dfb68..3704997 100644 (file)
@@ -280,7 +280,8 @@ void __init early_gart_iommu_check(void)
         * or BIOS forget to put that in reserved.
         * try to update e820 to make that region as reserved.
         */
-       int i, fix, slot;
+       u32 agp_aper_base = 0, agp_aper_order = 0;
+       int i, fix, slot, valid_agp = 0;
        u32 ctl;
        u32 aper_size = 0, aper_order = 0, last_aper_order = 0;
        u64 aper_base = 0, last_aper_base = 0;
@@ -290,6 +291,8 @@ void __init early_gart_iommu_check(void)
                return;
 
        /* This is mostly duplicate of iommu_hole_init */
+       agp_aper_base = search_agp_bridge(&agp_aper_order, &valid_agp);
+
        fix = 0;
        for (i = 0; i < ARRAY_SIZE(bus_dev_ranges); i++) {
                int bus;
@@ -342,10 +345,10 @@ void __init early_gart_iommu_check(void)
                }
        }
 
-       if (!fix)
+       if (valid_agp)
                return;
 
-       /* different nodes have different setting, disable them all at first*/
+       /* disable them all at first */
        for (i = 0; i < ARRAY_SIZE(bus_dev_ranges); i++) {
                int bus;
                int dev_base, dev_limit;
@@ -458,8 +461,6 @@ out:
 
        if (aper_alloc) {
                /* Got the aperture from the AGP bridge */
-       } else if (!valid_agp) {
-               /* Do nothing */
        } else if ((!no_iommu && max_pfn > MAX_DMA32_PFN) ||
                   force_iommu ||
                   valid_agp ||
index efb2b9c..aa57c07 100644 (file)
@@ -1341,7 +1341,7 @@ void enable_x2apic(void)
 
        rdmsr(MSR_IA32_APICBASE, msr, msr2);
        if (!(msr & X2APIC_ENABLE)) {
-               pr_info("Enabling x2apic\n");
+               printk_once(KERN_INFO "Enabling x2apic\n");
                wrmsr(MSR_IA32_APICBASE, msr | X2APIC_ENABLE, 0);
        }
 }
index d9acc3b..e31b9ff 100644 (file)
@@ -127,7 +127,7 @@ static u32 noop_apic_read(u32 reg)
 
 static void noop_apic_write(u32 reg, u32 v)
 {
-       WARN_ON_ONCE((cpu_has_apic || !disable_apic));
+       WARN_ON_ONCE(cpu_has_apic && !disable_apic);
 }
 
 struct apic apic_noop = {
index e85f8fb..dd2b5f2 100644 (file)
@@ -27,6 +27,9 @@
  *
  * http://www.unisys.com
  */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/notifier.h>
 #include <linux/spinlock.h>
 #include <linux/cpumask.h>
@@ -223,9 +226,9 @@ static int parse_unisys_oem(char *oemptr)
                        mip_addr = val;
                        mip = (struct mip_reg *)val;
                        mip_reg = __va(mip);
-                       pr_debug("es7000_mipcfg: host_reg = 0x%lx \n",
+                       pr_debug("host_reg = 0x%lx\n",
                                 (unsigned long)host_reg);
-                       pr_debug("es7000_mipcfg: mip_reg = 0x%lx \n",
+                       pr_debug("mip_reg = 0x%lx\n",
                                 (unsigned long)mip_reg);
                        success++;
                        break;
@@ -401,7 +404,7 @@ static void es7000_enable_apic_mode(void)
        if (!es7000_plat)
                return;
 
-       printk(KERN_INFO "ES7000: Enabling APIC mode.\n");
+       pr_info("Enabling APIC mode.\n");
        memset(&es7000_mip_reg, 0, sizeof(struct mip_reg));
        es7000_mip_reg.off_0x00 = MIP_SW_APIC;
        es7000_mip_reg.off_0x38 = MIP_VALID;
@@ -514,8 +517,7 @@ static void es7000_setup_apic_routing(void)
 {
        int apic = per_cpu(x86_bios_cpu_apicid, smp_processor_id());
 
-       printk(KERN_INFO
-         "Enabling APIC mode:  %s. Using %d I/O APICs, target cpus %lx\n",
+       pr_info("Enabling APIC mode:  %s. Using %d I/O APICs, target cpus %lx\n",
                (apic_version[apic] == 0x14) ?
                        "Physical Cluster" : "Logical Cluster",
                nr_ioapics, cpumask_bits(es7000_target_cpus())[0]);
index c965e52..468489b 100644 (file)
@@ -74,6 +74,7 @@ void __cpuinit detect_extended_topology(struct cpuinfo_x86 *c)
        unsigned int eax, ebx, ecx, edx, sub_index;
        unsigned int ht_mask_width, core_plus_mask_width;
        unsigned int core_select_mask, core_level_siblings;
+       static bool printed;
 
        if (c->cpuid_level < 0xb)
                return;
@@ -127,12 +128,14 @@ void __cpuinit detect_extended_topology(struct cpuinfo_x86 *c)
 
        c->x86_max_cores = (core_level_siblings / smp_num_siblings);
 
-
-       printk(KERN_INFO  "CPU: Physical Processor ID: %d\n",
-              c->phys_proc_id);
-       if (c->x86_max_cores > 1)
-               printk(KERN_INFO  "CPU: Processor Core ID: %d\n",
-                      c->cpu_core_id);
+       if (!printed) {
+               printk(KERN_INFO  "CPU: Physical Processor ID: %d\n",
+                      c->phys_proc_id);
+               if (c->x86_max_cores > 1)
+                       printk(KERN_INFO  "CPU: Processor Core ID: %d\n",
+                              c->cpu_core_id);
+               printed = 1;
+       }
        return;
 #endif
 }
index 7128b37..8dc3ea1 100644 (file)
@@ -375,8 +375,6 @@ static void __cpuinit srat_detect_node(struct cpuinfo_x86 *c)
                        node = nearby_node(apicid);
        }
        numa_set_node(cpu, node);
-
-       printk(KERN_INFO "CPU %d/0x%x -> Node %d\n", cpu, apicid, node);
 #endif
 }
 
index c1afa99..0ee9a32 100644 (file)
@@ -427,6 +427,7 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c)
 #ifdef CONFIG_X86_HT
        u32 eax, ebx, ecx, edx;
        int index_msb, core_bits;
+       static bool printed;
 
        if (!cpu_has(c, X86_FEATURE_HT))
                return;
@@ -442,7 +443,7 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c)
        smp_num_siblings = (ebx & 0xff0000) >> 16;
 
        if (smp_num_siblings == 1) {
-               printk(KERN_INFO  "CPU: Hyper-Threading is disabled\n");
+               printk_once(KERN_INFO "CPU0: Hyper-Threading is disabled\n");
                goto out;
        }
 
@@ -469,11 +470,12 @@ void __cpuinit detect_ht(struct cpuinfo_x86 *c)
                                       ((1 << core_bits) - 1);
 
 out:
-       if ((c->x86_max_cores * smp_num_siblings) > 1) {
+       if (!printed && (c->x86_max_cores * smp_num_siblings) > 1) {
                printk(KERN_INFO  "CPU: Physical Processor ID: %d\n",
                       c->phys_proc_id);
                printk(KERN_INFO  "CPU: Processor Core ID: %d\n",
                       c->cpu_core_id);
+               printed = 1;
        }
 #endif
 }
@@ -1115,7 +1117,7 @@ void __cpuinit cpu_init(void)
        if (cpumask_test_and_set_cpu(cpu, cpu_initialized_mask))
                panic("CPU#%d already initialized!\n", cpu);
 
-       printk(KERN_INFO "Initializing CPU#%d\n", cpu);
+       pr_debug("Initializing CPU#%d\n", cpu);
 
        clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
 
index cabd2fa..7e7eea4 100644 (file)
@@ -885,7 +885,7 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
 
        /* Find ACPI data for processor */
        acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
-                               ACPI_UINT32_MAX, &longhaul_walk_callback,
+                               ACPI_UINT32_MAX, &longhaul_walk_callback, NULL,
                                NULL, (void *)&pr);
 
        /* Check ACPI support for C3 state */
index c900b73..9c31e8b 100644 (file)
@@ -270,8 +270,6 @@ static void __cpuinit srat_detect_node(struct cpuinfo_x86 *c)
                node = cpu_to_node(cpu);
        }
        numa_set_node(cpu, node);
-
-       printk(KERN_INFO "CPU %d/0x%x -> Node %d\n", cpu, apicid, node);
 #endif
 }
 
index 6c40f6b..63ada17 100644 (file)
@@ -507,18 +507,19 @@ static void __cpuinit cache_shared_cpu_map_setup(unsigned int cpu, int index)
 {
        struct _cpuid4_info     *this_leaf, *sibling_leaf;
        unsigned long num_threads_sharing;
-       int index_msb, i;
+       int index_msb, i, sibling;
        struct cpuinfo_x86 *c = &cpu_data(cpu);
 
        if ((index == 3) && (c->x86_vendor == X86_VENDOR_AMD)) {
-               struct cpuinfo_x86 *d;
-               for_each_online_cpu(i) {
+               for_each_cpu(i, c->llc_shared_map) {
                        if (!per_cpu(cpuid4_info, i))
                                continue;
-                       d = &cpu_data(i);
                        this_leaf = CPUID4_INFO_IDX(i, index);
-                       cpumask_copy(to_cpumask(this_leaf->shared_cpu_map),
-                                    d->llc_shared_map);
+                       for_each_cpu(sibling, c->llc_shared_map) {
+                               if (!cpu_online(sibling))
+                                       continue;
+                               set_bit(sibling, this_leaf->shared_cpu_map);
+                       }
                }
                return;
        }
index d7ebf25..a8aacd4 100644 (file)
@@ -1388,13 +1388,14 @@ static void __mcheck_cpu_init_timer(void)
        struct timer_list *t = &__get_cpu_var(mce_timer);
        int *n = &__get_cpu_var(mce_next_interval);
 
+       setup_timer(t, mce_start_timer, smp_processor_id());
+
        if (mce_ignore_ce)
                return;
 
        *n = check_interval * HZ;
        if (!*n)
                return;
-       setup_timer(t, mce_start_timer, smp_processor_id());
        t->expires = round_jiffies(jiffies + *n);
        add_timer_on(t, smp_processor_id());
 }
@@ -1928,7 +1929,7 @@ error2:
                sysdev_remove_file(&per_cpu(mce_dev, cpu), &mce_banks[j].attr);
 error:
        while (--i >= 0)
-               sysdev_remove_file(&per_cpu(mce_dev, cpu), &mce_banks[i].attr);
+               sysdev_remove_file(&per_cpu(mce_dev, cpu), mce_attrs[i]);
 
        sysdev_unregister(&per_cpu(mce_dev, cpu));
 
index 4fef985..81c499e 100644 (file)
@@ -256,6 +256,16 @@ asmlinkage void smp_thermal_interrupt(struct pt_regs *regs)
        ack_APIC_irq();
 }
 
+/* Thermal monitoring depends on APIC, ACPI and clock modulation */
+static int intel_thermal_supported(struct cpuinfo_x86 *c)
+{
+       if (!cpu_has_apic)
+               return 0;
+       if (!cpu_has(c, X86_FEATURE_ACPI) || !cpu_has(c, X86_FEATURE_ACC))
+               return 0;
+       return 1;
+}
+
 void __init mcheck_intel_therm_init(void)
 {
        /*
@@ -263,8 +273,7 @@ void __init mcheck_intel_therm_init(void)
         * LVT value on BSP and use that value to restore APs' thermal LVT
         * entry BIOS programmed later
         */
-       if (cpu_has(&boot_cpu_data, X86_FEATURE_ACPI) &&
-               cpu_has(&boot_cpu_data, X86_FEATURE_ACC))
+       if (intel_thermal_supported(&boot_cpu_data))
                lvtthmr_init = apic_read(APIC_LVTTHMR);
 }
 
@@ -274,8 +283,7 @@ void intel_init_thermal(struct cpuinfo_x86 *c)
        int tm2 = 0;
        u32 l, h;
 
-       /* Thermal monitoring depends on ACPI and clock modulation*/
-       if (!cpu_has(c, X86_FEATURE_ACPI) || !cpu_has(c, X86_FEATURE_ACC))
+       if (!intel_thermal_supported(c))
                return;
 
        /*
@@ -339,8 +347,8 @@ void intel_init_thermal(struct cpuinfo_x86 *c)
        l = apic_read(APIC_LVTTHMR);
        apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED);
 
-       printk(KERN_INFO "CPU%d: Thermal monitoring enabled (%s)\n",
-              cpu, tm2 ? "TM2" : "TM1");
+       printk_once(KERN_INFO "CPU0: Thermal monitoring enabled (%s)\n",
+                      tm2 ? "TM2" : "TM1");
 
        /* enable thermal throttle processing */
        atomic_set(&therm_throt_en, 1);
index c1bbed1..ab1a8a8 100644 (file)
@@ -1286,7 +1286,7 @@ x86_perf_event_set_period(struct perf_event *event,
                return 0;
 
        /*
-        * If we are way outside a reasoable range then just skip forward:
+        * If we are way outside a reasonable range then just skip forward:
         */
        if (unlikely(left <= -period)) {
                left = period;
index d17d482..f50447d 100644 (file)
@@ -732,7 +732,16 @@ struct early_res {
        char overlap_ok;
 };
 static struct early_res early_res[MAX_EARLY_RES] __initdata = {
-       { 0, PAGE_SIZE, "BIOS data page" },     /* BIOS data page */
+       { 0, PAGE_SIZE, "BIOS data page", 1 },  /* BIOS data page */
+#ifdef CONFIG_X86_32
+       /*
+        * But first pinch a few for the stack/trampoline stuff
+        * FIXME: Don't need the extra page at 4K, but need to fix
+        * trampoline before removing it. (see the GDT stuff)
+        */
+       { PAGE_SIZE, PAGE_SIZE, "EX TRAMPOLINE", 1 },
+#endif
+
        {}
 };
 
index 4f8e250..5051b94 100644 (file)
@@ -29,8 +29,6 @@ static void __init i386_default_early_setup(void)
 
 void __init i386_start_kernel(void)
 {
-       reserve_trampoline_memory();
-
        reserve_early(__pa_symbol(&_text), __pa_symbol(&__bss_stop), "TEXT DATA BSS");
 
 #ifdef CONFIG_BLK_DEV_INITRD
index 0b06cd7..b5a9896 100644 (file)
@@ -98,8 +98,6 @@ void __init x86_64_start_reservations(char *real_mode_data)
 {
        copy_bootdata(__va(real_mode_data));
 
-       reserve_trampoline_memory();
-
        reserve_early(__pa_symbol(&_text), __pa_symbol(&__bss_stop), "TEXT DATA BSS");
 
 #ifdef CONFIG_BLK_DEV_INITRD
index 1f3186c..5b8c750 100644 (file)
@@ -481,7 +481,7 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
 
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate and they
- * remain disabled thorough out this function.
+ * remain disabled throughout this function.
  */
 static int __kprobes kprobe_handler(struct pt_regs *regs)
 {
@@ -818,7 +818,7 @@ no_change:
 
 /*
  * Interrupts are disabled on entry as trap1 is an interrupt gate and they
- * remain disabled thoroughout this function.
+ * remain disabled throughout this function.
  */
 static int __kprobes post_kprobe_handler(struct pt_regs *regs)
 {
index 63123d9..37542b6 100644 (file)
@@ -13,6 +13,9 @@
  *  Licensed under the terms of the GNU General Public
  *  License version 2. See file COPYING for details.
  */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/firmware.h>
 #include <linux/pci_ids.h>
 #include <linux/uaccess.h>
@@ -81,7 +84,7 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
 
        memset(csig, 0, sizeof(*csig));
        rdmsr(MSR_AMD64_PATCH_LEVEL, csig->rev, dummy);
-       pr_info("microcode: CPU%d: patch_level=0x%x\n", cpu, csig->rev);
+       pr_info("CPU%d: patch_level=0x%x\n", cpu, csig->rev);
        return 0;
 }
 
@@ -111,8 +114,8 @@ static int get_matching_microcode(int cpu, void *mc, int rev)
 
        /* ucode might be chipset specific -- currently we don't support this */
        if (mc_header->nb_dev_id || mc_header->sb_dev_id) {
-               pr_err(KERN_ERR "microcode: CPU%d: loading of chipset "
-                      "specific code not yet supported\n", cpu);
+               pr_err("CPU%d: loading of chipset specific code not yet supported\n",
+                      cpu);
                return 0;
        }
 
@@ -141,12 +144,12 @@ static int apply_microcode_amd(int cpu)
 
        /* check current patch id and patch's id for match */
        if (rev != mc_amd->hdr.patch_id) {
-               pr_err("microcode: CPU%d: update failed "
-                      "(for patch_level=0x%x)\n", cpu, mc_amd->hdr.patch_id);
+               pr_err("CPU%d: update failed (for patch_level=0x%x)\n",
+                      cpu, mc_amd->hdr.patch_id);
                return -1;
        }
 
-       pr_info("microcode: CPU%d: updated (new patch_level=0x%x)\n", cpu, rev);
+       pr_info("CPU%d: updated (new patch_level=0x%x)\n", cpu, rev);
        uci->cpu_sig.rev = rev;
 
        return 0;
@@ -169,15 +172,14 @@ get_next_ucode(const u8 *buf, unsigned int size, unsigned int *mc_size)
                return NULL;
 
        if (section_hdr[0] != UCODE_UCODE_TYPE) {
-               pr_err("microcode: error: invalid type field in "
-                      "container file section header\n");
+               pr_err("error: invalid type field in container file section header\n");
                return NULL;
        }
 
        total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8));
 
        if (total_size > size || total_size > UCODE_MAX_SIZE) {
-               pr_err("microcode: error: size mismatch\n");
+               pr_err("error: size mismatch\n");
                return NULL;
        }
 
@@ -206,14 +208,13 @@ static int install_equiv_cpu_table(const u8 *buf)
        size = buf_pos[2];
 
        if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) {
-               pr_err("microcode: error: invalid type field in "
-                      "container file section header\n");
+               pr_err("error: invalid type field in container file section header\n");
                return 0;
        }
 
        equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size);
        if (!equiv_cpu_table) {
-               pr_err("microcode: failed to allocate equivalent CPU table\n");
+               pr_err("failed to allocate equivalent CPU table\n");
                return 0;
        }
 
@@ -246,7 +247,7 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
 
        offset = install_equiv_cpu_table(ucode_ptr);
        if (!offset) {
-               pr_err("microcode: failed to create equivalent cpu table\n");
+               pr_err("failed to create equivalent cpu table\n");
                return UCODE_ERROR;
        }
 
@@ -277,8 +278,7 @@ generic_load_microcode(int cpu, const u8 *data, size_t size)
                if (!leftover) {
                        vfree(uci->mc);
                        uci->mc = new_mc;
-                       pr_debug("microcode: CPU%d found a matching microcode "
-                                "update with version 0x%x (current=0x%x)\n",
+                       pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
                                 cpu, new_rev, uci->cpu_sig.rev);
                } else {
                        vfree(new_mc);
@@ -300,7 +300,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)
                return UCODE_NFOUND;
 
        if (*(u32 *)firmware->data != UCODE_MAGIC) {
-               pr_err("microcode: invalid UCODE_MAGIC (0x%08x)\n",
+               pr_err("invalid UCODE_MAGIC (0x%08x)\n",
                       *(u32 *)firmware->data);
                return UCODE_ERROR;
        }
@@ -313,8 +313,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)
 static enum ucode_state
 request_microcode_user(int cpu, const void __user *buf, size_t size)
 {
-       pr_info("microcode: AMD microcode update via "
-               "/dev/cpu/microcode not supported\n");
+       pr_info("AMD microcode update via /dev/cpu/microcode not supported\n");
        return UCODE_ERROR;
 }
 
@@ -334,14 +333,13 @@ void init_microcode_amd(struct device *device)
        WARN_ON(c->x86_vendor != X86_VENDOR_AMD);
 
        if (c->x86 < 0x10) {
-               pr_warning("microcode: AMD CPU family 0x%x not supported\n",
-                          c->x86);
+               pr_warning("AMD CPU family 0x%x not supported\n", c->x86);
                return;
        }
        supported_cpu = 1;
 
        if (request_firmware(&firmware, fw_name, device))
-               pr_err("microcode: failed to load file %s\n", fw_name);
+               pr_err("failed to load file %s\n", fw_name);
 }
 
 void fini_microcode_amd(void)
index e68aae3..844c02c 100644 (file)
@@ -70,6 +70,9 @@
  *             Fix sigmatch() macro to handle old CPUs with pf == 0.
  *             Thanks to Stuart Swales for pointing out this bug.
  */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/platform_device.h>
 #include <linux/miscdevice.h>
 #include <linux/capability.h>
@@ -209,7 +212,7 @@ static ssize_t microcode_write(struct file *file, const char __user *buf,
        ssize_t ret = -EINVAL;
 
        if ((len >> PAGE_SHIFT) > totalram_pages) {
-               pr_err("microcode: too much data (max %ld pages)\n", totalram_pages);
+               pr_err("too much data (max %ld pages)\n", totalram_pages);
                return ret;
        }
 
@@ -244,7 +247,7 @@ static int __init microcode_dev_init(void)
 
        error = misc_register(&microcode_dev);
        if (error) {
-               pr_err("microcode: can't misc_register on minor=%d\n", MICROCODE_MINOR);
+               pr_err("can't misc_register on minor=%d\n", MICROCODE_MINOR);
                return error;
        }
 
@@ -359,7 +362,7 @@ static enum ucode_state microcode_resume_cpu(int cpu)
        if (!uci->mc)
                return UCODE_NFOUND;
 
-       pr_debug("microcode: CPU%d updated upon resume\n", cpu);
+       pr_debug("CPU%d updated upon resume\n", cpu);
        apply_microcode_on_target(cpu);
 
        return UCODE_OK;
@@ -379,7 +382,7 @@ static enum ucode_state microcode_init_cpu(int cpu)
        ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev);
 
        if (ustate == UCODE_OK) {
-               pr_debug("microcode: CPU%d updated upon init\n", cpu);
+               pr_debug("CPU%d updated upon init\n", cpu);
                apply_microcode_on_target(cpu);
        }
 
@@ -406,7 +409,7 @@ static int mc_sysdev_add(struct sys_device *sys_dev)
        if (!cpu_online(cpu))
                return 0;
 
-       pr_debug("microcode: CPU%d added\n", cpu);
+       pr_debug("CPU%d added\n", cpu);
 
        err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group);
        if (err)
@@ -425,7 +428,7 @@ static int mc_sysdev_remove(struct sys_device *sys_dev)
        if (!cpu_online(cpu))
                return 0;
 
-       pr_debug("microcode: CPU%d removed\n", cpu);
+       pr_debug("CPU%d removed\n", cpu);
        microcode_fini_cpu(cpu);
        sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
        return 0;
@@ -473,15 +476,15 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
                microcode_update_cpu(cpu);
        case CPU_DOWN_FAILED:
        case CPU_DOWN_FAILED_FROZEN:
-               pr_debug("microcode: CPU%d added\n", cpu);
+               pr_debug("CPU%d added\n", cpu);
                if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group))
-                       pr_err("microcode: Failed to create group for CPU%d\n", cpu);
+                       pr_err("Failed to create group for CPU%d\n", cpu);
                break;
        case CPU_DOWN_PREPARE:
        case CPU_DOWN_PREPARE_FROZEN:
                /* Suspend is in progress, only remove the interface */
                sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
-               pr_debug("microcode: CPU%d removed\n", cpu);
+               pr_debug("CPU%d removed\n", cpu);
                break;
        case CPU_DEAD:
        case CPU_UP_CANCELED_FROZEN:
@@ -507,7 +510,7 @@ static int __init microcode_init(void)
                microcode_ops = init_amd_microcode();
 
        if (!microcode_ops) {
-               pr_err("microcode: no support for this CPU vendor\n");
+               pr_err("no support for this CPU vendor\n");
                return -ENODEV;
        }
 
@@ -541,8 +544,7 @@ static int __init microcode_init(void)
        register_hotcpu_notifier(&mc_cpu_notifier);
 
        pr_info("Microcode Update Driver: v" MICROCODE_VERSION
-              " <tigran@aivazian.fsnet.co.uk>,"
-              " Peter Oruba\n");
+               " <tigran@aivazian.fsnet.co.uk>, Peter Oruba\n");
 
        return 0;
 }
index 0d334dd..ebd193e 100644 (file)
@@ -70,6 +70,9 @@
  *             Fix sigmatch() macro to handle old CPUs with pf == 0.
  *             Thanks to Stuart Swales for pointing out this bug.
  */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/firmware.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
@@ -146,8 +149,7 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
 
        if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
            cpu_has(c, X86_FEATURE_IA64)) {
-               printk(KERN_ERR "microcode: CPU%d not a capable Intel "
-                       "processor\n", cpu_num);
+               pr_err("CPU%d not a capable Intel processor\n", cpu_num);
                return -1;
        }
 
@@ -165,8 +167,8 @@ static int collect_cpu_info(int cpu_num, struct cpu_signature *csig)
        /* get the current revision from MSR 0x8B */
        rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev);
 
-       printk(KERN_INFO "microcode: CPU%d sig=0x%x, pf=0x%x, revision=0x%x\n",
-                       cpu_num, csig->sig, csig->pf, csig->rev);
+       pr_info("CPU%d sig=0x%x, pf=0x%x, revision=0x%x\n",
+               cpu_num, csig->sig, csig->pf, csig->rev);
 
        return 0;
 }
@@ -194,28 +196,24 @@ static int microcode_sanity_check(void *mc)
        data_size = get_datasize(mc_header);
 
        if (data_size + MC_HEADER_SIZE > total_size) {
-               printk(KERN_ERR "microcode: error! "
-                               "Bad data size in microcode data file\n");
+               pr_err("error! Bad data size in microcode data file\n");
                return -EINVAL;
        }
 
        if (mc_header->ldrver != 1 || mc_header->hdrver != 1) {
-               printk(KERN_ERR "microcode: error! "
-                               "Unknown microcode update format\n");
+               pr_err("error! Unknown microcode update format\n");
                return -EINVAL;
        }
        ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
        if (ext_table_size) {
                if ((ext_table_size < EXT_HEADER_SIZE)
                 || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
-                       printk(KERN_ERR "microcode: error! "
-                               "Small exttable size in microcode data file\n");
+                       pr_err("error! Small exttable size in microcode data file\n");
                        return -EINVAL;
                }
                ext_header = mc + MC_HEADER_SIZE + data_size;
                if (ext_table_size != exttable_size(ext_header)) {
-                       printk(KERN_ERR "microcode: error! "
-                               "Bad exttable size in microcode data file\n");
+                       pr_err("error! Bad exttable size in microcode data file\n");
                        return -EFAULT;
                }
                ext_sigcount = ext_header->count;
@@ -230,8 +228,7 @@ static int microcode_sanity_check(void *mc)
                while (i--)
                        ext_table_sum += ext_tablep[i];
                if (ext_table_sum) {
-                       printk(KERN_WARNING "microcode: aborting, "
-                               "bad extended signature table checksum\n");
+                       pr_warning("aborting, bad extended signature table checksum\n");
                        return -EINVAL;
                }
        }
@@ -242,7 +239,7 @@ static int microcode_sanity_check(void *mc)
        while (i--)
                orig_sum += ((int *)mc)[i];
        if (orig_sum) {
-               printk(KERN_ERR "microcode: aborting, bad checksum\n");
+               pr_err("aborting, bad checksum\n");
                return -EINVAL;
        }
        if (!ext_table_size)
@@ -255,7 +252,7 @@ static int microcode_sanity_check(void *mc)
                        - (mc_header->sig + mc_header->pf + mc_header->cksum)
                        + (ext_sig->sig + ext_sig->pf + ext_sig->cksum);
                if (sum) {
-                       printk(KERN_ERR "microcode: aborting, bad checksum\n");
+                       pr_err("aborting, bad checksum\n");
                        return -EINVAL;
                }
        }
@@ -327,13 +324,11 @@ static int apply_microcode(int cpu)
        rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
 
        if (val[1] != mc_intel->hdr.rev) {
-               printk(KERN_ERR "microcode: CPU%d update "
-                               "to revision 0x%x failed\n",
-                       cpu_num, mc_intel->hdr.rev);
+               pr_err("CPU%d update to revision 0x%x failed\n",
+                      cpu_num, mc_intel->hdr.rev);
                return -1;
        }
-       printk(KERN_INFO "microcode: CPU%d updated to revision "
-                        "0x%x, date = %04x-%02x-%02x \n",
+       pr_info("CPU%d updated to revision 0x%x, date = %04x-%02x-%02x \n",
                cpu_num, val[1],
                mc_intel->hdr.date & 0xffff,
                mc_intel->hdr.date >> 24,
@@ -362,8 +357,7 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,
 
                mc_size = get_totalsize(&mc_header);
                if (!mc_size || mc_size > leftover) {
-                       printk(KERN_ERR "microcode: error!"
-                                       "Bad data in microcode data file\n");
+                       pr_err("error! Bad data in microcode data file\n");
                        break;
                }
 
@@ -405,9 +399,8 @@ static enum ucode_state generic_load_microcode(int cpu, void *data, size_t size,
                vfree(uci->mc);
        uci->mc = (struct microcode_intel *)new_mc;
 
-       pr_debug("microcode: CPU%d found a matching microcode update with"
-                " version 0x%x (current=0x%x)\n",
-                       cpu, new_rev, uci->cpu_sig.rev);
+       pr_debug("CPU%d found a matching microcode update with version 0x%x (current=0x%x)\n",
+                cpu, new_rev, uci->cpu_sig.rev);
 out:
        return state;
 }
@@ -429,7 +422,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device)
                c->x86, c->x86_model, c->x86_mask);
 
        if (request_firmware(&firmware, name, device)) {
-               pr_debug("microcode: data file %s load failed\n", name);
+               pr_debug("data file %s load failed\n", name);
                return UCODE_NFOUND;
        }
 
index 35a57c9..40b54ce 100644 (file)
@@ -945,9 +945,6 @@ void __init early_reserve_e820_mpc_new(void)
 {
        if (enable_update_mptable && alloc_mptable) {
                u64 startt = 0;
-#ifdef CONFIG_X86_TRAMPOLINE
-               startt = TRAMPOLINE_BASE;
-#endif
                mpc_new_phys = early_reserve_e820(startt, mpc_new_length, 4);
        }
 }
index 5534499..572b07e 100644 (file)
@@ -172,11 +172,10 @@ static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg)
 
 static int msr_open(struct inode *inode, struct file *file)
 {
-       unsigned int cpu = iminor(file->f_path.dentry->d_inode);
-       struct cpuinfo_x86 *c = &cpu_data(cpu);
+       unsigned int cpu;
+       struct cpuinfo_x86 *c;
 
        cpu = iminor(file->f_path.dentry->d_inode);
-
        if (cpu >= nr_cpu_ids || !cpu_online(cpu))
                return -ENXIO;  /* No such CPU */
 
index afcc58b..75e14e2 100644 (file)
@@ -124,8 +124,8 @@ void __init pci_iommu_alloc(void)
        /* free the range so iommu could get some range less than 4G */
        dma32_free_bootmem();
 #endif
-       if (pci_swiotlb_init())
-               return;
+       if (pci_swiotlb_detect())
+               goto out;
 
        gart_iommu_hole_init();
 
@@ -135,6 +135,8 @@ void __init pci_iommu_alloc(void)
 
        /* needs to be called after gart_iommu_hole_init */
        amd_iommu_detect();
+out:
+       pci_swiotlb_init();
 }
 
 void *dma_generic_alloc_coherent(struct device *dev, size_t size,
index e6a0d40..56c0e73 100644 (file)
@@ -710,7 +710,8 @@ static void gart_iommu_shutdown(void)
        struct pci_dev *dev;
        int i;
 
-       if (no_agp)
+       /* don't shutdown it if there is AGP installed */
+       if (!no_agp)
                return;
 
        for (i = 0; i < num_k8_northbridges; i++) {
index e3c0a66..7d2829d 100644 (file)
@@ -43,12 +43,12 @@ static struct dma_map_ops swiotlb_dma_ops = {
 };
 
 /*
- * pci_swiotlb_init - initialize swiotlb if necessary
+ * pci_swiotlb_detect - set swiotlb to 1 if necessary
  *
  * This returns non-zero if we are forced to use swiotlb (by the boot
  * option).
  */
-int __init pci_swiotlb_init(void)
+int __init pci_swiotlb_detect(void)
 {
        int use_swiotlb = swiotlb | swiotlb_force;
 
@@ -60,10 +60,13 @@ int __init pci_swiotlb_init(void)
        if (swiotlb_force)
                swiotlb = 1;
 
+       return use_swiotlb;
+}
+
+void __init pci_swiotlb_init(void)
+{
        if (swiotlb) {
                swiotlb_init(0);
                dma_ops = &swiotlb_dma_ops;
        }
-
-       return use_swiotlb;
 }
index 8705cce..98c2cde 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/clockchips.h>
 #include <linux/random.h>
 #include <linux/user-return-notifier.h>
+#include <linux/dmi.h>
+#include <linux/utsname.h>
 #include <trace/events/power.h>
 #include <linux/hw_breakpoint.h>
 #include <asm/system.h>
@@ -90,6 +92,25 @@ void exit_thread(void)
        }
 }
 
+void show_regs_common(void)
+{
+       const char *board, *product;
+
+       board = dmi_get_system_info(DMI_BOARD_NAME);
+       if (!board)
+               board = "";
+       product = dmi_get_system_info(DMI_PRODUCT_NAME);
+       if (!product)
+               product = "";
+
+       printk("\n");
+       printk(KERN_INFO "Pid: %d, comm: %.20s %s %s %.*s %s/%s\n",
+               current->pid, current->comm, print_tainted(),
+               init_utsname()->release,
+               (int)strcspn(init_utsname()->version, " "),
+               init_utsname()->version, board, product);
+}
+
 void flush_thread(void)
 {
        struct task_struct *tsk = current;
index ccf2342..9c517b5 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/vmalloc.h>
 #include <linux/user.h>
 #include <linux/interrupt.h>
-#include <linux/utsname.h>
 #include <linux/delay.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
@@ -35,7 +34,6 @@
 #include <linux/tick.h>
 #include <linux/percpu.h>
 #include <linux/prctl.h>
-#include <linux/dmi.h>
 #include <linux/ftrace.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
@@ -128,7 +126,6 @@ void __show_regs(struct pt_regs *regs, int all)
        unsigned long d0, d1, d2, d3, d6, d7;
        unsigned long sp;
        unsigned short ss, gs;
-       const char *board;
 
        if (user_mode_vm(regs)) {
                sp = regs->sp;
@@ -140,16 +137,7 @@ void __show_regs(struct pt_regs *regs, int all)
                savesegment(gs, gs);
        }
 
-       printk("\n");
-
-       board = dmi_get_system_info(DMI_PRODUCT_NAME);
-       if (!board)
-               board = "";
-       printk("Pid: %d, comm: %s %s (%s %.*s) %s\n",
-                       task_pid_nr(current), current->comm,
-                       print_tainted(), init_utsname()->release,
-                       (int)strcspn(init_utsname()->version, " "),
-                       init_utsname()->version, board);
+       show_regs_common();
 
        printk("EIP: %04x:[<%08lx>] EFLAGS: %08lx CPU: %d\n",
                        (u16)regs->cs, regs->ip, regs->flags,
index 1a362c5..52fbd0c 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/slab.h>
 #include <linux/user.h>
 #include <linux/interrupt.h>
-#include <linux/utsname.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/ptrace.h>
@@ -38,7 +37,6 @@
 #include <linux/uaccess.h>
 #include <linux/io.h>
 #include <linux/ftrace.h>
-#include <linux/dmi.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -161,18 +159,8 @@ void __show_regs(struct pt_regs *regs, int all)
        unsigned long d0, d1, d2, d3, d6, d7;
        unsigned int fsindex, gsindex;
        unsigned int ds, cs, es;
-       const char *board;
-
-       printk("\n");
-       print_modules();
-       board = dmi_get_system_info(DMI_PRODUCT_NAME);
-       if (!board)
-               board = "";
-       printk(KERN_INFO "Pid: %d, comm: %.20s %s %s %.*s %s\n",
-               current->pid, current->comm, print_tainted(),
-               init_utsname()->release,
-               (int)strcspn(init_utsname()->version, " "),
-               init_utsname()->version, board);
+
+       show_regs_common();
        printk(KERN_INFO "RIP: %04lx:[<%016lx>] ", regs->cs & 0xffff, regs->ip);
        printk_address(regs->ip, 1);
        printk(KERN_INFO "RSP: %04lx:%016lx  EFLAGS: %08lx\n", regs->ss,
index 2b97fc5..1545bc0 100644 (file)
@@ -259,6 +259,14 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"),
                },
        },
+       {       /* Handle problems with rebooting on ASUS P4S800 */
+               .callback = set_bios_reboot,
+               .ident = "ASUS P4S800",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
+                       DMI_MATCH(DMI_BOARD_NAME, "P4S800"),
+               },
+       },
        { }
 };
 
index 946a311..f7b8b98 100644 (file)
@@ -73,6 +73,7 @@
 
 #include <asm/mtrr.h>
 #include <asm/apic.h>
+#include <asm/trampoline.h>
 #include <asm/e820.h>
 #include <asm/mpspec.h>
 #include <asm/setup.h>
@@ -875,6 +876,13 @@ void __init setup_arch(char **cmdline_p)
 
        reserve_brk();
 
+       /*
+        * Find and reserve possible boot-time SMP configuration:
+        */
+       find_smp_config();
+
+       reserve_trampoline_memory();
+
 #ifdef CONFIG_ACPI_SLEEP
        /*
         * Reserve low memory region for sleep support.
@@ -921,11 +929,6 @@ void __init setup_arch(char **cmdline_p)
 
        early_acpi_boot_init();
 
-       /*
-        * Find and reserve possible boot-time SMP configuration:
-        */
-       find_smp_config();
-
 #ifdef CONFIG_ACPI_NUMA
        /*
         * Parse SRAT to discover nodes.
index d559af9..35abcb8 100644 (file)
@@ -1,3 +1,5 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -20,9 +22,9 @@
 #include <asm/stackprotector.h>
 
 #ifdef CONFIG_DEBUG_PER_CPU_MAPS
-# define DBG(x...) printk(KERN_DEBUG x)
+# define DBG(fmt, ...) pr_dbg(fmt, ##__VA_ARGS__)
 #else
-# define DBG(x...)
+# define DBG(fmt, ...) do { if (0) pr_dbg(fmt, ##__VA_ARGS__); } while (0)
 #endif
 
 DEFINE_PER_CPU(int, cpu_number);
@@ -116,8 +118,8 @@ static void * __init pcpu_alloc_bootmem(unsigned int cpu, unsigned long size,
        } else {
                ptr = __alloc_bootmem_node_nopanic(NODE_DATA(node),
                                                   size, align, goal);
-               pr_debug("per cpu data for cpu%d %lu bytes on node%d at "
-                        "%016lx\n", cpu, size, node, __pa(ptr));
+               pr_debug("per cpu data for cpu%d %lu bytes on node%d at %016lx\n",
+                        cpu, size, node, __pa(ptr));
        }
        return ptr;
 #else
@@ -198,8 +200,7 @@ void __init setup_per_cpu_areas(void)
                                            pcpu_cpu_distance,
                                            pcpu_fc_alloc, pcpu_fc_free);
                if (rc < 0)
-                       pr_warning("PERCPU: %s allocator failed (%d), "
-                                  "falling back to page size\n",
+                       pr_warning("%s allocator failed (%d), falling back to page size\n",
                                   pcpu_fc_names[pcpu_chosen_fc], rc);
        }
        if (rc < 0)
index 324f2a4..678d0b8 100644 (file)
@@ -671,6 +671,26 @@ static void __cpuinit do_fork_idle(struct work_struct *work)
        complete(&c_idle->done);
 }
 
+/* reduce the number of lines printed when booting a large cpu count system */
+static void __cpuinit announce_cpu(int cpu, int apicid)
+{
+       static int current_node = -1;
+       int node = cpu_to_node(cpu);
+
+       if (system_state == SYSTEM_BOOTING) {
+               if (node != current_node) {
+                       if (current_node > (-1))
+                               pr_cont(" Ok.\n");
+                       current_node = node;
+                       pr_info("Booting Node %3d, Processors ", node);
+               }
+               pr_cont(" #%d%s", cpu, cpu == (nr_cpu_ids - 1) ? " Ok.\n" : "");
+               return;
+       } else
+               pr_info("Booting Node %d Processor %d APIC 0x%x\n",
+                       node, cpu, apicid);
+}
+
 /*
  * NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
  * (ie clustered apic addressing mode), this is a LOGICAL apic ID.
@@ -687,7 +707,7 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu)
                .done   = COMPLETION_INITIALIZER_ONSTACK(c_idle.done),
        };
 
-       INIT_WORK(&c_idle.work, do_fork_idle);
+       INIT_WORK_ON_STACK(&c_idle.work, do_fork_idle);
 
        alternatives_smp_switch(1);
 
@@ -713,6 +733,7 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu)
 
        if (IS_ERR(c_idle.idle)) {
                printk("failed fork for CPU %d\n", cpu);
+               destroy_work_on_stack(&c_idle.work);
                return PTR_ERR(c_idle.idle);
        }
 
@@ -736,9 +757,8 @@ do_rest:
        /* start_ip had better be page-aligned! */
        start_ip = setup_trampoline();
 
-       /* So we see what's up   */
-       printk(KERN_INFO "Booting processor %d APIC 0x%x ip 0x%lx\n",
-                         cpu, apicid, start_ip);
+       /* So we see what's up */
+       announce_cpu(cpu, apicid);
 
        /*
         * This grunge runs the startup process for
@@ -787,21 +807,17 @@ do_rest:
                        udelay(100);
                }
 
-               if (cpumask_test_cpu(cpu, cpu_callin_mask)) {
-                       /* number CPUs logically, starting from 1 (BSP is 0) */
-                       pr_debug("OK.\n");
-                       printk(KERN_INFO "CPU%d: ", cpu);
-                       print_cpu_info(&cpu_data(cpu));
-                       pr_debug("CPU has booted.\n");
-               } else {
+               if (cpumask_test_cpu(cpu, cpu_callin_mask))
+                       pr_debug("CPU%d: has booted.\n", cpu);
+               else {
                        boot_error = 1;
                        if (*((volatile unsigned char *)trampoline_base)
                                        == 0xA5)
                                /* trampoline started but...? */
-                               printk(KERN_ERR "Stuck ??\n");
+                               pr_err("CPU%d: Stuck ??\n", cpu);
                        else
                                /* trampoline code not run */
-                               printk(KERN_ERR "Not responding.\n");
+                               pr_err("CPU%d: Not responding.\n", cpu);
                        if (apic->inquire_remote_apic)
                                apic->inquire_remote_apic(apicid);
                }
@@ -831,6 +847,7 @@ do_rest:
                smpboot_restore_warm_reset_vector();
        }
 
+       destroy_work_on_stack(&c_idle.work);
        return boot_error;
 }
 
@@ -1291,14 +1308,16 @@ void native_cpu_die(unsigned int cpu)
        for (i = 0; i < 10; i++) {
                /* They ack this in play_dead by setting CPU_DEAD */
                if (per_cpu(cpu_state, cpu) == CPU_DEAD) {
-                       printk(KERN_INFO "CPU %d is now offline\n", cpu);
+                       if (system_state == SYSTEM_RUNNING)
+                               pr_info("CPU %u is now offline\n", cpu);
+
                        if (1 == num_online_cpus())
                                alternatives_smp_switch(0);
                        return;
                }
                msleep(100);
        }
-       printk(KERN_ERR "CPU %u didn't die...\n", cpu);
+       pr_err("CPU %u didn't die...\n", cpu);
 }
 
 void play_dead_common(void)
index cd02212..c652ef6 100644 (file)
 #endif
 
 /* ready for x86_64 and x86 */
-unsigned char *__trampinitdata trampoline_base = __va(TRAMPOLINE_BASE);
+unsigned char *__trampinitdata trampoline_base;
 
 void __init reserve_trampoline_memory(void)
 {
-#ifdef CONFIG_X86_32
-       /*
-        * But first pinch a few for the stack/trampoline stuff
-        * FIXME: Don't need the extra page at 4K, but need to fix
-        * trampoline before removing it. (see the GDT stuff)
-        */
-       reserve_early(PAGE_SIZE, PAGE_SIZE + PAGE_SIZE, "EX TRAMPOLINE");
-#endif
+       unsigned long mem;
+
        /* Has to be in very low memory so we can execute real-mode AP code. */
-       reserve_early(TRAMPOLINE_BASE, TRAMPOLINE_BASE + TRAMPOLINE_SIZE,
-                       "TRAMPOLINE");
+       mem = find_e820_area(0, 1<<20, TRAMPOLINE_SIZE, PAGE_SIZE);
+       if (mem == -1L)
+               panic("Cannot allocate trampoline\n");
+
+       trampoline_base = __va(mem);
+       reserve_early(mem, mem + TRAMPOLINE_SIZE, "TRAMPOLINE");
 }
 
 /*
index f3f2104..f92a0da 100644 (file)
@@ -319,9 +319,7 @@ SECTIONS
                __brk_limit = .;
        }
 
-       .end : AT(ADDR(.end) - LOAD_OFFSET) {
-               _end = .;
-       }
+       _end = .;
 
         STABS_DEBUG
         DWARF_DEBUG
index fab7440..296aba4 100644 (file)
@@ -29,6 +29,8 @@
  *   Based on QEMU and Xen.
  */
 
+#define pr_fmt(fmt) "pit: " fmt
+
 #include <linux/kvm_host.h>
 
 #include "irq.h"
@@ -262,7 +264,7 @@ void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
 
 static void destroy_pit_timer(struct kvm_timer *pt)
 {
-       pr_debug("pit: execute del timer!\n");
+       pr_debug("execute del timer!\n");
        hrtimer_cancel(&pt->timer);
 }
 
@@ -284,7 +286,7 @@ static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
 
        interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
 
-       pr_debug("pit: create pit timer, interval is %llu nsec\n", interval);
+       pr_debug("create pit timer, interval is %llu nsec\n", interval);
 
        /* TODO The new value only affected after the retriggered */
        hrtimer_cancel(&pt->timer);
@@ -309,7 +311,7 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
 
        WARN_ON(!mutex_is_locked(&ps->lock));
 
-       pr_debug("pit: load_count val is %d, channel is %d\n", val, channel);
+       pr_debug("load_count val is %d, channel is %d\n", val, channel);
 
        /*
         * The largest possible initial count is 0; this is equivalent
@@ -395,8 +397,8 @@ static int pit_ioport_write(struct kvm_io_device *this,
        mutex_lock(&pit_state->lock);
 
        if (val != 0)
-               pr_debug("pit: write addr is 0x%x, len is %d, val is 0x%x\n",
-                         (unsigned int)addr, len, val);
+               pr_debug("write addr is 0x%x, len is %d, val is 0x%x\n",
+                        (unsigned int)addr, len, val);
 
        if (addr == 3) {
                channel = val >> 6;
index 41628b1..8728341 100644 (file)
@@ -7,7 +7,6 @@ struct msr_info {
        u32 msr_no;
        struct msr reg;
        struct msr *msrs;
-       int off;
        int err;
 };
 
@@ -18,7 +17,7 @@ static void __rdmsr_on_cpu(void *info)
        int this_cpu = raw_smp_processor_id();
 
        if (rv->msrs)
-               reg = &rv->msrs[this_cpu - rv->off];
+               reg = per_cpu_ptr(rv->msrs, this_cpu);
        else
                reg = &rv->reg;
 
@@ -32,7 +31,7 @@ static void __wrmsr_on_cpu(void *info)
        int this_cpu = raw_smp_processor_id();
 
        if (rv->msrs)
-               reg = &rv->msrs[this_cpu - rv->off];
+               reg = per_cpu_ptr(rv->msrs, this_cpu);
        else
                reg = &rv->reg;
 
@@ -80,7 +79,6 @@ static void __rwmsr_on_cpus(const struct cpumask *mask, u32 msr_no,
 
        memset(&rv, 0, sizeof(rv));
 
-       rv.off    = cpumask_first(mask);
        rv.msrs   = msrs;
        rv.msr_no = msr_no;
 
@@ -120,6 +118,26 @@ void wrmsr_on_cpus(const struct cpumask *mask, u32 msr_no, struct msr *msrs)
 }
 EXPORT_SYMBOL(wrmsr_on_cpus);
 
+struct msr *msrs_alloc(void)
+{
+       struct msr *msrs = NULL;
+
+       msrs = alloc_percpu(struct msr);
+       if (!msrs) {
+               pr_warning("%s: error allocating msrs\n", __func__);
+               return NULL;
+       }
+
+       return msrs;
+}
+EXPORT_SYMBOL(msrs_alloc);
+
+void msrs_free(struct msr *msrs)
+{
+       free_percpu(msrs);
+}
+EXPORT_SYMBOL(msrs_free);
+
 /* These "safe" variants are slower and should be used when the target MSR
    may not actually exist. */
 static void __rdmsr_safe_on_cpu(void *info)
index 11a4ad4..c0f6198 100644 (file)
@@ -5,6 +5,8 @@
  *     2008 Pekka Paalanen <pq@iki.fi>
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/list.h>
 #include <linux/rculist.h>
 #include <linux/spinlock.h>
@@ -136,7 +138,7 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
        pte_t *pte = lookup_address(f->page, &level);
 
        if (!pte) {
-               pr_err("kmmio: no pte for page 0x%08lx\n", f->page);
+               pr_err("no pte for page 0x%08lx\n", f->page);
                return -1;
        }
 
@@ -148,7 +150,7 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
                clear_pte_presence(pte, clear, &f->old_presence);
                break;
        default:
-               pr_err("kmmio: unexpected page level 0x%x.\n", level);
+               pr_err("unexpected page level 0x%x.\n", level);
                return -1;
        }
 
@@ -170,13 +172,14 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
 static int arm_kmmio_fault_page(struct kmmio_fault_page *f)
 {
        int ret;
-       WARN_ONCE(f->armed, KERN_ERR "kmmio page already armed.\n");
+       WARN_ONCE(f->armed, KERN_ERR pr_fmt("kmmio page already armed.\n"));
        if (f->armed) {
-               pr_warning("kmmio double-arm: page 0x%08lx, ref %d, old %d\n",
-                                       f->page, f->count, !!f->old_presence);
+               pr_warning("double-arm: page 0x%08lx, ref %d, old %d\n",
+                          f->page, f->count, !!f->old_presence);
        }
        ret = clear_page_presence(f, true);
-       WARN_ONCE(ret < 0, KERN_ERR "kmmio arming 0x%08lx failed.\n", f->page);
+       WARN_ONCE(ret < 0, KERN_ERR pr_fmt("arming 0x%08lx failed.\n"),
+                 f->page);
        f->armed = true;
        return ret;
 }
@@ -203,7 +206,7 @@ static void disarm_kmmio_fault_page(struct kmmio_fault_page *f)
  */
 /*
  * Interrupts are disabled on entry as trap3 is an interrupt gate
- * and they remain disabled thorough out this function.
+ * and they remain disabled throughout this function.
  */
 int kmmio_handler(struct pt_regs *regs, unsigned long addr)
 {
@@ -240,24 +243,21 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
                         * condition needs handling by do_page_fault(), the
                         * page really not being present is the most common.
                         */
-                       pr_debug("kmmio: secondary hit for 0x%08lx CPU %d.\n",
-                                       addr, smp_processor_id());
+                       pr_debug("secondary hit for 0x%08lx CPU %d.\n",
+                                addr, smp_processor_id());
 
                        if (!faultpage->old_presence)
-                               pr_info("kmmio: unexpected secondary hit for "
-                                       "address 0x%08lx on CPU %d.\n", addr,
-                                       smp_processor_id());
+                               pr_info("unexpected secondary hit for address 0x%08lx on CPU %d.\n",
+                                       addr, smp_processor_id());
                } else {
                        /*
                         * Prevent overwriting already in-flight context.
                         * This should not happen, let's hope disarming at
                         * least prevents a panic.
                         */
-                       pr_emerg("kmmio: recursive probe hit on CPU %d, "
-                                       "for address 0x%08lx. Ignoring.\n",
-                                       smp_processor_id(), addr);
-                       pr_emerg("kmmio: previous hit was at 0x%08lx.\n",
-                                               ctx->addr);
+                       pr_emerg("recursive probe hit on CPU %d, for address 0x%08lx. Ignoring.\n",
+                                smp_processor_id(), addr);
+                       pr_emerg("previous hit was at 0x%08lx.\n", ctx->addr);
                        disarm_kmmio_fault_page(faultpage);
                }
                goto no_kmmio_ctx;
@@ -302,7 +302,7 @@ no_kmmio:
 
 /*
  * Interrupts are disabled on entry as trap1 is an interrupt gate
- * and they remain disabled thorough out this function.
+ * and they remain disabled throughout this function.
  * This must always get called as the pair to kmmio_handler().
  */
 static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs)
@@ -316,8 +316,8 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs)
                 * something external causing them (f.e. using a debugger while
                 * mmio tracing enabled), or erroneous behaviour
                 */
-               pr_warning("kmmio: unexpected debug trap on CPU %d.\n",
-                                                       smp_processor_id());
+               pr_warning("unexpected debug trap on CPU %d.\n",
+                          smp_processor_id());
                goto out;
        }
 
@@ -425,7 +425,7 @@ int register_kmmio_probe(struct kmmio_probe *p)
        list_add_rcu(&p->list, &kmmio_probes);
        while (size < size_lim) {
                if (add_kmmio_fault_page(p->addr + size))
-                       pr_err("kmmio: Unable to set page fault.\n");
+                       pr_err("Unable to set page fault.\n");
                size += PAGE_SIZE;
        }
 out:
@@ -490,7 +490,7 @@ static void remove_kmmio_fault_pages(struct rcu_head *head)
  * 2. remove_kmmio_fault_pages()
  *    Remove the pages from kmmio_page_table.
  * 3. rcu_free_kmmio_fault_pages()
- *    Actally free the kmmio_fault_page structs as with RCU.
+ *    Actually free the kmmio_fault_page structs as with RCU.
  */
 void unregister_kmmio_probe(struct kmmio_probe *p)
 {
@@ -511,7 +511,7 @@ void unregister_kmmio_probe(struct kmmio_probe *p)
 
        drelease = kmalloc(sizeof(*drelease), GFP_ATOMIC);
        if (!drelease) {
-               pr_crit("kmmio: leaking kmmio_fault_page objects.\n");
+               pr_crit("leaking kmmio_fault_page objects.\n");
                return;
        }
        drelease->release_list = release_list;
index 132772a..34a3291 100644 (file)
@@ -19,6 +19,9 @@
  *
  * Derived from the read-mod example from relay-examples by Tom Zanussi.
  */
+
+#define pr_fmt(fmt) "mmiotrace: " fmt
+
 #define DEBUG 1
 
 #include <linux/module.h>
@@ -36,8 +39,6 @@
 
 #include "pf_in.h"
 
-#define NAME "mmiotrace: "
-
 struct trap_reason {
        unsigned long addr;
        unsigned long ip;
@@ -96,17 +97,18 @@ static void print_pte(unsigned long address)
        pte_t *pte = lookup_address(address, &level);
 
        if (!pte) {
-               pr_err(NAME "Error in %s: no pte for page 0x%08lx\n",
-                                                       __func__, address);
+               pr_err("Error in %s: no pte for page 0x%08lx\n",
+                      __func__, address);
                return;
        }
 
        if (level == PG_LEVEL_2M) {
-               pr_emerg(NAME "4MB pages are not currently supported: "
-                                                       "0x%08lx\n", address);
+               pr_emerg("4MB pages are not currently supported: 0x%08lx\n",
+                        address);
                BUG();
        }
-       pr_info(NAME "pte for 0x%lx: 0x%llx 0x%llx\n", address,
+       pr_info("pte for 0x%lx: 0x%llx 0x%llx\n",
+               address,
                (unsigned long long)pte_val(*pte),
                (unsigned long long)pte_val(*pte) & _PAGE_PRESENT);
 }
@@ -118,22 +120,21 @@ static void print_pte(unsigned long address)
 static void die_kmmio_nesting_error(struct pt_regs *regs, unsigned long addr)
 {
        const struct trap_reason *my_reason = &get_cpu_var(pf_reason);
-       pr_emerg(NAME "unexpected fault for address: 0x%08lx, "
-                                       "last fault for address: 0x%08lx\n",
-                                       addr, my_reason->addr);
+       pr_emerg("unexpected fault for address: 0x%08lx, last fault for address: 0x%08lx\n",
+                addr, my_reason->addr);
        print_pte(addr);
        print_symbol(KERN_EMERG "faulting IP is at %s\n", regs->ip);
        print_symbol(KERN_EMERG "last faulting IP was at %s\n", my_reason->ip);
 #ifdef __i386__
        pr_emerg("eax: %08lx   ebx: %08lx   ecx: %08lx   edx: %08lx\n",
-                       regs->ax, regs->bx, regs->cx, regs->dx);
+                regs->ax, regs->bx, regs->cx, regs->dx);
        pr_emerg("esi: %08lx   edi: %08lx   ebp: %08lx   esp: %08lx\n",
-                       regs->si, regs->di, regs->bp, regs->sp);
+                regs->si, regs->di, regs->bp, regs->sp);
 #else
        pr_emerg("rax: %016lx   rcx: %016lx   rdx: %016lx\n",
-                                       regs->ax, regs->cx, regs->dx);
+                regs->ax, regs->cx, regs->dx);
        pr_emerg("rsi: %016lx   rdi: %016lx   rbp: %016lx   rsp: %016lx\n",
-                               regs->si, regs->di, regs->bp, regs->sp);
+                regs->si, regs->di, regs->bp, regs->sp);
 #endif
        put_cpu_var(pf_reason);
        BUG();
@@ -213,7 +214,7 @@ static void post(struct kmmio_probe *p, unsigned long condition,
        /* this should always return the active_trace count to 0 */
        my_reason->active_traces--;
        if (my_reason->active_traces) {
-               pr_emerg(NAME "unexpected post handler");
+               pr_emerg("unexpected post handler");
                BUG();
        }
 
@@ -244,7 +245,7 @@ static void ioremap_trace_core(resource_size_t offset, unsigned long size,
        };
 
        if (!trace) {
-               pr_err(NAME "kmalloc failed in ioremap\n");
+               pr_err("kmalloc failed in ioremap\n");
                return;
        }
 
@@ -282,8 +283,8 @@ void mmiotrace_ioremap(resource_size_t offset, unsigned long size,
        if (!is_enabled()) /* recheck and proper locking in *_core() */
                return;
 
-       pr_debug(NAME "ioremap_*(0x%llx, 0x%lx) = %p\n",
-                               (unsigned long long)offset, size, addr);
+       pr_debug("ioremap_*(0x%llx, 0x%lx) = %p\n",
+                (unsigned long long)offset, size, addr);
        if ((filter_offset) && (offset != filter_offset))
                return;
        ioremap_trace_core(offset, size, addr);
@@ -301,7 +302,7 @@ static void iounmap_trace_core(volatile void __iomem *addr)
        struct remap_trace *tmp;
        struct remap_trace *found_trace = NULL;
 
-       pr_debug(NAME "Unmapping %p.\n", addr);
+       pr_debug("Unmapping %p.\n", addr);
 
        spin_lock_irq(&trace_lock);
        if (!is_enabled())
@@ -363,9 +364,8 @@ static void clear_trace_list(void)
         * Caller also ensures is_enabled() cannot change.
         */
        list_for_each_entry(trace, &trace_list, list) {
-               pr_notice(NAME "purging non-iounmapped "
-                                       "trace @0x%08lx, size 0x%lx.\n",
-                                       trace->probe.addr, trace->probe.len);
+               pr_notice("purging non-iounmapped trace @0x%08lx, size 0x%lx.\n",
+                         trace->probe.addr, trace->probe.len);
                if (!nommiotrace)
                        unregister_kmmio_probe(&trace->probe);
        }
@@ -387,7 +387,7 @@ static void enter_uniprocessor(void)
 
        if (downed_cpus == NULL &&
            !alloc_cpumask_var(&downed_cpus, GFP_KERNEL)) {
-               pr_notice(NAME "Failed to allocate mask\n");
+               pr_notice("Failed to allocate mask\n");
                goto out;
        }
 
@@ -395,20 +395,19 @@ static void enter_uniprocessor(void)
        cpumask_copy(downed_cpus, cpu_online_mask);
        cpumask_clear_cpu(cpumask_first(cpu_online_mask), downed_cpus);
        if (num_online_cpus() > 1)
-               pr_notice(NAME "Disabling non-boot CPUs...\n");
+               pr_notice("Disabling non-boot CPUs...\n");
        put_online_cpus();
 
        for_each_cpu(cpu, downed_cpus) {
                err = cpu_down(cpu);
                if (!err)
-                       pr_info(NAME "CPU%d is down.\n", cpu);
+                       pr_info("CPU%d is down.\n", cpu);
                else
-                       pr_err(NAME "Error taking CPU%d down: %d\n", cpu, err);
+                       pr_err("Error taking CPU%d down: %d\n", cpu, err);
        }
 out:
        if (num_online_cpus() > 1)
-               pr_warning(NAME "multiple CPUs still online, "
-                                               "may miss events.\n");
+               pr_warning("multiple CPUs still online, may miss events.\n");
 }
 
 /* __ref because leave_uniprocessor calls cpu_up which is __cpuinit,
@@ -420,13 +419,13 @@ static void __ref leave_uniprocessor(void)
 
        if (downed_cpus == NULL || cpumask_weight(downed_cpus) == 0)
                return;
-       pr_notice(NAME "Re-enabling CPUs...\n");
+       pr_notice("Re-enabling CPUs...\n");
        for_each_cpu(cpu, downed_cpus) {
                err = cpu_up(cpu);
                if (!err)
-                       pr_info(NAME "enabled CPU%d.\n", cpu);
+                       pr_info("enabled CPU%d.\n", cpu);
                else
-                       pr_err(NAME "cannot re-enable CPU%d: %d\n", cpu, err);
+                       pr_err("cannot re-enable CPU%d: %d\n", cpu, err);
        }
 }
 
@@ -434,8 +433,8 @@ static void __ref leave_uniprocessor(void)
 static void enter_uniprocessor(void)
 {
        if (num_online_cpus() > 1)
-               pr_warning(NAME "multiple CPUs are online, may miss events. "
-                       "Suggest booting with maxcpus=1 kernel argument.\n");
+               pr_warning("multiple CPUs are online, may miss events. "
+                          "Suggest booting with maxcpus=1 kernel argument.\n");
 }
 
 static void leave_uniprocessor(void)
@@ -450,13 +449,13 @@ void enable_mmiotrace(void)
                goto out;
 
        if (nommiotrace)
-               pr_info(NAME "MMIO tracing disabled.\n");
+               pr_info("MMIO tracing disabled.\n");
        kmmio_init();
        enter_uniprocessor();
        spin_lock_irq(&trace_lock);
        atomic_inc(&mmiotrace_enabled);
        spin_unlock_irq(&trace_lock);
-       pr_info(NAME "enabled.\n");
+       pr_info("enabled.\n");
 out:
        mutex_unlock(&mmiotrace_mutex);
 }
@@ -475,7 +474,7 @@ void disable_mmiotrace(void)
        clear_trace_list(); /* guarantees: no more kmmio callbacks */
        leave_uniprocessor();
        kmmio_cleanup();
-       pr_info(NAME "disabled.\n");
+       pr_info("disabled.\n");
 out:
        mutex_unlock(&mmiotrace_mutex);
 }
index c462cea..b8e45f1 100644 (file)
@@ -138,24 +138,23 @@ static void xen_vcpu_setup(int cpu)
  */
 void xen_vcpu_restore(void)
 {
-       if (have_vcpu_info_placement) {
-               int cpu;
+       int cpu;
 
-               for_each_online_cpu(cpu) {
-                       bool other_cpu = (cpu != smp_processor_id());
+       for_each_online_cpu(cpu) {
+               bool other_cpu = (cpu != smp_processor_id());
 
-                       if (other_cpu &&
-                           HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL))
-                               BUG();
+               if (other_cpu &&
+                   HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL))
+                       BUG();
 
-                       xen_vcpu_setup(cpu);
+               xen_setup_runstate_info(cpu);
 
-                       if (other_cpu &&
-                           HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL))
-                               BUG();
-               }
+               if (have_vcpu_info_placement)
+                       xen_vcpu_setup(cpu);
 
-               BUG_ON(!have_vcpu_info_placement);
+               if (other_cpu &&
+                   HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL))
+                       BUG();
        }
 }
 
@@ -1180,6 +1179,8 @@ asmlinkage void __init xen_start_kernel(void)
 
        xen_raw_console_write("about to get started...\n");
 
+       xen_setup_runstate_info(0);
+
        /* Start the world */
 #ifdef CONFIG_X86_32
        i386_start_kernel();
index 3bf7b1d..bf4cd6b 100644 (file)
@@ -185,7 +185,7 @@ static inline unsigned p2m_index(unsigned long pfn)
 }
 
 /* Build the parallel p2m_top_mfn structures */
-static void __init xen_build_mfn_list_list(void)
+void xen_build_mfn_list_list(void)
 {
        unsigned pfn, idx;
 
index 738da0c..64757c0 100644 (file)
@@ -295,6 +295,7 @@ static int __cpuinit xen_cpu_up(unsigned int cpu)
                (unsigned long)task_stack_page(idle) -
                KERNEL_STACK_OFFSET + THREAD_SIZE;
 #endif
+       xen_setup_runstate_info(cpu);
        xen_setup_timer(cpu);
        xen_init_lock_cpu(cpu);
 
index 95be7b4..987267f 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/types.h>
+#include <linux/clockchips.h>
 
 #include <xen/interface/xen.h>
 #include <xen/grant_table.h>
@@ -27,6 +28,8 @@ void xen_pre_suspend(void)
 
 void xen_post_suspend(int suspend_cancelled)
 {
+       xen_build_mfn_list_list();
+
        xen_setup_shared_info();
 
        if (suspend_cancelled) {
@@ -44,7 +47,19 @@ void xen_post_suspend(int suspend_cancelled)
 
 }
 
+static void xen_vcpu_notify_restore(void *data)
+{
+       unsigned long reason = (unsigned long)data;
+
+       /* Boot processor notified via generic timekeeping_resume() */
+       if ( smp_processor_id() == 0)
+               return;
+
+       clockevents_notify(reason, NULL);
+}
+
 void xen_arch_resume(void)
 {
-       /* nothing */
+       smp_call_function(xen_vcpu_notify_restore,
+                              (void *)CLOCK_EVT_NOTIFY_RESUME, 1);
 }
index 0a5aa44..9d1f853 100644 (file)
@@ -100,7 +100,7 @@ bool xen_vcpu_stolen(int vcpu)
        return per_cpu(runstate, vcpu).state == RUNSTATE_runnable;
 }
 
-static void setup_runstate_info(int cpu)
+void xen_setup_runstate_info(int cpu)
 {
        struct vcpu_register_runstate_memory_area area;
 
@@ -434,7 +434,7 @@ void xen_setup_timer(int cpu)
                name = "<timer kasprintf failed>";
 
        irq = bind_virq_to_irqhandler(VIRQ_TIMER, cpu, xen_timer_interrupt,
-                                     IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING,
+                                     IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING|IRQF_TIMER,
                                      name, NULL);
 
        evt = &per_cpu(xen_clock_events, cpu);
@@ -442,8 +442,6 @@ void xen_setup_timer(int cpu)
 
        evt->cpumask = cpumask_of(cpu);
        evt->irq = irq;
-
-       setup_runstate_info(cpu);
 }
 
 void xen_teardown_timer(int cpu)
@@ -494,6 +492,7 @@ __init void xen_time_init(void)
 
        setup_force_cpu_cap(X86_FEATURE_TSC);
 
+       xen_setup_runstate_info(cpu);
        xen_setup_timer(cpu);
        xen_setup_cpu_clockevents();
 }
index 02f496a..53adefd 100644 (file)
@@ -96,7 +96,7 @@ ENTRY(xen_sysret32)
        pushq $__USER32_CS
        pushq %rcx
 
-       pushq $VGCF_in_syscall
+       pushq $0
 1:     jmp hypercall_iret
 ENDPATCH(xen_sysret32)
 RELOC(xen_sysret32, 1b+1)
@@ -151,7 +151,7 @@ ENTRY(xen_syscall32_target)
 ENTRY(xen_sysenter_target)
        lea 16(%rsp), %rsp      /* strip %rcx, %r11 */
        mov $-ENOSYS, %rax
-       pushq $VGCF_in_syscall
+       pushq $0
        jmp hypercall_iret
 ENDPROC(xen_syscall32_target)
 ENDPROC(xen_sysenter_target)
index 355fa6b..f9153a3 100644 (file)
@@ -25,6 +25,7 @@ extern struct shared_info *HYPERVISOR_shared_info;
 
 void xen_setup_mfn_list_list(void);
 void xen_setup_shared_info(void);
+void xen_build_mfn_list_list(void);
 void xen_setup_machphys_mapping(void);
 pgd_t *xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pfn);
 void xen_ident_map_ISA(void);
@@ -41,6 +42,7 @@ void __init xen_build_dynamic_phys_to_machine(void);
 
 void xen_init_irq_ops(void);
 void xen_setup_timer(int cpu);
+void xen_setup_runstate_info(int cpu);
 void xen_teardown_timer(int cpu);
 cycle_t xen_clocksource_read(void);
 void xen_setup_cpu_clockevents(void);
index ca56420..58916af 100644 (file)
@@ -28,7 +28,7 @@ static DEFINE_PER_CPU(struct list_head, blk_cpu_iopoll);
  * Description:
  *     Add this blk_iopoll structure to the pending poll list and trigger the
  *     raise of the blk iopoll softirq. The driver must already have gotten a
- *     succesful return from blk_iopoll_sched_prep() before calling this.
+ *     successful return from blk_iopoll_sched_prep() before calling this.
  **/
 void blk_iopoll_sched(struct blk_iopoll *iop)
 {
index 48bbdbe..26e434a 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/Kconfig
-
 menu "Device Drivers"
 
 source "drivers/base/Kconfig"
index 28ccdbc..3597d73 100644 (file)
@@ -537,7 +537,7 @@ static int __init acpi_memory_device_init(void)
 
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
                                     ACPI_UINT32_MAX,
-                                    acpi_memory_register_notify_handler,
+                                    acpi_memory_register_notify_handler, NULL,
                                     NULL, NULL);
 
        if (ACPI_FAILURE(status)) {
@@ -561,7 +561,7 @@ static void __exit acpi_memory_device_exit(void)
         */
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
                                     ACPI_UINT32_MAX,
-                                    acpi_memory_deregister_notify_handler,
+                                    acpi_memory_deregister_notify_handler, NULL,
                                     NULL, NULL);
 
        if (ACPI_FAILURE(status))
index e7973bc..7423052 100644 (file)
@@ -28,7 +28,7 @@ acpi-$(ACPI_FUTURE_USAGE) += hwtimer.o
 acpi-y += nsaccess.o  nsload.o    nssearch.o  nsxfeval.o \
         nsalloc.o   nseval.o    nsnames.o   nsutils.o   nsxfname.o \
         nsdump.o    nsinit.o    nsobject.o  nswalk.o    nsxfobj.o  \
-        nsparse.o   nspredef.o  nsrepair.o
+        nsparse.o   nspredef.o  nsrepair.o  nsrepair2.o
 
 acpi-$(ACPI_FUTURE_USAGE) += nsdumpdv.o
 
index 3acd9c6..7d9ba6e 100644 (file)
 #define ACPI_ERROR_NAMESPACE(s, e)      acpi_ns_report_error (AE_INFO, s, e);
 #define ACPI_ERROR_METHOD(s, n, p, e)   acpi_ns_report_method_error (AE_INFO, s, n, p, e);
 #define ACPI_WARN_PREDEFINED(plist)     acpi_ut_predefined_warning plist
+#define ACPI_INFO_PREDEFINED(plist)     acpi_ut_predefined_info plist
 
 #else
 
 #define ACPI_ERROR_NAMESPACE(s, e)
 #define ACPI_ERROR_METHOD(s, n, p, e)
 #define ACPI_WARN_PREDEFINED(plist)
+#define ACPI_INFO_PREDEFINED(plist)
+
 #endif         /* ACPI_NO_ERROR_MESSAGES */
 
 /*
index 09a2764..ab83919 100644 (file)
@@ -104,7 +104,8 @@ acpi_ns_walk_namespace(acpi_object_type type,
                       acpi_handle start_object,
                       u32 max_depth,
                       u32 flags,
-                      acpi_walk_callback user_function,
+                      acpi_walk_callback pre_order_visit,
+                      acpi_walk_callback post_order_visit,
                       void *context, void **return_value);
 
 struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node
@@ -272,7 +273,8 @@ acpi_ns_get_attached_data(struct acpi_namespace_node *node,
                          acpi_object_handler handler, void **data);
 
 /*
- * nsrepair - return object repair for predefined methods/objects
+ * nsrepair - General return object repair for all
+ * predefined methods/objects
  */
 acpi_status
 acpi_ns_repair_object(struct acpi_predefined_data *data,
@@ -284,6 +286,16 @@ acpi_status
 acpi_ns_repair_package_list(struct acpi_predefined_data *data,
                            union acpi_operand_object **obj_desc_ptr);
 
+/*
+ * nsrepair2 - Return object repair for specific
+ * predefined methods/objects
+ */
+acpi_status
+acpi_ns_complex_repairs(struct acpi_predefined_data *data,
+                       struct acpi_namespace_node *node,
+                       acpi_status validate_status,
+                       union acpi_operand_object **return_object_ptr);
+
 /*
  * nssearch - Namespace searching and entry
  */
index 863a264..3a451a2 100644 (file)
@@ -386,6 +386,8 @@ u8 acpi_ut_valid_internal_object(void *object);
 
 union acpi_operand_object *acpi_ut_create_package_object(u32 count);
 
+union acpi_operand_object *acpi_ut_create_integer_object(u64 value);
+
 union acpi_operand_object *acpi_ut_create_buffer_object(acpi_size buffer_size);
 
 union acpi_operand_object *acpi_ut_create_string_object(acpi_size string_size);
@@ -481,6 +483,11 @@ acpi_ut_predefined_warning(const char *module_name,
                           char *pathname,
                           u8 node_flags, const char *format, ...);
 
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+                       u32 line_number,
+                       char *pathname, u8 node_flags, const char *format, ...);
+
 /* Values for Base above (16=Hex, 10=Decimal) */
 
 #define ACPI_ANY_BASE        0
index 3aae13f..f23fa0b 100644 (file)
@@ -192,7 +192,7 @@ acpi_ds_initialize_objects(u32 table_index,
        status =
            acpi_ns_walk_namespace(ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX,
                                   ACPI_NS_WALK_UNLOCK, acpi_ds_init_one_object,
-                                  &info, NULL);
+                                  NULL, &info, NULL);
        if (ACPI_FAILURE(status)) {
                ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
        }
index 7d077bb..0ba19f8 100644 (file)
@@ -409,13 +409,11 @@ acpi_ds_method_data_get_value(u8 type,
                /* If slack enabled, init the local_x/arg_x to an Integer of value zero */
 
                if (acpi_gbl_enable_interpreter_slack) {
-                       object =
-                           acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+                       object = acpi_ut_create_integer_object((u64) 0);
                        if (!object) {
                                return_ACPI_STATUS(AE_NO_MEMORY);
                        }
 
-                       object->integer.value = 0;
                        node->object = object;
                }
 
index 507e1f0..9bc1ba0 100644 (file)
@@ -486,7 +486,7 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
                 *
                 * Note: technically, this is an error, from ACPI spec: "It is an error
                 * for NumElements to be less than the number of elements in the
-                * PackageList". However, we just print an error message and
+                * PackageList". However, we just print a message and
                 * no exception is returned. This provides Windows compatibility. Some
                 * BIOSs will alter the num_elements on the fly, creating this type
                 * of ill-formed package object.
@@ -510,9 +510,9 @@ acpi_ds_build_internal_package_obj(struct acpi_walk_state *walk_state,
                        arg = arg->common.next;
                }
 
-               ACPI_WARNING((AE_INFO,
-                           "Package List length (0x%X) larger than NumElements count (0x%X), truncated\n",
-                           i, element_count));
+               ACPI_INFO((AE_INFO,
+                          "Actual Package length (0x%X) is larger than NumElements field (0x%X), truncated\n",
+                          i, element_count));
        } else if (i < element_count) {
                /*
                 * Arg list (elements) was exhausted, but we did not reach num_elements count.
index 6de3a99..10fc785 100644 (file)
@@ -639,26 +639,42 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
                break;
 
        case AML_SCOPE_OP:
-               /*
-                * The Path is an object reference to an existing object.
-                * Don't enter the name into the namespace, but look it up
-                * for use later.
-                */
-               status =
-                   acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
-                                  object_type, ACPI_IMODE_EXECUTE,
-                                  ACPI_NS_SEARCH_PARENT, walk_state, &(node));
-               if (ACPI_FAILURE(status)) {
-#ifdef ACPI_ASL_COMPILER
-                       if (status == AE_NOT_FOUND) {
-                               status = AE_OK;
-                       } else {
-                               ACPI_ERROR_NAMESPACE(buffer_ptr, status);
+
+               /* Special case for Scope(\) -> refers to the Root node */
+
+               if (op && (op->named.node == acpi_gbl_root_node)) {
+                       node = op->named.node;
+
+                       status =
+                           acpi_ds_scope_stack_push(node, object_type,
+                                                    walk_state);
+                       if (ACPI_FAILURE(status)) {
+                               return_ACPI_STATUS(status);
                        }
+               } else {
+                       /*
+                        * The Path is an object reference to an existing object.
+                        * Don't enter the name into the namespace, but look it up
+                        * for use later.
+                        */
+                       status =
+                           acpi_ns_lookup(walk_state->scope_info, buffer_ptr,
+                                          object_type, ACPI_IMODE_EXECUTE,
+                                          ACPI_NS_SEARCH_PARENT, walk_state,
+                                          &(node));
+                       if (ACPI_FAILURE(status)) {
+#ifdef ACPI_ASL_COMPILER
+                               if (status == AE_NOT_FOUND) {
+                                       status = AE_OK;
+                               } else {
+                                       ACPI_ERROR_NAMESPACE(buffer_ptr,
+                                                            status);
+                               }
 #else
-                       ACPI_ERROR_NAMESPACE(buffer_ptr, status);
+                               ACPI_ERROR_NAMESPACE(buffer_ptr, status);
 #endif
-                       return_ACPI_STATUS(status);
+                               return_ACPI_STATUS(status);
+                       }
                }
 
                /*
index a60aaa7..2479209 100644 (file)
@@ -945,8 +945,8 @@ acpi_ev_create_gpe_block(struct acpi_namespace_node *gpe_device,
 
        status = acpi_ns_walk_namespace(ACPI_TYPE_METHOD, gpe_device,
                                        ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
-                                       acpi_ev_save_method_info, gpe_block,
-                                       NULL);
+                                       acpi_ev_save_method_info, NULL,
+                                       gpe_block, NULL);
 
        /* Return the new block */
 
@@ -1022,8 +1022,8 @@ acpi_ev_initialize_gpe_block(struct acpi_namespace_node *gpe_device,
                status =
                    acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
                                           ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
-                                          acpi_ev_match_prw_and_gpe, &gpe_info,
-                                          NULL);
+                                          acpi_ev_match_prw_and_gpe, NULL,
+                                          &gpe_info, NULL);
        }
 
        /*
index 98c7f9c..0bc807c 100644 (file)
 ACPI_MODULE_NAME("evregion")
 
 /* Local prototypes */
+static u8
+acpi_ev_has_default_handler(struct acpi_namespace_node *node,
+                           acpi_adr_space_type space_id);
+
 static acpi_status
 acpi_ev_reg_run(acpi_handle obj_handle,
                u32 level, void *context, void **return_value);
@@ -140,6 +144,50 @@ acpi_status acpi_ev_install_region_handlers(void)
        return_ACPI_STATUS(status);
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_has_default_handler
+ *
+ * PARAMETERS:  Node                - Namespace node for the device
+ *              space_id            - The address space ID
+ *
+ * RETURN:      TRUE if default handler is installed, FALSE otherwise
+ *
+ * DESCRIPTION: Check if the default handler is installed for the requested
+ *              space ID.
+ *
+ ******************************************************************************/
+
+static u8
+acpi_ev_has_default_handler(struct acpi_namespace_node *node,
+                           acpi_adr_space_type space_id)
+{
+       union acpi_operand_object *obj_desc;
+       union acpi_operand_object *handler_obj;
+
+       /* Must have an existing internal object */
+
+       obj_desc = acpi_ns_get_attached_object(node);
+       if (obj_desc) {
+               handler_obj = obj_desc->device.handler;
+
+               /* Walk the linked list of handlers for this object */
+
+               while (handler_obj) {
+                       if (handler_obj->address_space.space_id == space_id) {
+                               if (handler_obj->address_space.handler_flags &
+                                   ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) {
+                                       return (TRUE);
+                               }
+                       }
+
+                       handler_obj = handler_obj->address_space.next;
+               }
+       }
+
+       return (FALSE);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ev_initialize_op_regions
@@ -169,12 +217,18 @@ acpi_status acpi_ev_initialize_op_regions(void)
 
        for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) {
                /*
-                * TBD: Make sure handler is the DEFAULT handler, otherwise
-                * _REG will have already been run.
+                * Make sure the installed handler is the DEFAULT handler. If not the
+                * default, the _REG methods will have already been run (when the
+                * handler was installed)
                 */
-               status = acpi_ev_execute_reg_methods(acpi_gbl_root_node,
-                                                    acpi_gbl_default_address_spaces
-                                                    [i]);
+               if (acpi_ev_has_default_handler(acpi_gbl_root_node,
+                                               acpi_gbl_default_address_spaces
+                                               [i])) {
+                       status =
+                           acpi_ev_execute_reg_methods(acpi_gbl_root_node,
+                                                       acpi_gbl_default_address_spaces
+                                                       [i]);
+               }
        }
 
        (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
@@ -235,23 +289,20 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)
         *  connection status 1 for connecting the handler, 0 for disconnecting
         *  the handler (Passed as a parameter)
         */
-       args[0] = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+       args[0] =
+           acpi_ut_create_integer_object((u64) region_obj->region.space_id);
        if (!args[0]) {
                status = AE_NO_MEMORY;
                goto cleanup1;
        }
 
-       args[1] = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+       args[1] = acpi_ut_create_integer_object((u64) function);
        if (!args[1]) {
                status = AE_NO_MEMORY;
                goto cleanup2;
        }
 
-       /* Setup the parameter objects */
-
-       args[0]->integer.value = region_obj->region.space_id;
-       args[1]->integer.value = function;
-       args[2] = NULL;
+       args[2] = NULL;         /* Terminate list */
 
        /* Execute the method, no return value */
 
@@ -971,8 +1022,8 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
         */
        status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
                                        ACPI_NS_WALK_UNLOCK,
-                                       acpi_ev_install_handler, handler_obj,
-                                       NULL);
+                                       acpi_ev_install_handler, NULL,
+                                       handler_obj, NULL);
 
       unlock_and_exit:
        return_ACPI_STATUS(status);
@@ -1008,7 +1059,7 @@ acpi_ev_execute_reg_methods(struct acpi_namespace_node *node,
         */
        status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, node, ACPI_UINT32_MAX,
                                        ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run,
-                                       &space_id, NULL);
+                                       NULL, &space_id, NULL);
 
        return_ACPI_STATUS(status);
 }
index 24afef8..46adfa5 100644 (file)
@@ -170,14 +170,12 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
 
                /* Table not found, return an Integer=0 and AE_OK */
 
-               ddb_handle = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               ddb_handle = acpi_ut_create_integer_object((u64) 0);
                if (!ddb_handle) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
-               ddb_handle->integer.value = 0;
                *return_desc = ddb_handle;
-
                return_ACPI_STATUS(AE_OK);
        }
 
index 37d0d39..51d5f22 100644 (file)
@@ -167,7 +167,7 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
 
        /* Create a new integer */
 
-       return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+       return_desc = acpi_ut_create_integer_object(result);
        if (!return_desc) {
                return_ACPI_STATUS(AE_NO_MEMORY);
        }
@@ -177,7 +177,6 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
 
        /* Save the Result */
 
-       return_desc->integer.value = result;
        acpi_ex_truncate_for32bit_table(return_desc);
        *result_desc = return_desc;
        return_ACPI_STATUS(AE_OK);
index 0b33d6c..1588a2d 100644 (file)
@@ -162,13 +162,12 @@ acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
        } else {
                /* Field will fit within an Integer (normal case) */
 
-               buffer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               buffer_desc = acpi_ut_create_integer_object((u64) 0);
                if (!buffer_desc) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
                length = acpi_gbl_integer_byte_width;
-               buffer_desc->integer.value = 0;
                buffer = &buffer_desc->integer.value;
        }
 
index 9635d21..752fe48 100644 (file)
@@ -100,12 +100,12 @@ acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state)
 
                /* Create a return object of type Integer */
 
-               return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               return_desc =
+                   acpi_ut_create_integer_object(acpi_os_get_timer());
                if (!return_desc) {
                        status = AE_NO_MEMORY;
                        goto cleanup;
                }
-               return_desc->integer.value = acpi_os_get_timer();
                break;
 
        default:                /*  Unknown opcode  */
@@ -599,7 +599,7 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
        switch (walk_state->opcode) {
        case AML_LNOT_OP:       /* LNot (Operand) */
 
-               return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               return_desc = acpi_ut_create_integer_object((u64) 0);
                if (!return_desc) {
                        status = AE_NO_MEMORY;
                        goto cleanup;
@@ -702,13 +702,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
 
                /* Allocate a descriptor to hold the type. */
 
-               return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               return_desc = acpi_ut_create_integer_object((u64) type);
                if (!return_desc) {
                        status = AE_NO_MEMORY;
                        goto cleanup;
                }
-
-               return_desc->integer.value = type;
                break;
 
        case AML_SIZE_OF_OP:    /* size_of (source_object) */
@@ -777,13 +775,11 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
                 * Now that we have the size of the object, create a result
                 * object to hold the value
                 */
-               return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               return_desc = acpi_ut_create_integer_object(value);
                if (!return_desc) {
                        status = AE_NO_MEMORY;
                        goto cleanup;
                }
-
-               return_desc->integer.value = value;
                break;
 
        case AML_REF_OF_OP:     /* ref_of (source_object) */
@@ -946,24 +942,24 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
                                         * NOTE: index into a buffer is NOT a pointer to a
                                         * sub-buffer of the main buffer, it is only a pointer to a
                                         * single element (byte) of the buffer!
+                                        *
+                                        * Since we are returning the value of the buffer at the
+                                        * indexed location, we don't need to add an additional
+                                        * reference to the buffer itself.
                                         */
                                        return_desc =
-                                           acpi_ut_create_internal_object
-                                           (ACPI_TYPE_INTEGER);
+                                           acpi_ut_create_integer_object((u64)
+                                                                         temp_desc->
+                                                                         buffer.
+                                                                         pointer
+                                                                         [operand
+                                                                          [0]->
+                                                                          reference.
+                                                                          value]);
                                        if (!return_desc) {
                                                status = AE_NO_MEMORY;
                                                goto cleanup;
                                        }
-
-                                       /*
-                                        * Since we are returning the value of the buffer at the
-                                        * indexed location, we don't need to add an additional
-                                        * reference to the buffer itself.
-                                        */
-                                       return_desc->integer.value =
-                                           temp_desc->buffer.
-                                           pointer[operand[0]->reference.
-                                                   value];
                                        break;
 
                                case ACPI_TYPE_PACKAGE:
index ae43f76..295542e 100644 (file)
@@ -253,18 +253,15 @@ acpi_status acpi_ex_opcode_6A_0T_1R(struct acpi_walk_state * walk_state)
                }
 
                /* Create an integer for the return value */
+               /* Default return value is ACPI_INTEGER_MAX if no match found */
 
-               return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+               return_desc = acpi_ut_create_integer_object(ACPI_INTEGER_MAX);
                if (!return_desc) {
                        status = AE_NO_MEMORY;
                        goto cleanup;
 
                }
 
-               /* Default return value if no match found */
-
-               return_desc->integer.value = ACPI_INTEGER_MAX;
-
                /*
                 * Examine each element until a match is found. Both match conditions
                 * must be satisfied for a match to occur. Within the loop,
index 2bad613..2deb986 100644 (file)
@@ -634,8 +634,8 @@ acpi_ns_dump_objects(acpi_object_type type,
        (void)acpi_ns_walk_namespace(type, start_handle, max_depth,
                                     ACPI_NS_WALK_NO_UNLOCK |
                                     ACPI_NS_WALK_TEMP_NODES,
-                                    acpi_ns_dump_one_object, (void *)&info,
-                                    NULL);
+                                    acpi_ns_dump_one_object, NULL,
+                                    (void *)&info, NULL);
 }
 #endif                         /* ACPI_FUTURE_USAGE */
 
index 0fe87f1..36be7f0 100644 (file)
@@ -131,7 +131,8 @@ void acpi_ns_dump_root_devices(void)
 
        status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, sys_bus_handle,
                                        ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK,
-                                       acpi_ns_dump_one_device, NULL, NULL);
+                                       acpi_ns_dump_one_device, NULL, NULL,
+                                       NULL);
 }
 
 #endif
index 846d113..f771e97 100644 (file)
@@ -366,33 +366,49 @@ static void
 acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
                         struct acpi_evaluate_info *info)
 {
-       union acpi_operand_object *root_obj;
+       union acpi_operand_object *parent_obj;
+       struct acpi_namespace_node *parent_node;
+       acpi_object_type type;
        acpi_status status;
 
        ACPI_FUNCTION_TRACE(ns_exec_module_code);
 
+       /*
+        * Get the parent node. We cheat by using the next_object field
+        * of the method object descriptor.
+        */
+       parent_node = ACPI_CAST_PTR(struct acpi_namespace_node,
+                                   method_obj->method.next_object);
+       type = acpi_ns_get_type(parent_node);
+
+       /* Must clear next_object (acpi_ns_attach_object needs the field) */
+
+       method_obj->method.next_object = NULL;
+
        /* Initialize the evaluation information block */
 
        ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info));
-       info->prefix_node = acpi_gbl_root_node;
+       info->prefix_node = parent_node;
 
        /*
-        * Get the currently attached root object. Add a reference, because the
+        * Get the currently attached parent object. Add a reference, because the
         * ref count will be decreased when the method object is installed to
-        * the root node.
+        * the parent node.
         */
-       root_obj = acpi_ns_get_attached_object(acpi_gbl_root_node);
-       acpi_ut_add_reference(root_obj);
+       parent_obj = acpi_ns_get_attached_object(parent_node);
+       if (parent_obj) {
+               acpi_ut_add_reference(parent_obj);
+       }
 
-       /* Install the method (module-level code) in the root node */
+       /* Install the method (module-level code) in the parent node */
 
-       status = acpi_ns_attach_object(acpi_gbl_root_node, method_obj,
+       status = acpi_ns_attach_object(parent_node, method_obj,
                                       ACPI_TYPE_METHOD);
        if (ACPI_FAILURE(status)) {
                goto exit;
        }
 
-       /* Execute the root node as a control method */
+       /* Execute the parent node as a control method */
 
        status = acpi_ns_evaluate(info);
 
@@ -401,15 +417,19 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
 
        /* Detach the temporary method object */
 
-       acpi_ns_detach_object(acpi_gbl_root_node);
+       acpi_ns_detach_object(parent_node);
 
-       /* Restore the original root object */
+       /* Restore the original parent object */
 
-       status =
-           acpi_ns_attach_object(acpi_gbl_root_node, root_obj,
-                                 ACPI_TYPE_DEVICE);
+       if (parent_obj) {
+               status = acpi_ns_attach_object(parent_node, parent_obj, type);
+       } else {
+               parent_node->type = (u8)type;
+       }
 
       exit:
-       acpi_ut_remove_reference(root_obj);
+       if (parent_obj) {
+               acpi_ut_remove_reference(parent_obj);
+       }
        return_VOID;
 }
index 1d5b360..4f8abac 100644 (file)
@@ -96,7 +96,7 @@ acpi_status acpi_ns_initialize_objects(void)
        /* Walk entire namespace from the supplied root */
 
        status = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
-                                    ACPI_UINT32_MAX, acpi_ns_init_one_object,
+                                    ACPI_UINT32_MAX, acpi_ns_init_one_object, NULL,
                                     &info, NULL);
        if (ACPI_FAILURE(status)) {
                ACPI_EXCEPTION((AE_INFO, status, "During WalkNamespace"));
@@ -156,7 +156,8 @@ acpi_status acpi_ns_initialize_devices(void)
 
        status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
                                        ACPI_UINT32_MAX, FALSE,
-                                       acpi_ns_find_ini_methods, &info, NULL);
+                                       acpi_ns_find_ini_methods, NULL, &info,
+                                       NULL);
        if (ACPI_FAILURE(status)) {
                goto error_exit;
        }
@@ -189,7 +190,8 @@ acpi_status acpi_ns_initialize_devices(void)
 
        status = acpi_ns_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
                                        ACPI_UINT32_MAX, FALSE,
-                                       acpi_ns_init_one_device, &info, NULL);
+                                       acpi_ns_init_one_device, NULL, &info,
+                                       NULL);
 
        ACPI_FREE(info.evaluate_info);
        if (ACPI_FAILURE(status)) {
index f8427af..b05f429 100644 (file)
@@ -232,6 +232,12 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
                status = acpi_ns_check_package(data, return_object_ptr);
        }
 
+       /*
+        * Perform additional, more complicated repairs on a per-name
+        * basis.
+        */
+       status = acpi_ns_complex_repairs(data, node, status, return_object_ptr);
+
 check_validation_status:
        /*
         * If the object validation failed or if we successfully repaired one
@@ -601,7 +607,8 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
                 * there is only one entry). We may be able to repair this by
                 * wrapping the returned Package with a new outer Package.
                 */
-               if ((*elements)->common.type != ACPI_TYPE_PACKAGE) {
+               if (*elements
+                   && ((*elements)->common.type != ACPI_TYPE_PACKAGE)) {
 
                        /* Create the new outer package and populate it */
 
@@ -673,6 +680,7 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data,
        union acpi_operand_object *sub_package;
        union acpi_operand_object **sub_elements;
        acpi_status status;
+       u8 non_trailing_null = FALSE;
        u32 expected_count;
        u32 i;
        u32 j;
@@ -680,6 +688,45 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data,
        /* Validate each sub-Package in the parent Package */
 
        for (i = 0; i < count; i++) {
+               /*
+                * Handling for NULL package elements. For now, we will simply allow
+                * a parent package with trailing NULL elements. This can happen if
+                * the package was defined to be longer than the initializer list.
+                * This is legal as per the ACPI specification. It is often used
+                * to allow for dynamic initialization of a Package.
+                *
+                * A future enhancement may be to simply truncate the package to
+                * remove the trailing NULL elements.
+                */
+               if (!(*elements)) {
+                       if (!non_trailing_null) {
+
+                               /* Ensure the remaining elements are all NULL */
+
+                               for (j = 1; j < (count - i + 1); j++) {
+                                       if (elements[j]) {
+                                               non_trailing_null = TRUE;
+                                       }
+                               }
+
+                               if (!non_trailing_null) {
+
+                                       /* Ignore the trailing NULL elements */
+
+                                       return (AE_OK);
+                               }
+                       }
+
+                       /* There are trailing non-null elements, issue warning */
+
+                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
+                                             data->node_flags,
+                                             "Found NULL element at package index %u",
+                                             i));
+                       elements++;
+                       continue;
+               }
+
                sub_package = *elements;
                sub_elements = sub_package->package.elements;
 
index db2b2a9..d563f1a 100644 (file)
@@ -44,6 +44,7 @@
 #include <acpi/acpi.h>
 #include "accommon.h"
 #include "acnamesp.h"
+#include "acinterp.h"
 #include "acpredef.h"
 
 #define _COMPONENT          ACPI_NAMESPACE
@@ -76,7 +77,13 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
        union acpi_operand_object *return_object = *return_object_ptr;
        union acpi_operand_object *new_object;
        acpi_size length;
+       acpi_status status;
 
+       /*
+        * At this point, we know that the type of the returned object was not
+        * one of the expected types for this predefined name. Attempt to
+        * repair the object. Only a limited number of repairs are possible.
+        */
        switch (return_object->common.type) {
        case ACPI_TYPE_BUFFER:
 
@@ -111,43 +118,94 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
                 */
                ACPI_MEMCPY(new_object->string.pointer,
                            return_object->buffer.pointer, length);
+               break;
 
-               /*
-                * If the original object is a package element, we need to:
-                * 1. Set the reference count of the new object to match the
-                *    reference count of the old object.
-                * 2. Decrement the reference count of the original object.
-                */
-               if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
-                       new_object->common.reference_count =
-                           return_object->common.reference_count;
+       case ACPI_TYPE_INTEGER:
+
+               /* 1) Does the method/object legally return a buffer? */
+
+               if (expected_btypes & ACPI_RTYPE_BUFFER) {
+                       /*
+                        * Convert the Integer to a packed-byte buffer. _MAT needs
+                        * this sometimes, if a read has been performed on a Field
+                        * object that is less than or equal to the global integer
+                        * size (32 or 64 bits).
+                        */
+                       status =
+                           acpi_ex_convert_to_buffer(return_object,
+                                                     &new_object);
+                       if (ACPI_FAILURE(status)) {
+                               return (status);
+                       }
+               }
 
-                       if (return_object->common.reference_count > 1) {
-                               return_object->common.reference_count--;
+               /* 2) Does the method/object legally return a string? */
+
+               else if (expected_btypes & ACPI_RTYPE_STRING) {
+                       /*
+                        * The only supported Integer-to-String conversion is to convert
+                        * an integer of value 0 to a NULL string. The last element of
+                        * _BIF and _BIX packages occasionally need this fix.
+                        */
+                       if (return_object->integer.value != 0) {
+                               return (AE_AML_OPERAND_TYPE);
                        }
 
-                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
-                                             data->node_flags,
-                                             "Converted Buffer to expected String at index %u",
-                                             package_index));
+                       /* Allocate a new NULL string object */
+
+                       new_object = acpi_ut_create_string_object(0);
+                       if (!new_object) {
+                               return (AE_NO_MEMORY);
+                       }
                } else {
-                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
-                                             data->node_flags,
-                                             "Converted Buffer to expected String"));
+                       return (AE_AML_OPERAND_TYPE);
                }
+               break;
 
-               /* Delete old object, install the new return object */
+       default:
 
-               acpi_ut_remove_reference(return_object);
-               *return_object_ptr = new_object;
-               data->flags |= ACPI_OBJECT_REPAIRED;
-               return (AE_OK);
+               /* We cannot repair this object */
 
-       default:
-               break;
+               return (AE_AML_OPERAND_TYPE);
+       }
+
+       /* Object was successfully repaired */
+
+       /*
+        * If the original object is a package element, we need to:
+        * 1. Set the reference count of the new object to match the
+        *    reference count of the old object.
+        * 2. Decrement the reference count of the original object.
+        */
+       if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
+               new_object->common.reference_count =
+                   return_object->common.reference_count;
+
+               if (return_object->common.reference_count > 1) {
+                       return_object->common.reference_count--;
+               }
+
+               ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+                                     "Converted %s to expected %s at index %u",
+                                     acpi_ut_get_object_type_name
+                                     (return_object),
+                                     acpi_ut_get_object_type_name(new_object),
+                                     package_index));
+       } else {
+               ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+                                     "Converted %s to expected %s",
+                                     acpi_ut_get_object_type_name
+                                     (return_object),
+                                     acpi_ut_get_object_type_name
+                                     (new_object)));
        }
 
-       return (AE_AML_OPERAND_TYPE);
+       /* Delete old object, install the new return object */
+
+       acpi_ut_remove_reference(return_object);
+       *return_object_ptr = new_object;
+       data->flags |= ACPI_OBJECT_REPAIRED;
+       return (AE_OK);
 }
 
 /*******************************************************************************
@@ -196,8 +254,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
        *obj_desc_ptr = pkg_obj_desc;
        data->flags |= ACPI_OBJECT_REPAIRED;
 
-       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
-                             "Incorrectly formed Package, attempting repair"));
+       ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+                             "Repaired Incorrectly formed Package"));
 
        return (AE_OK);
 }
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
new file mode 100644 (file)
index 0000000..d07b686
--- /dev/null
@@ -0,0 +1,540 @@
+/******************************************************************************
+ *
+ * Module Name: nsrepair2 - Repair for objects returned by specific
+ *                          predefined methods
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 - 2009, Intel Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <acpi/acpi.h>
+#include "accommon.h"
+#include "acnamesp.h"
+
+#define _COMPONENT          ACPI_NAMESPACE
+ACPI_MODULE_NAME("nsrepair2")
+
+/*
+ * Information structure and handler for ACPI predefined names that can
+ * be repaired on a per-name basis.
+ */
+typedef
+acpi_status(*acpi_repair_function) (struct acpi_predefined_data *data,
+                                   union acpi_operand_object **return_object_ptr);
+
+typedef struct acpi_repair_info {
+       char name[ACPI_NAME_SIZE];
+       acpi_repair_function repair_function;
+
+} acpi_repair_info;
+
+/* Local prototypes */
+
+static const struct acpi_repair_info *acpi_ns_match_repairable_name(struct
+                                                                   acpi_namespace_node
+                                                                   *node);
+
+static acpi_status
+acpi_ns_repair_ALR(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_PSS(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_repair_TSS(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr);
+
+static acpi_status
+acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
+                         union acpi_operand_object *return_object,
+                         u32 expected_count,
+                         u32 sort_index,
+                         u8 sort_direction, char *sort_key_name);
+
+static acpi_status
+acpi_ns_remove_null_elements(union acpi_operand_object *package);
+
+static acpi_status
+acpi_ns_sort_list(union acpi_operand_object **elements,
+                 u32 count, u32 index, u8 sort_direction);
+
+/* Values for sort_direction above */
+
+#define ACPI_SORT_ASCENDING     0
+#define ACPI_SORT_DESCENDING    1
+
+/*
+ * This table contains the names of the predefined methods for which we can
+ * perform more complex repairs.
+ *
+ * _ALR: Sort the list ascending by ambient_illuminance if necessary
+ * _PSS: Sort the list descending by Power if necessary
+ * _TSS: Sort the list descending by Power if necessary
+ */
+static const struct acpi_repair_info acpi_ns_repairable_names[] = {
+       {"_ALR", acpi_ns_repair_ALR},
+       {"_PSS", acpi_ns_repair_PSS},
+       {"_TSS", acpi_ns_repair_TSS},
+       {{0, 0, 0, 0}, NULL}    /* Table terminator */
+};
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_complex_repairs
+ *
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              Node                - Namespace node for the method/object
+ *              validate_status     - Original status of earlier validation
+ *              return_object_ptr   - Pointer to the object returned from the
+ *                                    evaluation of a method or object
+ *
+ * RETURN:      Status. AE_OK if repair was successful. If name is not
+ *              matched, validate_status is returned.
+ *
+ * DESCRIPTION: Attempt to repair/convert a return object of a type that was
+ *              not expected.
+ *
+ *****************************************************************************/
+
+acpi_status
+acpi_ns_complex_repairs(struct acpi_predefined_data *data,
+                       struct acpi_namespace_node *node,
+                       acpi_status validate_status,
+                       union acpi_operand_object **return_object_ptr)
+{
+       const struct acpi_repair_info *predefined;
+       acpi_status status;
+
+       /* Check if this name is in the list of repairable names */
+
+       predefined = acpi_ns_match_repairable_name(node);
+       if (!predefined) {
+               return (validate_status);
+       }
+
+       status = predefined->repair_function(data, return_object_ptr);
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_match_repairable_name
+ *
+ * PARAMETERS:  Node                - Namespace node for the method/object
+ *
+ * RETURN:      Pointer to entry in repair table. NULL indicates not found.
+ *
+ * DESCRIPTION: Check an object name against the repairable object list.
+ *
+ *****************************************************************************/
+
+static const struct acpi_repair_info *acpi_ns_match_repairable_name(struct
+                                                                   acpi_namespace_node
+                                                                   *node)
+{
+       const struct acpi_repair_info *this_name;
+
+       /* Search info table for a repairable predefined method/object name */
+
+       this_name = acpi_ns_repairable_names;
+       while (this_name->repair_function) {
+               if (ACPI_COMPARE_NAME(node->name.ascii, this_name->name)) {
+                       return (this_name);
+               }
+               this_name++;
+       }
+
+       return (NULL);          /* Not found */
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_repair_ALR
+ *
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              return_object_ptr   - Pointer to the object returned from the
+ *                                    evaluation of a method or object
+ *
+ * RETURN:      Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _ALR object. If necessary, sort the object list
+ *              ascending by the ambient illuminance values.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_ALR(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr)
+{
+       union acpi_operand_object *return_object = *return_object_ptr;
+       acpi_status status;
+
+       status = acpi_ns_check_sorted_list(data, return_object, 2, 1,
+                                          ACPI_SORT_ASCENDING,
+                                          "AmbientIlluminance");
+
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_repair_TSS
+ *
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              return_object_ptr   - Pointer to the object returned from the
+ *                                    evaluation of a method or object
+ *
+ * RETURN:      Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _TSS object. If necessary, sort the object list
+ *              descending by the power dissipation values.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_TSS(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr)
+{
+       union acpi_operand_object *return_object = *return_object_ptr;
+       acpi_status status;
+
+       status = acpi_ns_check_sorted_list(data, return_object, 5, 1,
+                                          ACPI_SORT_DESCENDING,
+                                          "PowerDissipation");
+
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_repair_PSS
+ *
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              return_object_ptr   - Pointer to the object returned from the
+ *                                    evaluation of a method or object
+ *
+ * RETURN:      Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _PSS object. If necessary, sort the object list
+ *              by the CPU frequencies. Check that the power dissipation values
+ *              are all proportional to CPU frequency (i.e., sorting by
+ *              frequency should be the same as sorting by power.)
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_PSS(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr)
+{
+       union acpi_operand_object *return_object = *return_object_ptr;
+       union acpi_operand_object **outer_elements;
+       u32 outer_element_count;
+       union acpi_operand_object **elements;
+       union acpi_operand_object *obj_desc;
+       u32 previous_value;
+       acpi_status status;
+       u32 i;
+
+       /*
+        * Entries (sub-packages) in the _PSS Package must be sorted by power
+        * dissipation, in descending order. If it appears that the list is
+        * incorrectly sorted, sort it. We sort by cpu_frequency, since this
+        * should be proportional to the power.
+        */
+       status = acpi_ns_check_sorted_list(data, return_object, 6, 0,
+                                          ACPI_SORT_DESCENDING,
+                                          "CpuFrequency");
+       if (ACPI_FAILURE(status)) {
+               return (status);
+       }
+
+       /*
+        * We now know the list is correctly sorted by CPU frequency. Check if
+        * the power dissipation values are proportional.
+        */
+       previous_value = ACPI_UINT32_MAX;
+       outer_elements = return_object->package.elements;
+       outer_element_count = return_object->package.count;
+
+       for (i = 0; i < outer_element_count; i++) {
+               elements = (*outer_elements)->package.elements;
+               obj_desc = elements[1]; /* Index1 = power_dissipation */
+
+               if ((u32) obj_desc->integer.value > previous_value) {
+                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
+                                             data->node_flags,
+                                             "SubPackage[%u,%u] - suspicious power dissipation values",
+                                             i - 1, i));
+               }
+
+               previous_value = (u32) obj_desc->integer.value;
+               outer_elements++;
+       }
+
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_check_sorted_list
+ *
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              return_object       - Pointer to the top-level returned object
+ *              expected_count      - Minimum length of each sub-package
+ *              sort_index          - Sub-package entry to sort on
+ *              sort_direction      - Ascending or descending
+ *              sort_key_name       - Name of the sort_index field
+ *
+ * RETURN:      Status. AE_OK if the list is valid and is sorted correctly or
+ *              has been repaired by sorting the list.
+ *
+ * DESCRIPTION: Check if the package list is valid and sorted correctly by the
+ *              sort_index. If not, then sort the list.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
+                         union acpi_operand_object *return_object,
+                         u32 expected_count,
+                         u32 sort_index,
+                         u8 sort_direction, char *sort_key_name)
+{
+       u32 outer_element_count;
+       union acpi_operand_object **outer_elements;
+       union acpi_operand_object **elements;
+       union acpi_operand_object *obj_desc;
+       u32 i;
+       u32 previous_value;
+       acpi_status status;
+
+       /* The top-level object must be a package */
+
+       if (return_object->common.type != ACPI_TYPE_PACKAGE) {
+               return (AE_AML_OPERAND_TYPE);
+       }
+
+       /*
+        * Detect any NULL package elements and remove them from the
+        * package.
+        *
+        * TBD: We may want to do this for all predefined names that
+        * return a variable-length package of packages.
+        */
+       status = acpi_ns_remove_null_elements(return_object);
+       if (status == AE_NULL_ENTRY) {
+               ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
+                                     "NULL elements removed from package"));
+
+               /* Exit if package is now zero length */
+
+               if (!return_object->package.count) {
+                       return (AE_NULL_ENTRY);
+               }
+       }
+
+       outer_elements = return_object->package.elements;
+       outer_element_count = return_object->package.count;
+       if (!outer_element_count) {
+               return (AE_AML_PACKAGE_LIMIT);
+       }
+
+       previous_value = 0;
+       if (sort_direction == ACPI_SORT_DESCENDING) {
+               previous_value = ACPI_UINT32_MAX;
+       }
+
+       /* Examine each subpackage */
+
+       for (i = 0; i < outer_element_count; i++) {
+
+               /* Each element of the top-level package must also be a package */
+
+               if ((*outer_elements)->common.type != ACPI_TYPE_PACKAGE) {
+                       return (AE_AML_OPERAND_TYPE);
+               }
+
+               /* Each sub-package must have the minimum length */
+
+               if ((*outer_elements)->package.count < expected_count) {
+                       return (AE_AML_PACKAGE_LIMIT);
+               }
+
+               elements = (*outer_elements)->package.elements;
+               obj_desc = elements[sort_index];
+
+               if (obj_desc->common.type != ACPI_TYPE_INTEGER) {
+                       return (AE_AML_OPERAND_TYPE);
+               }
+
+               /*
+                * The list must be sorted in the specified order. If we detect a
+                * discrepancy, issue a warning and sort the entire list
+                */
+               if (((sort_direction == ACPI_SORT_ASCENDING) &&
+                    (obj_desc->integer.value < previous_value)) ||
+                   ((sort_direction == ACPI_SORT_DESCENDING) &&
+                    (obj_desc->integer.value > previous_value))) {
+                       status =
+                           acpi_ns_sort_list(return_object->package.elements,
+                                             outer_element_count, sort_index,
+                                             sort_direction);
+                       if (ACPI_FAILURE(status)) {
+                               return (status);
+                       }
+
+                       data->flags |= ACPI_OBJECT_REPAIRED;
+
+                       ACPI_INFO_PREDEFINED((AE_INFO, data->pathname,
+                                             data->node_flags,
+                                             "Repaired unsorted list - now sorted by %s",
+                                             sort_key_name));
+                       return (AE_OK);
+               }
+
+               previous_value = (u32) obj_desc->integer.value;
+               outer_elements++;
+       }
+
+       return (AE_OK);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_remove_null_elements
+ *
+ * PARAMETERS:  obj_desc            - A Package object
+ *
+ * RETURN:      Status. AE_NULL_ENTRY means that one or more elements were
+ *              removed.
+ *
+ * DESCRIPTION: Remove all NULL package elements and update the package count.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
+{
+       union acpi_operand_object **source;
+       union acpi_operand_object **dest;
+       acpi_status status = AE_OK;
+       u32 count;
+       u32 new_count;
+       u32 i;
+
+       count = obj_desc->package.count;
+       new_count = count;
+
+       source = obj_desc->package.elements;
+       dest = source;
+
+       /* Examine all elements of the package object */
+
+       for (i = 0; i < count; i++) {
+               if (!*source) {
+                       status = AE_NULL_ENTRY;
+                       new_count--;
+               } else {
+                       *dest = *source;
+                       dest++;
+               }
+               source++;
+       }
+
+       if (status == AE_NULL_ENTRY) {
+
+               /* NULL terminate list and update the package count */
+
+               *dest = NULL;
+               obj_desc->package.count = new_count;
+       }
+
+       return (status);
+}
+
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_sort_list
+ *
+ * PARAMETERS:  Elements            - Package object element list
+ *              Count               - Element count for above
+ *              Index               - Sort by which package element
+ *              sort_direction      - Ascending or Descending sort
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Sort the objects that are in a package element list.
+ *
+ * NOTE: Assumes that all NULL elements have been removed from the package.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_sort_list(union acpi_operand_object **elements,
+                 u32 count, u32 index, u8 sort_direction)
+{
+       union acpi_operand_object *obj_desc1;
+       union acpi_operand_object *obj_desc2;
+       union acpi_operand_object *temp_obj;
+       u32 i;
+       u32 j;
+
+       /* Simple bubble sort */
+
+       for (i = 1; i < count; i++) {
+               for (j = (count - 1); j >= i; j--) {
+                       obj_desc1 = elements[j - 1]->package.elements[index];
+                       obj_desc2 = elements[j]->package.elements[index];
+
+                       if (((sort_direction == ACPI_SORT_ASCENDING) &&
+                            (obj_desc1->integer.value >
+                             obj_desc2->integer.value))
+                           || ((sort_direction == ACPI_SORT_DESCENDING)
+                               && (obj_desc1->integer.value <
+                                   obj_desc2->integer.value))) {
+                               temp_obj = elements[j - 1];
+                               elements[j - 1] = elements[j];
+                               elements[j] = temp_obj;
+                       }
+               }
+       }
+
+       return (AE_OK);
+}
index 35539df..d7e6b52 100644 (file)
@@ -165,24 +165,27 @@ struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
  *              max_depth           - Depth to which search is to reach
  *              Flags               - Whether to unlock the NS before invoking
  *                                    the callback routine
- *              user_function       - Called when an object of "Type" is found
- *              Context             - Passed to user function
- *              return_value        - from the user_function if terminated early.
- *                                    Otherwise, returns NULL.
+ *              pre_order_visit     - Called during tree pre-order visit
+ *                                    when an object of "Type" is found
+ *              post_order_visit    - Called during tree post-order visit
+ *                                    when an object of "Type" is found
+ *              Context             - Passed to user function(s) above
+ *              return_value        - from the user_function if terminated
+ *                                    early. Otherwise, returns NULL.
  * RETURNS:     Status
  *
  * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
  *              starting (and ending) at the node specified by start_handle.
- *              The user_function is called whenever a node that matches
- *              the type parameter is found.  If the user function returns
+ *              The callback function is called whenever a node that matches
+ *              the type parameter is found. If the callback function returns
  *              a non-zero value, the search is terminated immediately and
  *              this value is returned to the caller.
  *
  *              The point of this procedure is to provide a generic namespace
  *              walk routine that can be called from multiple places to
- *              provide multiple services;  the User Function can be tailored
- *              to each task, whether it is a print function, a compare
- *              function, etc.
+ *              provide multiple services; the callback function(s) can be
+ *              tailored to each task, whether it is a print function,
+ *              a compare function, etc.
  *
  ******************************************************************************/
 
@@ -191,7 +194,8 @@ acpi_ns_walk_namespace(acpi_object_type type,
                       acpi_handle start_node,
                       u32 max_depth,
                       u32 flags,
-                      acpi_walk_callback user_function,
+                      acpi_walk_callback pre_order_visit,
+                      acpi_walk_callback post_order_visit,
                       void *context, void **return_value)
 {
        acpi_status status;
@@ -200,6 +204,7 @@ acpi_ns_walk_namespace(acpi_object_type type,
        struct acpi_namespace_node *parent_node;
        acpi_object_type child_type;
        u32 level;
+       u8 node_previously_visited = FALSE;
 
        ACPI_FUNCTION_TRACE(ns_walk_namespace);
 
@@ -212,7 +217,7 @@ acpi_ns_walk_namespace(acpi_object_type type,
        /* Null child means "get first node" */
 
        parent_node = start_node;
-       child_node = NULL;
+       child_node = acpi_ns_get_next_node(parent_node, NULL);
        child_type = ACPI_TYPE_ANY;
        level = 1;
 
@@ -221,102 +226,129 @@ acpi_ns_walk_namespace(acpi_object_type type,
         * started. When Level is zero, the loop is done because we have
         * bubbled up to (and passed) the original parent handle (start_entry)
         */
-       while (level > 0) {
+       while (level > 0 && child_node) {
+               status = AE_OK;
 
-               /* Get the next node in this scope.  Null if not found */
+               /* Found next child, get the type if we are not searching for ANY */
 
-               status = AE_OK;
-               child_node = acpi_ns_get_next_node(parent_node, child_node);
-               if (child_node) {
+               if (type != ACPI_TYPE_ANY) {
+                       child_type = child_node->type;
+               }
 
-                       /* Found next child, get the type if we are not searching for ANY */
+               /*
+                * Ignore all temporary namespace nodes (created during control
+                * method execution) unless told otherwise. These temporary nodes
+                * can cause a race condition because they can be deleted during
+                * the execution of the user function (if the namespace is
+                * unlocked before invocation of the user function.) Only the
+                * debugger namespace dump will examine the temporary nodes.
+                */
+               if ((child_node->flags & ANOBJ_TEMPORARY) &&
+                   !(flags & ACPI_NS_WALK_TEMP_NODES)) {
+                       status = AE_CTRL_DEPTH;
+               }
 
-                       if (type != ACPI_TYPE_ANY) {
-                               child_type = child_node->type;
-                       }
+               /* Type must match requested type */
 
+               else if (child_type == type) {
                        /*
-                        * Ignore all temporary namespace nodes (created during control
-                        * method execution) unless told otherwise. These temporary nodes
-                        * can cause a race condition because they can be deleted during
-                        * the execution of the user function (if the namespace is
-                        * unlocked before invocation of the user function.) Only the
-                        * debugger namespace dump will examine the temporary nodes.
+                        * Found a matching node, invoke the user callback function.
+                        * Unlock the namespace if flag is set.
                         */
-                       if ((child_node->flags & ANOBJ_TEMPORARY) &&
-                           !(flags & ACPI_NS_WALK_TEMP_NODES)) {
-                               status = AE_CTRL_DEPTH;
+                       if (flags & ACPI_NS_WALK_UNLOCK) {
+                               mutex_status =
+                                   acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+                               if (ACPI_FAILURE(mutex_status)) {
+                                       return_ACPI_STATUS(mutex_status);
+                               }
                        }
 
-                       /* Type must match requested type */
-
-                       else if (child_type == type) {
-                               /*
-                                * Found a matching node, invoke the user callback function.
-                                * Unlock the namespace if flag is set.
-                                */
-                               if (flags & ACPI_NS_WALK_UNLOCK) {
-                                       mutex_status =
-                                           acpi_ut_release_mutex
-                                           (ACPI_MTX_NAMESPACE);
-                                       if (ACPI_FAILURE(mutex_status)) {
-                                               return_ACPI_STATUS
-                                                   (mutex_status);
-                                       }
+                       /*
+                        * Invoke the user function, either pre-order or post-order
+                        * or both.
+                        */
+                       if (!node_previously_visited) {
+                               if (pre_order_visit) {
+                                       status =
+                                           pre_order_visit(child_node, level,
+                                                           context,
+                                                           return_value);
                                }
+                       } else {
+                               if (post_order_visit) {
+                                       status =
+                                           post_order_visit(child_node, level,
+                                                            context,
+                                                            return_value);
+                               }
+                       }
 
-                               status =
-                                   user_function(child_node, level, context,
-                                                 return_value);
-
-                               if (flags & ACPI_NS_WALK_UNLOCK) {
-                                       mutex_status =
-                                           acpi_ut_acquire_mutex
-                                           (ACPI_MTX_NAMESPACE);
-                                       if (ACPI_FAILURE(mutex_status)) {
-                                               return_ACPI_STATUS
-                                                   (mutex_status);
-                                       }
+                       if (flags & ACPI_NS_WALK_UNLOCK) {
+                               mutex_status =
+                                   acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+                               if (ACPI_FAILURE(mutex_status)) {
+                                       return_ACPI_STATUS(mutex_status);
                                }
+                       }
 
-                               switch (status) {
-                               case AE_OK:
-                               case AE_CTRL_DEPTH:
+                       switch (status) {
+                       case AE_OK:
+                       case AE_CTRL_DEPTH:
 
-                                       /* Just keep going */
-                                       break;
+                               /* Just keep going */
+                               break;
 
-                               case AE_CTRL_TERMINATE:
+                       case AE_CTRL_TERMINATE:
 
-                                       /* Exit now, with OK status */
+                               /* Exit now, with OK status */
 
-                                       return_ACPI_STATUS(AE_OK);
+                               return_ACPI_STATUS(AE_OK);
 
-                               default:
+                       default:
 
-                                       /* All others are valid exceptions */
+                               /* All others are valid exceptions */
 
-                                       return_ACPI_STATUS(status);
-                               }
+                               return_ACPI_STATUS(status);
+                       }
+               }
+
+               /*
+                * Depth first search: Attempt to go down another level in the
+                * namespace if we are allowed to.  Don't go any further if we have
+                * reached the caller specified maximum depth or if the user
+                * function has specified that the maximum depth has been reached.
+                */
+               if (!node_previously_visited &&
+                   (level < max_depth) && (status != AE_CTRL_DEPTH)) {
+                       if (child_node->child) {
+
+                               /* There is at least one child of this node, visit it */
+
+                               level++;
+                               parent_node = child_node;
+                               child_node =
+                                   acpi_ns_get_next_node(parent_node, NULL);
+                               continue;
                        }
+               }
 
-                       /*
-                        * Depth first search: Attempt to go down another level in the
-                        * namespace if we are allowed to.  Don't go any further if we have
-                        * reached the caller specified maximum depth or if the user
-                        * function has specified that the maximum depth has been reached.
-                        */
-                       if ((level < max_depth) && (status != AE_CTRL_DEPTH)) {
-                               if (child_node->child) {
+               /* No more children, re-visit this node */
 
-                                       /* There is at least one child of this node, visit it */
+               if (!node_previously_visited) {
+                       node_previously_visited = TRUE;
+                       continue;
+               }
 
-                                       level++;
-                                       parent_node = child_node;
-                                       child_node = NULL;
-                               }
-                       }
-               } else {
+               /* No more children, visit peers */
+
+               child_node = acpi_ns_get_next_node(parent_node, child_node);
+               if (child_node) {
+                       node_previously_visited = FALSE;
+               }
+
+               /* No peers, re-visit parent */
+
+               else {
                        /*
                         * No more children of this node (acpi_ns_get_next_node failed), go
                         * back upwards in the namespace tree to the node's parent.
@@ -324,6 +356,8 @@ acpi_ns_walk_namespace(acpi_object_type type,
                        level--;
                        child_node = parent_node;
                        parent_node = acpi_ns_get_parent_node(parent_node);
+
+                       node_previously_visited = TRUE;
                }
        }
 
index 4929dbd..f2bd1da 100644 (file)
@@ -433,8 +433,11 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info)
  * PARAMETERS:  Type                - acpi_object_type to search for
  *              start_object        - Handle in namespace where search begins
  *              max_depth           - Depth to which search is to reach
- *              user_function       - Called when an object of "Type" is found
- *              Context             - Passed to user function
+ *              pre_order_visit     - Called during tree pre-order visit
+ *                                    when an object of "Type" is found
+ *              post_order_visit    - Called during tree post-order visit
+ *                                    when an object of "Type" is found
+ *              Context             - Passed to user function(s) above
  *              return_value        - Location where return value of
  *                                    user_function is put if terminated early
  *
@@ -443,16 +446,16 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info)
  *
  * DESCRIPTION: Performs a modified depth-first walk of the namespace tree,
  *              starting (and ending) at the object specified by start_handle.
- *              The user_function is called whenever an object that matches
- *              the type parameter is found.  If the user function returns
+ *              The callback function is called whenever an object that matches
+ *              the type parameter is found. If the callback function returns
  *              a non-zero value, the search is terminated immediately and this
  *              value is returned to the caller.
  *
  *              The point of this procedure is to provide a generic namespace
  *              walk routine that can be called from multiple places to
- *              provide multiple services;  the User Function can be tailored
- *              to each task, whether it is a print function, a compare
- *              function, etc.
+ *              provide multiple services; the callback function(s) can be
+ *              tailored to each task, whether it is a print function,
+ *              a compare function, etc.
  *
  ******************************************************************************/
 
@@ -460,7 +463,8 @@ acpi_status
 acpi_walk_namespace(acpi_object_type type,
                    acpi_handle start_object,
                    u32 max_depth,
-                   acpi_walk_callback user_function,
+                   acpi_walk_callback pre_order_visit,
+                   acpi_walk_callback post_order_visit,
                    void *context, void **return_value)
 {
        acpi_status status;
@@ -469,7 +473,8 @@ acpi_walk_namespace(acpi_object_type type,
 
        /* Parameter validation */
 
-       if ((type > ACPI_TYPE_LOCAL_MAX) || (!max_depth) || (!user_function)) {
+       if ((type > ACPI_TYPE_LOCAL_MAX) ||
+           (!max_depth) || (!pre_order_visit && !post_order_visit)) {
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
@@ -501,8 +506,9 @@ acpi_walk_namespace(acpi_object_type type,
        }
 
        status = acpi_ns_walk_namespace(type, start_object, max_depth,
-                                       ACPI_NS_WALK_UNLOCK, user_function,
-                                       context, return_value);
+                                       ACPI_NS_WALK_UNLOCK, pre_order_visit,
+                                       post_order_visit, context,
+                                       return_value);
 
        (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 
@@ -681,8 +687,8 @@ acpi_get_devices(const char *HID,
 
        status = acpi_ns_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
                                        ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK,
-                                       acpi_ns_get_device_callback, &info,
-                                       return_value);
+                                       acpi_ns_get_device_callback, NULL,
+                                       &info, return_value);
 
        (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
        return_ACPI_STATUS(status);
index cd7995b..0988e4a 100644 (file)
@@ -87,7 +87,8 @@ acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
                          union acpi_parse_object *op, acpi_status status);
 
 static void
-acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id);
+acpi_ps_link_module_code(union acpi_parse_object *parent_op,
+                        u8 *aml_start, u32 aml_length, acpi_owner_id owner_id);
 
 /*******************************************************************************
  *
@@ -479,11 +480,14 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
                                 */
                                if (walk_state->pass_number ==
                                    ACPI_IMODE_LOAD_PASS1) {
-                                       acpi_ps_link_module_code(aml_op_start,
-                                                                walk_state->
+                                       acpi_ps_link_module_code(op->common.
+                                                                parent,
+                                                                aml_op_start,
+                                                                (u32)
+                                                                (walk_state->
                                                                 parser_state.
                                                                 pkg_end -
-                                                                aml_op_start,
+                                                                aml_op_start),
                                                                 walk_state->
                                                                 owner_id);
                                }
@@ -598,7 +602,8 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
  *
  * FUNCTION:    acpi_ps_link_module_code
  *
- * PARAMETERS:  aml_start           - Pointer to the AML
+ * PARAMETERS:  parent_op           - Parent parser op
+ *              aml_start           - Pointer to the AML
  *              aml_length          - Length of executable AML
  *              owner_id            - owner_id of module level code
  *
@@ -611,11 +616,13 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
  ******************************************************************************/
 
 static void
-acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
+acpi_ps_link_module_code(union acpi_parse_object *parent_op,
+                        u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
 {
        union acpi_operand_object *prev;
        union acpi_operand_object *next;
        union acpi_operand_object *method_obj;
+       struct acpi_namespace_node *parent_node;
 
        /* Get the tail of the list */
 
@@ -639,11 +646,24 @@ acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
                        return;
                }
 
+               if (parent_op->common.node) {
+                       parent_node = parent_op->common.node;
+               } else {
+                       parent_node = acpi_gbl_root_node;
+               }
+
                method_obj->method.aml_start = aml_start;
                method_obj->method.aml_length = aml_length;
                method_obj->method.owner_id = owner_id;
                method_obj->method.flags |= AOPOBJ_MODULE_LEVEL;
 
+               /*
+                * Save the parent node in next_object. This is cheating, but we
+                * don't want to expand the method object.
+                */
+               method_obj->method.next_object =
+                   ACPI_CAST_PTR(union acpi_operand_object, parent_node);
+
                if (!prev) {
                        acpi_gbl_module_code_list = method_obj;
                } else {
index 70838e9..4df8f13 100644 (file)
@@ -610,17 +610,13 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
                                            implicit_return_obj) {
                                                previous_walk_state->
                                                    implicit_return_obj =
-                                                   acpi_ut_create_internal_object
-                                                   (ACPI_TYPE_INTEGER);
+                                                   acpi_ut_create_integer_object
+                                                   ((u64) 0);
                                                if (!previous_walk_state->
                                                    implicit_return_obj) {
                                                        return_ACPI_STATUS
                                                            (AE_NO_MEMORY);
                                                }
-
-                                               previous_walk_state->
-                                                   implicit_return_obj->
-                                                   integer.value = 0;
                                        }
 
                                        /* Restart the calling control method */
index dd9731c..12934ad 100644 (file)
@@ -306,14 +306,12 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info)
         */
        if (acpi_gbl_enable_interpreter_slack) {
                walk_state->implicit_return_obj =
-                   acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+                   acpi_ut_create_integer_object((u64) 0);
                if (!walk_state->implicit_return_obj) {
                        status = AE_NO_MEMORY;
                        acpi_ds_delete_walk_state(walk_state);
                        goto cleanup;
                }
-
-               walk_state->implicit_return_obj->integer.value = 0;
        }
 
        /* Parse the AML */
index 61f6315..6c6a513 100644 (file)
@@ -1161,3 +1161,45 @@ acpi_ut_predefined_warning(const char *module_name,
        ACPI_COMMON_MSG_SUFFIX;
        va_end(args);
 }
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_predefined_info
+ *
+ * PARAMETERS:  module_name     - Caller's module name (for error output)
+ *              line_number     - Caller's line number (for error output)
+ *              Pathname        - Full pathname to the node
+ *              node_flags      - From Namespace node for the method/object
+ *              Format          - Printf format string + additional args
+ *
+ * RETURN:      None
+ *
+ * DESCRIPTION: Info messages for the predefined validation module. Messages
+ *              are only emitted the first time a problem with a particular
+ *              method/object is detected. This prevents a flood of
+ *              messages for methods that are repeatedly evaluated.
+ *
+ ******************************************************************************/
+
+void ACPI_INTERNAL_VAR_XFACE
+acpi_ut_predefined_info(const char *module_name,
+                       u32 line_number,
+                       char *pathname, u8 node_flags, const char *format, ...)
+{
+       va_list args;
+
+       /*
+        * Warning messages for this method/object will be disabled after the
+        * first time a validation fails or an object is successfully repaired.
+        */
+       if (node_flags & ANOBJ_EVALUATED) {
+               return;
+       }
+
+       acpi_os_printf("ACPI Info for %s: ", pathname);
+
+       va_start(args, format);
+       acpi_os_vprintf(format, args);
+       ACPI_COMMON_MSG_SUFFIX;
+       va_end(args);
+}
index 0207b62..42e658b 100644 (file)
@@ -188,6 +188,35 @@ union acpi_operand_object *acpi_ut_create_package_object(u32 count)
        return_PTR(package_desc);
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ut_create_integer_object
+ *
+ * PARAMETERS:  initial_value       - Initial value for the integer
+ *
+ * RETURN:      Pointer to a new Integer object, null on failure
+ *
+ * DESCRIPTION: Create an initialized integer object
+ *
+ ******************************************************************************/
+
+union acpi_operand_object *acpi_ut_create_integer_object(u64 initial_value)
+{
+       union acpi_operand_object *integer_desc;
+
+       ACPI_FUNCTION_TRACE(ut_create_integer_object);
+
+       /* Create and initialize a new integer object */
+
+       integer_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER);
+       if (!integer_desc) {
+               return_PTR(NULL);
+       }
+
+       integer_desc->integer.value = initial_value;
+       return_PTR(integer_desc);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ut_create_buffer_object
index 642bb30..5faf6c2 100644 (file)
@@ -258,7 +258,7 @@ static int __init acpi_container_init(void)
        acpi_walk_namespace(ACPI_TYPE_DEVICE,
                            ACPI_ROOT_OBJECT,
                            ACPI_UINT32_MAX,
-                           container_walk_namespace_cb, &action, NULL);
+                           container_walk_namespace_cb, NULL, &action, NULL);
 
        return (0);
 }
@@ -271,7 +271,7 @@ static void __exit acpi_container_exit(void)
        acpi_walk_namespace(ACPI_TYPE_DEVICE,
                            ACPI_ROOT_OBJECT,
                            ACPI_UINT32_MAX,
-                           container_walk_namespace_cb, &action, NULL);
+                           container_walk_namespace_cb, NULL, &action, NULL);
 
        acpi_bus_unregister_driver(&acpi_container_driver);
 
index 7338b6a..30be3c1 100644 (file)
@@ -1030,8 +1030,8 @@ static int dock_add(acpi_handle handle)
 
        /* Find dependent devices */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                           ACPI_UINT32_MAX, find_dock_devices, dock_station,
-                           NULL);
+                           ACPI_UINT32_MAX, find_dock_devices, NULL,
+                           dock_station, NULL);
 
        /* add the dock station as a device dependent on itself */
        dd = alloc_dock_dependent_device(handle);
@@ -1127,11 +1127,11 @@ static int __init dock_init(void)
 
        /* look for a dock station */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                           ACPI_UINT32_MAX, find_dock, NULL, NULL);
+                           ACPI_UINT32_MAX, find_dock, NULL, NULL, NULL);
 
        /* look for bay */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                       ACPI_UINT32_MAX, find_bay, NULL, NULL);
+                       ACPI_UINT32_MAX, find_bay, NULL, NULL, NULL);
        if (!dock_station_count) {
                printk(KERN_INFO PREFIX "No dock devices found.\n");
                return 0;
index baef28c..75b147f 100644 (file)
@@ -820,7 +820,7 @@ static int acpi_ec_add(struct acpi_device *device)
 
        /* Find and register all query methods */
        acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
-                           acpi_ec_register_query_methods, ec, NULL);
+                           acpi_ec_register_query_methods, NULL, ec, NULL);
 
        if (!first_ec)
                first_ec = ec;
index c6645f2..4c8fcff 100644 (file)
@@ -113,7 +113,7 @@ acpi_handle acpi_get_child(acpi_handle parent, acpi_integer address)
        if (!parent)
                return NULL;
        acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
-                           1, do_acpi_find_child, &find, NULL);
+                           1, do_acpi_find_child, NULL, &find, NULL);
        return find.handle;
 }
 
index 45da2ba..11f2197 100644 (file)
@@ -219,12 +219,12 @@ walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
 
        dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number);
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                    user_function, &child_context, NULL);
+                                    user_function, NULL, &child_context, NULL);
        if (ACPI_FAILURE(status))
                goto out;
 
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                    walk_p2p_bridge, &child_context, NULL);
+                                    walk_p2p_bridge, NULL, &child_context, NULL);
 out:
        pci_dev_put(dev);
        return AE_OK;
@@ -277,12 +277,12 @@ walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
 
        dbg("root bridge walk, pci_bus = %x\n", pci_bus->number);
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                    user_function, &context, NULL);
+                                    user_function, NULL, &context, NULL);
        if (ACPI_FAILURE(status))
                return status;
 
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                    walk_p2p_bridge, &context, NULL);
+                                    walk_p2p_bridge, NULL, &context, NULL);
        if (ACPI_FAILURE(status))
                err("%s: walk_p2p_bridge failure - %d\n", __func__, status);
 
index ec742a4..cb4283f 100644 (file)
@@ -1102,7 +1102,7 @@ void acpi_processor_install_hotplug_notify(void)
        acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
                            ACPI_ROOT_OBJECT,
                            ACPI_UINT32_MAX,
-                           processor_walk_namespace_cb, &action, NULL);
+                           processor_walk_namespace_cb, NULL, &action, NULL);
 #endif
        register_hotcpu_notifier(&acpi_cpu_notifier);
 }
@@ -1115,7 +1115,7 @@ void acpi_processor_uninstall_hotplug_notify(void)
        acpi_walk_namespace(ACPI_TYPE_PROCESSOR,
                            ACPI_ROOT_OBJECT,
                            ACPI_UINT32_MAX,
-                           processor_walk_namespace_cb, &action, NULL);
+                           processor_walk_namespace_cb, NULL, &action, NULL);
 #endif
        unregister_hotcpu_notifier(&acpi_cpu_notifier);
 }
index 14a7481..ff9f622 100644 (file)
@@ -1332,7 +1332,7 @@ static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
        status = acpi_bus_check_add(handle, 0, ops, &device);
        if (ACPI_SUCCESS(status))
                acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
-                                   acpi_bus_check_add, ops, &device);
+                                   acpi_bus_check_add, NULL, ops, &device);
 
        if (child)
                *child = device;
index 575593a..fc2f26b 100644 (file)
@@ -7,7 +7,7 @@
  * video_detect.c:
  * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c
  * There a Linux specific (Spec does not provide a HID for video devices) is
- * assinged
+ * assigned
  *
  * After PCI devices are glued with ACPI devices
  * acpi_get_pci_dev() can be called to identify ACPI graphics
@@ -83,16 +83,16 @@ long acpi_is_video_device(struct acpi_device *device)
        if (!device)
                return 0;
 
-       /* Does this device able to support video switching ? */
+       /* Is this device able to support video switching ? */
        if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) ||
            ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy)))
                video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING;
 
-       /* Does this device able to retrieve a video ROM ? */
+       /* Is this device able to retrieve a video ROM ? */
        if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy)))
                video_caps |= ACPI_VIDEO_ROM_AVAILABLE;
 
-       /* Does this device able to configure which video head to be POSTed ? */
+       /* Is this device able to configure which video head to be POSTed ? */
        if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) &&
            ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) &&
            ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy)))
@@ -101,7 +101,7 @@ long acpi_is_video_device(struct acpi_device *device)
        /* Only check for backlight functionality if one of the above hit. */
        if (video_caps)
                acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle,
-                                   ACPI_UINT32_MAX, acpi_backlight_cap_match,
+                                   ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL,
                                    &video_caps, NULL);
 
        return video_caps;
@@ -137,7 +137,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
  *
  * if NULL is passed as argument all ACPI devices are enumerated and
  * all graphics capabilities of physically present devices are
- * summerized and returned. This is cached and done only once.
+ * summarized and returned. This is cached and done only once.
  */
 long acpi_video_get_capabilities(acpi_handle graphics_handle)
 {
@@ -151,7 +151,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
        if (!graphics_handle) {
                /* Only do the global walk through all graphics devices once */
                acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                                   ACPI_UINT32_MAX, find_video,
+                                   ACPI_UINT32_MAX, find_video, NULL,
                                    &caps, NULL);
                /* There might be boot param flags set already... */
                acpi_video_support |= caps;
@@ -173,7 +173,7 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle)
                        return 0;
                }
                acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle,
-                                   ACPI_UINT32_MAX, find_video,
+                                   ACPI_UINT32_MAX, find_video, NULL,
                                    &caps, NULL);
        }
        ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n",
index 0c6155f..19136a7 100644 (file)
@@ -599,7 +599,7 @@ static const struct ich_laptop ich_laptop[] = {
        { 0x27DF, 0x1028, 0x02b0 },     /* ICH7 on unknown Dell */
        { 0x27DF, 0x1043, 0x1267 },     /* ICH7 on Asus W5F */
        { 0x27DF, 0x103C, 0x30A1 },     /* ICH7 on HP Compaq nc2400 */
-       { 0x27DF, 0x103C, 0x361a },     /* ICH7 on unkown HP  */
+       { 0x27DF, 0x103C, 0x361a },     /* ICH7 on unknown HP  */
        { 0x27DF, 0x1071, 0xD221 },     /* ICH7 on Hercules EC-900 */
        { 0x27DF, 0x152D, 0x0778 },     /* ICH7 on unknown Intel */
        { 0x24CA, 0x1025, 0x0061 },     /* ICH4 on ACER Aspire 2023WLMi */
index 62e6b9e..1683ebd 100644 (file)
@@ -1208,6 +1208,7 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
  *     ata_scsi_change_queue_depth - SCSI callback for queue depth config
  *     @sdev: SCSI device to configure queue depth for
  *     @queue_depth: new queue depth
+ *     @reason: calling context
  *
  *     This is libata standard hostt->change_queue_depth callback.
  *     SCSI will call into this callback when user tries to set queue
@@ -1219,12 +1220,16 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
  *     RETURNS:
  *     Newly configured queue depth.
  */
-int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+                               int reason)
 {
        struct ata_port *ap = ata_shost_to_port(sdev->host);
        struct ata_device *dev;
        unsigned long flags;
 
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (queue_depth < 1 || queue_depth == sdev->queue_depth)
                return sdev->queue_depth;
 
index 51eb1e2..efa8773 100644 (file)
@@ -736,7 +736,7 @@ unsigned int ata_sff_data_xfer(struct ata_device *dev, unsigned char *buf,
 
                /*
                 * Use io*16_rep() accessors here as well to avoid pointlessly
-                * swapping bytes to and fro on the big endian machines...
+                * swapping bytes to and from on the big endian machines...
                 */
                if (rw == READ) {
                        ioread16_rep(data_addr, pad, 1);
@@ -776,7 +776,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
        void __iomem *data_addr = ap->ioaddr.data_addr;
        unsigned int words = buflen >> 2;
        int slop = buflen & 3;
-       
+
        if (!(ap->pflags & ATA_PFLAG_PIO32))
                return ata_sff_data_xfer(dev, buf, buflen, rw);
 
@@ -795,7 +795,7 @@ unsigned int ata_sff_data_xfer32(struct ata_device *dev, unsigned char *buf,
 
                /*
                 * Use io*_rep() accessors here as well to avoid pointlessly
-                * swapping bytes to and fro on the big endian machines...
+                * swapping bytes to and from on the big endian machines...
                 */
                if (rw == READ) {
                        if (slop < 3)
index 403f561..71cef9a 100644 (file)
@@ -4,7 +4,7 @@
  *                       Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  * based upon cs5535.c from AMD <Jens.Altmann@amd.com> as cleaned up and
- * made readable and Linux style by Wolfgang Zuleger <wolfgang.zuleger@gmx.de
+ * made readable and Linux style by Wolfgang Zuleger <wolfgang.zuleger@gmx.de>
  * and Alexander Kiausch <alex.kiausch@t-online.de>
  *
  * This program is free software; you can redistribute it and/or modify
index 8a5d35b..ce4136e 100644 (file)
@@ -43,9 +43,9 @@ enum {
        /*
         * SATA-FSL host controller supports a max. of (15+1) direct PRDEs, and
         * chained indirect PRDEs upto a max count of 63.
-        * We are allocating an array of 63 PRDEs contigiously, but PRDE#15 will
+        * We are allocating an array of 63 PRDEs contiguously, but PRDE#15 will
         * be setup as an indirect descriptor, pointing to it's next
-        * (contigious) PRDE. Though chained indirect PRDE arrays are
+        * (contiguous) PRDE. Though chained indirect PRDE arrays are
         * supported,it will be more efficient to use a direct PRDT and
         * a single chain/link to indirect PRDE array/PRDT.
         */
@@ -314,7 +314,7 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
        u32 ttl_dwords = 0;
 
        /*
-        * NOTE : direct & indirect prdt's are contigiously allocated
+        * NOTE : direct & indirect prdt's are contiguously allocated
         */
        struct prde *prd = (struct prde *)&((struct command_desc *)
                                            cmd_desc)->prdt;
index 1eb4e02..0c82d33 100644 (file)
@@ -1975,7 +1975,7 @@ static int nv_swncq_slave_config(struct scsi_device *sdev)
        ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
 
        if (strncmp(model_num, "Maxtor", 6) == 0) {
-               ata_scsi_change_queue_depth(sdev, 1);
+               ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT);
                ata_dev_printk(dev, KERN_NOTICE,
                        "Disabling SWNCQ mode (depth %x)\n", sdev->queue_depth);
        }
index b2c1b37..f734b34 100644 (file)
@@ -1132,7 +1132,7 @@ static int rx_pkt(struct atm_dev *dev)
                     IF_ERR(printk(" cause: packet time out\n");)
                 }
                 else {
-                    IF_ERR(printk(" cause: buffer over flow\n");)
+                    IF_ERR(printk(" cause: buffer overflow\n");)
                 }
                goto out_free_desc;
        }  
index 979d159..ee95c76 100644 (file)
@@ -188,7 +188,7 @@ EXPORT_SYMBOL_GPL(wait_for_device_probe);
  * @dev: device to try to bind to the driver
  *
  * This function returns -ENODEV if the device is not registered,
- * 1 if the device is bound sucessfully and 0 otherwise.
+ * 1 if the device is bound successfully and 0 otherwise.
  *
  * This function must be called with @dev->sem held.  When called for a
  * USB interface, @dev->parent->sem must be held as well.
index 847a9e5..a5af1d6 100644 (file)
@@ -1478,10 +1478,7 @@ void do_fd_request(struct request_queue * q)
        stdma_lock(floppy_irq, NULL);
 
        atari_disable_irq( IRQ_MFP_FDC );
-       local_save_flags(flags);        /* The request function is called with ints
-       local_irq_disable();             * disabled... so must save the IPL for later */ 
        redo_fd_request();
-       local_irq_restore(flags);
        atari_enable_irq( IRQ_MFP_FDC );
 }
 
index b50a9b2..6afa700 100644 (file)
@@ -5,7 +5,7 @@
 //###########################################################################
 #define CISS_VERSION "1.00"
 
-//general boundary defintions
+//general boundary definitions
 #define SENSEINFOBYTES          32//note that this value may vary between host implementations
 #define MAXSGENTRIES            32
 #define CCISS_SG_CHAIN          0x80000000
index 1e6eb1a..f36defa 100644 (file)
@@ -535,7 +535,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
                break;
 
        default:
-               BT_ERR("Unknow packet type:%d", type);
+               BT_ERR("Unknown packet type:%d", type);
                print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, payload,
                                                blksz * buf_block_len);
 
index 4895f0e..aa09193 100644 (file)
@@ -214,7 +214,7 @@ static int hci_uart_send_frame(struct sk_buff *skb)
        struct hci_uart *hu;
 
        if (!hdev) {
-               BT_ERR("Frame for uknown device (hdev=NULL)");
+               BT_ERR("Frame for unknown device (hdev=NULL)");
                return -ENODEV;
        }
 
index a762283..e789e6c 100644 (file)
@@ -214,7 +214,7 @@ static void gdrom_spicommand(void *spi_string, int buflen)
                gdrom_getsense(NULL);
                return;
        }
-       outsw(PHYSADDR(GDROM_DATA_REG), cmd, 6);
+       outsw(GDROM_DATA_REG, cmd, 6);
 }
 
 
@@ -298,7 +298,7 @@ static int gdrom_readtoc_cmd(struct gdromtoc *toc, int session)
                err = -EINVAL;
                goto cleanup_readtoc;
        }
-       insw(PHYSADDR(GDROM_DATA_REG), toc, tocsize/2);
+       insw(GDROM_DATA_REG, toc, tocsize/2);
        if (gd.status & 0x01)
                err = -EINVAL;
 
@@ -449,7 +449,7 @@ static int gdrom_getsense(short *bufstring)
                GDROM_DEFAULT_TIMEOUT);
        if (gd.pending)
                goto cleanup_sense;
-       insw(PHYSADDR(GDROM_DATA_REG), &sense, sense_command->buflen/2);
+       insw(GDROM_DATA_REG, &sense, sense_command->buflen/2);
        if (sense[1] & 40) {
                printk(KERN_INFO "GDROM: Drive not ready - command aborted\n");
                goto cleanup_sense;
@@ -586,7 +586,7 @@ static void gdrom_readdisk_dma(struct work_struct *work)
                spin_unlock(&gdrom_lock);
                block = blk_rq_pos(req)/GD_TO_BLK + GD_SESSION_OFFSET;
                block_cnt = blk_rq_sectors(req)/GD_TO_BLK;
-               ctrl_outl(PHYSADDR(req->buffer), GDROM_DMA_STARTADDR_REG);
+               ctrl_outl(virt_to_phys(req->buffer), GDROM_DMA_STARTADDR_REG);
                ctrl_outl(block_cnt * GDROM_HARD_SECTOR, GDROM_DMA_LENGTH_REG);
                ctrl_outl(1, GDROM_DMA_DIRECTION_REG);
                ctrl_outl(1, GDROM_DMA_ENABLE_REG);
@@ -615,7 +615,7 @@ static void gdrom_readdisk_dma(struct work_struct *work)
                        cpu_relax();
                gd.pending = 1;
                gd.transfer = 1;
-               outsw(PHYSADDR(GDROM_DATA_REG), &read_command->cmd, 6);
+               outsw(GDROM_DATA_REG, &read_command->cmd, 6);
                timeout = jiffies + HZ / 2;
                /* Wait for any pending DMA to finish */
                while (ctrl_inb(GDROM_DMA_STATUS_REG) &&
index 3cb56a0..30c36ac 100644 (file)
 #define PCI_DEVICE_ID_INTEL_82965GME_IG     0x2A12
 #define PCI_DEVICE_ID_INTEL_82945GME_HB     0x27AC
 #define PCI_DEVICE_ID_INTEL_82945GME_IG     0x27AE
-#define PCI_DEVICE_ID_INTEL_IGDGM_HB        0xA010
-#define PCI_DEVICE_ID_INTEL_IGDGM_IG        0xA011
-#define PCI_DEVICE_ID_INTEL_IGDG_HB         0xA000
-#define PCI_DEVICE_ID_INTEL_IGDG_IG         0xA001
+#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB        0xA010
+#define PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG        0xA011
+#define PCI_DEVICE_ID_INTEL_PINEVIEW_HB         0xA000
+#define PCI_DEVICE_ID_INTEL_PINEVIEW_IG         0xA001
 #define PCI_DEVICE_ID_INTEL_G33_HB          0x29C0
 #define PCI_DEVICE_ID_INTEL_G33_IG          0x29C2
 #define PCI_DEVICE_ID_INTEL_Q35_HB          0x29B0
 #define PCI_DEVICE_ID_INTEL_B43_IG          0x2E42
 #define PCI_DEVICE_ID_INTEL_GM45_HB         0x2A40
 #define PCI_DEVICE_ID_INTEL_GM45_IG         0x2A42
-#define PCI_DEVICE_ID_INTEL_IGD_E_HB        0x2E00
-#define PCI_DEVICE_ID_INTEL_IGD_E_IG        0x2E02
+#define PCI_DEVICE_ID_INTEL_EAGLELAKE_HB        0x2E00
+#define PCI_DEVICE_ID_INTEL_EAGLELAKE_IG        0x2E02
 #define PCI_DEVICE_ID_INTEL_Q45_HB          0x2E10
 #define PCI_DEVICE_ID_INTEL_Q45_IG          0x2E12
 #define PCI_DEVICE_ID_INTEL_G45_HB          0x2E20
 #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_MA_HB            0x0062
-#define PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB    0x006a
-#define PCI_DEVICE_ID_INTEL_IGDNG_M_IG     0x0046
+#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB          0x0040
+#define PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG          0x0042
+#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB          0x0044
+#define PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB         0x0062
+#define PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB    0x006a
+#define PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG          0x0046
 
 /* cover 915 and 945 variants */
 #define IS_I915 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_E7221_HB || \
 #define IS_G33 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G33_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q35_HB || \
                agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_Q33_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDGM_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDG_HB)
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB)
 
-#define IS_IGD (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDGM_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDG_HB)
+#define IS_PINEVIEW (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_PINEVIEW_HB)
 
-#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGD_E_HB || \
+#define IS_G4X (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_EAGLELAKE_HB || \
                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_B43_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB || \
-               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB)
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB || \
+               agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB)
 
 extern int agp_memory_reserved;
 
@@ -178,6 +178,7 @@ static struct _intel_private {
         * popup and for the GTT.
         */
        int gtt_entries;                        /* i830+ */
+       int gtt_total_size;
        union {
                void __iomem *i9xx_flush_page;
                void *i8xx_flush_page;
@@ -653,7 +654,7 @@ static void intel_i830_init_gtt_entries(void)
                        size = 512;
                }
                size += 4; /* add in BIOS popup space */
-       } else if (IS_G33 && !IS_IGD) {
+       } else if (IS_G33 && !IS_PINEVIEW) {
        /* G33's GTT size defined in gmch_ctrl */
                switch (gmch_ctrl & G33_PGETBL_SIZE_MASK) {
                case G33_PGETBL_SIZE_1M:
@@ -669,7 +670,7 @@ static void intel_i830_init_gtt_entries(void)
                        size = 512;
                }
                size += 4;
-       } else if (IS_G4X || IS_IGD) {
+       } else if (IS_G4X || IS_PINEVIEW) {
                /* On 4 series hardware, GTT stolen is separate from graphics
                 * stolen, ignore it in stolen gtt entries counting.  However,
                 * 4KB of the stolen memory doesn't get mapped to the GTT.
@@ -1153,7 +1154,7 @@ static int intel_i915_configure(void)
        readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */
 
        if (agp_bridge->driver->needs_scratch_page) {
-               for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) {
+               for (i = intel_private.gtt_entries; i < intel_private.gtt_total_size; i++) {
                        writel(agp_bridge->scratch_page, intel_private.gtt+i);
                }
                readl(intel_private.gtt+i-1);   /* PCI Posting. */
@@ -1308,6 +1309,8 @@ static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge)
        if (!intel_private.gtt)
                return -ENOMEM;
 
+       intel_private.gtt_total_size = gtt_map_size / 4;
+
        temp &= 0xfff80000;
 
        intel_private.registers = ioremap(temp, 128 * 4096);
@@ -1352,15 +1355,15 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size)
 {
        switch (agp_bridge->dev->device) {
        case PCI_DEVICE_ID_INTEL_GM45_HB:
-       case PCI_DEVICE_ID_INTEL_IGD_E_HB:
+       case PCI_DEVICE_ID_INTEL_EAGLELAKE_HB:
        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_B43_HB:
-       case PCI_DEVICE_ID_INTEL_IGDNG_D_HB:
-       case PCI_DEVICE_ID_INTEL_IGDNG_M_HB:
-       case PCI_DEVICE_ID_INTEL_IGDNG_MA_HB:
-       case PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB:
+       case PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB:
+       case PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB:
+       case PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB:
+       case PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB:
                *gtt_offset = *gtt_size = MB(2);
                break;
        default:
@@ -1395,6 +1398,8 @@ static int intel_i965_create_gatt_table(struct agp_bridge_data *bridge)
        if (!intel_private.gtt)
                return -ENOMEM;
 
+       intel_private.gtt_total_size = gtt_size / 4;
+
        intel_private.registers = ioremap(temp, 128 * 4096);
        if (!intel_private.registers) {
                iounmap(intel_private.gtt);
@@ -2340,14 +2345,14 @@ static const struct intel_driver_description {
                NULL, &intel_g33_driver },
        { PCI_DEVICE_ID_INTEL_Q33_HB, PCI_DEVICE_ID_INTEL_Q33_IG, 0, "Q33",
                NULL, &intel_g33_driver },
-       { PCI_DEVICE_ID_INTEL_IGDGM_HB, PCI_DEVICE_ID_INTEL_IGDGM_IG, 0, "IGD",
+       { PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, 0, "Pineview",
                NULL, &intel_g33_driver },
-       { PCI_DEVICE_ID_INTEL_IGDG_HB, PCI_DEVICE_ID_INTEL_IGDG_IG, 0, "IGD",
+       { PCI_DEVICE_ID_INTEL_PINEVIEW_HB, PCI_DEVICE_ID_INTEL_PINEVIEW_IG, 0, "Pineview",
                NULL, &intel_g33_driver },
        { PCI_DEVICE_ID_INTEL_GM45_HB, PCI_DEVICE_ID_INTEL_GM45_IG, 0,
-           "Mobile Intel® GM45 Express", NULL, &intel_i965_driver },
-       { PCI_DEVICE_ID_INTEL_IGD_E_HB, PCI_DEVICE_ID_INTEL_IGD_E_IG, 0,
-           "Intel Integrated Graphics Device", NULL, &intel_i965_driver },
+           "GM45", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_EAGLELAKE_HB, PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, 0,
+           "Eaglelake", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_Q45_HB, PCI_DEVICE_ID_INTEL_Q45_IG, 0,
            "Q45/Q43", NULL, &intel_i965_driver },
        { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0,
@@ -2356,14 +2361,14 @@ static const struct intel_driver_description {
            "B43", 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 },
-       { PCI_DEVICE_ID_INTEL_IGDNG_MA_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0,
-           "IGDNG/MA", NULL, &intel_i965_driver },
-       { PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB, PCI_DEVICE_ID_INTEL_IGDNG_M_IG, 0,
-           "IGDNG/MC2", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, 0,
+           "Ironlake/D", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0,
+           "Ironlake/M", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0,
+           "Ironlake/MA", NULL, &intel_i965_driver },
+       { PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB, PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, 0,
+           "Ironlake/MC2", NULL, &intel_i965_driver },
        { 0, 0, 0, NULL, NULL, NULL }
 };
 
@@ -2545,8 +2550,8 @@ static struct pci_device_id agp_intel_pci_table[] = {
        ID(PCI_DEVICE_ID_INTEL_82945G_HB),
        ID(PCI_DEVICE_ID_INTEL_82945GM_HB),
        ID(PCI_DEVICE_ID_INTEL_82945GME_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGDGM_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGDG_HB),
+       ID(PCI_DEVICE_ID_INTEL_PINEVIEW_M_HB),
+       ID(PCI_DEVICE_ID_INTEL_PINEVIEW_HB),
        ID(PCI_DEVICE_ID_INTEL_82946GZ_HB),
        ID(PCI_DEVICE_ID_INTEL_82G35_HB),
        ID(PCI_DEVICE_ID_INTEL_82965Q_HB),
@@ -2557,15 +2562,15 @@ static struct pci_device_id agp_intel_pci_table[] = {
        ID(PCI_DEVICE_ID_INTEL_Q35_HB),
        ID(PCI_DEVICE_ID_INTEL_Q33_HB),
        ID(PCI_DEVICE_ID_INTEL_GM45_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGD_E_HB),
+       ID(PCI_DEVICE_ID_INTEL_EAGLELAKE_HB),
        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_B43_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGDNG_MA_HB),
-       ID(PCI_DEVICE_ID_INTEL_IGDNG_MC2_HB),
+       ID(PCI_DEVICE_ID_INTEL_IRONLAKE_D_HB),
+       ID(PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB),
+       ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MA_HB),
+       ID(PCI_DEVICE_ID_INTEL_IRONLAKE_MC2_HB),
        { }
 };
 
index 950837c..5619007 100644 (file)
@@ -46,8 +46,6 @@
 
 extern void ctrl_alt_del(void);
 
-#define to_handle_h(n) container_of(n, struct input_handle, h_node)
-
 /*
  * Exported functions/variables
  */
@@ -132,6 +130,7 @@ int shift_state = 0;
  */
 
 static struct input_handler kbd_handler;
+static DEFINE_SPINLOCK(kbd_event_lock);
 static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
 static unsigned char shift_down[NR_SHIFT];             /* shift state counters.. */
 static int dead_key_next;
@@ -190,78 +189,85 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
  *  etc.). So this means that scancodes for the extra function keys won't
  *  be valid for the first event device, but will be for the second.
  */
+
+struct getset_keycode_data {
+       unsigned int scancode;
+       unsigned int keycode;
+       int error;
+};
+
+static int getkeycode_helper(struct input_handle *handle, void *data)
+{
+       struct getset_keycode_data *d = data;
+
+       d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
+
+       return d->error == 0; /* stop as soon as we successfully get one */
+}
+
 int getkeycode(unsigned int scancode)
 {
-       struct input_handle *handle;
-       int keycode;
-       int error = -ENODEV;
+       struct getset_keycode_data d = { scancode, 0, -ENODEV };
 
-       list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
-               error = input_get_keycode(handle->dev, scancode, &keycode);
-               if (!error)
-                       return keycode;
-       }
+       input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
 
-       return error;
+       return d.error ?: d.keycode;
+}
+
+static int setkeycode_helper(struct input_handle *handle, void *data)
+{
+       struct getset_keycode_data *d = data;
+
+       d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
+
+       return d->error == 0; /* stop as soon as we successfully set one */
 }
 
 int setkeycode(unsigned int scancode, unsigned int keycode)
 {
-       struct input_handle *handle;
-       int error = -ENODEV;
+       struct getset_keycode_data d = { scancode, keycode, -ENODEV };
 
-       list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
-               error = input_set_keycode(handle->dev, scancode, keycode);
-               if (!error)
-                       break;
-       }
+       input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
 
-       return error;
+       return d.error;
 }
 
 /*
  * Making beeps and bells.
  */
-static void kd_nosound(unsigned long ignored)
+
+static int kd_sound_helper(struct input_handle *handle, void *data)
 {
-       struct input_handle *handle;
+       unsigned int *hz = data;
+       struct input_dev *dev = handle->dev;
 
-       list_for_each_entry(handle, &kbd_handler.h_list, h_node) {
-               if (test_bit(EV_SND, handle->dev->evbit)) {
-                       if (test_bit(SND_TONE, handle->dev->sndbit))
-                               input_inject_event(handle, EV_SND, SND_TONE, 0);
-                       if (test_bit(SND_BELL, handle->dev->sndbit))
-                               input_inject_event(handle, EV_SND, SND_BELL, 0);
-               }
+       if (test_bit(EV_SND, dev->evbit)) {
+               if (test_bit(SND_TONE, dev->sndbit))
+                       input_inject_event(handle, EV_SND, SND_TONE, *hz);
+               if (test_bit(SND_BELL, handle->dev->sndbit))
+                       input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0);
        }
+
+       return 0;
+}
+
+static void kd_nosound(unsigned long ignored)
+{
+       static unsigned int zero;
+
+       input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
 }
 
 static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0);
 
 void kd_mksound(unsigned int hz, unsigned int ticks)
 {
-       struct list_head *node;
+       del_timer_sync(&kd_mksound_timer);
 
-       del_timer(&kd_mksound_timer);
+       input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
 
-       if (hz) {
-               list_for_each_prev(node, &kbd_handler.h_list) {
-                       struct input_handle *handle = to_handle_h(node);
-                       if (test_bit(EV_SND, handle->dev->evbit)) {
-                               if (test_bit(SND_TONE, handle->dev->sndbit)) {
-                                       input_inject_event(handle, EV_SND, SND_TONE, hz);
-                                       break;
-                               }
-                               if (test_bit(SND_BELL, handle->dev->sndbit)) {
-                                       input_inject_event(handle, EV_SND, SND_BELL, 1);
-                                       break;
-                               }
-                       }
-               }
-               if (ticks)
-                       mod_timer(&kd_mksound_timer, jiffies + ticks);
-       } else
-               kd_nosound(0);
+       if (hz && ticks)
+               mod_timer(&kd_mksound_timer, jiffies + ticks);
 }
 EXPORT_SYMBOL(kd_mksound);
 
@@ -269,27 +275,34 @@ EXPORT_SYMBOL(kd_mksound);
  * Setting the keyboard rate.
  */
 
-int kbd_rate(struct kbd_repeat *rep)
+static int kbd_rate_helper(struct input_handle *handle, void *data)
 {
-       struct list_head *node;
-       unsigned int d = 0;
-       unsigned int p = 0;
-
-       list_for_each(node, &kbd_handler.h_list) {
-               struct input_handle *handle = to_handle_h(node);
-               struct input_dev *dev = handle->dev;
-
-               if (test_bit(EV_REP, dev->evbit)) {
-                       if (rep->delay > 0)
-                               input_inject_event(handle, EV_REP, REP_DELAY, rep->delay);
-                       if (rep->period > 0)
-                               input_inject_event(handle, EV_REP, REP_PERIOD, rep->period);
-                       d = dev->rep[REP_DELAY];
-                       p = dev->rep[REP_PERIOD];
-               }
+       struct input_dev *dev = handle->dev;
+       struct kbd_repeat *rep = data;
+
+       if (test_bit(EV_REP, dev->evbit)) {
+
+               if (rep[0].delay > 0)
+                       input_inject_event(handle,
+                                          EV_REP, REP_DELAY, rep[0].delay);
+               if (rep[0].period > 0)
+                       input_inject_event(handle,
+                                          EV_REP, REP_PERIOD, rep[0].period);
+
+               rep[1].delay = dev->rep[REP_DELAY];
+               rep[1].period = dev->rep[REP_PERIOD];
        }
-       rep->delay  = d;
-       rep->period = p;
+
+       return 0;
+}
+
+int kbd_rate(struct kbd_repeat *rep)
+{
+       struct kbd_repeat data[2] = { *rep };
+
+       input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper);
+       *rep = data[1]; /* Copy currently used settings */
+
        return 0;
 }
 
@@ -997,36 +1010,36 @@ static inline unsigned char getleds(void)
        return leds;
 }
 
+static int kbd_update_leds_helper(struct input_handle *handle, void *data)
+{
+       unsigned char leds = *(unsigned char *)data;
+
+       if (test_bit(EV_LED, handle->dev->evbit)) {
+               input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
+               input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & 0x02));
+               input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & 0x04));
+               input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
+       }
+
+       return 0;
+}
+
 /*
- * This routine is the bottom half of the keyboard interrupt
- * routine, and runs with all interrupts enabled. It does
- * console changing, led setting and copy_to_cooked, which can
- * take a reasonably long time.
- *
- * Aside from timing (which isn't really that important for
- * keyboard interrupts as they happen often), using the software
- * interrupt routines for this thing allows us to easily mask
- * this when we don't want any of the above to happen.
- * This allows for easy and efficient race-condition prevention
- * for kbd_start => input_inject_event(dev, EV_LED, ...) => ...
+ * This is the tasklet that updates LED state on all keyboards
+ * attached to the box. The reason we use tasklet is that we
+ * need to handle the scenario when keyboard handler is not
+ * registered yet but we already getting updates form VT to
+ * update led state.
  */
-
 static void kbd_bh(unsigned long dummy)
 {
-       struct list_head *node;
        unsigned char leds = getleds();
 
        if (leds != ledstate) {
-               list_for_each(node, &kbd_handler.h_list) {
-                       struct input_handle *handle = to_handle_h(node);
-                       input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
-                       input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & 0x02));
-                       input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & 0x04));
-                       input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
-               }
+               input_handler_for_each_handle(&kbd_handler, &leds,
+                                             kbd_update_leds_helper);
+               ledstate = leds;
        }
-
-       ledstate = leds;
 }
 
 DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
@@ -1136,7 +1149,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char u
 static void kbd_rawcode(unsigned char data)
 {
        struct vc_data *vc = vc_cons[fg_console].d;
-       kbd = kbd_table + fg_console;
+       kbd = kbd_table + vc->vc_num;
        if (kbd->kbdmode == VC_RAW)
                put_queue(vc, data);
 }
@@ -1157,7 +1170,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
                tty->driver_data = vc;
        }
 
-       kbd = kbd_table + fg_console;
+       kbd = kbd_table + vc->vc_num;
 
        if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
                sysrq_alt = down ? keycode : 0;
@@ -1296,10 +1309,16 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 static void kbd_event(struct input_handle *handle, unsigned int event_type,
                      unsigned int event_code, int value)
 {
+       /* We are called with interrupts disabled, just take the lock */
+       spin_lock(&kbd_event_lock);
+
        if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
                kbd_rawcode(value);
        if (event_type == EV_KEY)
                kbd_keycode(event_code, value, HW_RAW(handle->dev));
+
+       spin_unlock(&kbd_event_lock);
+
        tasklet_schedule(&keyboard_tasklet);
        do_poke_blanked_console = 1;
        schedule_console_callback();
@@ -1363,15 +1382,11 @@ static void kbd_disconnect(struct input_handle *handle)
  */
 static void kbd_start(struct input_handle *handle)
 {
-       unsigned char leds = ledstate;
-
        tasklet_disable(&keyboard_tasklet);
-       if (leds != 0xff) {
-               input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
-               input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & 0x02));
-               input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & 0x04));
-               input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
-       }
+
+       if (ledstate != 0xff)
+               kbd_update_leds_helper(handle, &ledstate);
+
        tasklet_enable(&keyboard_tasklet);
 }
 
index ad82ec9..30eff80 100644 (file)
@@ -5,7 +5,7 @@
  *
  *  Added devfs support. 
  *    Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
- *  Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
+ *  Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
  */
 
 #include <linux/mm.h>
index 1997270..ecb89d7 100644 (file)
@@ -248,7 +248,7 @@ static const struct vm_operations_struct mspec_vm_ops = {
 /*
  * mspec_mmap
  *
- * Called when mmaping the device.  Initializes the vma with a fault handler
+ * Called when mmapping the device.  Initializes the vma with a fault handler
  * and private data structure necessary to allocate, track, and free the
  * underlying pages.
  */
index 6934025..c1d8b54 100644 (file)
@@ -602,7 +602,7 @@ static void receive_char(struct r3964_info *pInfo, const unsigned char c)
                }
                break;
        case R3964_WAIT_FOR_RX_REPEAT:
-               /* FALLTROUGH */
+               /* FALLTHROUGH */
        case R3964_IDLE:
                if (c == STX) {
                        /* Prevent rx_queue from overflow: */
index 2100a8f..fdbcc9f 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/smp_lock.h>
 
 #include <asm/system.h>
 
@@ -263,10 +264,16 @@ static ssize_t nvram_write(struct file *file, const char __user *buf,
        unsigned char contents[NVRAM_BYTES];
        unsigned i = *ppos;
        unsigned char *tmp;
-       int len;
 
-       len = (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count;
-       if (copy_from_user(contents, buf, len))
+       if (i >= NVRAM_BYTES)
+               return 0;       /* Past EOF */
+
+       if (count > NVRAM_BYTES - i)
+               count = NVRAM_BYTES - i;
+       if (count > NVRAM_BYTES)
+               return -EFAULT; /* Can't happen, but prove it to gcc */
+
+       if (copy_from_user(contents, buf, count))
                return -EFAULT;
 
        spin_lock_irq(&rtc_lock);
@@ -274,7 +281,7 @@ static ssize_t nvram_write(struct file *file, const char __user *buf,
        if (!__nvram_check_checksum())
                goto checksum_err;
 
-       for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)
+       for (tmp = contents; count--; ++i, ++tmp)
                __nvram_write_byte(*tmp, i);
 
        __nvram_set_checksum();
index 20ed73f..46e9637 100644 (file)
@@ -67,7 +67,7 @@
 typedef struct COST_ROUTE COST_ROUTE;
 struct COST_ROUTE {
        unsigned char cost;     /* Cost down this link */
-       unsigned char route[NODE_BYTES];        /* Nodes thorough this route */
+       unsigned char route[NODE_BYTES];        /* Nodes through this route */
 };
 
 typedef struct ROUTE_STR ROUTE_STR;
index 8e67d5c..6bd5f88 100644 (file)
@@ -315,7 +315,7 @@ EXPORT_SYMBOL(tty_termios_input_baud_rate);
  *     For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
  *     we need to carefully set the bits when the user does not get the
  *     desired speed. We allow small margins and preserve as much of possible
- *     of the input intent to keep compatiblity.
+ *     of the input intent to keep compatibility.
  *
  *     Locking: Caller should hold termios lock. This is already held
  *     when calling this function from the driver termios handler.
index 70b5964..724c164 100644 (file)
@@ -21,7 +21,7 @@ struct cpuidle_governor *cpuidle_curr_governor;
  * __cpuidle_find_governor - finds a governor of the specified name
  * @str: the name
  *
- * Must be called with cpuidle_lock aquired.
+ * Must be called with cpuidle_lock acquired.
  */
 static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
 {
@@ -39,7 +39,7 @@ static struct cpuidle_governor * __cpuidle_find_governor(const char *str)
  * @gov: the new target governor
  *
  * NOTE: "gov" can be NULL to specify disabled
- * Must be called with cpuidle_lock aquired.
+ * Must be called with cpuidle_lock acquired.
  */
 int cpuidle_switch_governor(struct cpuidle_governor *gov)
 {
index 5f753fc..09ad915 100644 (file)
@@ -863,7 +863,7 @@ static int hifn_init_pubrng(struct hifn_device *dev)
                dev->dmareg |= HIFN_DMAIER_PUBDONE;
                hifn_write_1(dev, HIFN_1_DMA_IER, dev->dmareg);
 
-               dprintk("Chip %s: Public key engine has been sucessfully "
+               dprintk("Chip %s: Public key engine has been successfully "
                                "initialised.\n", dev->name);
        }
 
index 7585c41..c52ac9e 100644 (file)
@@ -99,7 +99,7 @@ static struct at_desc *atc_alloc_descriptor(struct dma_chan *chan,
 }
 
 /**
- * atc_desc_get - get a unsused descriptor from free_list
+ * atc_desc_get - get an unused descriptor from free_list
  * @atchan: channel we want a new descriptor for
  */
 static struct at_desc *atc_desc_get(struct at_dma_chan *atchan)
index a38831c..df5b684 100644 (file)
@@ -13,32 +13,56 @@ module_param(report_gart_errors, int, 0644);
 static int ecc_enable_override;
 module_param(ecc_enable_override, int, 0644);
 
+static struct msr *msrs;
+
 /* Lookup table for all possible MC control instances */
 struct amd64_pvt;
 static struct mem_ctl_info *mci_lookup[EDAC_MAX_NUMNODES];
 static struct amd64_pvt *pvt_lookup[EDAC_MAX_NUMNODES];
 
 /*
- * See F2x80 for K8 and F2x[1,0]80 for Fam10 and later. The table below is only
- * for DDR2 DRAM mapping.
+ * Address to DRAM bank mapping: see F2x80 for K8 and F2x[1,0]80 for Fam10 and
+ * later.
  */
-u32 revf_quad_ddr2_shift[] = {
-       0,      /* 0000b NULL DIMM (128mb) */
-       28,     /* 0001b 256mb */
-       29,     /* 0010b 512mb */
-       29,     /* 0011b 512mb */
-       29,     /* 0100b 512mb */
-       30,     /* 0101b 1gb */
-       30,     /* 0110b 1gb */
-       31,     /* 0111b 2gb */
-       31,     /* 1000b 2gb */
-       32,     /* 1001b 4gb */
-       32,     /* 1010b 4gb */
-       33,     /* 1011b 8gb */
-       0,      /* 1100b future */
-       0,      /* 1101b future */
-       0,      /* 1110b future */
-       0       /* 1111b future */
+static int ddr2_dbam_revCG[] = {
+                          [0]          = 32,
+                          [1]          = 64,
+                          [2]          = 128,
+                          [3]          = 256,
+                          [4]          = 512,
+                          [5]          = 1024,
+                          [6]          = 2048,
+};
+
+static int ddr2_dbam_revD[] = {
+                          [0]          = 32,
+                          [1]          = 64,
+                          [2 ... 3]    = 128,
+                          [4]          = 256,
+                          [5]          = 512,
+                          [6]          = 256,
+                          [7]          = 512,
+                          [8 ... 9]    = 1024,
+                          [10]         = 2048,
+};
+
+static int ddr2_dbam[] = { [0]         = 128,
+                          [1]          = 256,
+                          [2 ... 4]    = 512,
+                          [5 ... 6]    = 1024,
+                          [7 ... 8]    = 2048,
+                          [9 ... 10]   = 4096,
+                          [11]         = 8192,
+};
+
+static int ddr3_dbam[] = { [0]         = -1,
+                          [1]          = 256,
+                          [2]          = 512,
+                          [3 ... 4]    = -1,
+                          [5 ... 6]    = 1024,
+                          [7 ... 8]    = 2048,
+                          [9 ... 10]   = 4096,
+                          [11] = 8192,
 };
 
 /*
@@ -164,11 +188,9 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
 {
        struct amd64_pvt *pvt = mci->pvt_info;
        u32 scrubval = 0;
-       int status = -1, i, ret = 0;
+       int status = -1, i;
 
-       ret = pci_read_config_dword(pvt->misc_f3_ctl, K8_SCRCTRL, &scrubval);
-       if (ret)
-               debugf0("Reading K8_SCRCTRL failed\n");
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_SCRCTRL, &scrubval);
 
        scrubval = scrubval & 0x001F;
 
@@ -189,7 +211,7 @@ static int amd64_get_scrub_rate(struct mem_ctl_info *mci, u32 *bw)
 /* Map from a CSROW entry to the mask entry that operates on it */
 static inline u32 amd64_map_to_dcs_mask(struct amd64_pvt *pvt, int csrow)
 {
-       if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_F)
+       if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F)
                return csrow;
        else
                return csrow >> 1;
@@ -437,7 +459,7 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
        u64 base;
 
        /* only revE and later have the DRAM Hole Address Register */
-       if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_E) {
+       if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_E) {
                debugf1("  revision %d for node %d does not support DHAR\n",
                        pvt->ext_model, pvt->mc_node_id);
                return 1;
@@ -743,21 +765,6 @@ static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
        *input_addr_max = base | mask | pvt->dcs_mask_notused;
 }
 
-/*
- * Extract error address from MCA NB Address Low (section 3.6.4.5) and MCA NB
- * Address High (section 3.6.4.6) register values and return the result. Address
- * is located in the info structure (nbeah and nbeal), the encoding is device
- * specific.
- */
-static u64 extract_error_address(struct mem_ctl_info *mci,
-                                struct err_regs *info)
-{
-       struct amd64_pvt *pvt = mci->pvt_info;
-
-       return pvt->ops->get_error_address(mci, info);
-}
-
-
 /* Map the Error address to a PAGE and PAGE OFFSET. */
 static inline void error_address_to_page_and_offset(u64 error_address,
                                                    u32 *page, u32 *offset)
@@ -787,7 +794,7 @@ static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
        return csrow;
 }
 
-static int get_channel_from_ecc_syndrome(unsigned short syndrome);
+static int get_channel_from_ecc_syndrome(struct mem_ctl_info *, u16);
 
 static void amd64_cpu_display_info(struct amd64_pvt *pvt)
 {
@@ -797,7 +804,7 @@ static void amd64_cpu_display_info(struct amd64_pvt *pvt)
                edac_printk(KERN_DEBUG, EDAC_MC, "F10h CPU detected\n");
        else if (boot_cpu_data.x86 == 0xf)
                edac_printk(KERN_DEBUG, EDAC_MC, "%s detected\n",
-                       (pvt->ext_model >= OPTERON_CPU_REV_F) ?
+                       (pvt->ext_model >= K8_REV_F) ?
                        "Rev F or later" : "Rev E or earlier");
        else
                /* we'll hardly ever ever get here */
@@ -813,7 +820,7 @@ static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
        int bit;
        enum dev_type edac_cap = EDAC_FLAG_NONE;
 
-       bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= OPTERON_CPU_REV_F)
+       bit = (boot_cpu_data.x86 > 0xf || pvt->ext_model >= K8_REV_F)
                ? 19
                : 17;
 
@@ -824,111 +831,86 @@ static enum edac_type amd64_determine_edac_cap(struct amd64_pvt *pvt)
 }
 
 
-static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt,
-                                        int ganged);
+static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt);
+
+static void amd64_dump_dramcfg_low(u32 dclr, int chan)
+{
+       debugf1("F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
+
+       debugf1("  DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
+               (dclr & BIT(16)) ?  "un" : "",
+               (dclr & BIT(19)) ? "yes" : "no");
+
+       debugf1("  PAR/ERR parity: %s\n",
+               (dclr & BIT(8)) ?  "enabled" : "disabled");
+
+       debugf1("  DCT 128bit mode width: %s\n",
+               (dclr & BIT(11)) ?  "128b" : "64b");
+
+       debugf1("  x4 logical DIMMs present: L0: %s L1: %s L2: %s L3: %s\n",
+               (dclr & BIT(12)) ?  "yes" : "no",
+               (dclr & BIT(13)) ?  "yes" : "no",
+               (dclr & BIT(14)) ?  "yes" : "no",
+               (dclr & BIT(15)) ?  "yes" : "no");
+}
 
 /* Display and decode various NB registers for debug purposes. */
 static void amd64_dump_misc_regs(struct amd64_pvt *pvt)
 {
        int ganged;
 
-       debugf1("  nbcap:0x%8.08x DctDualCap=%s DualNode=%s 8-Node=%s\n",
-               pvt->nbcap,
-               (pvt->nbcap & K8_NBCAP_DCT_DUAL) ? "True" : "False",
-               (pvt->nbcap & K8_NBCAP_DUAL_NODE) ? "True" : "False",
-               (pvt->nbcap & K8_NBCAP_8_NODE) ? "True" : "False");
-       debugf1("    ECC Capable=%s   ChipKill Capable=%s\n",
-               (pvt->nbcap & K8_NBCAP_SECDED) ? "True" : "False",
-               (pvt->nbcap & K8_NBCAP_CHIPKILL) ? "True" : "False");
-       debugf1("  DramCfg0-low=0x%08x DIMM-ECC=%s Parity=%s Width=%s\n",
-               pvt->dclr0,
-               (pvt->dclr0 & BIT(19)) ?  "Enabled" : "Disabled",
-               (pvt->dclr0 & BIT(8)) ?  "Enabled" : "Disabled",
-               (pvt->dclr0 & BIT(11)) ?  "128b" : "64b");
-       debugf1("    DIMM x4 Present: L0=%s L1=%s L2=%s L3=%s  DIMM Type=%s\n",
-               (pvt->dclr0 & BIT(12)) ?  "Y" : "N",
-               (pvt->dclr0 & BIT(13)) ?  "Y" : "N",
-               (pvt->dclr0 & BIT(14)) ?  "Y" : "N",
-               (pvt->dclr0 & BIT(15)) ?  "Y" : "N",
-               (pvt->dclr0 & BIT(16)) ?  "UN-Buffered" : "Buffered");
-
-
-       debugf1("  online-spare: 0x%8.08x\n", pvt->online_spare);
+       debugf1("F3xE8 (NB Cap): 0x%08x\n", pvt->nbcap);
 
-       if (boot_cpu_data.x86 == 0xf) {
-               debugf1("  dhar: 0x%8.08x Base=0x%08x Offset=0x%08x\n",
-                       pvt->dhar, dhar_base(pvt->dhar),
-                       k8_dhar_offset(pvt->dhar));
-               debugf1("      DramHoleValid=%s\n",
-                       (pvt->dhar & DHAR_VALID) ?  "True" : "False");
+       debugf1("  NB two channel DRAM capable: %s\n",
+               (pvt->nbcap & K8_NBCAP_DCT_DUAL) ? "yes" : "no");
 
-               debugf1("  dbam-dkt: 0x%8.08x\n", pvt->dbam0);
+       debugf1("  ECC capable: %s, ChipKill ECC capable: %s\n",
+               (pvt->nbcap & K8_NBCAP_SECDED) ? "yes" : "no",
+               (pvt->nbcap & K8_NBCAP_CHIPKILL) ? "yes" : "no");
 
-               /* everything below this point is Fam10h and above */
-               return;
+       amd64_dump_dramcfg_low(pvt->dclr0, 0);
 
-       } else {
-               debugf1("  dhar: 0x%8.08x Base=0x%08x Offset=0x%08x\n",
-                       pvt->dhar, dhar_base(pvt->dhar),
-                       f10_dhar_offset(pvt->dhar));
-               debugf1("    DramMemHoistValid=%s DramHoleValid=%s\n",
-                       (pvt->dhar & F10_DRAM_MEM_HOIST_VALID) ?
-                       "True" : "False",
-                       (pvt->dhar & DHAR_VALID) ?
-                       "True" : "False");
-       }
+       debugf1("F3xB0 (Online Spare): 0x%08x\n", pvt->online_spare);
 
-       /* Only if NOT ganged does dcl1 have valid info */
-       if (!dct_ganging_enabled(pvt)) {
-               debugf1("  DramCfg1-low=0x%08x DIMM-ECC=%s Parity=%s "
-                       "Width=%s\n", pvt->dclr1,
-                       (pvt->dclr1 & BIT(19)) ?  "Enabled" : "Disabled",
-                       (pvt->dclr1 & BIT(8)) ?  "Enabled" : "Disabled",
-                       (pvt->dclr1 & BIT(11)) ?  "128b" : "64b");
-               debugf1("    DIMM x4 Present: L0=%s L1=%s L2=%s L3=%s  "
-                       "DIMM Type=%s\n",
-                       (pvt->dclr1 & BIT(12)) ?  "Y" : "N",
-                       (pvt->dclr1 & BIT(13)) ?  "Y" : "N",
-                       (pvt->dclr1 & BIT(14)) ?  "Y" : "N",
-                       (pvt->dclr1 & BIT(15)) ?  "Y" : "N",
-                       (pvt->dclr1 & BIT(16)) ?  "UN-Buffered" : "Buffered");
+       debugf1("F1xF0 (DRAM Hole Address): 0x%08x, base: 0x%08x, "
+                       "offset: 0x%08x\n",
+                       pvt->dhar,
+                       dhar_base(pvt->dhar),
+                       (boot_cpu_data.x86 == 0xf) ? k8_dhar_offset(pvt->dhar)
+                                                  : f10_dhar_offset(pvt->dhar));
+
+       debugf1("  DramHoleValid: %s\n",
+               (pvt->dhar & DHAR_VALID) ? "yes" : "no");
+
+       /* everything below this point is Fam10h and above */
+       if (boot_cpu_data.x86 == 0xf) {
+               amd64_debug_display_dimm_sizes(0, pvt);
+               return;
        }
 
+       /* Only if NOT ganged does dclr1 have valid info */
+       if (!dct_ganging_enabled(pvt))
+               amd64_dump_dramcfg_low(pvt->dclr1, 1);
+
        /*
         * Determine if ganged and then dump memory sizes for first controller,
         * and if NOT ganged dump info for 2nd controller.
         */
        ganged = dct_ganging_enabled(pvt);
 
-       f10_debug_display_dimm_sizes(0, pvt, ganged);
+       amd64_debug_display_dimm_sizes(0, pvt);
 
        if (!ganged)
-               f10_debug_display_dimm_sizes(1, pvt, ganged);
+               amd64_debug_display_dimm_sizes(1, pvt);
 }
 
 /* Read in both of DBAM registers */
 static void amd64_read_dbam_reg(struct amd64_pvt *pvt)
 {
-       int err = 0;
-       unsigned int reg;
-
-       reg = DBAM0;
-       err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dbam0);
-       if (err)
-               goto err_reg;
-
-       if (boot_cpu_data.x86 >= 0x10) {
-               reg = DBAM1;
-               err = pci_read_config_dword(pvt->dram_f2_ctl, reg, &pvt->dbam1);
-
-               if (err)
-                       goto err_reg;
-       }
-
-       return;
+       amd64_read_pci_cfg(pvt->dram_f2_ctl, DBAM0, &pvt->dbam0);
 
-err_reg:
-       debugf0("Error reading F2x%03x.\n", reg);
+       if (boot_cpu_data.x86 >= 0x10)
+               amd64_read_pci_cfg(pvt->dram_f2_ctl, DBAM1, &pvt->dbam1);
 }
 
 /*
@@ -963,7 +945,7 @@ err_reg:
 static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt)
 {
 
-       if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_F) {
+       if (boot_cpu_data.x86 == 0xf && pvt->ext_model < K8_REV_F) {
                pvt->dcsb_base          = REV_E_DCSB_BASE_BITS;
                pvt->dcsm_mask          = REV_E_DCSM_MASK_BITS;
                pvt->dcs_mask_notused   = REV_E_DCS_NOTUSED_BITS;
@@ -991,28 +973,21 @@ static void amd64_set_dct_base_and_mask(struct amd64_pvt *pvt)
  */
 static void amd64_read_dct_base_mask(struct amd64_pvt *pvt)
 {
-       int cs, reg, err = 0;
+       int cs, reg;
 
        amd64_set_dct_base_and_mask(pvt);
 
        for (cs = 0; cs < pvt->cs_count; cs++) {
                reg = K8_DCSB0 + (cs * 4);
-               err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
-                                               &pvt->dcsb0[cs]);
-               if (unlikely(err))
-                       debugf0("Reading K8_DCSB0[%d] failed\n", cs);
-               else
+               if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg, &pvt->dcsb0[cs]))
                        debugf0("  DCSB0[%d]=0x%08x reg: F2x%x\n",
                                cs, pvt->dcsb0[cs], reg);
 
                /* If DCT are NOT ganged, then read in DCT1's base */
                if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
                        reg = F10_DCSB1 + (cs * 4);
-                       err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
-                                                       &pvt->dcsb1[cs]);
-                       if (unlikely(err))
-                               debugf0("Reading F10_DCSB1[%d] failed\n", cs);
-                       else
+                       if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg,
+                                               &pvt->dcsb1[cs]))
                                debugf0("  DCSB1[%d]=0x%08x reg: F2x%x\n",
                                        cs, pvt->dcsb1[cs], reg);
                } else {
@@ -1022,26 +997,20 @@ static void amd64_read_dct_base_mask(struct amd64_pvt *pvt)
 
        for (cs = 0; cs < pvt->num_dcsm; cs++) {
                reg = K8_DCSM0 + (cs * 4);
-               err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
-                                       &pvt->dcsm0[cs]);
-               if (unlikely(err))
-                       debugf0("Reading K8_DCSM0 failed\n");
-               else
+               if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg, &pvt->dcsm0[cs]))
                        debugf0("    DCSM0[%d]=0x%08x reg: F2x%x\n",
                                cs, pvt->dcsm0[cs], reg);
 
                /* If DCT are NOT ganged, then read in DCT1's mask */
                if (boot_cpu_data.x86 >= 0x10 && !dct_ganging_enabled(pvt)) {
                        reg = F10_DCSM1 + (cs * 4);
-                       err = pci_read_config_dword(pvt->dram_f2_ctl, reg,
-                                       &pvt->dcsm1[cs]);
-                       if (unlikely(err))
-                               debugf0("Reading F10_DCSM1[%d] failed\n", cs);
-                       else
+                       if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, reg,
+                                               &pvt->dcsm1[cs]))
                                debugf0("    DCSM1[%d]=0x%08x reg: F2x%x\n",
                                        cs, pvt->dcsm1[cs], reg);
-               } else
+               } else {
                        pvt->dcsm1[cs] = 0;
+               }
        }
 }
 
@@ -1049,18 +1018,16 @@ static enum mem_type amd64_determine_memory_type(struct amd64_pvt *pvt)
 {
        enum mem_type type;
 
-       if (boot_cpu_data.x86 >= 0x10 || pvt->ext_model >= OPTERON_CPU_REV_F) {
-               /* Rev F and later */
-               type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
+       if (boot_cpu_data.x86 >= 0x10 || pvt->ext_model >= K8_REV_F) {
+               if (pvt->dchr0 & DDR3_MODE)
+                       type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
+               else
+                       type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
        } else {
-               /* Rev E and earlier */
                type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
        }
 
-       debugf1("  Memory type is: %s\n",
-               (type == MEM_DDR2) ? "MEM_DDR2" :
-               (type == MEM_RDDR2) ? "MEM_RDDR2" :
-               (type == MEM_DDR) ? "MEM_DDR" : "MEM_RDDR");
+       debugf1("  Memory type is: %s\n", edac_mem_types[type]);
 
        return type;
 }
@@ -1078,11 +1045,11 @@ static int k8_early_channel_count(struct amd64_pvt *pvt)
 {
        int flag, err = 0;
 
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+       err = amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
        if (err)
                return err;
 
-       if ((boot_cpu_data.x86_model >> 4) >= OPTERON_CPU_REV_F) {
+       if ((boot_cpu_data.x86_model >> 4) >= K8_REV_F) {
                /* RevF (NPT) and later */
                flag = pvt->dclr0 & F10_WIDTH_128;
        } else {
@@ -1114,22 +1081,15 @@ static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
 {
        u32 low;
        u32 off = dram << 3;    /* 8 bytes between DRAM entries */
-       int err;
 
-       err = pci_read_config_dword(pvt->addr_f1_ctl,
-                                   K8_DRAM_BASE_LOW + off, &low);
-       if (err)
-               debugf0("Reading K8_DRAM_BASE_LOW failed\n");
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, K8_DRAM_BASE_LOW + off, &low);
 
        /* Extract parts into separate data entries */
        pvt->dram_base[dram] = ((u64) low & 0xFFFF0000) << 8;
        pvt->dram_IntlvEn[dram] = (low >> 8) & 0x7;
        pvt->dram_rw_en[dram] = (low & 0x3);
 
-       err = pci_read_config_dword(pvt->addr_f1_ctl,
-                                   K8_DRAM_LIMIT_LOW + off, &low);
-       if (err)
-               debugf0("Reading K8_DRAM_LIMIT_LOW failed\n");
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, K8_DRAM_LIMIT_LOW + off, &low);
 
        /*
         * Extract parts into separate data entries. Limit is the HIGHEST memory
@@ -1142,7 +1102,7 @@ static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
 
 static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
                                        struct err_regs *info,
-                                       u64 SystemAddress)
+                                       u64 sys_addr)
 {
        struct mem_ctl_info *src_mci;
        unsigned short syndrome;
@@ -1155,7 +1115,7 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
 
        /* CHIPKILL enabled */
        if (info->nbcfg & K8_NBCFG_CHIPKILL) {
-               channel = get_channel_from_ecc_syndrome(syndrome);
+               channel = get_channel_from_ecc_syndrome(mci, syndrome);
                if (channel < 0) {
                        /*
                         * Syndrome didn't map, so we don't know which of the
@@ -1177,64 +1137,46 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
                 * was obtained from email communication with someone at AMD.
                 * (Wish the email was placed in this comment - norsk)
                 */
-               channel = ((SystemAddress & BIT(3)) != 0);
+               channel = ((sys_addr & BIT(3)) != 0);
        }
 
        /*
         * Find out which node the error address belongs to. This may be
         * different from the node that detected the error.
         */
-       src_mci = find_mc_by_sys_addr(mci, SystemAddress);
+       src_mci = find_mc_by_sys_addr(mci, sys_addr);
        if (!src_mci) {
                amd64_mc_printk(mci, KERN_ERR,
                             "failed to map error address 0x%lx to a node\n",
-                            (unsigned long)SystemAddress);
+                            (unsigned long)sys_addr);
                edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
                return;
        }
 
-       /* Now map the SystemAddress to a CSROW */
-       csrow = sys_addr_to_csrow(src_mci, SystemAddress);
+       /* Now map the sys_addr to a CSROW */
+       csrow = sys_addr_to_csrow(src_mci, sys_addr);
        if (csrow < 0) {
                edac_mc_handle_ce_no_info(src_mci, EDAC_MOD_STR);
        } else {
-               error_address_to_page_and_offset(SystemAddress, &page, &offset);
+               error_address_to_page_and_offset(sys_addr, &page, &offset);
 
                edac_mc_handle_ce(src_mci, page, offset, syndrome, csrow,
                                  channel, EDAC_MOD_STR);
        }
 }
 
-/*
- * determrine the number of PAGES in for this DIMM's size based on its DRAM
- * Address Mapping.
- *
- * First step is to calc the number of bits to shift a value of 1 left to
- * indicate show many pages. Start with the DBAM value as the starting bits,
- * then proceed to adjust those shift bits, based on CPU rev and the table.
- * See BKDG on the DBAM
- */
-static int k8_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
+static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
 {
-       int nr_pages;
+       int *dbam_map;
 
-       if (pvt->ext_model >= OPTERON_CPU_REV_F) {
-               nr_pages = 1 << (revf_quad_ddr2_shift[dram_map] - PAGE_SHIFT);
-       } else {
-               /*
-                * RevE and less section; this line is tricky. It collapses the
-                * table used by RevD and later to one that matches revisions CG
-                * and earlier.
-                */
-               dram_map -= (pvt->ext_model >= OPTERON_CPU_REV_D) ?
-                               (dram_map > 8 ? 4 : (dram_map > 5 ?
-                               3 : (dram_map > 2 ? 1 : 0))) : 0;
-
-               /* 25 shift is 32MiB minimum DIMM size in RevE and prior */
-               nr_pages = 1 << (dram_map + 25 - PAGE_SHIFT);
-       }
+       if (pvt->ext_model >= K8_REV_F)
+               dbam_map = ddr2_dbam;
+       else if (pvt->ext_model >= K8_REV_D)
+               dbam_map = ddr2_dbam_revD;
+       else
+               dbam_map = ddr2_dbam_revCG;
 
-       return nr_pages;
+       return dbam_map[cs_mode];
 }
 
 /*
@@ -1248,34 +1190,24 @@ static int k8_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
 static int f10_early_channel_count(struct amd64_pvt *pvt)
 {
        int dbams[] = { DBAM0, DBAM1 };
-       int err = 0, channels = 0;
-       int i, j;
+       int i, j, channels = 0;
        u32 dbam;
 
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
-       if (err)
-               goto err_reg;
-
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1, &pvt->dclr1);
-       if (err)
-               goto err_reg;
-
        /* If we are in 128 bit mode, then we are using 2 channels */
        if (pvt->dclr0 & F10_WIDTH_128) {
-               debugf0("Data WIDTH is 128 bits - 2 channels\n");
                channels = 2;
                return channels;
        }
 
        /*
-        * Need to check if in UN-ganged mode: In such, there are 2 channels,
-        * but they are NOT in 128 bit mode and thus the above 'dcl0' status bit
-        * will be OFF.
+        * Need to check if in unganged mode: In such, there are 2 channels,
+        * but they are not in 128 bit mode and thus the above 'dclr0' status
+        * bit will be OFF.
         *
         * Need to check DCT0[0] and DCT1[0] to see if only one of them has
         * their CSEnable bit on. If so, then SINGLE DIMM case.
         */
-       debugf0("Data WIDTH is NOT 128 bits - need more decoding\n");
+       debugf0("Data width is not 128 bits - need more decoding\n");
 
        /*
         * Check DRAM Bank Address Mapping values for each DIMM to see if there
@@ -1283,8 +1215,7 @@ static int f10_early_channel_count(struct amd64_pvt *pvt)
         * both controllers since DIMMs can be placed in either one.
         */
        for (i = 0; i < ARRAY_SIZE(dbams); i++) {
-               err = pci_read_config_dword(pvt->dram_f2_ctl, dbams[i], &dbam);
-               if (err)
+               if (amd64_read_pci_cfg(pvt->dram_f2_ctl, dbams[i], &dbam))
                        goto err_reg;
 
                for (j = 0; j < 4; j++) {
@@ -1295,6 +1226,9 @@ static int f10_early_channel_count(struct amd64_pvt *pvt)
                }
        }
 
+       if (channels > 2)
+               channels = 2;
+
        debugf0("MCT channel count: %d\n", channels);
 
        return channels;
@@ -1304,9 +1238,16 @@ err_reg:
 
 }
 
-static int f10_dbam_map_to_pages(struct amd64_pvt *pvt, int dram_map)
+static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, int cs_mode)
 {
-       return 1 << (revf_quad_ddr2_shift[dram_map] - PAGE_SHIFT);
+       int *dbam_map;
+
+       if (pvt->dchr0 & DDR3_MODE || pvt->dchr1 & DDR3_MODE)
+               dbam_map = ddr3_dbam;
+       else
+               dbam_map = ddr2_dbam;
+
+       return dbam_map[cs_mode];
 }
 
 /* Enable extended configuration access via 0xCF8 feature */
@@ -1314,7 +1255,7 @@ static void amd64_setup(struct amd64_pvt *pvt)
 {
        u32 reg;
 
-       pci_read_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
 
        pvt->flags.cf8_extcfg = !!(reg & F10_NB_CFG_LOW_ENABLE_EXT_CFG);
        reg |= F10_NB_CFG_LOW_ENABLE_EXT_CFG;
@@ -1326,7 +1267,7 @@ static void amd64_teardown(struct amd64_pvt *pvt)
 {
        u32 reg;
 
-       pci_read_config_dword(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, F10_NB_CFG_HIGH, &reg);
 
        reg &= ~F10_NB_CFG_LOW_ENABLE_EXT_CFG;
        if (pvt->flags.cf8_extcfg)
@@ -1355,10 +1296,10 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
        high_offset = F10_DRAM_BASE_HIGH + (dram << 3);
 
        /* read the 'raw' DRAM BASE Address register */
-       pci_read_config_dword(pvt->addr_f1_ctl, low_offset, &low_base);
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, low_offset, &low_base);
 
        /* Read from the ECS data register */
-       pci_read_config_dword(pvt->addr_f1_ctl, high_offset, &high_base);
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, high_offset, &high_base);
 
        /* Extract parts into separate data entries */
        pvt->dram_rw_en[dram] = (low_base & 0x3);
@@ -1375,13 +1316,10 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
        high_offset = F10_DRAM_LIMIT_HIGH + (dram << 3);
 
        /* read the 'raw' LIMIT registers */
-       pci_read_config_dword(pvt->addr_f1_ctl, low_offset, &low_limit);
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, low_offset, &low_limit);
 
        /* Read from the ECS data register for the HIGH portion */
-       pci_read_config_dword(pvt->addr_f1_ctl, high_offset, &high_limit);
-
-       debugf0("  HW Regs: BASE=0x%08x-%08x      LIMIT=  0x%08x-%08x\n",
-               high_base, low_base, high_limit, low_limit);
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, high_offset, &high_limit);
 
        pvt->dram_DstNode[dram] = (low_limit & 0x7);
        pvt->dram_IntlvSel[dram] = (low_limit >> 8) & 0x7;
@@ -1397,32 +1335,35 @@ static void f10_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
 
 static void f10_read_dram_ctl_register(struct amd64_pvt *pvt)
 {
-       int err = 0;
 
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_LOW,
-                                   &pvt->dram_ctl_select_low);
-       if (err) {
-               debugf0("Reading F10_DCTL_SEL_LOW failed\n");
-       } else {
-               debugf0("DRAM_DCTL_SEL_LOW=0x%x  DctSelBaseAddr=0x%x\n",
-                       pvt->dram_ctl_select_low, dct_sel_baseaddr(pvt));
-
-               debugf0("  DRAM DCTs are=%s DRAM Is=%s DRAM-Ctl-"
-                               "sel-hi-range=%s\n",
-                       (dct_ganging_enabled(pvt) ? "GANGED" : "NOT GANGED"),
-                       (dct_dram_enabled(pvt) ? "Enabled"   : "Disabled"),
-                       (dct_high_range_enabled(pvt) ? "Enabled" : "Disabled"));
-
-               debugf0("  DctDatIntLv=%s MemCleared=%s DctSelIntLvAddr=0x%x\n",
-                       (dct_data_intlv_enabled(pvt) ? "Enabled" : "Disabled"),
-                       (dct_memory_cleared(pvt) ? "True " : "False "),
+       if (!amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCTL_SEL_LOW,
+                               &pvt->dram_ctl_select_low)) {
+               debugf0("F2x110 (DCTL Sel. Low): 0x%08x, "
+                       "High range addresses at: 0x%x\n",
+                       pvt->dram_ctl_select_low,
+                       dct_sel_baseaddr(pvt));
+
+               debugf0("  DCT mode: %s, All DCTs on: %s\n",
+                       (dct_ganging_enabled(pvt) ? "ganged" : "unganged"),
+                       (dct_dram_enabled(pvt) ? "yes"   : "no"));
+
+               if (!dct_ganging_enabled(pvt))
+                       debugf0("  Address range split per DCT: %s\n",
+                               (dct_high_range_enabled(pvt) ? "yes" : "no"));
+
+               debugf0("  DCT data interleave for ECC: %s, "
+                       "DRAM cleared since last warm reset: %s\n",
+                       (dct_data_intlv_enabled(pvt) ? "enabled" : "disabled"),
+                       (dct_memory_cleared(pvt) ? "yes" : "no"));
+
+               debugf0("  DCT channel interleave: %s, "
+                       "DCT interleave bits selector: 0x%x\n",
+                       (dct_interleave_enabled(pvt) ? "enabled" : "disabled"),
                        dct_sel_interleave_addr(pvt));
        }
 
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCTL_SEL_HIGH,
-                                   &pvt->dram_ctl_select_high);
-       if (err)
-               debugf0("Reading F10_DCTL_SEL_HIGH failed\n");
+       amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCTL_SEL_HIGH,
+                          &pvt->dram_ctl_select_high);
 }
 
 /*
@@ -1706,10 +1647,11 @@ static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
 }
 
 /*
- * This the F10h reference code from AMD to map a @sys_addr to NodeID,
- * CSROW, Channel.
+ * For reference see "2.8.5 Routing DRAM Requests" in F10 BKDG. This code maps
+ * a @sys_addr to NodeID, DCT (channel) and chip select (CSROW).
  *
- * The @sys_addr is usually an error address received from the hardware.
+ * The @sys_addr is usually an error address received from the hardware
+ * (MCX_ADDR).
  */
 static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
                                     struct err_regs *info,
@@ -1722,133 +1664,76 @@ static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
 
        csrow = f10_translate_sysaddr_to_cs(pvt, sys_addr, &nid, &chan);
 
-       if (csrow >= 0) {
-               error_address_to_page_and_offset(sys_addr, &page, &offset);
-
-               syndrome  = HIGH_SYNDROME(info->nbsl) << 8;
-               syndrome |= LOW_SYNDROME(info->nbsh);
-
-               /*
-                * Is CHIPKILL on? If so, then we can attempt to use the
-                * syndrome to isolate which channel the error was on.
-                */
-               if (pvt->nbcfg & K8_NBCFG_CHIPKILL)
-                       chan = get_channel_from_ecc_syndrome(syndrome);
-
-               if (chan >= 0) {
-                       edac_mc_handle_ce(mci, page, offset, syndrome,
-                                       csrow, chan, EDAC_MOD_STR);
-               } else {
-                       /*
-                        * Channel unknown, report all channels on this
-                        * CSROW as failed.
-                        */
-                       for (chan = 0; chan < mci->csrows[csrow].nr_channels;
-                                                               chan++) {
-                                       edac_mc_handle_ce(mci, page, offset,
-                                                       syndrome,
-                                                       csrow, chan,
-                                                       EDAC_MOD_STR);
-                       }
-               }
-
-       } else {
+       if (csrow < 0) {
                edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR);
+               return;
        }
-}
 
-/*
- * Input (@index) is the DBAM DIMM value (1 of 4) used as an index into a shift
- * table (revf_quad_ddr2_shift) which starts at 128MB DIMM size. Index of 0
- * indicates an empty DIMM slot, as reported by Hardware on empty slots.
- *
- * Normalize to 128MB by subracting 27 bit shift.
- */
-static int map_dbam_to_csrow_size(int index)
-{
-       int mega_bytes = 0;
+       error_address_to_page_and_offset(sys_addr, &page, &offset);
 
-       if (index > 0 && index <= DBAM_MAX_VALUE)
-               mega_bytes = ((128 << (revf_quad_ddr2_shift[index]-27)));
+       syndrome  = HIGH_SYNDROME(info->nbsl) << 8;
+       syndrome |= LOW_SYNDROME(info->nbsh);
+
+       /*
+        * We need the syndromes for channel detection only when we're
+        * ganged. Otherwise @chan should already contain the channel at
+        * this point.
+        */
+       if (dct_ganging_enabled(pvt) && pvt->nbcfg & K8_NBCFG_CHIPKILL)
+               chan = get_channel_from_ecc_syndrome(mci, syndrome);
 
-       return mega_bytes;
+       if (chan >= 0)
+               edac_mc_handle_ce(mci, page, offset, syndrome, csrow, chan,
+                                 EDAC_MOD_STR);
+       else
+               /*
+                * Channel unknown, report all channels on this CSROW as failed.
+                */
+               for (chan = 0; chan < mci->csrows[csrow].nr_channels; chan++)
+                       edac_mc_handle_ce(mci, page, offset, syndrome,
+                                         csrow, chan, EDAC_MOD_STR);
 }
 
 /*
- * debug routine to display the memory sizes of a DIMM (ganged or not) and it
+ * debug routine to display the memory sizes of all logical DIMMs and its
  * CSROWs as well
  */
-static void f10_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt,
-                                        int ganged)
+static void amd64_debug_display_dimm_sizes(int ctrl, struct amd64_pvt *pvt)
 {
        int dimm, size0, size1;
        u32 dbam;
        u32 *dcsb;
 
-       debugf1("  dbam%d: 0x%8.08x  CSROW is %s\n", ctrl,
-                       ctrl ? pvt->dbam1 : pvt->dbam0,
-                       ganged ? "GANGED - dbam1 not used" : "NON-GANGED");
+       if (boot_cpu_data.x86 == 0xf) {
+               /* K8 families < revF not supported yet */
+              if (pvt->ext_model < K8_REV_F)
+                       return;
+              else
+                      WARN_ON(ctrl != 0);
+       }
+
+       debugf1("F2x%d80 (DRAM Bank Address Mapping): 0x%08x\n",
+               ctrl, ctrl ? pvt->dbam1 : pvt->dbam0);
 
        dbam = ctrl ? pvt->dbam1 : pvt->dbam0;
        dcsb = ctrl ? pvt->dcsb1 : pvt->dcsb0;
 
+       edac_printk(KERN_DEBUG, EDAC_MC, "DCT%d chip selects:\n", ctrl);
+
        /* Dump memory sizes for DIMM and its CSROWs */
        for (dimm = 0; dimm < 4; dimm++) {
 
                size0 = 0;
                if (dcsb[dimm*2] & K8_DCSB_CS_ENABLE)
-                       size0 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam));
+                       size0 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
 
                size1 = 0;
                if (dcsb[dimm*2 + 1] & K8_DCSB_CS_ENABLE)
-                       size1 = map_dbam_to_csrow_size(DBAM_DIMM(dimm, dbam));
-
-               debugf1("     CTRL-%d DIMM-%d=%5dMB   CSROW-%d=%5dMB "
-                               "CSROW-%d=%5dMB\n",
-                               ctrl,
-                               dimm,
-                               size0 + size1,
-                               dimm * 2,
-                               size0,
-                               dimm * 2 + 1,
-                               size1);
-       }
-}
-
-/*
- * Very early hardware probe on pci_probe thread to determine if this module
- * supports the hardware.
- *
- * Return:
- *      0 for OK
- *      1 for error
- */
-static int f10_probe_valid_hardware(struct amd64_pvt *pvt)
-{
-       int ret = 0;
-
-       /*
-        * If we are on a DDR3 machine, we don't know yet if
-        * we support that properly at this time
-        */
-       if ((pvt->dchr0 & F10_DCHR_Ddr3Mode) ||
-           (pvt->dchr1 & F10_DCHR_Ddr3Mode)) {
-
-               amd64_printk(KERN_WARNING,
-                       "%s() This machine is running with DDR3 memory. "
-                       "This is not currently supported. "
-                       "DCHR0=0x%x DCHR1=0x%x\n",
-                       __func__, pvt->dchr0, pvt->dchr1);
-
-               amd64_printk(KERN_WARNING,
-                       "   Contact '%s' module MAINTAINER to help add"
-                       " support.\n",
-                       EDAC_MOD_STR);
-
-               ret = 1;
+                       size1 = pvt->ops->dbam_to_cs(pvt, DBAM_DIMM(dimm, dbam));
 
+               edac_printk(KERN_DEBUG, EDAC_MC, " %d: %5dMB %d: %5dMB\n",
+                           dimm * 2, size0, dimm * 2 + 1, size1);
        }
-       return ret;
 }
 
 /*
@@ -1868,11 +1753,11 @@ static struct amd64_family_type amd64_family_types[] = {
                .addr_f1_ctl = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP,
                .misc_f3_ctl = PCI_DEVICE_ID_AMD_K8_NB_MISC,
                .ops = {
-                       .early_channel_count = k8_early_channel_count,
-                       .get_error_address = k8_get_error_address,
-                       .read_dram_base_limit = k8_read_dram_base_limit,
-                       .map_sysaddr_to_csrow = k8_map_sysaddr_to_csrow,
-                       .dbam_map_to_pages = k8_dbam_map_to_pages,
+                       .early_channel_count    = k8_early_channel_count,
+                       .get_error_address      = k8_get_error_address,
+                       .read_dram_base_limit   = k8_read_dram_base_limit,
+                       .map_sysaddr_to_csrow   = k8_map_sysaddr_to_csrow,
+                       .dbam_to_cs             = k8_dbam_to_chip_select,
                }
        },
        [F10_CPUS] = {
@@ -1880,13 +1765,12 @@ static struct amd64_family_type amd64_family_types[] = {
                .addr_f1_ctl = PCI_DEVICE_ID_AMD_10H_NB_MAP,
                .misc_f3_ctl = PCI_DEVICE_ID_AMD_10H_NB_MISC,
                .ops = {
-                       .probe_valid_hardware = f10_probe_valid_hardware,
-                       .early_channel_count = f10_early_channel_count,
-                       .get_error_address = f10_get_error_address,
-                       .read_dram_base_limit = f10_read_dram_base_limit,
-                       .read_dram_ctl_register = f10_read_dram_ctl_register,
-                       .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
-                       .dbam_map_to_pages = f10_dbam_map_to_pages,
+                       .early_channel_count    = f10_early_channel_count,
+                       .get_error_address      = f10_get_error_address,
+                       .read_dram_base_limit   = f10_read_dram_base_limit,
+                       .read_dram_ctl_register = f10_read_dram_ctl_register,
+                       .map_sysaddr_to_csrow   = f10_map_sysaddr_to_csrow,
+                       .dbam_to_cs             = f10_dbam_to_chip_select,
                }
        },
        [F11_CPUS] = {
@@ -1894,13 +1778,12 @@ static struct amd64_family_type amd64_family_types[] = {
                .addr_f1_ctl = PCI_DEVICE_ID_AMD_11H_NB_MAP,
                .misc_f3_ctl = PCI_DEVICE_ID_AMD_11H_NB_MISC,
                .ops = {
-                       .probe_valid_hardware = f10_probe_valid_hardware,
-                       .early_channel_count = f10_early_channel_count,
-                       .get_error_address = f10_get_error_address,
-                       .read_dram_base_limit = f10_read_dram_base_limit,
-                       .read_dram_ctl_register = f10_read_dram_ctl_register,
-                       .map_sysaddr_to_csrow = f10_map_sysaddr_to_csrow,
-                       .dbam_map_to_pages = f10_dbam_map_to_pages,
+                       .early_channel_count    = f10_early_channel_count,
+                       .get_error_address      = f10_get_error_address,
+                       .read_dram_base_limit   = f10_read_dram_base_limit,
+                       .read_dram_ctl_register = f10_read_dram_ctl_register,
+                       .map_sysaddr_to_csrow   = f10_map_sysaddr_to_csrow,
+                       .dbam_to_cs             = f10_dbam_to_chip_select,
                }
        },
 };
@@ -1923,142 +1806,170 @@ static struct pci_dev *pci_get_related_function(unsigned int vendor,
 }
 
 /*
- * syndrome mapping table for ECC ChipKill devices
- *
- * The comment in each row is the token (nibble) number that is in error.
- * The least significant nibble of the syndrome is the mask for the bits
- * that are in error (need to be toggled) for the particular nibble.
- *
- * Each row contains 16 entries.
- * The first entry (0th) is the channel number for that row of syndromes.
- * The remaining 15 entries are the syndromes for the respective Error
- * bit mask index.
+ * These are tables of eigenvectors (one per line) which can be used for the
+ * construction of the syndrome tables. The modified syndrome search algorithm
+ * uses those to find the symbol in error and thus the DIMM.
  *
- * 1st index entry is 0x0001 mask, indicating that the rightmost bit is the
- * bit in error.
- * The 2nd index entry is 0x0010 that the second bit is damaged.
- * The 3rd index entry is 0x0011 indicating that the rightmost 2 bits
- * are damaged.
- * Thus so on until index 15, 0x1111, whose entry has the syndrome
- * indicating that all 4 bits are damaged.
- *
- * A search is performed on this table looking for a given syndrome.
- *
- * See the AMD documentation for ECC syndromes. This ECC table is valid
- * across all the versions of the AMD64 processors.
- *
- * A fast lookup is to use the LAST four bits of the 16-bit syndrome as a
- * COLUMN index, then search all ROWS of that column, looking for a match
- * with the input syndrome. The ROW value will be the token number.
- *
- * The 0'th entry on that row, can be returned as the CHANNEL (0 or 1) of this
- * error.
+ * Algorithm courtesy of Ross LaFetra from AMD.
  */
-#define NUMBER_ECC_ROWS  36
-static const unsigned short ecc_chipkill_syndromes[NUMBER_ECC_ROWS][16] = {
-       /* Channel 0 syndromes */
-       {/*0*/  0, 0xe821, 0x7c32, 0x9413, 0xbb44, 0x5365, 0xc776, 0x2f57,
-          0xdd88, 0x35a9, 0xa1ba, 0x499b, 0x66cc, 0x8eed, 0x1afe, 0xf2df },
-       {/*1*/  0, 0x5d31, 0xa612, 0xfb23, 0x9584, 0xc8b5, 0x3396, 0x6ea7,
-          0xeac8, 0xb7f9, 0x4cda, 0x11eb, 0x7f4c, 0x227d, 0xd95e, 0x846f },
-       {/*2*/  0, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
-          0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f },
-       {/*3*/  0, 0x2021, 0x3032, 0x1013, 0x4044, 0x6065, 0x7076, 0x5057,
-          0x8088, 0xa0a9, 0xb0ba, 0x909b, 0xc0cc, 0xe0ed, 0xf0fe, 0xd0df },
-       {/*4*/  0, 0x5041, 0xa082, 0xf0c3, 0x9054, 0xc015, 0x30d6, 0x6097,
-          0xe0a8, 0xb0e9, 0x402a, 0x106b, 0x70fc, 0x20bd, 0xd07e, 0x803f },
-       {/*5*/  0, 0xbe21, 0xd732, 0x6913, 0x2144, 0x9f65, 0xf676, 0x4857,
-          0x3288, 0x8ca9, 0xe5ba, 0x5b9b, 0x13cc, 0xaded, 0xc4fe, 0x7adf },
-       {/*6*/  0, 0x4951, 0x8ea2, 0xc7f3, 0x5394, 0x1ac5, 0xdd36, 0x9467,
-          0xa1e8, 0xe8b9, 0x2f4a, 0x661b, 0xf27c, 0xbb2d, 0x7cde, 0x358f },
-       {/*7*/  0, 0x74e1, 0x9872, 0xec93, 0xd6b4, 0xa255, 0x4ec6, 0x3a27,
-          0x6bd8, 0x1f39, 0xf3aa, 0x874b, 0xbd6c, 0xc98d, 0x251e, 0x51ff },
-       {/*8*/  0, 0x15c1, 0x2a42, 0x3f83, 0xcef4, 0xdb35, 0xe4b6, 0xf177,
-          0x4758, 0x5299, 0x6d1a, 0x78db, 0x89ac, 0x9c6d, 0xa3ee, 0xb62f },
-       {/*9*/  0, 0x3d01, 0x1602, 0x2b03, 0x8504, 0xb805, 0x9306, 0xae07,
-          0xca08, 0xf709, 0xdc0a, 0xe10b, 0x4f0c, 0x720d, 0x590e, 0x640f },
-       {/*a*/  0, 0x9801, 0xec02, 0x7403, 0x6b04, 0xf305, 0x8706, 0x1f07,
-          0xbd08, 0x2509, 0x510a, 0xc90b, 0xd60c, 0x4e0d, 0x3a0e, 0xa20f },
-       {/*b*/  0, 0xd131, 0x6212, 0xb323, 0x3884, 0xe9b5, 0x5a96, 0x8ba7,
-          0x1cc8, 0xcdf9, 0x7eda, 0xafeb, 0x244c, 0xf57d, 0x465e, 0x976f },
-       {/*c*/  0, 0xe1d1, 0x7262, 0x93b3, 0xb834, 0x59e5, 0xca56, 0x2b87,
-          0xdc18, 0x3dc9, 0xae7a, 0x4fab, 0x542c, 0x85fd, 0x164e, 0xf79f },
-       {/*d*/  0, 0x6051, 0xb0a2, 0xd0f3, 0x1094, 0x70c5, 0xa036, 0xc067,
-          0x20e8, 0x40b9, 0x904a, 0x601b, 0x307c, 0x502d, 0x80de, 0xe08f },
-       {/*e*/  0, 0xa4c1, 0xf842, 0x5c83, 0xe6f4, 0x4235, 0x1eb6, 0xba77,
-          0x7b58, 0xdf99, 0x831a, 0x27db, 0x9dac, 0x396d, 0x65ee, 0xc12f },
-       {/*f*/  0, 0x11c1, 0x2242, 0x3383, 0xc8f4, 0xd935, 0xeab6, 0xfb77,
-          0x4c58, 0x5d99, 0x6e1a, 0x7fdb, 0x84ac, 0x956d, 0xa6ee, 0xb72f },
-
-       /* Channel 1 syndromes */
-       {/*10*/ 1, 0x45d1, 0x8a62, 0xcfb3, 0x5e34, 0x1be5, 0xd456, 0x9187,
-          0xa718, 0xe2c9, 0x2d7a, 0x68ab, 0xf92c, 0xbcfd, 0x734e, 0x369f },
-       {/*11*/ 1, 0x63e1, 0xb172, 0xd293, 0x14b4, 0x7755, 0xa5c6, 0xc627,
-          0x28d8, 0x4b39, 0x99aa, 0xfa4b, 0x3c6c, 0x5f8d, 0x8d1e, 0xeeff },
-       {/*12*/ 1, 0xb741, 0xd982, 0x6ec3, 0x2254, 0x9515, 0xfbd6, 0x4c97,
-          0x33a8, 0x84e9, 0xea2a, 0x5d6b, 0x11fc, 0xa6bd, 0xc87e, 0x7f3f },
-       {/*13*/ 1, 0xdd41, 0x6682, 0xbbc3, 0x3554, 0xe815, 0x53d6, 0xce97,
-          0x1aa8, 0xc7e9, 0x7c2a, 0xa1fb, 0x2ffc, 0xf2bd, 0x497e, 0x943f },
-       {/*14*/ 1, 0x2bd1, 0x3d62, 0x16b3, 0x4f34, 0x64e5, 0x7256, 0x5987,
-          0x8518, 0xaec9, 0xb87a, 0x93ab, 0xca2c, 0xe1fd, 0xf74e, 0xdc9f },
-       {/*15*/ 1, 0x83c1, 0xc142, 0x4283, 0xa4f4, 0x2735, 0x65b6, 0xe677,
-          0xf858, 0x7b99, 0x391a, 0xbadb, 0x5cac, 0xdf6d, 0x9dee, 0x1e2f },
-       {/*16*/ 1, 0x8fd1, 0xc562, 0x4ab3, 0xa934, 0x26e5, 0x6c56, 0xe387,
-          0xfe18, 0x71c9, 0x3b7a, 0xb4ab, 0x572c, 0xd8fd, 0x924e, 0x1d9f },
-       {/*17*/ 1, 0x4791, 0x89e2, 0xce73, 0x5264, 0x15f5, 0xdb86, 0x9c17,
-          0xa3b8, 0xe429, 0x2a5a, 0x6dcb, 0xf1dc, 0xb64d, 0x783e, 0x3faf },
-       {/*18*/ 1, 0x5781, 0xa9c2, 0xfe43, 0x92a4, 0xc525, 0x3b66, 0x6ce7,
-          0xe3f8, 0xb479, 0x4a3a, 0x1dbb, 0x715c, 0x26dd, 0xd89e, 0x8f1f },
-       {/*19*/ 1, 0xbf41, 0xd582, 0x6ac3, 0x2954, 0x9615, 0xfcd6, 0x4397,
-          0x3ea8, 0x81e9, 0xeb2a, 0x546b, 0x17fc, 0xa8bd, 0xc27e, 0x7d3f },
-       {/*1a*/ 1, 0x9891, 0xe1e2, 0x7273, 0x6464, 0xf7f5, 0x8586, 0x1617,
-          0xb8b8, 0x2b29, 0x595a, 0xcacb, 0xdcdc, 0x4f4d, 0x3d3e, 0xaeaf },
-       {/*1b*/ 1, 0xcce1, 0x4472, 0x8893, 0xfdb4, 0x3f55, 0xb9c6, 0x7527,
-          0x56d8, 0x9a39, 0x12aa, 0xde4b, 0xab6c, 0x678d, 0xef1e, 0x23ff },
-       {/*1c*/ 1, 0xa761, 0xf9b2, 0x5ed3, 0xe214, 0x4575, 0x1ba6, 0xbcc7,
-          0x7328, 0xd449, 0x8a9a, 0x2dfb, 0x913c, 0x365d, 0x688e, 0xcfef },
-       {/*1d*/ 1, 0xff61, 0x55b2, 0xaad3, 0x7914, 0x8675, 0x2ca6, 0xd3c7,
-          0x9e28, 0x6149, 0xcb9a, 0x34fb, 0xe73c, 0x185d, 0xb28e, 0x4def },
-       {/*1e*/ 1, 0x5451, 0xa8a2, 0xfcf3, 0x9694, 0xc2c5, 0x3e36, 0x6a67,
-          0xebe8, 0xbfb9, 0x434a, 0x171b, 0x7d7c, 0x292d, 0xd5de, 0x818f },
-       {/*1f*/ 1, 0x6fc1, 0xb542, 0xda83, 0x19f4, 0x7635, 0xacb6, 0xc377,
-          0x2e58, 0x4199, 0x9b1a, 0xf4db, 0x37ac, 0x586d, 0x82ee, 0xed2f },
-
-       /* ECC bits are also in the set of tokens and they too can go bad
-        * first 2 cover channel 0, while the second 2 cover channel 1
-        */
-       {/*20*/ 0, 0xbe01, 0xd702, 0x6903, 0x2104, 0x9f05, 0xf606, 0x4807,
-          0x3208, 0x8c09, 0xe50a, 0x5b0b, 0x130c, 0xad0d, 0xc40e, 0x7a0f },
-       {/*21*/ 0, 0x4101, 0x8202, 0xc303, 0x5804, 0x1905, 0xda06, 0x9b07,
-          0xac08, 0xed09, 0x2e0a, 0x6f0b, 0x640c, 0xb50d, 0x760e, 0x370f },
-       {/*22*/ 1, 0xc441, 0x4882, 0x8cc3, 0xf654, 0x3215, 0xbed6, 0x7a97,
-          0x5ba8, 0x9fe9, 0x132a, 0xd76b, 0xadfc, 0x69bd, 0xe57e, 0x213f },
-       {/*23*/ 1, 0x7621, 0x9b32, 0xed13, 0xda44, 0xac65, 0x4176, 0x3757,
-          0x6f88, 0x19a9, 0xf4ba, 0x829b, 0xb5cc, 0xc3ed, 0x2efe, 0x58df }
+static u16 x4_vectors[] = {
+       0x2f57, 0x1afe, 0x66cc, 0xdd88,
+       0x11eb, 0x3396, 0x7f4c, 0xeac8,
+       0x0001, 0x0002, 0x0004, 0x0008,
+       0x1013, 0x3032, 0x4044, 0x8088,
+       0x106b, 0x30d6, 0x70fc, 0xe0a8,
+       0x4857, 0xc4fe, 0x13cc, 0x3288,
+       0x1ac5, 0x2f4a, 0x5394, 0xa1e8,
+       0x1f39, 0x251e, 0xbd6c, 0x6bd8,
+       0x15c1, 0x2a42, 0x89ac, 0x4758,
+       0x2b03, 0x1602, 0x4f0c, 0xca08,
+       0x1f07, 0x3a0e, 0x6b04, 0xbd08,
+       0x8ba7, 0x465e, 0x244c, 0x1cc8,
+       0x2b87, 0x164e, 0x642c, 0xdc18,
+       0x40b9, 0x80de, 0x1094, 0x20e8,
+       0x27db, 0x1eb6, 0x9dac, 0x7b58,
+       0x11c1, 0x2242, 0x84ac, 0x4c58,
+       0x1be5, 0x2d7a, 0x5e34, 0xa718,
+       0x4b39, 0x8d1e, 0x14b4, 0x28d8,
+       0x4c97, 0xc87e, 0x11fc, 0x33a8,
+       0x8e97, 0x497e, 0x2ffc, 0x1aa8,
+       0x16b3, 0x3d62, 0x4f34, 0x8518,
+       0x1e2f, 0x391a, 0x5cac, 0xf858,
+       0x1d9f, 0x3b7a, 0x572c, 0xfe18,
+       0x15f5, 0x2a5a, 0x5264, 0xa3b8,
+       0x1dbb, 0x3b66, 0x715c, 0xe3f8,
+       0x4397, 0xc27e, 0x17fc, 0x3ea8,
+       0x1617, 0x3d3e, 0x6464, 0xb8b8,
+       0x23ff, 0x12aa, 0xab6c, 0x56d8,
+       0x2dfb, 0x1ba6, 0x913c, 0x7328,
+       0x185d, 0x2ca6, 0x7914, 0x9e28,
+       0x171b, 0x3e36, 0x7d7c, 0xebe8,
+       0x4199, 0x82ee, 0x19f4, 0x2e58,
+       0x4807, 0xc40e, 0x130c, 0x3208,
+       0x1905, 0x2e0a, 0x5804, 0xac08,
+       0x213f, 0x132a, 0xadfc, 0x5ba8,
+       0x19a9, 0x2efe, 0xb5cc, 0x6f88,
 };
 
-/*
- * Given the syndrome argument, scan each of the channel tables for a syndrome
- * match. Depending on which table it is found, return the channel number.
- */
-static int get_channel_from_ecc_syndrome(unsigned short syndrome)
+static u16 x8_vectors[] = {
+       0x0145, 0x028a, 0x2374, 0x43c8, 0xa1f0, 0x0520, 0x0a40, 0x1480,
+       0x0211, 0x0422, 0x0844, 0x1088, 0x01b0, 0x44e0, 0x23c0, 0xed80,
+       0x1011, 0x0116, 0x022c, 0x0458, 0x08b0, 0x8c60, 0x2740, 0x4e80,
+       0x0411, 0x0822, 0x1044, 0x0158, 0x02b0, 0x2360, 0x46c0, 0xab80,
+       0x0811, 0x1022, 0x012c, 0x0258, 0x04b0, 0x4660, 0x8cc0, 0x2780,
+       0x2071, 0x40e2, 0xa0c4, 0x0108, 0x0210, 0x0420, 0x0840, 0x1080,
+       0x4071, 0x80e2, 0x0104, 0x0208, 0x0410, 0x0820, 0x1040, 0x2080,
+       0x8071, 0x0102, 0x0204, 0x0408, 0x0810, 0x1020, 0x2040, 0x4080,
+       0x019d, 0x03d6, 0x136c, 0x2198, 0x50b0, 0xb2e0, 0x0740, 0x0e80,
+       0x0189, 0x03ea, 0x072c, 0x0e58, 0x1cb0, 0x56e0, 0x37c0, 0xf580,
+       0x01fd, 0x0376, 0x06ec, 0x0bb8, 0x1110, 0x2220, 0x4440, 0x8880,
+       0x0163, 0x02c6, 0x1104, 0x0758, 0x0eb0, 0x2be0, 0x6140, 0xc280,
+       0x02fd, 0x01c6, 0x0b5c, 0x1108, 0x07b0, 0x25a0, 0x8840, 0x6180,
+       0x0801, 0x012e, 0x025c, 0x04b8, 0x1370, 0x26e0, 0x57c0, 0xb580,
+       0x0401, 0x0802, 0x015c, 0x02b8, 0x22b0, 0x13e0, 0x7140, 0xe280,
+       0x0201, 0x0402, 0x0804, 0x01b8, 0x11b0, 0x31a0, 0x8040, 0x7180,
+       0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080,
+       0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+       0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000,
+};
+
+static int decode_syndrome(u16 syndrome, u16 *vectors, int num_vecs,
+                                int v_dim)
 {
-       int row;
-       int column;
+       unsigned int i, err_sym;
 
-       /* Determine column to scan */
-       column = syndrome & 0xF;
+       for (err_sym = 0; err_sym < num_vecs / v_dim; err_sym++) {
+               u16 s = syndrome;
+               int v_idx =  err_sym * v_dim;
+               int v_end = (err_sym + 1) * v_dim;
 
-       /* Scan all rows, looking for syndrome, or end of table */
-       for (row = 0; row < NUMBER_ECC_ROWS; row++) {
-               if (ecc_chipkill_syndromes[row][column] == syndrome)
-                       return ecc_chipkill_syndromes[row][0];
+               /* walk over all 16 bits of the syndrome */
+               for (i = 1; i < (1U << 16); i <<= 1) {
+
+                       /* if bit is set in that eigenvector... */
+                       if (v_idx < v_end && vectors[v_idx] & i) {
+                               u16 ev_comp = vectors[v_idx++];
+
+                               /* ... and bit set in the modified syndrome, */
+                               if (s & i) {
+                                       /* remove it. */
+                                       s ^= ev_comp;
+
+                                       if (!s)
+                                               return err_sym;
+                               }
+
+                       } else if (s & i)
+                               /* can't get to zero, move to next symbol */
+                               break;
+               }
        }
 
        debugf0("syndrome(%x) not found\n", syndrome);
        return -1;
 }
 
+static int map_err_sym_to_channel(int err_sym, int sym_size)
+{
+       if (sym_size == 4)
+               switch (err_sym) {
+               case 0x20:
+               case 0x21:
+                       return 0;
+                       break;
+               case 0x22:
+               case 0x23:
+                       return 1;
+                       break;
+               default:
+                       return err_sym >> 4;
+                       break;
+               }
+       /* x8 symbols */
+       else
+               switch (err_sym) {
+               /* imaginary bits not in a DIMM */
+               case 0x10:
+                       WARN(1, KERN_ERR "Invalid error symbol: 0x%x\n",
+                                         err_sym);
+                       return -1;
+                       break;
+
+               case 0x11:
+                       return 0;
+                       break;
+               case 0x12:
+                       return 1;
+                       break;
+               default:
+                       return err_sym >> 3;
+                       break;
+               }
+       return -1;
+}
+
+static int get_channel_from_ecc_syndrome(struct mem_ctl_info *mci, u16 syndrome)
+{
+       struct amd64_pvt *pvt = mci->pvt_info;
+       u32 value = 0;
+       int err_sym = 0;
+
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, 0x180, &value);
+
+       /* F3x180[EccSymbolSize]=1, x8 symbols */
+       if (boot_cpu_data.x86 == 0x10 &&
+           boot_cpu_data.x86_model > 7 &&
+           value & BIT(25)) {
+               err_sym = decode_syndrome(syndrome, x8_vectors,
+                                         ARRAY_SIZE(x8_vectors), 8);
+               return map_err_sym_to_channel(err_sym, 8);
+       } else {
+               err_sym = decode_syndrome(syndrome, x4_vectors,
+                                         ARRAY_SIZE(x4_vectors), 4);
+               return map_err_sym_to_channel(err_sym, 4);
+       }
+}
+
 /*
  * Check for valid error in the NB Status High register. If so, proceed to read
  * NB Status Low, NB Address Low and NB Address High registers and store data
@@ -2073,40 +1984,24 @@ static int amd64_get_error_info_regs(struct mem_ctl_info *mci,
 {
        struct amd64_pvt *pvt;
        struct pci_dev *misc_f3_ctl;
-       int err = 0;
 
        pvt = mci->pvt_info;
        misc_f3_ctl = pvt->misc_f3_ctl;
 
-       err = pci_read_config_dword(misc_f3_ctl, K8_NBSH, &regs->nbsh);
-       if (err)
-               goto err_reg;
+       if (amd64_read_pci_cfg(misc_f3_ctl, K8_NBSH, &regs->nbsh))
+               return 0;
 
        if (!(regs->nbsh & K8_NBSH_VALID_BIT))
                return 0;
 
        /* valid error, read remaining error information registers */
-       err = pci_read_config_dword(misc_f3_ctl, K8_NBSL, &regs->nbsl);
-       if (err)
-               goto err_reg;
-
-       err = pci_read_config_dword(misc_f3_ctl, K8_NBEAL, &regs->nbeal);
-       if (err)
-               goto err_reg;
-
-       err = pci_read_config_dword(misc_f3_ctl, K8_NBEAH, &regs->nbeah);
-       if (err)
-               goto err_reg;
-
-       err = pci_read_config_dword(misc_f3_ctl, K8_NBCFG, &regs->nbcfg);
-       if (err)
-               goto err_reg;
+       if (amd64_read_pci_cfg(misc_f3_ctl, K8_NBSL, &regs->nbsl) ||
+           amd64_read_pci_cfg(misc_f3_ctl, K8_NBEAL, &regs->nbeal) ||
+           amd64_read_pci_cfg(misc_f3_ctl, K8_NBEAH, &regs->nbeah) ||
+           amd64_read_pci_cfg(misc_f3_ctl, K8_NBCFG, &regs->nbcfg))
+               return 0;
 
        return 1;
-
-err_reg:
-       debugf0("Reading error info register failed\n");
-       return 0;
 }
 
 /*
@@ -2184,7 +2079,7 @@ static void amd64_handle_ce(struct mem_ctl_info *mci,
                            struct err_regs *info)
 {
        struct amd64_pvt *pvt = mci->pvt_info;
-       u64 SystemAddress;
+       u64 sys_addr;
 
        /* Ensure that the Error Address is VALID */
        if ((info->nbsh & K8_NBSH_VALID_ERROR_ADDR) == 0) {
@@ -2194,22 +2089,23 @@ static void amd64_handle_ce(struct mem_ctl_info *mci,
                return;
        }
 
-       SystemAddress = extract_error_address(mci, info);
+       sys_addr = pvt->ops->get_error_address(mci, info);
 
        amd64_mc_printk(mci, KERN_ERR,
-               "CE ERROR_ADDRESS= 0x%llx\n", SystemAddress);
+               "CE ERROR_ADDRESS= 0x%llx\n", sys_addr);
 
-       pvt->ops->map_sysaddr_to_csrow(mci, info, SystemAddress);
+       pvt->ops->map_sysaddr_to_csrow(mci, info, sys_addr);
 }
 
 /* Handle any Un-correctable Errors (UEs) */
 static void amd64_handle_ue(struct mem_ctl_info *mci,
                            struct err_regs *info)
 {
+       struct amd64_pvt *pvt = mci->pvt_info;
+       struct mem_ctl_info *log_mci, *src_mci = NULL;
        int csrow;
-       u64 SystemAddress;
+       u64 sys_addr;
        u32 page, offset;
-       struct mem_ctl_info *log_mci, *src_mci = NULL;
 
        log_mci = mci;
 
@@ -2220,31 +2116,31 @@ static void amd64_handle_ue(struct mem_ctl_info *mci,
                return;
        }
 
-       SystemAddress = extract_error_address(mci, info);
+       sys_addr = pvt->ops->get_error_address(mci, info);
 
        /*
         * Find out which node the error address belongs to. This may be
         * different from the node that detected the error.
         */
-       src_mci = find_mc_by_sys_addr(mci, SystemAddress);
+       src_mci = find_mc_by_sys_addr(mci, sys_addr);
        if (!src_mci) {
                amd64_mc_printk(mci, KERN_CRIT,
                        "ERROR ADDRESS (0x%lx) value NOT mapped to a MC\n",
-                       (unsigned long)SystemAddress);
+                       (unsigned long)sys_addr);
                edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
                return;
        }
 
        log_mci = src_mci;
 
-       csrow = sys_addr_to_csrow(log_mci, SystemAddress);
+       csrow = sys_addr_to_csrow(log_mci, sys_addr);
        if (csrow < 0) {
                amd64_mc_printk(mci, KERN_CRIT,
                        "ERROR_ADDRESS (0x%lx) value NOT mapped to 'csrow'\n",
-                       (unsigned long)SystemAddress);
+                       (unsigned long)sys_addr);
                edac_mc_handle_ue_no_info(log_mci, EDAC_MOD_STR);
        } else {
-               error_address_to_page_and_offset(SystemAddress, &page, &offset);
+               error_address_to_page_and_offset(sys_addr, &page, &offset);
                edac_mc_handle_ue(log_mci, page, offset, csrow, EDAC_MOD_STR);
        }
 }
@@ -2384,30 +2280,26 @@ static void amd64_free_mc_sibling_devices(struct amd64_pvt *pvt)
 static void amd64_read_mc_registers(struct amd64_pvt *pvt)
 {
        u64 msr_val;
-       int dram, err = 0;
+       int dram;
 
        /*
         * Retrieve TOP_MEM and TOP_MEM2; no masking off of reserved bits since
         * those are Read-As-Zero
         */
-       rdmsrl(MSR_K8_TOP_MEM1, msr_val);
-       pvt->top_mem = msr_val >> 23;
-       debugf0("  TOP_MEM=0x%08llx\n", pvt->top_mem);
+       rdmsrl(MSR_K8_TOP_MEM1, pvt->top_mem);
+       debugf0("  TOP_MEM:  0x%016llx\n", pvt->top_mem);
 
        /* check first whether TOP_MEM2 is enabled */
        rdmsrl(MSR_K8_SYSCFG, msr_val);
        if (msr_val & (1U << 21)) {
-               rdmsrl(MSR_K8_TOP_MEM2, msr_val);
-               pvt->top_mem2 = msr_val >> 23;
-               debugf0("  TOP_MEM2=0x%08llx\n", pvt->top_mem2);
+               rdmsrl(MSR_K8_TOP_MEM2, pvt->top_mem2);
+               debugf0("  TOP_MEM2: 0x%016llx\n", pvt->top_mem2);
        } else
                debugf0("  TOP_MEM2 disabled.\n");
 
        amd64_cpu_display_info(pvt);
 
-       err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCAP, &pvt->nbcap);
-       if (err)
-               goto err_reg;
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCAP, &pvt->nbcap);
 
        if (pvt->ops->read_dram_ctl_register)
                pvt->ops->read_dram_ctl_register(pvt);
@@ -2425,13 +2317,12 @@ static void amd64_read_mc_registers(struct amd64_pvt *pvt)
                 * debug output block away.
                 */
                if (pvt->dram_rw_en[dram] != 0) {
-                       debugf1("  DRAM_BASE[%d]: 0x%8.08x-%8.08x "
-                               "DRAM_LIMIT:  0x%8.08x-%8.08x\n",
+                       debugf1("  DRAM-BASE[%d]: 0x%016llx "
+                               "DRAM-LIMIT:  0x%016llx\n",
                                dram,
-                               (u32)(pvt->dram_base[dram] >> 32),
-                               (u32)(pvt->dram_base[dram] & 0xFFFFFFFF),
-                               (u32)(pvt->dram_limit[dram] >> 32),
-                               (u32)(pvt->dram_limit[dram] & 0xFFFFFFFF));
+                               pvt->dram_base[dram],
+                               pvt->dram_limit[dram]);
+
                        debugf1("        IntlvEn=%s %s %s "
                                "IntlvSel=%d DstNode=%d\n",
                                pvt->dram_IntlvEn[dram] ?
@@ -2445,44 +2336,20 @@ static void amd64_read_mc_registers(struct amd64_pvt *pvt)
 
        amd64_read_dct_base_mask(pvt);
 
-       err = pci_read_config_dword(pvt->addr_f1_ctl, K8_DHAR, &pvt->dhar);
-       if (err)
-               goto err_reg;
-
+       amd64_read_pci_cfg(pvt->addr_f1_ctl, K8_DHAR, &pvt->dhar);
        amd64_read_dbam_reg(pvt);
 
-       err = pci_read_config_dword(pvt->misc_f3_ctl,
-                               F10_ONLINE_SPARE, &pvt->online_spare);
-       if (err)
-               goto err_reg;
+       amd64_read_pci_cfg(pvt->misc_f3_ctl,
+                          F10_ONLINE_SPARE, &pvt->online_spare);
 
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
-       if (err)
-               goto err_reg;
-
-       err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_0, &pvt->dchr0);
-       if (err)
-               goto err_reg;
+       amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCLR_0, &pvt->dclr0);
+       amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCHR_0, &pvt->dchr0);
 
        if (!dct_ganging_enabled(pvt)) {
-               err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCLR_1,
-                                               &pvt->dclr1);
-               if (err)
-                       goto err_reg;
-
-               err = pci_read_config_dword(pvt->dram_f2_ctl, F10_DCHR_1,
-                                               &pvt->dchr1);
-               if (err)
-                       goto err_reg;
+               amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCLR_1, &pvt->dclr1);
+               amd64_read_pci_cfg(pvt->dram_f2_ctl, F10_DCHR_1, &pvt->dchr1);
        }
-
        amd64_dump_misc_regs(pvt);
-
-       return;
-
-err_reg:
-       debugf0("Reading an MC register failed\n");
-
 }
 
 /*
@@ -2521,7 +2388,7 @@ err_reg:
  */
 static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
 {
-       u32 dram_map, nr_pages;
+       u32 cs_mode, nr_pages;
 
        /*
         * The math on this doesn't look right on the surface because x/2*4 can
@@ -2530,9 +2397,9 @@ static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
         * number of bits to shift the DBAM register to extract the proper CSROW
         * field.
         */
-       dram_map = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
+       cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
 
-       nr_pages = pvt->ops->dbam_map_to_pages(pvt, dram_map);
+       nr_pages = pvt->ops->dbam_to_cs(pvt, cs_mode) << (20 - PAGE_SHIFT);
 
        /*
         * If dual channel then double the memory size of single channel.
@@ -2540,7 +2407,7 @@ static u32 amd64_csrow_nr_pages(int csrow_nr, struct amd64_pvt *pvt)
         */
        nr_pages <<= (pvt->channel_count - 1);
 
-       debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, dram_map);
+       debugf0("  (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
        debugf0("    nr_pages= %u  channel-count = %d\n",
                nr_pages, pvt->channel_count);
 
@@ -2556,13 +2423,11 @@ static int amd64_init_csrows(struct mem_ctl_info *mci)
        struct csrow_info *csrow;
        struct amd64_pvt *pvt;
        u64 input_addr_min, input_addr_max, sys_addr;
-       int i, err = 0, empty = 1;
+       int i, empty = 1;
 
        pvt = mci->pvt_info;
 
-       err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &pvt->nbcfg);
-       if (err)
-               debugf0("Reading K8_NBCFG failed\n");
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &pvt->nbcfg);
 
        debugf0("NBCFG= 0x%x  CHIPKILL= %s DRAM ECC= %s\n", pvt->nbcfg,
                (pvt->nbcfg & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
@@ -2618,6 +2483,90 @@ static int amd64_init_csrows(struct mem_ctl_info *mci)
        return empty;
 }
 
+/* get all cores on this DCT */
+static void get_cpus_on_this_dct_cpumask(struct cpumask *mask, int nid)
+{
+       int cpu;
+
+       for_each_online_cpu(cpu)
+               if (amd_get_nb_id(cpu) == nid)
+                       cpumask_set_cpu(cpu, mask);
+}
+
+/* check MCG_CTL on all the cpus on this node */
+static bool amd64_nb_mce_bank_enabled_on_node(int nid)
+{
+       cpumask_var_t mask;
+       int cpu, nbe;
+       bool ret = false;
+
+       if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
+               amd64_printk(KERN_WARNING, "%s: error allocating mask\n",
+                            __func__);
+               return false;
+       }
+
+       get_cpus_on_this_dct_cpumask(mask, nid);
+
+       rdmsr_on_cpus(mask, MSR_IA32_MCG_CTL, msrs);
+
+       for_each_cpu(cpu, mask) {
+               struct msr *reg = per_cpu_ptr(msrs, cpu);
+               nbe = reg->l & K8_MSR_MCGCTL_NBE;
+
+               debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
+                       cpu, reg->q,
+                       (nbe ? "enabled" : "disabled"));
+
+               if (!nbe)
+                       goto out;
+       }
+       ret = true;
+
+out:
+       free_cpumask_var(mask);
+       return ret;
+}
+
+static int amd64_toggle_ecc_err_reporting(struct amd64_pvt *pvt, bool on)
+{
+       cpumask_var_t cmask;
+       int cpu;
+
+       if (!zalloc_cpumask_var(&cmask, GFP_KERNEL)) {
+               amd64_printk(KERN_WARNING, "%s: error allocating mask\n",
+                            __func__);
+               return false;
+       }
+
+       get_cpus_on_this_dct_cpumask(cmask, pvt->mc_node_id);
+
+       rdmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
+
+       for_each_cpu(cpu, cmask) {
+
+               struct msr *reg = per_cpu_ptr(msrs, cpu);
+
+               if (on) {
+                       if (reg->l & K8_MSR_MCGCTL_NBE)
+                               pvt->flags.ecc_report = 1;
+
+                       reg->l |= K8_MSR_MCGCTL_NBE;
+               } else {
+                       /*
+                        * Turn off ECC reporting only when it was off before
+                        */
+                       if (!pvt->flags.ecc_report)
+                               reg->l &= ~K8_MSR_MCGCTL_NBE;
+               }
+       }
+       wrmsr_on_cpus(cmask, MSR_IA32_MCG_CTL, msrs);
+
+       free_cpumask_var(cmask);
+
+       return 0;
+}
+
 /*
  * Only if 'ecc_enable_override' is set AND BIOS had ECC disabled, do "we"
  * enable it.
@@ -2625,24 +2574,16 @@ static int amd64_init_csrows(struct mem_ctl_info *mci)
 static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
 {
        struct amd64_pvt *pvt = mci->pvt_info;
-       const cpumask_t *cpumask = cpumask_of_node(pvt->mc_node_id);
-       int cpu, idx = 0, err = 0;
-       struct msr msrs[cpumask_weight(cpumask)];
-       u32 value;
-       u32 mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
+       u32 value, mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
 
        if (!ecc_enable_override)
                return;
 
-       memset(msrs, 0, sizeof(msrs));
-
        amd64_printk(KERN_WARNING,
                "'ecc_enable_override' parameter is active, "
                "Enabling AMD ECC hardware now: CAUTION\n");
 
-       err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCTL, &value);
-       if (err)
-               debugf0("Reading K8_NBCTL failed\n");
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCTL, &value);
 
        /* turn on UECCn and CECCEn bits */
        pvt->old_nbctl = value & mask;
@@ -2651,20 +2592,11 @@ static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
        value |= mask;
        pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCTL, value);
 
-       rdmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
+       if (amd64_toggle_ecc_err_reporting(pvt, ON))
+               amd64_printk(KERN_WARNING, "Error enabling ECC reporting over "
+                                          "MCGCTL!\n");
 
-       for_each_cpu(cpu, cpumask) {
-               if (msrs[idx].l & K8_MSR_MCGCTL_NBE)
-                       set_bit(idx, &pvt->old_mcgctl);
-
-               msrs[idx].l |= K8_MSR_MCGCTL_NBE;
-               idx++;
-       }
-       wrmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-
-       err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
-       if (err)
-               debugf0("Reading K8_NBCFG failed\n");
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &value);
 
        debugf0("NBCFG(1)= 0x%x  CHIPKILL= %s ECC_ENABLE= %s\n", value,
                (value & K8_NBCFG_CHIPKILL) ? "Enabled" : "Disabled",
@@ -2679,9 +2611,7 @@ static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
                value |= K8_NBCFG_ECC_ENABLE;
                pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCFG, value);
 
-               err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
-               if (err)
-                       debugf0("Reading K8_NBCFG failed\n");
+               amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &value);
 
                if (!(value & K8_NBCFG_ECC_ENABLE)) {
                        amd64_printk(KERN_WARNING,
@@ -2701,86 +2631,21 @@ static void amd64_enable_ecc_error_reporting(struct mem_ctl_info *mci)
 
 static void amd64_restore_ecc_error_reporting(struct amd64_pvt *pvt)
 {
-       const cpumask_t *cpumask = cpumask_of_node(pvt->mc_node_id);
-       int cpu, idx = 0, err = 0;
-       struct msr msrs[cpumask_weight(cpumask)];
-       u32 value;
-       u32 mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
+       u32 value, mask = K8_NBCTL_CECCEn | K8_NBCTL_UECCEn;
 
        if (!pvt->nbctl_mcgctl_saved)
                return;
 
-       memset(msrs, 0, sizeof(msrs));
-
-       err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCTL, &value);
-       if (err)
-               debugf0("Reading K8_NBCTL failed\n");
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCTL, &value);
        value &= ~mask;
        value |= pvt->old_nbctl;
 
        /* restore the NB Enable MCGCTL bit */
        pci_write_config_dword(pvt->misc_f3_ctl, K8_NBCTL, value);
 
-       rdmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-
-       for_each_cpu(cpu, cpumask) {
-               msrs[idx].l &= ~K8_MSR_MCGCTL_NBE;
-               msrs[idx].l |=
-                       test_bit(idx, &pvt->old_mcgctl) << K8_MSR_MCGCTL_NBE;
-               idx++;
-       }
-
-       wrmsr_on_cpus(cpumask, K8_MSR_MCGCTL, msrs);
-}
-
-/* get all cores on this DCT */
-static void get_cpus_on_this_dct_cpumask(cpumask_t *mask, int nid)
-{
-       int cpu;
-
-       for_each_online_cpu(cpu)
-               if (amd_get_nb_id(cpu) == nid)
-                       cpumask_set_cpu(cpu, mask);
-}
-
-/* check MCG_CTL on all the cpus on this node */
-static bool amd64_nb_mce_bank_enabled_on_node(int nid)
-{
-       cpumask_t mask;
-       struct msr *msrs;
-       int cpu, nbe, idx = 0;
-       bool ret = false;
-
-       cpumask_clear(&mask);
-
-       get_cpus_on_this_dct_cpumask(&mask, nid);
-
-       msrs = kzalloc(sizeof(struct msr) * cpumask_weight(&mask), GFP_KERNEL);
-       if (!msrs) {
-               amd64_printk(KERN_WARNING, "%s: error allocating msrs\n",
-                             __func__);
-                return false;
-       }
-
-       rdmsr_on_cpus(&mask, MSR_IA32_MCG_CTL, msrs);
-
-       for_each_cpu(cpu, &mask) {
-               nbe = msrs[idx].l & K8_MSR_MCGCTL_NBE;
-
-               debugf0("core: %u, MCG_CTL: 0x%llx, NB MSR is %s\n",
-                       cpu, msrs[idx].q,
-                       (nbe ? "enabled" : "disabled"));
-
-               if (!nbe)
-                       goto out;
-
-               idx++;
-       }
-       ret = true;
-
-out:
-       kfree(msrs);
-       return ret;
+       if (amd64_toggle_ecc_err_reporting(pvt, OFF))
+               amd64_printk(KERN_WARNING, "Error restoring ECC reporting over "
+                                          "MCGCTL!\n");
 }
 
 /*
@@ -2797,13 +2662,10 @@ static const char *ecc_warning =
 static int amd64_check_ecc_enabled(struct amd64_pvt *pvt)
 {
        u32 value;
-       int err = 0;
        u8 ecc_enabled = 0;
        bool nb_mce_en = false;
 
-       err = pci_read_config_dword(pvt->misc_f3_ctl, K8_NBCFG, &value);
-       if (err)
-               debugf0("Reading K8_NBCTL failed\n");
+       amd64_read_pci_cfg(pvt->misc_f3_ctl, K8_NBCFG, &value);
 
        ecc_enabled = !!(value & K8_NBCFG_ECC_ENABLE);
        if (!ecc_enabled)
@@ -2909,7 +2771,6 @@ static int amd64_probe_one_instance(struct pci_dev *dram_f2_ctl,
        pvt->ext_model          = boot_cpu_data.x86_model >> 4;
        pvt->mc_type_index      = mc_type_index;
        pvt->ops                = family_ops(mc_type_index);
-       pvt->old_mcgctl         = 0;
 
        /*
         * We have the dram_f2_ctl device as an argument, now go reserve its
@@ -2959,17 +2820,10 @@ static int amd64_init_2nd_stage(struct amd64_pvt *pvt)
 {
        int node_id = pvt->mc_node_id;
        struct mem_ctl_info *mci;
-       int ret, err = 0;
+       int ret = -ENODEV;
 
        amd64_read_mc_registers(pvt);
 
-       ret = -ENODEV;
-       if (pvt->ops->probe_valid_hardware) {
-               err = pvt->ops->probe_valid_hardware(pvt);
-               if (err)
-                       goto err_exit;
-       }
-
        /*
         * We need to determine how many memory channels there are. Then use
         * that information for calculating the size of the dynamic instance
@@ -3165,6 +3019,8 @@ static int __init amd64_edac_init(void)
        if (cache_k8_northbridges() < 0)
                return err;
 
+       msrs = msrs_alloc();
+
        err = pci_register_driver(&amd64_pci_driver);
        if (err)
                return err;
@@ -3200,6 +3056,9 @@ static void __exit amd64_edac_exit(void)
                edac_pci_release_generic_ctl(amd64_ctl_pci);
 
        pci_unregister_driver(&amd64_pci_driver);
+
+       msrs_free(msrs);
+       msrs = NULL;
 }
 
 module_init(amd64_edac_init);
index c6f359a..41bc561 100644 (file)
  *         sections 3.5.4 and 3.5.5 for more information.
  */
 
-#define EDAC_AMD64_VERSION             " Ver: 3.2.0 " __DATE__
+#define EDAC_AMD64_VERSION             " Ver: 3.3.0 " __DATE__
 #define EDAC_MOD_STR                   "amd64_edac"
 
 #define EDAC_MAX_NUMNODES              8
 
 /* Extended Model from CPUID, for CPU Revision numbers */
-#define OPTERON_CPU_LE_REV_C           0
-#define OPTERON_CPU_REV_D              1
-#define OPTERON_CPU_REV_E              2
-
-/* NPT processors have the following Extended Models */
-#define OPTERON_CPU_REV_F              4
-#define OPTERON_CPU_REV_FA             5
+#define K8_REV_D                       1
+#define K8_REV_E                       2
+#define K8_REV_F                       4
 
 /* Hardware limit on ChipSelect rows per MC and processors per system */
 #define MAX_CS_COUNT                   8
 #define DRAM_REG_COUNT                 8
 
+#define ON true
+#define OFF false
 
 /*
  * PCI-defined configuration space registers
 #define F10_DCHR_1                     0x194
 
 #define F10_DCHR_FOUR_RANK_DIMM                BIT(18)
-#define F10_DCHR_Ddr3Mode              BIT(8)
+#define DDR3_MODE                      BIT(8)
 #define F10_DCHR_MblMode               BIT(6)
 
 
@@ -382,14 +380,9 @@ enum {
 #define K8_NBCAP_CORES                 (BIT(12)|BIT(13))
 #define K8_NBCAP_CHIPKILL              BIT(4)
 #define K8_NBCAP_SECDED                        BIT(3)
-#define K8_NBCAP_8_NODE                        BIT(2)
-#define K8_NBCAP_DUAL_NODE             BIT(1)
 #define K8_NBCAP_DCT_DUAL              BIT(0)
 
-/*
- * MSR Regs
- */
-#define K8_MSR_MCGCTL                  0x017b
+/* MSRs */
 #define K8_MSR_MCGCTL_NBE              BIT(4)
 
 #define K8_MSR_MC4CTL                  0x0410
@@ -487,7 +480,6 @@ struct amd64_pvt {
        /* Save old hw registers' values before we modified them */
        u32 nbctl_mcgctl_saved;         /* When true, following 2 are valid */
        u32 old_nbctl;
-       unsigned long old_mcgctl;       /* per core on this node */
 
        /* MC Type Index value: socket F vs Family 10h */
        u32 mc_type_index;
@@ -495,6 +487,7 @@ struct amd64_pvt {
        /* misc settings */
        struct flags {
                unsigned long cf8_extcfg:1;
+               unsigned long ecc_report:1;
        } flags;
 };
 
@@ -504,7 +497,6 @@ struct scrubrate {
 };
 
 extern struct scrubrate scrubrates[23];
-extern u32 revf_quad_ddr2_shift[16];
 extern const char *tt_msgs[4];
 extern const char *ll_msgs[4];
 extern const char *rrrr_msgs[16];
@@ -534,17 +526,15 @@ extern struct mcidev_sysfs_attribute amd64_dbg_attrs[NUM_DBG_ATTRS],
  * functions and per device encoding/decoding logic.
  */
 struct low_ops {
-       int (*probe_valid_hardware)(struct amd64_pvt *pvt);
-       int (*early_channel_count)(struct amd64_pvt *pvt);
-
-       u64 (*get_error_address)(struct mem_ctl_info *mci,
-                       struct err_regs *info);
-       void (*read_dram_base_limit)(struct amd64_pvt *pvt, int dram);
-       void (*read_dram_ctl_register)(struct amd64_pvt *pvt);
-       void (*map_sysaddr_to_csrow)(struct mem_ctl_info *mci,
-                                       struct err_regs *info,
-                                       u64 SystemAddr);
-       int (*dbam_map_to_pages)(struct amd64_pvt *pvt, int dram_map);
+       int (*early_channel_count)      (struct amd64_pvt *pvt);
+
+       u64 (*get_error_address)        (struct mem_ctl_info *mci,
+                                        struct err_regs *info);
+       void (*read_dram_base_limit)    (struct amd64_pvt *pvt, int dram);
+       void (*read_dram_ctl_register)  (struct amd64_pvt *pvt);
+       void (*map_sysaddr_to_csrow)    (struct mem_ctl_info *mci,
+                                        struct err_regs *info, u64 SystemAddr);
+       int (*dbam_to_cs)               (struct amd64_pvt *pvt, int cs_mode);
 };
 
 struct amd64_family_type {
@@ -566,6 +556,22 @@ static inline struct low_ops *family_ops(int index)
        return &amd64_family_types[index].ops;
 }
 
+static inline int amd64_read_pci_cfg_dword(struct pci_dev *pdev, int offset,
+                                          u32 *val, const char *func)
+{
+       int err = 0;
+
+       err = pci_read_config_dword(pdev, offset, val);
+       if (err)
+               amd64_printk(KERN_WARNING, "%s: error reading F%dx%x.\n",
+                            func, PCI_FUNC(pdev->devfn), offset);
+
+       return err;
+}
+
+#define amd64_read_pci_cfg(pdev, offset, val)  \
+       amd64_read_pci_cfg_dword(pdev, offset, val, __func__)
+
 /*
  * For future CPU versions, verify the following as new 'slow' rates appear and
  * modify the necessary skip values for the supported CPU.
index 12f355c..001b2e7 100644 (file)
@@ -74,6 +74,7 @@
 
 #ifdef CONFIG_EDAC_DEBUG
 extern int edac_debug_level;
+extern const char *edac_mem_types[];
 
 #ifndef CONFIG_EDAC_DEBUG_VERBOSE
 #define edac_debug_printk(level, fmt, arg...)                           \
index b629c41..3630308 100644 (file)
@@ -76,6 +76,30 @@ static void edac_mc_dump_mci(struct mem_ctl_info *mci)
        debugf3("\tpvt_info = %p\n\n", mci->pvt_info);
 }
 
+/*
+ * keep those in sync with the enum mem_type
+ */
+const char *edac_mem_types[] = {
+       "Empty csrow",
+       "Reserved csrow type",
+       "Unknown csrow type",
+       "Fast page mode RAM",
+       "Extended data out RAM",
+       "Burst Extended data out RAM",
+       "Single data rate SDRAM",
+       "Registered single data rate SDRAM",
+       "Double data rate SDRAM",
+       "Registered Double data rate SDRAM",
+       "Rambus DRAM",
+       "Unbuffered DDR2 RAM",
+       "Fully buffered DDR2",
+       "Registered DDR2 RAM",
+       "Rambus XDR",
+       "Unbuffered DDR3 RAM",
+       "Registered DDR3 RAM",
+};
+EXPORT_SYMBOL_GPL(edac_mem_types);
+
 #endif                         /* CONFIG_EDAC_DEBUG */
 
 /* 'ptr' points to a possibly unaligned item X such that sizeof(X) is 'size'.
index 689cc6a..c693fcc 100644 (file)
@@ -306,7 +306,7 @@ void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors)
         * value encoding has changed so interpret those differently
         */
        if ((boot_cpu_data.x86 == 0x10) &&
-           (boot_cpu_data.x86_model > 8)) {
+           (boot_cpu_data.x86_model > 7)) {
                if (regs->nbsh & K8_NBSH_ERR_CPU_VAL)
                        pr_cont(", core: %u\n", (u8)(regs->nbsh & 0xf));
        } else {
index 9a5f38c..93ec64c 100644 (file)
@@ -183,7 +183,7 @@ static inline struct fw_node *fw_node(struct list_head *l)
  * This function builds the tree representation of the topology given
  * by the self IDs from the latest bus reset.  During the construction
  * of the tree, the function checks that the self IDs are valid and
- * internally consistent.  On succcess this function returns the
+ * internally consistent.  On success this function returns the
  * fw_node corresponding to the local card otherwise NULL.
  */
 static struct fw_node *build_tree(struct fw_card *card,
index 938100f..3a2ccb0 100644 (file)
@@ -429,7 +429,7 @@ static bool dmi_matches(const struct dmi_system_id *dmi)
        for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) {
                int s = dmi->matches[i].slot;
                if (s == DMI_NONE)
-                       continue;
+                       break;
                if (dmi_ident[s]
                    && strstr(dmi_ident[s], dmi->matches[i].substr))
                        continue;
@@ -439,6 +439,15 @@ static bool dmi_matches(const struct dmi_system_id *dmi)
        return true;
 }
 
+/**
+ *     dmi_is_end_of_table - check for end-of-table marker
+ *     @dmi: pointer to the dmi_system_id structure to check
+ */
+static bool dmi_is_end_of_table(const struct dmi_system_id *dmi)
+{
+       return dmi->matches[0].slot == DMI_NONE;
+}
+
 /**
  *     dmi_check_system - check system DMI data
  *     @list: array of dmi_system_id structures to match against
@@ -457,7 +466,7 @@ int dmi_check_system(const struct dmi_system_id *list)
        int count = 0;
        const struct dmi_system_id *d;
 
-       for (d = list; d->ident; d++)
+       for (d = list; !dmi_is_end_of_table(d); d++)
                if (dmi_matches(d)) {
                        count++;
                        if (d->callback && d->callback(d))
@@ -484,7 +493,7 @@ const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list)
 {
        const struct dmi_system_id *d;
 
-       for (d = list; d->ident; d++)
+       for (d = list; !dmi_is_end_of_table(d); d++)
                if (dmi_matches(d))
                        return d;
 
index 3c8827a..91567ac 100644 (file)
@@ -15,7 +15,7 @@ drm-y       :=        drm_auth.o drm_bufs.o drm_cache.o \
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 
-drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o
+drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
 
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
index 5cae0b3..5124401 100644 (file)
@@ -125,6 +125,15 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
 DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
                 drm_tv_subconnector_enum_list)
 
+static struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
+       { DRM_MODE_DIRTY_OFF,      "Off"      },
+       { DRM_MODE_DIRTY_ON,       "On"       },
+       { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
+};
+
+DRM_ENUM_NAME_FN(drm_get_dirty_info_name,
+                drm_dirty_info_enum_list)
+
 struct drm_conn_prop_enum_list {
        int type;
        char *name;
@@ -247,7 +256,8 @@ static void drm_mode_object_put(struct drm_device *dev,
        mutex_unlock(&dev->mode_config.idr_mutex);
 }
 
-void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
+struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
+               uint32_t id, uint32_t type)
 {
        struct drm_mode_object *obj = NULL;
 
@@ -272,7 +282,7 @@ EXPORT_SYMBOL(drm_mode_object_find);
  * functions & device file and adds it to the master fd list.
  *
  * RETURNS:
- * Zero on success, error code on falure.
+ * Zero on success, error code on failure.
  */
 int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
                         const struct drm_framebuffer_funcs *funcs)
@@ -801,6 +811,36 @@ int drm_mode_create_dithering_property(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_create_dithering_property);
 
+/**
+ * drm_mode_create_dirty_property - create dirty property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_dirty_info_property(struct drm_device *dev)
+{
+       struct drm_property *dirty_info;
+       int i;
+
+       if (dev->mode_config.dirty_info_property)
+               return 0;
+
+       dirty_info =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM |
+                                   DRM_MODE_PROP_IMMUTABLE,
+                                   "dirty",
+                                   ARRAY_SIZE(drm_dirty_info_enum_list));
+       for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
+               drm_property_add_enum(dirty_info, i,
+                                     drm_dirty_info_enum_list[i].type,
+                                     drm_dirty_info_enum_list[i].name);
+       dev->mode_config.dirty_info_property = dirty_info;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
+
 /**
  * drm_mode_config_init - initialize DRM mode_configuration structure
  * @dev: DRM device
@@ -1753,6 +1793,71 @@ out:
        return ret;
 }
 
+int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_clip_rect __user *clips_ptr;
+       struct drm_clip_rect *clips = NULL;
+       struct drm_mode_fb_dirty_cmd *r = data;
+       struct drm_mode_object *obj;
+       struct drm_framebuffer *fb;
+       unsigned flags;
+       int num_clips;
+       int ret = 0;
+
+       mutex_lock(&dev->mode_config.mutex);
+       obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
+       if (!obj) {
+               DRM_ERROR("invalid framebuffer id\n");
+               ret = -EINVAL;
+               goto out_err1;
+       }
+       fb = obj_to_fb(obj);
+
+       num_clips = r->num_clips;
+       clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr;
+
+       if (!num_clips != !clips_ptr) {
+               ret = -EINVAL;
+               goto out_err1;
+       }
+
+       flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
+
+       /* If userspace annotates copy, clips must come in pairs */
+       if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
+               ret = -EINVAL;
+               goto out_err1;
+       }
+
+       if (num_clips && clips_ptr) {
+               clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
+               if (!clips) {
+                       ret = -ENOMEM;
+                       goto out_err1;
+               }
+
+               ret = copy_from_user(clips, clips_ptr,
+                                    num_clips * sizeof(*clips));
+               if (ret)
+                       goto out_err2;
+       }
+
+       if (fb->funcs->dirty) {
+               ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips);
+       } else {
+               ret = -ENOSYS;
+               goto out_err2;
+       }
+
+out_err2:
+       kfree(clips);
+out_err1:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
+
+
 /**
  * drm_fb_release - remove and free the FBs on this file
  * @filp: file * from the ioctl
@@ -2328,7 +2433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
        } else if (connector->funcs->set_property)
                ret = connector->funcs->set_property(connector, property, out_resp->value);
 
-       /* store the property value if succesful */
+       /* store the property value if successful */
        if (!ret)
                drm_connector_property_set_value(connector, property, out_resp->value);
 out:
@@ -2478,3 +2583,72 @@ out:
        mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
+
+int drm_mode_page_flip_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_page_flip *page_flip = data;
+       struct drm_mode_object *obj;
+       struct drm_crtc *crtc;
+       struct drm_framebuffer *fb;
+       struct drm_pending_vblank_event *e = NULL;
+       unsigned long flags;
+       int ret = -EINVAL;
+
+       if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
+           page_flip->reserved != 0)
+               return -EINVAL;
+
+       mutex_lock(&dev->mode_config.mutex);
+       obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
+       if (!obj)
+               goto out;
+       crtc = obj_to_crtc(obj);
+
+       if (crtc->funcs->page_flip == NULL)
+               goto out;
+
+       obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB);
+       if (!obj)
+               goto out;
+       fb = obj_to_fb(obj);
+
+       if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+               ret = -ENOMEM;
+               spin_lock_irqsave(&dev->event_lock, flags);
+               if (file_priv->event_space < sizeof e->event) {
+                       spin_unlock_irqrestore(&dev->event_lock, flags);
+                       goto out;
+               }
+               file_priv->event_space -= sizeof e->event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+
+               e = kzalloc(sizeof *e, GFP_KERNEL);
+               if (e == NULL) {
+                       spin_lock_irqsave(&dev->event_lock, flags);
+                       file_priv->event_space += sizeof e->event;
+                       spin_unlock_irqrestore(&dev->event_lock, flags);
+                       goto out;
+               }
+
+               e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
+               e->event.base.length = sizeof e->event;
+               e->event.user_data = page_flip->user_data;
+               e->base.event = &e->event.base;
+               e->base.file_priv = file_priv;
+               e->base.destroy =
+                       (void (*) (struct drm_pending_event *)) kfree;
+       }
+
+       ret = crtc->funcs->page_flip(crtc, fb, e);
+       if (ret) {
+               spin_lock_irqsave(&dev->event_lock, flags);
+               file_priv->event_space += sizeof e->event;
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               kfree(e);
+       }
+
+out:
+       mutex_unlock(&dev->mode_config.mutex);
+       return ret;
+}
index bbfd110..4231d6d 100644 (file)
@@ -109,7 +109,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
 
        count = (*connector_funcs->get_modes)(connector);
        if (!count) {
-               count = drm_add_modes_noedid(connector, 800, 600);
+               count = drm_add_modes_noedid(connector, 1024, 768);
                if (!count)
                        return 0;
        }
@@ -1020,6 +1020,9 @@ bool drm_helper_initial_config(struct drm_device *dev)
 {
        int count = 0;
 
+       /* disable all the possible outputs/crtcs before entering KMS mode */
+       drm_helper_disable_unused_functions(dev);
+
        drm_fb_helper_parse_command_line(dev);
 
        count = drm_helper_probe_connector_modes(dev,
similarity index 79%
rename from drivers/gpu/drm/i915/intel_dp_i2c.c
rename to drivers/gpu/drm/drm_dp_i2c_helper.c
index a63b6f5..548887c 100644 (file)
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/i2c.h>
-#include "intel_dp.h"
+#include "drm_dp_helper.h"
 #include "drmP.h"
 
 /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
-
-#define MODE_I2C_START 1
-#define MODE_I2C_WRITE 2
-#define MODE_I2C_READ  4
-#define MODE_I2C_STOP  8
-
 static int
 i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
                            uint8_t write_byte, uint8_t *read_byte)
 {
        struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
-       uint16_t address = algo_data->address;
-       uint8_t msg[5];
-       uint8_t reply[2];
-       int msg_bytes;
-       int reply_bytes;
        int ret;
-
-       /* Set up the command byte */
-       if (mode & MODE_I2C_READ)
-               msg[0] = AUX_I2C_READ << 4;
-       else
-               msg[0] = AUX_I2C_WRITE << 4;
-
-       if (!(mode & MODE_I2C_STOP))
-               msg[0] |= AUX_I2C_MOT << 4;
-
-       msg[1] = address >> 8;
-       msg[2] = address;
-
-       switch (mode) {
-       case MODE_I2C_WRITE:
-               msg[3] = 0;
-               msg[4] = write_byte;
-               msg_bytes = 5;
-               reply_bytes = 1;
-               break;
-       case MODE_I2C_READ:
-               msg[3] = 0;
-               msg_bytes = 4;
-               reply_bytes = 2;
-               break;
-       default:
-               msg_bytes = 3;
-               reply_bytes = 1;
-               break;
-       }
-
-       for (;;) {
-               ret = (*algo_data->aux_ch)(adapter,
-                                          msg, msg_bytes,
-                                          reply, reply_bytes);
-               if (ret < 0) {
-                       DRM_DEBUG("aux_ch failed %d\n", ret);
-                       return ret;
-               }
-               switch (reply[0] & AUX_I2C_REPLY_MASK) {
-               case AUX_I2C_REPLY_ACK:
-                       if (mode == MODE_I2C_READ) {
-                               *read_byte = reply[1];
-                       }
-                       return reply_bytes - 1;
-               case AUX_I2C_REPLY_NACK:
-                       DRM_DEBUG("aux_ch nack\n");
-                       return -EREMOTEIO;
-               case AUX_I2C_REPLY_DEFER:
-                       DRM_DEBUG("aux_ch defer\n");
-                       udelay(100);
-                       break;
-               default:
-                       DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]);
-                       return -EREMOTEIO;
-               }
-       }
+       
+       ret = (*algo_data->aux_ch)(adapter, mode,
+                                  write_byte, read_byte);
+       return ret;
 }
 
 /*
@@ -224,7 +160,7 @@ i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
        if (ret >= 0)
                ret = num;
        i2c_algo_dp_aux_stop(adapter, reading);
-       DRM_DEBUG("dp_aux_xfer return %d\n", ret);
+       DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
        return ret;
 }
 
index a75ca63..ff2f104 100644 (file)
@@ -145,6 +145,8 @@ static struct drm_ioctl_desc drm_ioctls[] = {
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
        DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
+       DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW)
 };
 
 #define DRM_CORE_IOCTL_COUNT   ARRAY_SIZE( drm_ioctls )
@@ -365,6 +367,29 @@ static void __exit drm_core_exit(void)
 module_init(drm_core_init);
 module_exit(drm_core_exit);
 
+/**
+ * Copy and IOCTL return string to user space
+ */
+static int drm_copy_field(char *buf, size_t *buf_len, const char *value)
+{
+       int len;
+
+       /* don't overflow userbuf */
+       len = strlen(value);
+       if (len > *buf_len)
+               len = *buf_len;
+
+       /* let userspace know exact length of driver value (which could be
+        * larger than the userspace-supplied buffer) */
+       *buf_len = strlen(value);
+
+       /* finally, try filling in the userbuf */
+       if (len && buf)
+               if (copy_to_user(buf, value, len))
+                       return -EFAULT;
+       return 0;
+}
+
 /**
  * Get version information
  *
@@ -380,16 +405,21 @@ static int drm_version(struct drm_device *dev, void *data,
                       struct drm_file *file_priv)
 {
        struct drm_version *version = data;
-       int len;
+       int err;
 
        version->version_major = dev->driver->major;
        version->version_minor = dev->driver->minor;
        version->version_patchlevel = dev->driver->patchlevel;
-       DRM_COPY(version->name, dev->driver->name);
-       DRM_COPY(version->date, dev->driver->date);
-       DRM_COPY(version->desc, dev->driver->desc);
-
-       return 0;
+       err = drm_copy_field(version->name, &version->name_len,
+                       dev->driver->name);
+       if (!err)
+               err = drm_copy_field(version->date, &version->date_len,
+                               dev->driver->date);
+       if (!err)
+               err = drm_copy_field(version->desc, &version->desc_len,
+                               dev->driver->desc);
+
+       return err;
 }
 
 /**
index b54ba63..c39b26f 100644 (file)
@@ -123,18 +123,20 @@ static const u8 edid_header[] = {
  */
 static bool edid_is_valid(struct edid *edid)
 {
-       int i;
+       int i, score = 0;
        u8 csum = 0;
        u8 *raw_edid = (u8 *)edid;
 
-       if (memcmp(edid->header, edid_header, sizeof(edid_header)))
-               goto bad;
-       if (edid->version != 1) {
-               DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
+       for (i = 0; i < sizeof(edid_header); i++)
+               if (raw_edid[i] == edid_header[i])
+                       score++;
+
+       if (score == 8) ;
+       else if (score >= 6) {
+               DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
+               memcpy(raw_edid, edid_header, sizeof(edid_header));
+       } else
                goto bad;
-       }
-       if (edid->revision > 4)
-               DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
 
        for (i = 0; i < EDID_LENGTH; i++)
                csum += raw_edid[i];
@@ -143,6 +145,14 @@ static bool edid_is_valid(struct edid *edid)
                goto bad;
        }
 
+       if (edid->version != 1) {
+               DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
+               goto bad;
+       }
+
+       if (edid->revision > 4)
+               DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
+
        return 1;
 
 bad:
@@ -481,16 +491,17 @@ static struct drm_display_mode drm_dmt_modes[] = {
                   3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
                   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
 };
+static const int drm_num_dmt_modes =
+       sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
 
 static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
                        int hsize, int vsize, int fresh)
 {
-       int i, count;
+       int i;
        struct drm_display_mode *ptr, *mode;
 
-       count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
        mode = NULL;
-       for (i = 0; i < count; i++) {
+       for (i = 0; i < drm_num_dmt_modes; i++) {
                ptr = &drm_dmt_modes[i];
                if (hsize == ptr->hdisplay &&
                        vsize == ptr->vdisplay &&
@@ -834,8 +845,165 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
        return modes;
 }
 
+/*
+ * XXX fix this for:
+ * - GTF secondary curve formula
+ * - EDID 1.4 range offsets
+ * - CVT extended bits
+ */
+static bool
+mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing)
+{
+       struct detailed_data_monitor_range *range;
+       int hsync, vrefresh;
+
+       range = &timing->data.other_data.data.range;
+
+       hsync = drm_mode_hsync(mode);
+       vrefresh = drm_mode_vrefresh(mode);
+
+       if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz)
+               return false;
+
+       if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq)
+               return false;
+
+       if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) {
+               /* be forgiving since it's in units of 10MHz */
+               int max_clock = range->pixel_clock_mhz * 10 + 9;
+               max_clock *= 1000;
+               if (mode->clock > max_clock)
+                       return false;
+       }
+
+       return true;
+}
+
+/*
+ * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
+ * need to account for them.
+ */
+static int drm_gtf_modes_for_range(struct drm_connector *connector,
+                                  struct detailed_timing *timing)
+{
+       int i, modes = 0;
+       struct drm_display_mode *newmode;
+       struct drm_device *dev = connector->dev;
+
+       for (i = 0; i < drm_num_dmt_modes; i++) {
+               if (mode_in_range(drm_dmt_modes + i, timing)) {
+                       newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
+                       if (newmode) {
+                               drm_mode_probed_add(connector, newmode);
+                               modes++;
+                       }
+               }
+       }
+
+       return modes;
+}
+
+static int drm_cvt_modes(struct drm_connector *connector,
+                        struct detailed_timing *timing)
+{
+       int i, j, modes = 0;
+       struct drm_display_mode *newmode;
+       struct drm_device *dev = connector->dev;
+       struct cvt_timing *cvt;
+       const int rates[] = { 60, 85, 75, 60, 50 };
+
+       for (i = 0; i < 4; i++) {
+               int width, height;
+               cvt = &(timing->data.other_data.data.cvt[i]);
+
+               height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 8) + 1) * 2;
+               switch (cvt->code[1] & 0xc0) {
+               case 0x00:
+                       width = height * 4 / 3;
+                       break;
+               case 0x40:
+                       width = height * 16 / 9;
+                       break;
+               case 0x80:
+                       width = height * 16 / 10;
+                       break;
+               case 0xc0:
+                       width = height * 15 / 9;
+                       break;
+               }
+
+               for (j = 1; j < 5; j++) {
+                       if (cvt->code[2] & (1 << j)) {
+                               newmode = drm_cvt_mode(dev, width, height,
+                                                      rates[j], j == 0,
+                                                      false, false);
+                               if (newmode) {
+                                       drm_mode_probed_add(connector, newmode);
+                                       modes++;
+                               }
+                       }
+               }
+       }
+
+       return modes;
+}
+
+static int add_detailed_modes(struct drm_connector *connector,
+                             struct detailed_timing *timing,
+                             struct edid *edid, u32 quirks, int preferred)
+{
+       int i, modes = 0;
+       struct detailed_non_pixel *data = &timing->data.other_data;
+       int timing_level = standard_timing_level(edid);
+       int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
+       struct drm_display_mode *newmode;
+       struct drm_device *dev = connector->dev;
+
+       if (timing->pixel_clock) {
+               newmode = drm_mode_detailed(dev, edid, timing, quirks);
+               if (!newmode)
+                       return 0;
+
+               if (preferred)
+                       newmode->type |= DRM_MODE_TYPE_PREFERRED;
+
+               drm_mode_probed_add(connector, newmode);
+               return 1;
+       }
+
+       /* other timing types */
+       switch (data->type) {
+       case EDID_DETAIL_MONITOR_RANGE:
+               if (gtf)
+                       modes += drm_gtf_modes_for_range(connector, timing);
+               break;
+       case EDID_DETAIL_STD_MODES:
+               /* Six modes per detailed section */
+               for (i = 0; i < 6; i++) {
+                       struct std_timing *std;
+                       struct drm_display_mode *newmode;
+
+                       std = &data->data.timings[i];
+                       newmode = drm_mode_std(dev, std, edid->revision,
+                                              timing_level);
+                       if (newmode) {
+                               drm_mode_probed_add(connector, newmode);
+                               modes++;
+                       }
+               }
+               break;
+       case EDID_DETAIL_CVT_3BYTE:
+               modes += drm_cvt_modes(connector, timing);
+               break;
+       default:
+               break;
+       }
+
+       return modes;
+}
+
 /**
- * add_detailed_modes - get detailed mode info from EDID data
+ * add_detailed_info - get detailed mode info from EDID data
  * @connector: attached connector
  * @edid: EDID block to scan
  * @quirks: quirks to apply
@@ -846,67 +1014,24 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
 static int add_detailed_info(struct drm_connector *connector,
                             struct edid *edid, u32 quirks)
 {
-       struct drm_device *dev = connector->dev;
-       int i, j, modes = 0;
-       int timing_level;
-
-       timing_level = standard_timing_level(edid);
+       int i, modes = 0;
 
        for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
                struct detailed_timing *timing = &edid->detailed_timings[i];
-               struct detailed_non_pixel *data = &timing->data.other_data;
-               struct drm_display_mode *newmode;
-
-               /* X server check is version 1.1 or higher */
-               if (edid->version == 1 && edid->revision >= 1 &&
-                   !timing->pixel_clock) {
-                       /* Other timing or info */
-                       switch (data->type) {
-                       case EDID_DETAIL_MONITOR_SERIAL:
-                               break;
-                       case EDID_DETAIL_MONITOR_STRING:
-                               break;
-                       case EDID_DETAIL_MONITOR_RANGE:
-                               /* Get monitor range data */
-                               break;
-                       case EDID_DETAIL_MONITOR_NAME:
-                               break;
-                       case EDID_DETAIL_MONITOR_CPDATA:
-                               break;
-                       case EDID_DETAIL_STD_MODES:
-                               for (j = 0; j < 6; i++) {
-                                       struct std_timing *std;
-                                       struct drm_display_mode *newmode;
-
-                                       std = &data->data.timings[j];
-                                       newmode = drm_mode_std(dev, std,
-                                                              edid->revision,
-                                                              timing_level);
-                                       if (newmode) {
-                                               drm_mode_probed_add(connector, newmode);
-                                               modes++;
-                                       }
-                               }
-                               break;
-                       default:
-                               break;
-                       }
-               } else {
-                       newmode = drm_mode_detailed(dev, edid, timing, quirks);
-                       if (!newmode)
-                               continue;
+               int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
 
-                       /* First detailed mode is preferred */
-                       if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING))
-                               newmode->type |= DRM_MODE_TYPE_PREFERRED;
-                       drm_mode_probed_add(connector, newmode);
+               /* In 1.0, only timings are allowed */
+               if (!timing->pixel_clock && edid->version == 1 &&
+                       edid->revision == 0)
+                       continue;
 
-                       modes++;
-               }
+               modes += add_detailed_modes(connector, timing, edid, quirks,
+                                           preferred);
        }
 
        return modes;
 }
+
 /**
  * add_detailed_mode_eedid - get detailed mode info from addtional timing
  *                     EDID block
@@ -920,12 +1045,9 @@ static int add_detailed_info(struct drm_connector *connector,
 static int add_detailed_info_eedid(struct drm_connector *connector,
                             struct edid *edid, u32 quirks)
 {
-       struct drm_device *dev = connector->dev;
-       int i, j, modes = 0;
+       int i, modes = 0;
        char *edid_ext = NULL;
        struct detailed_timing *timing;
-       struct detailed_non_pixel *data;
-       struct drm_display_mode *newmode;
        int edid_ext_num;
        int start_offset, end_offset;
        int timing_level;
@@ -976,51 +1098,7 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
        for (i = start_offset; i < end_offset;
                        i += sizeof(struct detailed_timing)) {
                timing = (struct detailed_timing *)(edid_ext + i);
-               data = &timing->data.other_data;
-               /* Detailed mode timing */
-               if (timing->pixel_clock) {
-                       newmode = drm_mode_detailed(dev, edid, timing, quirks);
-                       if (!newmode)
-                               continue;
-
-                       drm_mode_probed_add(connector, newmode);
-
-                       modes++;
-                       continue;
-               }
-
-               /* Other timing or info */
-               switch (data->type) {
-               case EDID_DETAIL_MONITOR_SERIAL:
-                       break;
-               case EDID_DETAIL_MONITOR_STRING:
-                       break;
-               case EDID_DETAIL_MONITOR_RANGE:
-                       /* Get monitor range data */
-                       break;
-               case EDID_DETAIL_MONITOR_NAME:
-                       break;
-               case EDID_DETAIL_MONITOR_CPDATA:
-                       break;
-               case EDID_DETAIL_STD_MODES:
-                       /* Five modes per detailed section */
-                       for (j = 0; j < 5; i++) {
-                               struct std_timing *std;
-                               struct drm_display_mode *newmode;
-
-                               std = &data->data.timings[j];
-                               newmode = drm_mode_std(dev, std,
-                                                      edid->revision,
-                                                      timing_level);
-                               if (newmode) {
-                                       drm_mode_probed_add(connector, newmode);
-                                       modes++;
-                               }
-                       }
-                       break;
-               default:
-                       break;
-               }
+               modes += add_detailed_modes(connector, timing, edid, quirks, 0);
        }
 
        return modes;
@@ -1066,19 +1144,19 @@ static int drm_ddc_read_edid(struct drm_connector *connector,
                             struct i2c_adapter *adapter,
                             char *buf, int len)
 {
-       int ret;
+       int i;
 
-       ret = drm_do_probe_ddc_edid(adapter, buf, len);
-       if (ret != 0) {
-               goto end;
-       }
-       if (!edid_is_valid((struct edid *)buf)) {
-               dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
-                        drm_get_connector_name(connector));
-               ret = -1;
+       for (i = 0; i < 4; i++) {
+               if (drm_do_probe_ddc_edid(adapter, buf, len))
+                       return -1;
+               if (edid_is_valid((struct edid *)buf))
+                       return 0;
        }
-end:
-       return ret;
+
+       /* repeated checksum failures; warn, but carry on */
+       dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+                drm_get_connector_name(connector));
+       return -1;
 }
 
 /**
@@ -1296,6 +1374,8 @@ int drm_add_modes_noedid(struct drm_connector *connector,
                                        ptr->vdisplay > vdisplay)
                                continue;
                }
+               if (drm_mode_vrefresh(ptr) > 61)
+                       continue;
                mode = drm_mode_duplicate(dev, ptr);
                if (mode) {
                        drm_mode_probed_add(connector, mode);
index 65ef011..1b49fa0 100644 (file)
@@ -373,11 +373,9 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
                                        mutex_unlock(&dev->mode_config.mutex);
                                }
                        }
-                       if (dpms_mode == DRM_MODE_DPMS_OFF) {
-                               mutex_lock(&dev->mode_config.mutex);
-                               crtc_funcs->dpms(crtc, dpms_mode);
-                               mutex_unlock(&dev->mode_config.mutex);
-                       }
+                       mutex_lock(&dev->mode_config.mutex);
+                       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+                       mutex_unlock(&dev->mode_config.mutex);
                }
        }
 }
@@ -385,18 +383,23 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
 int drm_fb_helper_blank(int blank, struct fb_info *info)
 {
        switch (blank) {
+       /* Display: On; HSync: On, VSync: On */
        case FB_BLANK_UNBLANK:
                drm_fb_helper_on(info);
                break;
+       /* Display: Off; HSync: On, VSync: On */
        case FB_BLANK_NORMAL:
-               drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+               drm_fb_helper_off(info, DRM_MODE_DPMS_ON);
                break;
+       /* Display: Off; HSync: Off, VSync: On */
        case FB_BLANK_HSYNC_SUSPEND:
                drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
                break;
+       /* Display: Off; HSync: On, VSync: Off */
        case FB_BLANK_VSYNC_SUSPEND:
                drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
                break;
+       /* Display: Off; HSync: Off, VSync: Off */
        case FB_BLANK_POWERDOWN:
                drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
                break;
@@ -905,8 +908,13 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 
        if (new_fb) {
                info->var.pixclock = 0;
-               if (register_framebuffer(info) < 0)
+               ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
+               if (ret)
+                       return ret;
+               if (register_framebuffer(info) < 0) {
+                       fb_dealloc_cmap(&info->cmap);
                        return -EINVAL;
+               }
        } else {
                drm_fb_helper_set_par(info);
        }
@@ -936,6 +944,7 @@ void drm_fb_helper_free(struct drm_fb_helper *helper)
                unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
        }
        drm_fb_helper_crtc_free(helper);
+       fb_dealloc_cmap(&helper->fb->fbdev->cmap);
 }
 EXPORT_SYMBOL(drm_fb_helper_free);
 
index 251bc0e..08d14df 100644 (file)
@@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
 
        INIT_LIST_HEAD(&priv->lhead);
        INIT_LIST_HEAD(&priv->fbs);
+       INIT_LIST_HEAD(&priv->event_list);
+       init_waitqueue_head(&priv->event_wait);
+       priv->event_space = 4096; /* set aside 4k for event buffer */
 
        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_open(dev, priv);
@@ -297,6 +300,18 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
                                goto out_free;
                        }
                }
+               mutex_lock(&dev->struct_mutex);
+               if (dev->driver->master_set) {
+                       ret = dev->driver->master_set(dev, priv, true);
+                       if (ret) {
+                               /* drop both references if this fails */
+                               drm_master_put(&priv->minor->master);
+                               drm_master_put(&priv->master);
+                               mutex_unlock(&dev->struct_mutex);
+                               goto out_free;
+                       }
+               }
+               mutex_unlock(&dev->struct_mutex);
        } else {
                /* get a reference to the master */
                priv->master = drm_master_get(priv->minor->master);
@@ -413,6 +428,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp)
        }
 }
 
+static void drm_events_release(struct drm_file *file_priv)
+{
+       struct drm_device *dev = file_priv->minor->dev;
+       struct drm_pending_event *e, *et;
+       struct drm_pending_vblank_event *v, *vt;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       /* Remove pending flips */
+       list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
+               if (v->base.file_priv == file_priv) {
+                       list_del(&v->base.link);
+                       drm_vblank_put(dev, v->pipe);
+                       v->base.destroy(&v->base);
+               }
+
+       /* Remove unconsumed events */
+       list_for_each_entry_safe(e, et, &file_priv->event_list, link)
+               e->destroy(e);
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 /**
  * Release file.
  *
@@ -451,6 +490,8 @@ int drm_release(struct inode *inode, struct file *filp)
        if (file_priv->minor->master)
                drm_master_release(dev, filp);
 
+       drm_events_release(file_priv);
+
        if (dev->driver->driver_features & DRIVER_GEM)
                drm_gem_release(dev, file_priv);
 
@@ -504,6 +545,8 @@ int drm_release(struct inode *inode, struct file *filp)
 
                if (file_priv->minor->master == file_priv->master) {
                        /* drop the reference held my the minor */
+                       if (dev->driver->master_drop)
+                               dev->driver->master_drop(dev, file_priv, true);
                        drm_master_put(&file_priv->minor->master);
                }
        }
@@ -544,9 +587,74 @@ int drm_release(struct inode *inode, struct file *filp)
 }
 EXPORT_SYMBOL(drm_release);
 
-/** No-op. */
+static bool
+drm_dequeue_event(struct drm_file *file_priv,
+                 size_t total, size_t max, struct drm_pending_event **out)
+{
+       struct drm_device *dev = file_priv->minor->dev;
+       struct drm_pending_event *e;
+       unsigned long flags;
+       bool ret = false;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       *out = NULL;
+       if (list_empty(&file_priv->event_list))
+               goto out;
+       e = list_first_entry(&file_priv->event_list,
+                            struct drm_pending_event, link);
+       if (e->event->length + total > max)
+               goto out;
+
+       file_priv->event_space += e->event->length;
+       list_del(&e->link);
+       *out = e;
+       ret = true;
+
+out:
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+       return ret;
+}
+
+ssize_t drm_read(struct file *filp, char __user *buffer,
+                size_t count, loff_t *offset)
+{
+       struct drm_file *file_priv = filp->private_data;
+       struct drm_pending_event *e;
+       size_t total;
+       ssize_t ret;
+
+       ret = wait_event_interruptible(file_priv->event_wait,
+                                      !list_empty(&file_priv->event_list));
+       if (ret < 0)
+               return ret;
+
+       total = 0;
+       while (drm_dequeue_event(file_priv, total, count, &e)) {
+               if (copy_to_user(buffer + total,
+                                e->event, e->event->length)) {
+                       total = -EFAULT;
+                       break;
+               }
+
+               total += e->event->length;
+               e->destroy(e);
+       }
+
+       return total;
+}
+EXPORT_SYMBOL(drm_read);
+
 unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
 {
-       return 0;
+       struct drm_file *file_priv = filp->private_data;
+       unsigned int mask = 0;
+
+       poll_wait(filp, &file_priv->event_wait, wait);
+
+       if (!list_empty(&file_priv->event_list))
+               mask |= POLLIN | POLLRDNORM;
+
+       return mask;
 }
 EXPORT_SYMBOL(drm_poll);
index 0a6f0b3..7998ee6 100644 (file)
@@ -429,15 +429,21 @@ int drm_vblank_get(struct drm_device *dev, int crtc)
 
        spin_lock_irqsave(&dev->vbl_lock, irqflags);
        /* Going from 0->1 means we have to enable interrupts again */
-       if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 &&
-           !dev->vblank_enabled[crtc]) {
-               ret = dev->driver->enable_vblank(dev, crtc);
-               DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
-               if (ret)
+       if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) {
+               if (!dev->vblank_enabled[crtc]) {
+                       ret = dev->driver->enable_vblank(dev, crtc);
+                       DRM_DEBUG("enabling vblank on crtc %d, ret: %d\n", crtc, ret);
+                       if (ret)
+                               atomic_dec(&dev->vblank_refcount[crtc]);
+                       else {
+                               dev->vblank_enabled[crtc] = 1;
+                               drm_update_vblank_count(dev, crtc);
+                       }
+               }
+       } else {
+               if (!dev->vblank_enabled[crtc]) {
                        atomic_dec(&dev->vblank_refcount[crtc]);
-               else {
-                       dev->vblank_enabled[crtc] = 1;
-                       drm_update_vblank_count(dev, crtc);
+                       ret = -EINVAL;
                }
        }
        spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
@@ -464,6 +470,18 @@ void drm_vblank_put(struct drm_device *dev, int crtc)
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
+void drm_vblank_off(struct drm_device *dev, int crtc)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&dev->vbl_lock, irqflags);
+       DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       dev->vblank_enabled[crtc] = 0;
+       dev->last_vblank[crtc] = dev->driver->get_vblank_counter(dev, crtc);
+       spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+}
+EXPORT_SYMBOL(drm_vblank_off);
+
 /**
  * drm_vblank_pre_modeset - account for vblanks across mode sets
  * @dev: DRM device
@@ -550,6 +568,63 @@ out:
        return ret;
 }
 
+static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
+                                 union drm_wait_vblank *vblwait,
+                                 struct drm_file *file_priv)
+{
+       struct drm_pending_vblank_event *e;
+       struct timeval now;
+       unsigned long flags;
+       unsigned int seq;
+
+       e = kzalloc(sizeof *e, GFP_KERNEL);
+       if (e == NULL)
+               return -ENOMEM;
+
+       e->pipe = pipe;
+       e->event.base.type = DRM_EVENT_VBLANK;
+       e->event.base.length = sizeof e->event;
+       e->event.user_data = vblwait->request.signal;
+       e->base.event = &e->event.base;
+       e->base.file_priv = file_priv;
+       e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
+
+       do_gettimeofday(&now);
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       if (file_priv->event_space < sizeof e->event) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               kfree(e);
+               return -ENOMEM;
+       }
+
+       file_priv->event_space -= sizeof e->event;
+       seq = drm_vblank_count(dev, pipe);
+       if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
+           (seq - vblwait->request.sequence) <= (1 << 23)) {
+               vblwait->request.sequence = seq + 1;
+               vblwait->reply.sequence = vblwait->request.sequence;
+       }
+
+       DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
+                 vblwait->request.sequence, seq, pipe);
+
+       e->event.sequence = vblwait->request.sequence;
+       if ((seq - vblwait->request.sequence) <= (1 << 23)) {
+               e->event.tv_sec = now.tv_sec;
+               e->event.tv_usec = now.tv_usec;
+               drm_vblank_put(dev, e->pipe);
+               list_add_tail(&e->base.link, &e->base.file_priv->event_list);
+               wake_up_interruptible(&e->base.file_priv->event_wait);
+       } else {
+               list_add_tail(&e->base.link, &dev->vblank_event_list);
+       }
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       return 0;
+}
+
 /**
  * Wait for VBLANK.
  *
@@ -609,6 +684,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
                goto done;
        }
 
+       if (flags & _DRM_VBLANK_EVENT)
+               return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
+
        if ((flags & _DRM_VBLANK_NEXTONMISS) &&
            (seq - vblwait->request.sequence) <= (1<<23)) {
                vblwait->request.sequence = seq + 1;
@@ -641,6 +719,38 @@ done:
        return ret;
 }
 
+void drm_handle_vblank_events(struct drm_device *dev, int crtc)
+{
+       struct drm_pending_vblank_event *e, *t;
+       struct timeval now;
+       unsigned long flags;
+       unsigned int seq;
+
+       do_gettimeofday(&now);
+       seq = drm_vblank_count(dev, crtc);
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+
+       list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+               if (e->pipe != crtc)
+                       continue;
+               if ((seq - e->event.sequence) > (1<<23))
+                       continue;
+
+               DRM_DEBUG("vblank event on %d, current %d\n",
+                         e->event.sequence, seq);
+
+               e->event.sequence = seq;
+               e->event.tv_sec = now.tv_sec;
+               e->event.tv_usec = now.tv_usec;
+               drm_vblank_put(dev, e->pipe);
+               list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+               wake_up_interruptible(&e->base.file_priv->event_wait);
+       }
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 /**
  * drm_handle_vblank - handle a vblank event
  * @dev: DRM device
@@ -651,7 +761,11 @@ done:
  */
 void drm_handle_vblank(struct drm_device *dev, int crtc)
 {
+       if (!dev->num_crtcs)
+               return;
+
        atomic_inc(&dev->_vblank_count[crtc]);
        DRM_WAKEUP(&dev->vbl_queue[crtc]);
+       drm_handle_vblank_events(dev, crtc);
 }
 EXPORT_SYMBOL(drm_handle_vblank);
index 97dc5a4..d7d7eac 100644 (file)
@@ -226,6 +226,44 @@ struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
 }
 EXPORT_SYMBOL(drm_mm_get_block_generic);
 
+struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *node,
+                                               unsigned long size,
+                                               unsigned alignment,
+                                               unsigned long start,
+                                               unsigned long end,
+                                               int atomic)
+{
+       struct drm_mm_node *align_splitoff = NULL;
+       unsigned tmp = 0;
+       unsigned wasted = 0;
+
+       if (node->start < start)
+               wasted += start - node->start;
+       if (alignment)
+               tmp = ((node->start + wasted) % alignment);
+
+       if (tmp)
+               wasted += alignment - tmp;
+       if (wasted) {
+               align_splitoff = drm_mm_split_at_start(node, wasted, atomic);
+               if (unlikely(align_splitoff == NULL))
+                       return NULL;
+       }
+
+       if (node->size == size) {
+               list_del_init(&node->fl_entry);
+               node->free = 0;
+       } else {
+               node = drm_mm_split_at_start(node, size, atomic);
+       }
+
+       if (align_splitoff)
+               drm_mm_put_block(align_splitoff);
+
+       return node;
+}
+EXPORT_SYMBOL(drm_mm_get_block_range_generic);
+
 /*
  * Put a block. Merge with the previous and / or next block if they are free.
  * Otherwise add to the free stack.
@@ -331,6 +369,56 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
 }
 EXPORT_SYMBOL(drm_mm_search_free);
 
+struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
+                                               unsigned long size,
+                                               unsigned alignment,
+                                               unsigned long start,
+                                               unsigned long end,
+                                               int best_match)
+{
+       struct list_head *list;
+       const struct list_head *free_stack = &mm->fl_entry;
+       struct drm_mm_node *entry;
+       struct drm_mm_node *best;
+       unsigned long best_size;
+       unsigned wasted;
+
+       best = NULL;
+       best_size = ~0UL;
+
+       list_for_each(list, free_stack) {
+               entry = list_entry(list, struct drm_mm_node, fl_entry);
+               wasted = 0;
+
+               if (entry->size < size)
+                       continue;
+
+               if (entry->start > end || (entry->start+entry->size) < start)
+                       continue;
+
+               if (entry->start < start)
+                       wasted += start - entry->start;
+
+               if (alignment) {
+                       register unsigned tmp = (entry->start + wasted) % alignment;
+                       if (tmp)
+                               wasted += alignment - tmp;
+               }
+
+               if (entry->size >= size + wasted) {
+                       if (!best_match)
+                               return entry;
+                       if (size < best_size) {
+                               best = entry;
+                               best_size = entry->size;
+                       }
+               }
+       }
+
+       return best;
+}
+EXPORT_SYMBOL(drm_mm_search_free_in_range);
+
 int drm_mm_clean(struct drm_mm * mm)
 {
        struct list_head *head = &mm->ml_entry;
@@ -381,6 +469,26 @@ void drm_mm_takedown(struct drm_mm * mm)
 }
 EXPORT_SYMBOL(drm_mm_takedown);
 
+void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
+{
+       struct drm_mm_node *entry;
+       int total_used = 0, total_free = 0, total = 0;
+
+       list_for_each_entry(entry, &mm->ml_entry, ml_entry) {
+               printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8ld: %s\n",
+                       prefix, entry->start, entry->start + entry->size,
+                       entry->size, entry->free ? "free" : "used");
+               total += entry->size;
+               if (entry->free)
+                       total_free += entry->size;
+               else
+                       total_used += entry->size;
+       }
+       printk(KERN_DEBUG "%s total: %d, used %d free %d\n", prefix, total,
+               total_used, total_free);
+}
+EXPORT_SYMBOL(drm_mm_debug_table);
+
 #if defined(CONFIG_DEBUG_FS)
 int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
 {
@@ -395,7 +503,7 @@ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
                else
                        total_used += entry->size;
        }
-       seq_printf(m, "total: %d, used %d free %d\n", total, total_free, total_used);
+       seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free);
        return 0;
 }
 EXPORT_SYMBOL(drm_mm_dump_table);
index 51f6772..6d81a02 100644 (file)
@@ -553,6 +553,32 @@ int drm_mode_height(struct drm_display_mode *mode)
 }
 EXPORT_SYMBOL(drm_mode_height);
 
+/** drm_mode_hsync - get the hsync of a mode
+ * @mode: mode
+ *
+ * LOCKING:
+ * None.
+ *
+ * Return @modes's hsync rate in kHz, rounded to the nearest int.
+ */
+int drm_mode_hsync(struct drm_display_mode *mode)
+{
+       unsigned int calc_val;
+
+       if (mode->hsync)
+               return mode->hsync;
+
+       if (mode->htotal < 0)
+               return 0;
+
+       calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */
+       calc_val += 500;                                /* round to 1000Hz */
+       calc_val /= 1000;                               /* truncate to kHz */
+
+       return calc_val;
+}
+EXPORT_SYMBOL(drm_mode_hsync);
+
 /**
  * drm_mode_vrefresh - get the vrefresh of a mode
  * @mode: mode
@@ -560,7 +586,7 @@ EXPORT_SYMBOL(drm_mode_height);
  * LOCKING:
  * None.
  *
- * Return @mode's vrefresh rate or calculate it if necessary.
+ * Return @mode's vrefresh rate in Hz or calculate it if necessary.
  *
  * FIXME: why is this needed?  shouldn't vrefresh be set already?
  *
index 55bb8a8..ad73e14 100644 (file)
@@ -128,6 +128,7 @@ struct drm_master *drm_master_get(struct drm_master *master)
        kref_get(&master->refcount);
        return master;
 }
+EXPORT_SYMBOL(drm_master_get);
 
 static void drm_master_destroy(struct kref *kref)
 {
@@ -170,10 +171,13 @@ void drm_master_put(struct drm_master **master)
        kref_put(&(*master)->refcount, drm_master_destroy);
        *master = NULL;
 }
+EXPORT_SYMBOL(drm_master_put);
 
 int drm_setmaster_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file_priv)
 {
+       int ret = 0;
+
        if (file_priv->is_master)
                return 0;
 
@@ -188,6 +192,13 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
                mutex_lock(&dev->struct_mutex);
                file_priv->minor->master = drm_master_get(file_priv->master);
                file_priv->is_master = 1;
+               if (dev->driver->master_set) {
+                       ret = dev->driver->master_set(dev, file_priv, false);
+                       if (unlikely(ret != 0)) {
+                               file_priv->is_master = 0;
+                               drm_master_put(&file_priv->minor->master);
+                       }
+               }
                mutex_unlock(&dev->struct_mutex);
        }
 
@@ -204,6 +215,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
+       if (dev->driver->master_drop)
+               dev->driver->master_drop(dev, file_priv, false);
        drm_master_put(&file_priv->minor->master);
        file_priv->is_master = 0;
        mutex_unlock(&dev->struct_mutex);
@@ -220,9 +233,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
        INIT_LIST_HEAD(&dev->ctxlist);
        INIT_LIST_HEAD(&dev->vmalist);
        INIT_LIST_HEAD(&dev->maplist);
+       INIT_LIST_HEAD(&dev->vblank_event_list);
 
        spin_lock_init(&dev->count_lock);
        spin_lock_init(&dev->drw_lock);
+       spin_lock_init(&dev->event_lock);
        init_timer(&dev->timer);
        mutex_init(&dev->struct_mutex);
        mutex_init(&dev->ctxlist_mutex);
index fa7b9be..9929f84 100644 (file)
@@ -15,7 +15,6 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
          intel_lvds.o \
          intel_bios.o \
          intel_dp.o \
-         intel_dp_i2c.o \
          intel_hdmi.o \
          intel_sdvo.o \
          intel_modes.o \
@@ -23,6 +22,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
          intel_fb.o \
          intel_tv.o \
          intel_dvo.o \
+         intel_overlay.o \
          dvo_ch7xxx.o \
          dvo_ch7017.o \
          dvo_ivch.o \
index 621815b..1184c14 100644 (file)
@@ -249,7 +249,8 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
        if (val != CH7017_DEVICE_ID_VALUE &&
            val != CH7018_DEVICE_ID_VALUE &&
            val != CH7019_DEVICE_ID_VALUE) {
-               DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
+               DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
+                               "Slave %d.\n",
                          val, i2cbus->adapter.name,dvo->slave_addr);
                goto fail;
        }
@@ -284,7 +285,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
        uint8_t horizontal_active_pixel_output, vertical_active_line_output;
        uint8_t active_input_line_output;
 
-       DRM_DEBUG("Registers before mode setting\n");
+       DRM_DEBUG_KMS("Registers before mode setting\n");
        ch7017_dump_regs(dvo);
 
        /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
@@ -346,7 +347,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
        /* Turn the LVDS back on with new settings. */
        ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
 
-       DRM_DEBUG("Registers after mode setting\n");
+       DRM_DEBUG_KMS("Registers after mode setting\n");
        ch7017_dump_regs(dvo);
 }
 
@@ -386,7 +387,7 @@ static void ch7017_dump_regs(struct intel_dvo_device *dvo)
 #define DUMP(reg)                                      \
 do {                                                   \
        ch7017_read(dvo, reg, &val);                    \
-       DRM_DEBUG(#reg ": %02x\n", val);                \
+       DRM_DEBUG_KMS(#reg ": %02x\n", val);            \
 } while (0)
 
        DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
index a9b8962..d56ff5c 100644 (file)
@@ -152,7 +152,7 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
        };
 
        if (!ch7xxx->quiet) {
-               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+               DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
        return false;
@@ -179,7 +179,7 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
                return true;
 
        if (!ch7xxx->quiet) {
-               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+               DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
 
@@ -207,7 +207,8 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
 
        name = ch7xxx_get_id(vendor);
        if (!name) {
-               DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+               DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
+                               "slave %d.\n",
                          vendor, adapter->name, dvo->slave_addr);
                goto out;
        }
@@ -217,13 +218,14 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
                goto out;
 
        if (device != CH7xxx_DID) {
-               DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+               DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
+                               "slave %d.\n",
                          vendor, adapter->name, dvo->slave_addr);
                goto out;
        }
 
        ch7xxx->quiet = false;
-       DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
+       DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
                  name, vendor, device);
        return true;
 out:
@@ -315,8 +317,8 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
 
        for (i = 0; i < CH7xxx_NUM_REGS; i++) {
                if ((i % 8) == 0 )
-                       DRM_DEBUG("\n %02X: ", i);
-               DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
+                       DRM_LOG_KMS("\n %02X: ", i);
+               DRM_LOG_KMS("%02X ", ch7xxx->mode_reg.regs[i]);
        }
 }
 
index aa176f9..24169e5 100644 (file)
@@ -202,7 +202,8 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
        };
 
        if (!priv->quiet) {
-               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+               DRM_DEBUG_KMS("Unable to read register 0x%02x from "
+                               "%s:%02x.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
        return false;
@@ -230,7 +231,7 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
                return true;
 
        if (!priv->quiet) {
-               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+               DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
 
@@ -261,7 +262,7 @@ static bool ivch_init(struct intel_dvo_device *dvo,
         * the address it's responding on.
         */
        if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
-               DRM_DEBUG("ivch detect failed due to address mismatch "
+               DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
                          "(%d vs %d)\n",
                          (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
                goto out;
@@ -367,41 +368,41 @@ static void ivch_dump_regs(struct intel_dvo_device *dvo)
        uint16_t val;
 
        ivch_read(dvo, VR00, &val);
-       DRM_DEBUG("VR00: 0x%04x\n", val);
+       DRM_LOG_KMS("VR00: 0x%04x\n", val);
        ivch_read(dvo, VR01, &val);
-       DRM_DEBUG("VR01: 0x%04x\n", val);
+       DRM_LOG_KMS("VR01: 0x%04x\n", val);
        ivch_read(dvo, VR30, &val);
-       DRM_DEBUG("VR30: 0x%04x\n", val);
+       DRM_LOG_KMS("VR30: 0x%04x\n", val);
        ivch_read(dvo, VR40, &val);
-       DRM_DEBUG("VR40: 0x%04x\n", val);
+       DRM_LOG_KMS("VR40: 0x%04x\n", val);
 
        /* GPIO registers */
        ivch_read(dvo, VR80, &val);
-       DRM_DEBUG("VR80: 0x%04x\n", val);
+       DRM_LOG_KMS("VR80: 0x%04x\n", val);
        ivch_read(dvo, VR81, &val);
-       DRM_DEBUG("VR81: 0x%04x\n", val);
+       DRM_LOG_KMS("VR81: 0x%04x\n", val);
        ivch_read(dvo, VR82, &val);
-       DRM_DEBUG("VR82: 0x%04x\n", val);
+       DRM_LOG_KMS("VR82: 0x%04x\n", val);
        ivch_read(dvo, VR83, &val);
-       DRM_DEBUG("VR83: 0x%04x\n", val);
+       DRM_LOG_KMS("VR83: 0x%04x\n", val);
        ivch_read(dvo, VR84, &val);
-       DRM_DEBUG("VR84: 0x%04x\n", val);
+       DRM_LOG_KMS("VR84: 0x%04x\n", val);
        ivch_read(dvo, VR85, &val);
-       DRM_DEBUG("VR85: 0x%04x\n", val);
+       DRM_LOG_KMS("VR85: 0x%04x\n", val);
        ivch_read(dvo, VR86, &val);
-       DRM_DEBUG("VR86: 0x%04x\n", val);
+       DRM_LOG_KMS("VR86: 0x%04x\n", val);
        ivch_read(dvo, VR87, &val);
-       DRM_DEBUG("VR87: 0x%04x\n", val);
+       DRM_LOG_KMS("VR87: 0x%04x\n", val);
        ivch_read(dvo, VR88, &val);
-       DRM_DEBUG("VR88: 0x%04x\n", val);
+       DRM_LOG_KMS("VR88: 0x%04x\n", val);
 
        /* Scratch register 0 - AIM Panel type */
        ivch_read(dvo, VR8E, &val);
-       DRM_DEBUG("VR8E: 0x%04x\n", val);
+       DRM_LOG_KMS("VR8E: 0x%04x\n", val);
 
        /* Scratch register 1 - Status register */
        ivch_read(dvo, VR8F, &val);
-       DRM_DEBUG("VR8F: 0x%04x\n", val);
+       DRM_LOG_KMS("VR8F: 0x%04x\n", val);
 }
 
 static void ivch_save(struct intel_dvo_device *dvo)
index e1c1f73..0001c13 100644 (file)
@@ -105,7 +105,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
        };
 
        if (!sil->quiet) {
-               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+               DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
        return false;
@@ -131,7 +131,7 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
                return true;
 
        if (!sil->quiet) {
-               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+               DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
 
@@ -158,7 +158,7 @@ static bool sil164_init(struct intel_dvo_device *dvo,
                goto out;
 
        if (ch != (SIL164_VID & 0xff)) {
-               DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+               DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
                          ch, adapter->name, dvo->slave_addr);
                goto out;
        }
@@ -167,13 +167,13 @@ static bool sil164_init(struct intel_dvo_device *dvo,
                goto out;
 
        if (ch != (SIL164_DID & 0xff)) {
-               DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+               DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
                          ch, adapter->name, dvo->slave_addr);
                goto out;
        }
        sil->quiet = false;
 
-       DRM_DEBUG("init sil164 dvo controller successfully!\n");
+       DRM_DEBUG_KMS("init sil164 dvo controller successfully!\n");
        return true;
 
 out:
@@ -241,15 +241,15 @@ static void sil164_dump_regs(struct intel_dvo_device *dvo)
        uint8_t val;
 
        sil164_readb(dvo, SIL164_FREQ_LO, &val);
-       DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
+       DRM_LOG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
        sil164_readb(dvo, SIL164_FREQ_HI, &val);
-       DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
+       DRM_LOG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
        sil164_readb(dvo, SIL164_REG8, &val);
-       DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
+       DRM_LOG_KMS("SIL164_REG8: 0x%02x\n", val);
        sil164_readb(dvo, SIL164_REG9, &val);
-       DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
+       DRM_LOG_KMS("SIL164_REG9: 0x%02x\n", val);
        sil164_readb(dvo, SIL164_REGC, &val);
-       DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
+       DRM_LOG_KMS("SIL164_REGC: 0x%02x\n", val);
 }
 
 static void sil164_save(struct intel_dvo_device *dvo)
index 9ecc907..c7c391b 100644 (file)
@@ -130,7 +130,7 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
        };
 
        if (!tfp->quiet) {
-               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+               DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
        return false;
@@ -156,7 +156,7 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
                return true;
 
        if (!tfp->quiet) {
-               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+               DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
                          addr, i2cbus->adapter.name, dvo->slave_addr);
        }
 
@@ -191,13 +191,15 @@ static bool tfp410_init(struct intel_dvo_device *dvo,
        tfp->quiet = true;
 
        if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
-               DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
+               DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s "
+                               "Slave %d.\n",
                          id, adapter->name, dvo->slave_addr);
                goto out;
        }
 
        if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
-               DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
+               DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s "
+                               "Slave %d.\n",
                          id, adapter->name, dvo->slave_addr);
                goto out;
        }
@@ -262,33 +264,33 @@ static void tfp410_dump_regs(struct intel_dvo_device *dvo)
        uint8_t val, val2;
 
        tfp410_readb(dvo, TFP410_REV, &val);
-       DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_REV: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_CTL_1, &val);
-       DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_CTL1: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_CTL_2, &val);
-       DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_CTL2: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_CTL_3, &val);
-       DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_CTL3: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_USERCFG, &val);
-       DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_USERCFG: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_DE_DLY, &val);
-       DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_DE_CTL, &val);
-       DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_DE_TOP, &val);
-       DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
+       DRM_LOG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
        tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
        tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
-       DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+       DRM_LOG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
        tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
        tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
-       DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+       DRM_LOG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
        tfp410_readb(dvo, TFP410_H_RES_LO, &val);
        tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
-       DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+       DRM_LOG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
        tfp410_readb(dvo, TFP410_V_RES_LO, &val);
        tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
-       DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+       DRM_LOG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
 }
 
 static void tfp410_save(struct intel_dvo_device *dvo)
index 26bf055..18476bf 100644 (file)
@@ -27,6 +27,7 @@
  */
 
 #include <linux/seq_file.h>
+#include <linux/debugfs.h>
 #include "drmP.h"
 #include "drm.h"
 #include "i915_drm.h"
@@ -96,13 +97,14 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
        {
                struct drm_gem_object *obj = obj_priv->obj;
 
-               seq_printf(m, "    %p: %s %8zd %08x %08x %d %s",
+               seq_printf(m, "    %p: %s %8zd %08x %08x %d%s%s",
                           obj,
                           get_pin_flag(obj_priv),
                           obj->size,
                           obj->read_domains, obj->write_domain,
                           obj_priv->last_rendering_seqno,
-                          obj_priv->dirty ? "dirty" : "");
+                          obj_priv->dirty ? " dirty" : "",
+                          obj_priv->madv == I915_MADV_DONTNEED ? " purgeable" : "");
 
                if (obj->name)
                        seq_printf(m, " (name: %d)", obj->name);
@@ -160,7 +162,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       if (!IS_IGDNG(dev)) {
+       if (!IS_IRONLAKE(dev)) {
                seq_printf(m, "Interrupt enable:    %08x\n",
                           I915_READ(IER));
                seq_printf(m, "Interrupt identity:  %08x\n",
@@ -412,6 +414,109 @@ static int i915_registers_info(struct seq_file *m, void *data) {
        return 0;
 }
 
+static int
+i915_wedged_open(struct inode *inode,
+                struct file *filp)
+{
+       filp->private_data = inode->i_private;
+       return 0;
+}
+
+static ssize_t
+i915_wedged_read(struct file *filp,
+                char __user *ubuf,
+                size_t max,
+                loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       char buf[80];
+       int len;
+
+       len = snprintf(buf, sizeof (buf),
+                      "wedged :  %d\n",
+                      atomic_read(&dev_priv->mm.wedged));
+
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+}
+
+static ssize_t
+i915_wedged_write(struct file *filp,
+                 const char __user *ubuf,
+                 size_t cnt,
+                 loff_t *ppos)
+{
+       struct drm_device *dev = filp->private_data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       char buf[20];
+       int val = 1;
+
+       if (cnt > 0) {
+               if (cnt > sizeof (buf) - 1)
+                       return -EINVAL;
+
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+
+               val = simple_strtoul(buf, NULL, 0);
+       }
+
+       DRM_INFO("Manually setting wedged to %d\n", val);
+
+       atomic_set(&dev_priv->mm.wedged, val);
+       if (val) {
+               DRM_WAKEUP(&dev_priv->irq_queue);
+               queue_work(dev_priv->wq, &dev_priv->error_work);
+       }
+
+       return cnt;
+}
+
+static const struct file_operations i915_wedged_fops = {
+       .owner = THIS_MODULE,
+       .open = i915_wedged_open,
+       .read = i915_wedged_read,
+       .write = i915_wedged_write,
+};
+
+/* As the drm_debugfs_init() routines are called before dev->dev_private is
+ * allocated we need to hook into the minor for release. */
+static int
+drm_add_fake_info_node(struct drm_minor *minor,
+                      struct dentry *ent,
+                      const void *key)
+{
+       struct drm_info_node *node;
+
+       node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
+       if (node == NULL) {
+               debugfs_remove(ent);
+               return -ENOMEM;
+       }
+
+       node->minor = minor;
+       node->dent = ent;
+       node->info_ent = (void *) key;
+       list_add(&node->list, &minor->debugfs_nodes.list);
+
+       return 0;
+}
+
+static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
+{
+       struct drm_device *dev = minor->dev;
+       struct dentry *ent;
+
+       ent = debugfs_create_file("i915_wedged",
+                                 S_IRUGO | S_IWUSR,
+                                 root, dev,
+                                 &i915_wedged_fops);
+       if (IS_ERR(ent))
+               return PTR_ERR(ent);
+
+       return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
+}
 
 static struct drm_info_list i915_debugfs_list[] = {
        {"i915_regs", i915_registers_info, 0},
@@ -432,6 +537,12 @@ static struct drm_info_list i915_debugfs_list[] = {
 
 int i915_debugfs_init(struct drm_minor *minor)
 {
+       int ret;
+
+       ret = i915_wedged_create(minor->debugfs_root, minor);
+       if (ret)
+               return ret;
+
        return drm_debugfs_create_files(i915_debugfs_list,
                                        I915_DEBUGFS_ENTRIES,
                                        minor->debugfs_root, minor);
@@ -441,7 +552,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
 {
        drm_debugfs_remove_files(i915_debugfs_list,
                                 I915_DEBUGFS_ENTRIES, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
+                                1, minor);
 }
 
 #endif /* CONFIG_DEBUG_FS */
-
index e5b138b..701bfea 100644 (file)
@@ -807,6 +807,12 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_NUM_FENCES_AVAIL:
                value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
                break;
+       case I915_PARAM_HAS_OVERLAY:
+               value = dev_priv->overlay ? 1 : 0;
+               break;
+       case I915_PARAM_HAS_PAGEFLIPPING:
+               value = 1;
+               break;
        default:
                DRM_DEBUG_DRIVER("Unknown parameter %d\n",
                                        param->param);
@@ -962,7 +968,7 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *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) || IS_IGDNG(dev))
+       if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev))
                overhead = 4096;
        else
                overhead = (*aperture_size / 1024) + 4096;
@@ -1048,7 +1054,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
        int gtt_offset, gtt_size;
 
        if (IS_I965G(dev)) {
-               if (IS_G4X(dev) || IS_IGDNG(dev)) {
+               if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
                        gtt_offset = 2*1024*1024;
                        gtt_size = 2*1024*1024;
                } else {
@@ -1070,7 +1076,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
 
        entry = *(volatile u32 *)(gtt + (gtt_addr / 1024));
 
-       DRM_DEBUG("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
+       DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
 
        /* Mask out these reserved bits on this hardware. */
        if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) ||
@@ -1096,7 +1102,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
        phys =(entry & PTE_ADDRESS_MASK) |
                ((uint64_t)(entry & PTE_ADDRESS_MASK_HIGH) << (32 - 4));
 
-       DRM_DEBUG("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
+       DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
 
        return phys;
 }
@@ -1306,7 +1312,7 @@ static void i915_get_mem_freq(struct drm_device *dev)
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 tmp;
 
-       if (!IS_IGD(dev))
+       if (!IS_PINEVIEW(dev))
                return;
 
        tmp = I915_READ(CLKCFG);
@@ -1413,7 +1419,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        if (ret)
                goto out_iomapfree;
 
-       dev_priv->wq = create_workqueue("i915");
+       dev_priv->wq = create_singlethread_workqueue("i915");
        if (dev_priv->wq == NULL) {
                DRM_ERROR("Failed to create our workqueue.\n");
                ret = -ENOMEM;
@@ -1434,7 +1440,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        dev->driver->get_vblank_counter = i915_get_vblank_counter;
        dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
-       if (IS_G4X(dev) || IS_IGDNG(dev)) {
+       if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
                dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
                dev->driver->get_vblank_counter = gm45_get_vblank_counter;
        }
@@ -1489,9 +1495,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        }
 
        /* Must be done after probing outputs */
-       /* FIXME: verify on IGDNG */
-       if (!IS_IGDNG(dev))
-               intel_opregion_init(dev, 0);
+       intel_opregion_init(dev, 0);
 
        setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
                    (unsigned long) dev);
@@ -1525,6 +1529,15 @@ int i915_driver_unload(struct drm_device *dev)
        }
 
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               /*
+                * free the memory space allocated for the child device
+                * config parsed from VBT
+                */
+               if (dev_priv->child_dev && dev_priv->child_dev_num) {
+                       kfree(dev_priv->child_dev);
+                       dev_priv->child_dev = NULL;
+                       dev_priv->child_dev_num = 0;
+               }
                drm_irq_uninstall(dev);
                vga_client_register(dev->pdev, NULL, NULL, NULL);
        }
@@ -1535,8 +1548,7 @@ int i915_driver_unload(struct drm_device *dev)
        if (dev_priv->regs != NULL)
                iounmap(dev_priv->regs);
 
-       if (!IS_IGDNG(dev))
-               intel_opregion_free(dev, 0);
+       intel_opregion_free(dev, 0);
 
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                intel_modeset_cleanup(dev);
@@ -1548,6 +1560,8 @@ int i915_driver_unload(struct drm_device *dev)
                mutex_unlock(&dev->struct_mutex);
                drm_mm_takedown(&dev_priv->vram);
                i915_gem_lastclose(dev);
+
+               intel_cleanup_overlay(dev);
        }
 
        pci_dev_put(dev_priv->bridge_dev);
@@ -1656,6 +1670,8 @@ struct drm_ioctl_desc i915_ioctls[] = {
        DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
        DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
        DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, 0),
+       DRM_IOCTL_DEF(DRM_I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW),
+       DRM_IOCTL_DEF(DRM_I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW),
 };
 
 int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
index 7f436ec..2fa2178 100644 (file)
@@ -333,6 +333,7 @@ static struct drm_driver driver = {
                 .mmap = drm_gem_mmap,
                 .poll = drm_poll,
                 .fasync = drm_fasync,
+                .read = drm_read,
 #ifdef CONFIG_COMPAT
                 .compat_ioctl = i915_compat_ioctl,
 #endif
index a725f65..fbecac7 100644 (file)
@@ -170,6 +170,8 @@ struct drm_i915_display_funcs {
        /* clock gating init */
 };
 
+struct intel_overlay;
+
 typedef struct drm_i915_private {
        struct drm_device *dev;
 
@@ -187,6 +189,7 @@ typedef struct drm_i915_private {
        unsigned int status_gfx_addr;
        drm_local_map_t hws_map;
        struct drm_gem_object *hws_obj;
+       struct drm_gem_object *pwrctx;
 
        struct resource mch_res;
 
@@ -206,11 +209,13 @@ 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,
+       /** splitted irq regs for graphics and display engine on Ironlake,
            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 pch_irq_mask_reg;
+       u32 pch_irq_enable_reg;
 
        u32 hotplug_supported_mask;
        struct work_struct hotplug_work;
@@ -240,6 +245,9 @@ typedef struct drm_i915_private {
 
        struct intel_opregion opregion;
 
+       /* overlay */
+       struct intel_overlay *overlay;
+
        /* LVDS info */
        int backlight_duty_cycle;  /* restore backlight to this value */
        bool panel_wants_dither;
@@ -258,7 +266,7 @@ typedef struct drm_i915_private {
 
        struct notifier_block lid_notifier;
 
-       int crt_ddc_bus; /* -1 = unknown, else GPIO to use for CRT DDC */
+       int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */
        struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
        int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
        int num_fence_regs; /* 8 on pre-965, 16 otherwise */
@@ -280,6 +288,7 @@ typedef struct drm_i915_private {
        u32 saveDSPBCNTR;
        u32 saveDSPARB;
        u32 saveRENDERSTANDBY;
+       u32 savePWRCTXA;
        u32 saveHWS;
        u32 savePIPEACONF;
        u32 savePIPEBCONF;
@@ -374,8 +383,6 @@ typedef struct drm_i915_private {
        u32 saveFDI_RXA_IMR;
        u32 saveFDI_RXB_IMR;
        u32 saveCACHE_MODE_0;
-       u32 saveD_STATE;
-       u32 saveDSPCLK_GATE_D;
        u32 saveMI_ARB_STATE;
        u32 saveSWF0[16];
        u32 saveSWF1[16];
@@ -539,13 +546,21 @@ typedef struct drm_i915_private {
        /* indicate whether the LVDS_BORDER should be enabled or not */
        unsigned int lvds_border_bits;
 
+       struct drm_crtc *plane_to_crtc_mapping[2];
+       struct drm_crtc *pipe_to_crtc_mapping[2];
+       wait_queue_head_t pending_flip_queue;
+
        /* Reclocking support */
        bool render_reclock_avail;
        bool lvds_downclock_avail;
+       /* indicates the reduced downclock for LVDS*/
+       int lvds_downclock;
        struct work_struct idle_work;
        struct timer_list idle_timer;
        bool busy;
        u16 orig_clock;
+       int child_dev_num;
+       struct child_device_config *child_dev;
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
@@ -638,6 +653,13 @@ struct drm_i915_gem_object {
         * Advice: are the backing pages purgeable?
         */
        int madv;
+
+       /**
+        * Number of crtcs where this object is currently the fb, but
+        * will be page flipped away on the next vblank.  When it
+        * reaches 0, dev_priv->pending_flip_queue will be woken up.
+        */
+       atomic_t pending_flip;
 };
 
 /**
@@ -738,6 +760,8 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
 void
 i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
 
+void intel_enable_asle (struct drm_device *dev);
+
 
 /* i915_mem.c */
 extern int i915_mem_alloc(struct drm_device *dev, void *data,
@@ -813,6 +837,9 @@ void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
 int i915_gem_do_init(struct drm_device *dev, unsigned long start,
                     unsigned long end);
 int i915_gem_idle(struct drm_device *dev);
+uint32_t i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+                         uint32_t flush_domains);
+int i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible);
 int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
                                      int write);
@@ -824,6 +851,7 @@ 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);
+void i915_gem_object_flush_write_domain(struct drm_gem_object *obj);
 
 void i915_gem_shrinker_init(void);
 void i915_gem_shrinker_exit(void);
@@ -863,11 +891,13 @@ extern int i915_restore_state(struct drm_device *dev);
 extern int intel_opregion_init(struct drm_device *dev, int resume);
 extern void intel_opregion_free(struct drm_device *dev, int suspend);
 extern void opregion_asle_intr(struct drm_device *dev);
+extern void ironlake_opregion_gse_intr(struct drm_device *dev);
 extern void opregion_enable_asle(struct drm_device *dev);
 #else
 static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
 static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; }
 static inline void opregion_asle_intr(struct drm_device *dev) { return; }
+static inline void ironlake_opregion_gse_intr(struct drm_device *dev) { return; }
 static inline void opregion_enable_asle(struct drm_device *dev) { return; }
 #endif
 
@@ -955,8 +985,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define IS_I830(dev) ((dev)->pci_device == 0x3577)
 #define IS_845G(dev) ((dev)->pci_device == 0x2562)
 #define IS_I85X(dev) ((dev)->pci_device == 0x3582)
-#define IS_I855(dev) ((dev)->pci_device == 0x3582)
 #define IS_I865G(dev) ((dev)->pci_device == 0x2572)
+#define IS_I8XX(dev) (IS_I830(dev) || IS_845G(dev) || IS_I85X(dev) || IS_I865G(dev))
 
 #define IS_I915G(dev) ((dev)->pci_device == 0x2582 || (dev)->pci_device == 0x258a)
 #define IS_I915GM(dev) ((dev)->pci_device == 0x2592)
@@ -990,47 +1020,51 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
                     (dev)->pci_device == 0x2E42 || \
                     IS_GM45(dev))
 
-#define IS_IGDG(dev) ((dev)->pci_device == 0xa001)
-#define IS_IGDGM(dev) ((dev)->pci_device == 0xa011)
-#define IS_IGD(dev) (IS_IGDG(dev) || IS_IGDGM(dev))
+#define IS_PINEVIEW_G(dev) ((dev)->pci_device == 0xa001)
+#define IS_PINEVIEW_M(dev) ((dev)->pci_device == 0xa011)
+#define IS_PINEVIEW(dev) (IS_PINEVIEW_G(dev) || IS_PINEVIEW_M(dev))
 
 #define IS_G33(dev)    ((dev)->pci_device == 0x29C2 || \
                        (dev)->pci_device == 0x29B2 ||  \
                        (dev)->pci_device == 0x29D2 ||  \
-                       (IS_IGD(dev)))
+                       (IS_PINEVIEW(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_IRONLAKE_D(dev)     ((dev)->pci_device == 0x0042)
+#define IS_IRONLAKE_M(dev)     ((dev)->pci_device == 0x0046)
+#define IS_IRONLAKE(dev)       (IS_IRONLAKE_D(dev) || IS_IRONLAKE_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_IGDNG(dev))
+                     IS_IRONLAKE(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_IGDNG_M(dev))
+                       IS_PINEVIEW(dev) || IS_IRONLAKE_M(dev))
 
 #define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev) || \
-                               IS_IGDNG(dev))
+                               IS_IRONLAKE(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) || IS_IGDNG(dev))
-#define SUPPORTS_INTEGRATED_DP(dev)    (IS_G4X(dev) || IS_IGDNG(dev))
-#define SUPPORTS_EDP(dev)              (IS_IGDNG_M(dev))
+#define SUPPORTS_DIGITAL_OUTPUTS(dev)  (IS_I9XX(dev) && !IS_PINEVIEW(dev))
+#define SUPPORTS_INTEGRATED_HDMI(dev)  (IS_G4X(dev) || IS_IRONLAKE(dev))
+#define SUPPORTS_INTEGRATED_DP(dev)    (IS_G4X(dev) || IS_IRONLAKE(dev))
+#define SUPPORTS_EDP(dev)              (IS_IRONLAKE_M(dev))
+#define SUPPORTS_TV(dev)               (IS_I9XX(dev) && IS_MOBILE(dev) && \
+                                       !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev))
 #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev) || IS_I965G(dev))
 /* dsparb controlled by hw only */
-#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
 
-#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IGDNG(dev))
-#define HAS_PIPE_CXSR(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IRONLAKE(dev))
+#define HAS_PIPE_CXSR(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
 #define I915_HAS_FBC(dev) (IS_MOBILE(dev) && \
                           (IS_I9XX(dev) || IS_GM45(dev)) && \
-                          !IS_IGD(dev) && \
-                          !IS_IGDNG(dev))
+                          !IS_PINEVIEW(dev) && \
+                          !IS_IRONLAKE(dev))
+#define I915_HAS_RC6(dev) (IS_I965GM(dev) || IS_GM45(dev) || IS_IRONLAKE_M(dev))
 
 #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
 
index abfc27b..8c463cf 100644 (file)
@@ -1288,6 +1288,7 @@ i915_gem_create_mmap_offset(struct drm_gem_object *obj)
        list->hash.key = list->file_offset_node->start;
        if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
                DRM_ERROR("failed to add to map hash\n");
+               ret = -ENOMEM;
                goto out_free_mm;
        }
 
@@ -1309,7 +1310,7 @@ out_free_list:
  * i915_gem_release_mmap - remove physical page mappings
  * @obj: obj in question
  *
- * Preserve the reservation of the mmaping with the DRM core code, but
+ * Preserve the reservation of the mmapping with the DRM core code, but
  * relinquish ownership of the pages back to the system.
  *
  * It is vital that we remove the page mapping if we have mapped a tiled
@@ -1583,7 +1584,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
  *
  * Returned sequence numbers are nonzero on success.
  */
-static uint32_t
+uint32_t
 i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
                 uint32_t flush_domains)
 {
@@ -1617,7 +1618,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
        OUT_RING(MI_USER_INTERRUPT);
        ADVANCE_LP_RING();
 
-       DRM_DEBUG("%d\n", seqno);
+       DRM_DEBUG_DRIVER("%d\n", seqno);
 
        request->seqno = seqno;
        request->emitted_jiffies = jiffies;
@@ -1820,12 +1821,8 @@ i915_gem_retire_work_handler(struct work_struct *work)
        mutex_unlock(&dev->struct_mutex);
 }
 
-/**
- * Waits for a sequence number to be signaled, and cleans up the
- * request and object lists appropriately for that event.
- */
-static int
-i915_wait_request(struct drm_device *dev, uint32_t seqno)
+int
+i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 ier;
@@ -1837,7 +1834,7 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
                return -EIO;
 
        if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
-               if (IS_IGDNG(dev))
+               if (IS_IRONLAKE(dev))
                        ier = I915_READ(DEIER) | I915_READ(GTIER);
                else
                        ier = I915_READ(IER);
@@ -1852,10 +1849,15 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
 
                dev_priv->mm.waiting_gem_seqno = seqno;
                i915_user_irq_get(dev);
-               ret = wait_event_interruptible(dev_priv->irq_queue,
-                                              i915_seqno_passed(i915_get_gem_seqno(dev),
-                                                                seqno) ||
-                                              atomic_read(&dev_priv->mm.wedged));
+               if (interruptible)
+                       ret = wait_event_interruptible(dev_priv->irq_queue,
+                               i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
+                               atomic_read(&dev_priv->mm.wedged));
+               else
+                       wait_event(dev_priv->irq_queue,
+                               i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
+                               atomic_read(&dev_priv->mm.wedged));
+
                i915_user_irq_put(dev);
                dev_priv->mm.waiting_gem_seqno = 0;
 
@@ -1879,6 +1881,16 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
        return ret;
 }
 
+/**
+ * Waits for a sequence number to be signaled, and cleans up the
+ * request and object lists appropriately for that event.
+ */
+static int
+i915_wait_request(struct drm_device *dev, uint32_t seqno)
+{
+       return i915_do_wait_request(dev, seqno, 1);
+}
+
 static void
 i915_gem_flush(struct drm_device *dev,
               uint32_t invalidate_domains,
@@ -1947,7 +1959,7 @@ i915_gem_flush(struct drm_device *dev,
 #endif
                BEGIN_LP_RING(2);
                OUT_RING(cmd);
-               OUT_RING(0); /* noop */
+               OUT_RING(MI_NOOP);
                ADVANCE_LP_RING();
        }
 }
@@ -2760,6 +2772,22 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
                                            old_write_domain);
 }
 
+void
+i915_gem_object_flush_write_domain(struct drm_gem_object *obj)
+{
+       switch (obj->write_domain) {
+       case I915_GEM_DOMAIN_GTT:
+               i915_gem_object_flush_gtt_write_domain(obj);
+               break;
+       case I915_GEM_DOMAIN_CPU:
+               i915_gem_object_flush_cpu_write_domain(obj);
+               break;
+       default:
+               i915_gem_object_flush_gpu_write_domain(obj);
+               break;
+       }
+}
+
 /**
  * Moves a single object to the GTT read, and possibly write domain.
  *
@@ -3525,6 +3553,41 @@ i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
        return 0;
 }
 
+static int
+i915_gem_wait_for_pending_flip(struct drm_device *dev,
+                              struct drm_gem_object **object_list,
+                              int count)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_gem_object *obj_priv;
+       DEFINE_WAIT(wait);
+       int i, ret = 0;
+
+       for (;;) {
+               prepare_to_wait(&dev_priv->pending_flip_queue,
+                               &wait, TASK_INTERRUPTIBLE);
+               for (i = 0; i < count; i++) {
+                       obj_priv = object_list[i]->driver_private;
+                       if (atomic_read(&obj_priv->pending_flip) > 0)
+                               break;
+               }
+               if (i == count)
+                       break;
+
+               if (!signal_pending(current)) {
+                       mutex_unlock(&dev->struct_mutex);
+                       schedule();
+                       mutex_lock(&dev->struct_mutex);
+                       continue;
+               }
+               ret = -ERESTARTSYS;
+               break;
+       }
+       finish_wait(&dev_priv->pending_flip_queue, &wait);
+
+       return ret;
+}
+
 int
 i915_gem_execbuffer(struct drm_device *dev, void *data,
                    struct drm_file *file_priv)
@@ -3540,7 +3603,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
        int ret, ret2, i, pinned = 0;
        uint64_t exec_offset;
        uint32_t seqno, flush_domains, reloc_index;
-       int pin_tries;
+       int pin_tries, flips;
 
 #if WATCH_EXEC
        DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
@@ -3552,8 +3615,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                return -EINVAL;
        }
        /* Copy in the exec list from userland */
-       exec_list = drm_calloc_large(sizeof(*exec_list), args->buffer_count);
-       object_list = drm_calloc_large(sizeof(*object_list), args->buffer_count);
+       exec_list = drm_malloc_ab(sizeof(*exec_list), args->buffer_count);
+       object_list = drm_malloc_ab(sizeof(*object_list), args->buffer_count);
        if (exec_list == NULL || object_list == NULL) {
                DRM_ERROR("Failed to allocate exec or object list "
                          "for %d buffers\n",
@@ -3598,20 +3661,19 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
        i915_verify_inactive(dev, __FILE__, __LINE__);
 
        if (atomic_read(&dev_priv->mm.wedged)) {
-               DRM_ERROR("Execbuf while wedged\n");
                mutex_unlock(&dev->struct_mutex);
                ret = -EIO;
                goto pre_mutex_err;
        }
 
        if (dev_priv->mm.suspended) {
-               DRM_ERROR("Execbuf while VT-switched.\n");
                mutex_unlock(&dev->struct_mutex);
                ret = -EBUSY;
                goto pre_mutex_err;
        }
 
        /* Look up object handles */
+       flips = 0;
        for (i = 0; i < args->buffer_count; i++) {
                object_list[i] = drm_gem_object_lookup(dev, file_priv,
                                                       exec_list[i].handle);
@@ -3630,6 +3692,14 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
                        goto err;
                }
                obj_priv->in_execbuffer = true;
+               flips += atomic_read(&obj_priv->pending_flip);
+       }
+
+       if (flips > 0) {
+               ret = i915_gem_wait_for_pending_flip(dev, object_list,
+                                                    args->buffer_count);
+               if (ret)
+                       goto err;
        }
 
        /* Pin and relocate */
@@ -4356,7 +4426,7 @@ i915_gem_init_hws(struct drm_device *dev)
        memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
        I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
        I915_READ(HWS_PGA); /* posting read */
-       DRM_DEBUG("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
+       DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
 
        return 0;
 }
@@ -4614,8 +4684,8 @@ i915_gem_load(struct drm_device *dev)
                        for (i = 0; i < 8; i++)
                                I915_WRITE(FENCE_REG_945_8 + (i * 4), 0);
        }
-
        i915_gem_detect_bit_6_swizzle(dev);
+       init_waitqueue_head(&dev_priv->pending_flip_queue);
 }
 
 /*
@@ -4790,7 +4860,7 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
        user_data = (char __user *) (uintptr_t) args->data_ptr;
        obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
 
-       DRM_DEBUG("obj_addr %p, %lld\n", obj_addr, args->size);
+       DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size);
        ret = copy_from_user(obj_addr, user_data, args->size);
        if (ret)
                return -EFAULT;
index 200e398..30d6af6 100644 (file)
@@ -121,7 +121,7 @@ intel_alloc_mchbar_resource(struct drm_device *dev)
                                     0,   pcibios_align_resource,
                                     dev_priv->bridge_dev);
        if (ret) {
-               DRM_DEBUG("failed bus alloc: %d\n", ret);
+               DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret);
                dev_priv->mch_res.start = 0;
                goto out;
        }
@@ -209,8 +209,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
        uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
        bool need_disable;
 
-       if (IS_IGDNG(dev)) {
-               /* On IGDNG whatever DRAM config, GPU always do
+       if (IS_IRONLAKE(dev)) {
+               /* On Ironlake whatever DRAM config, GPU always do
                 * same swizzling setup.
                 */
                swizzle_x = I915_BIT_6_SWIZZLE_9_10;
index aa7fd82..85f4c5d 100644 (file)
  * we leave them always unmasked in IMR and then control enabling them through
  * PIPESTAT alone.
  */
-#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT |                \
-                                  I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
-                                  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
-                                  I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+#define I915_INTERRUPT_ENABLE_FIX                      \
+       (I915_ASLE_INTERRUPT |                          \
+        I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |          \
+        I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |          \
+        I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |  \
+        I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT |  \
+        I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
 
 /** Interrupts that we mask and unmask at runtime. */
 #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
@@ -61,7 +64,7 @@
                                         DRM_I915_VBLANK_PIPE_B)
 
 void
-igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+ironlake_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;
@@ -71,7 +74,7 @@ igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
 }
 
 static inline void
-igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+ironlake_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;
@@ -82,7 +85,7 @@ igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
 
 /* For display hotplug interrupt */
 void
-igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ironlake_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;
@@ -92,7 +95,7 @@ igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
 }
 
 static inline void
-igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ironlake_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;
@@ -156,6 +159,20 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
        }
 }
 
+/**
+ * intel_enable_asle - enable ASLE interrupt for OpRegion
+ */
+void intel_enable_asle (struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+       if (IS_IRONLAKE(dev))
+               ironlake_enable_display_irq(dev_priv, DE_GSE);
+       else
+               i915_enable_pipestat(dev_priv, 1,
+                                    I915_LEGACY_BLC_EVENT_ENABLE);
+}
+
 /**
  * i915_pipe_enabled - check if a pipe is enabled
  * @dev: DRM device
@@ -191,7 +208,8 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
        low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-               DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
+               DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
+                               "pipe %d\n", pipe);
                return 0;
        }
 
@@ -220,7 +238,8 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
        int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
 
        if (!i915_pipe_enabled(dev, pipe)) {
-               DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
+               DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
+                                       "pipe %d\n", pipe);
                return 0;
        }
 
@@ -250,12 +269,12 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_sysfs_hotplug_event(dev);
 }
 
-irqreturn_t igdng_irq_handler(struct drm_device *dev)
+irqreturn_t ironlake_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, de_ier;
-       u32 new_de_iir, new_gt_iir;
+       u32 de_iir, gt_iir, de_ier, pch_iir;
+       u32 new_de_iir, new_gt_iir, new_pch_iir;
        struct drm_i915_master_private *master_priv;
 
        /* disable master interrupt before clearing iir  */
@@ -265,13 +284,18 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
 
        de_iir = I915_READ(DEIIR);
        gt_iir = I915_READ(GTIIR);
+       pch_iir = I915_READ(SDEIIR);
 
        for (;;) {
-               if (de_iir == 0 && gt_iir == 0)
+               if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
                        break;
 
                ret = IRQ_HANDLED;
 
+               /* should clear PCH hotplug event before clear CPU irq */
+               I915_WRITE(SDEIIR, pch_iir);
+               new_pch_iir = I915_READ(SDEIIR);
+
                I915_WRITE(DEIIR, de_iir);
                new_de_iir = I915_READ(DEIIR);
                I915_WRITE(GTIIR, gt_iir);
@@ -291,8 +315,18 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
                        DRM_WAKEUP(&dev_priv->irq_queue);
                }
 
+               if (de_iir & DE_GSE)
+                       ironlake_opregion_gse_intr(dev);
+
+               /* check event from PCH */
+               if ((de_iir & DE_PCH_EVENT) &&
+                       (pch_iir & SDE_HOTPLUG_MASK)) {
+                       queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+               }
+
                de_iir = new_de_iir;
                gt_iir = new_gt_iir;
+               pch_iir = new_pch_iir;
        }
 
        I915_WRITE(DEIER, de_ier);
@@ -317,19 +351,19 @@ static void i915_error_work_func(struct work_struct *work)
        char *reset_event[] = { "RESET=1", NULL };
        char *reset_done_event[] = { "ERROR=0", NULL };
 
-       DRM_DEBUG("generating error event\n");
+       DRM_DEBUG_DRIVER("generating error event\n");
        kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
 
        if (atomic_read(&dev_priv->mm.wedged)) {
                if (IS_I965G(dev)) {
-                       DRM_DEBUG("resetting chip\n");
+                       DRM_DEBUG_DRIVER("resetting chip\n");
                        kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
                        if (!i965_reset(dev, GDRST_RENDER)) {
                                atomic_set(&dev_priv->mm.wedged, 0);
                                kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
                        }
                } else {
-                       printk("reboot required\n");
+                       DRM_DEBUG_DRIVER("reboot required\n");
                }
        }
 }
@@ -355,7 +389,7 @@ static void i915_capture_error_state(struct drm_device *dev)
 
        error = kmalloc(sizeof(*error), GFP_ATOMIC);
        if (!error) {
-               DRM_DEBUG("out ot memory, not capturing error state\n");
+               DRM_DEBUG_DRIVER("out ot memory, not capturing error state\n");
                goto out;
        }
 
@@ -512,7 +546,6 @@ static void i915_handle_error(struct drm_device *dev, bool wedged)
                /*
                 * Wakeup waiting processes so they don't hang
                 */
-               printk("i915: Waking up sleeping processes\n");
                DRM_WAKEUP(&dev_priv->irq_queue);
        }
 
@@ -535,8 +568,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 
        atomic_inc(&dev_priv->irq_received);
 
-       if (IS_IGDNG(dev))
-               return igdng_irq_handler(dev);
+       if (IS_IRONLAKE(dev))
+               return ironlake_irq_handler(dev);
 
        iir = I915_READ(IIR);
 
@@ -568,14 +601,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                 */
                if (pipea_stats & 0x8000ffff) {
                        if (pipea_stats &  PIPE_FIFO_UNDERRUN_STATUS)
-                               DRM_DEBUG("pipe a underrun\n");
+                               DRM_DEBUG_DRIVER("pipe a underrun\n");
                        I915_WRITE(PIPEASTAT, pipea_stats);
                        irq_received = 1;
                }
 
                if (pipeb_stats & 0x8000ffff) {
                        if (pipeb_stats &  PIPE_FIFO_UNDERRUN_STATUS)
-                               DRM_DEBUG("pipe b underrun\n");
+                               DRM_DEBUG_DRIVER("pipe b underrun\n");
                        I915_WRITE(PIPEBSTAT, pipeb_stats);
                        irq_received = 1;
                }
@@ -591,7 +624,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                    (iir & I915_DISPLAY_PORT_INTERRUPT)) {
                        u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
 
-                       DRM_DEBUG("hotplug event received, stat 0x%08x\n",
+                       DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
                                  hotplug_status);
                        if (hotplug_status & dev_priv->hotplug_supported_mask)
                                queue_work(dev_priv->wq,
@@ -599,27 +632,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 
                        I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
                        I915_READ(PORT_HOTPLUG_STAT);
-
-                       /* EOS interrupts occurs */
-                       if (IS_IGD(dev) &&
-                               (hotplug_status & CRT_EOS_INT_STATUS)) {
-                               u32 temp;
-
-                               DRM_DEBUG("EOS interrupt occurs\n");
-                               /* status is already cleared */
-                               temp = I915_READ(ADPA);
-                               temp &= ~ADPA_DAC_ENABLE;
-                               I915_WRITE(ADPA, temp);
-
-                               temp = I915_READ(PORT_HOTPLUG_EN);
-                               temp &= ~CRT_EOS_INT_EN;
-                               I915_WRITE(PORT_HOTPLUG_EN, temp);
-
-                               temp = I915_READ(PORT_HOTPLUG_STAT);
-                               if (temp & CRT_EOS_INT_STATUS)
-                                       I915_WRITE(PORT_HOTPLUG_STAT,
-                                               CRT_EOS_INT_STATUS);
-                       }
                }
 
                I915_WRITE(IIR, iir);
@@ -641,14 +653,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                        mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
                }
 
+               if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT)
+                       intel_prepare_page_flip(dev, 0);
+
+               if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT)
+                       intel_prepare_page_flip(dev, 1);
+
                if (pipea_stats & vblank_status) {
                        vblank++;
                        drm_handle_vblank(dev, 0);
+                       intel_finish_page_flip(dev, 0);
                }
 
                if (pipeb_stats & vblank_status) {
                        vblank++;
                        drm_handle_vblank(dev, 1);
+                       intel_finish_page_flip(dev, 1);
                }
 
                if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
@@ -684,7 +704,7 @@ static int i915_emit_irq(struct drm_device * dev)
 
        i915_kernel_lost_context(dev);
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_DRIVER("\n");
 
        dev_priv->counter++;
        if (dev_priv->counter > 0x7FFFFFFFUL)
@@ -709,8 +729,8 @@ void i915_user_irq_get(struct drm_device *dev)
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
        if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
-               if (IS_IGDNG(dev))
-                       igdng_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+               if (IS_IRONLAKE(dev))
+                       ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
                else
                        i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
        }
@@ -725,8 +745,8 @@ 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)) {
-               if (IS_IGDNG(dev))
-                       igdng_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+               if (IS_IRONLAKE(dev))
+                       ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
                else
                        i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
        }
@@ -749,7 +769,7 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
        struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
        int ret = 0;
 
-       DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
+       DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr,
                  READ_BREADCRUMB(dev_priv));
 
        if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
@@ -832,7 +852,7 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
        if (!(pipeconf & PIPEACONF_ENABLE))
                return -EINVAL;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return 0;
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
@@ -854,7 +874,7 @@ 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))
+       if (IS_IRONLAKE(dev))
                return;
 
        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
@@ -868,7 +888,7 @@ void i915_enable_interrupt (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                opregion_enable_asle(dev);
        dev_priv->irq_enabled = 1;
 }
@@ -976,7 +996,7 @@ void i915_hangcheck_elapsed(unsigned long data)
 
 /* drm_dma.h hooks
 */
-static void igdng_irq_preinstall(struct drm_device *dev)
+static void ironlake_irq_preinstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
 
@@ -992,14 +1012,21 @@ static void igdng_irq_preinstall(struct drm_device *dev)
        I915_WRITE(GTIMR, 0xffffffff);
        I915_WRITE(GTIER, 0x0);
        (void) I915_READ(GTIER);
+
+       /* south display irq */
+       I915_WRITE(SDEIMR, 0xffffffff);
+       I915_WRITE(SDEIER, 0x0);
+       (void) I915_READ(SDEIER);
 }
 
-static int igdng_irq_postinstall(struct drm_device *dev)
+static int ironlake_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 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT;
        u32 render_mask = GT_USER_INTERRUPT;
+       u32 hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
+                          SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
 
        dev_priv->irq_mask_reg = ~display_mask;
        dev_priv->de_irq_enable_reg = display_mask;
@@ -1019,6 +1046,14 @@ static int igdng_irq_postinstall(struct drm_device *dev)
        I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
        (void) I915_READ(GTIER);
 
+       dev_priv->pch_irq_mask_reg = ~hotplug_mask;
+       dev_priv->pch_irq_enable_reg = hotplug_mask;
+
+       I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+       I915_WRITE(SDEIMR, dev_priv->pch_irq_mask_reg);
+       I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg);
+       (void) I915_READ(SDEIER);
+
        return 0;
 }
 
@@ -1031,8 +1066,8 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
        INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
        INIT_WORK(&dev_priv->error_work, i915_error_work_func);
 
-       if (IS_IGDNG(dev)) {
-               igdng_irq_preinstall(dev);
+       if (IS_IRONLAKE(dev)) {
+               ironlake_irq_preinstall(dev);
                return;
        }
 
@@ -1059,8 +1094,8 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
 
        dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
 
-       if (IS_IGDNG(dev))
-               return igdng_irq_postinstall(dev);
+       if (IS_IRONLAKE(dev))
+               return ironlake_irq_postinstall(dev);
 
        /* Unmask the interrupts that we always want on. */
        dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
@@ -1120,7 +1155,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
        return 0;
 }
 
-static void igdng_irq_uninstall(struct drm_device *dev)
+static void ironlake_irq_uninstall(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        I915_WRITE(HWSTAM, 0xffffffff);
@@ -1143,8 +1178,8 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
 
        dev_priv->vblank_pipe = 0;
 
-       if (IS_IGDNG(dev)) {
-               igdng_irq_uninstall(dev);
+       if (IS_IRONLAKE(dev)) {
+               ironlake_irq_uninstall(dev);
                return;
        }
 
index 2d51935..7cc8410 100644 (file)
@@ -118,6 +118,10 @@ struct opregion_asle {
 #define ASLE_BACKLIGHT_FAIL    (2<<12)
 #define ASLE_PFIT_FAIL         (2<<14)
 #define ASLE_PWM_FREQ_FAIL     (2<<16)
+#define ASLE_ALS_ILLUM_FAILED  (1<<10)
+#define ASLE_BACKLIGHT_FAILED  (1<<12)
+#define ASLE_PFIT_FAILED       (1<<14)
+#define ASLE_PWM_FREQ_FAILED   (1<<16)
 
 /* ASLE backlight brightness to set */
 #define ASLE_BCLP_VALID                (1<<31)
@@ -163,7 +167,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
        if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE))
                pci_write_config_dword(dev->pdev, PCI_LBPC, bclp);
        else {
-               if (IS_IGD(dev)) {
+               if (IS_PINEVIEW(dev)) {
                        blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
                        max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >> 
                                        BACKLIGHT_MODULATION_FREQ_SHIFT;
@@ -224,7 +228,7 @@ void opregion_asle_intr(struct drm_device *dev)
        asle_req = asle->aslc & ASLE_REQ_MSK;
 
        if (!asle_req) {
-               DRM_DEBUG("non asle set request??\n");
+               DRM_DEBUG_DRIVER("non asle set request??\n");
                return;
        }
 
@@ -243,6 +247,73 @@ void opregion_asle_intr(struct drm_device *dev)
        asle->aslc = asle_stat;
 }
 
+static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct opregion_asle *asle = dev_priv->opregion.asle;
+       u32 cpu_pwm_ctl, pch_pwm_ctl2;
+       u32 max_backlight, level;
+
+       if (!(bclp & ASLE_BCLP_VALID))
+               return ASLE_BACKLIGHT_FAILED;
+
+       bclp &= ASLE_BCLP_MSK;
+       if (bclp < 0 || bclp > 255)
+               return ASLE_BACKLIGHT_FAILED;
+
+       cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL);
+       pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
+       /* get the max PWM frequency */
+       max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK;
+       /* calculate the expected PMW frequency */
+       level = (bclp * max_backlight) / 255;
+       /* reserve the high 16 bits */
+       cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK);
+       /* write the updated PWM frequency */
+       I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level);
+
+       asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
+
+       return 0;
+}
+
+void ironlake_opregion_gse_intr(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct opregion_asle *asle = dev_priv->opregion.asle;
+       u32 asle_stat = 0;
+       u32 asle_req;
+
+       if (!asle)
+               return;
+
+       asle_req = asle->aslc & ASLE_REQ_MSK;
+
+       if (!asle_req) {
+               DRM_DEBUG_DRIVER("non asle set request??\n");
+               return;
+       }
+
+       if (asle_req & ASLE_SET_ALS_ILLUM) {
+               DRM_DEBUG_DRIVER("Illum is not supported\n");
+               asle_stat |= ASLE_ALS_ILLUM_FAILED;
+       }
+
+       if (asle_req & ASLE_SET_BACKLIGHT)
+               asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp);
+
+       if (asle_req & ASLE_SET_PFIT) {
+               DRM_DEBUG_DRIVER("Pfit is not supported\n");
+               asle_stat |= ASLE_PFIT_FAILED;
+       }
+
+       if (asle_req & ASLE_SET_PWM_FREQ) {
+               DRM_DEBUG_DRIVER("PWM freq is not supported\n");
+               asle_stat |= ASLE_PWM_FREQ_FAILED;
+       }
+
+       asle->aslc = asle_stat;
+}
 #define ASLE_ALS_EN    (1<<0)
 #define ASLE_BLC_EN    (1<<1)
 #define ASLE_PFIT_EN   (1<<2)
@@ -258,8 +329,7 @@ void opregion_enable_asle(struct drm_device *dev)
                        unsigned long irqflags;
 
                        spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
-                       i915_enable_pipestat(dev_priv, 1,
-                                            I915_LEGACY_BLC_EVENT_ENABLE);
+                       intel_enable_asle(dev);
                        spin_unlock_irqrestore(&dev_priv->user_irq_lock,
                                               irqflags);
                }
@@ -361,9 +431,9 @@ int intel_opregion_init(struct drm_device *dev, int resume)
        int err = 0;
 
        pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
-       DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
+       DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
        if (asls == 0) {
-               DRM_DEBUG("ACPI OpRegion not supported!\n");
+               DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
                return -ENOTSUPP;
        }
 
@@ -373,30 +443,30 @@ int intel_opregion_init(struct drm_device *dev, int resume)
 
        opregion->header = base;
        if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
-               DRM_DEBUG("opregion signature mismatch\n");
+               DRM_DEBUG_DRIVER("opregion signature mismatch\n");
                err = -EINVAL;
                goto err_out;
        }
 
        mboxes = opregion->header->mboxes;
        if (mboxes & MBOX_ACPI) {
-               DRM_DEBUG("Public ACPI methods supported\n");
+               DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
                opregion->acpi = base + OPREGION_ACPI_OFFSET;
                if (drm_core_check_feature(dev, DRIVER_MODESET))
                        intel_didl_outputs(dev);
        } else {
-               DRM_DEBUG("Public ACPI methods not supported\n");
+               DRM_DEBUG_DRIVER("Public ACPI methods not supported\n");
                err = -ENOTSUPP;
                goto err_out;
        }
        opregion->enabled = 1;
 
        if (mboxes & MBOX_SWSCI) {
-               DRM_DEBUG("SWSCI supported\n");
+               DRM_DEBUG_DRIVER("SWSCI supported\n");
                opregion->swsci = base + OPREGION_SWSCI_OFFSET;
        }
        if (mboxes & MBOX_ASLE) {
-               DRM_DEBUG("ASLE supported\n");
+               DRM_DEBUG_DRIVER("ASLE supported\n");
                opregion->asle = base + OPREGION_ASLE_OFFSET;
                opregion_enable_asle(dev);
        }
index 1687edf..974b3cf 100644 (file)
 #define MI_NOOP                        MI_INSTR(0, 0)
 #define MI_USER_INTERRUPT      MI_INSTR(0x02, 0)
 #define MI_WAIT_FOR_EVENT       MI_INSTR(0x03, 0)
+#define   MI_WAIT_FOR_OVERLAY_FLIP     (1<<16)
 #define   MI_WAIT_FOR_PLANE_B_FLIP      (1<<6)
 #define   MI_WAIT_FOR_PLANE_A_FLIP      (1<<2)
 #define   MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
 #define   MI_END_SCENE         (1 << 4) /* flush binner and incr scene count */
 #define MI_BATCH_BUFFER_END    MI_INSTR(0x0a, 0)
 #define MI_REPORT_HEAD         MI_INSTR(0x07, 0)
+#define MI_OVERLAY_FLIP                MI_INSTR(0x11,0)
+#define   MI_OVERLAY_CONTINUE  (0x0<<21)
+#define   MI_OVERLAY_ON                (0x1<<21)
+#define   MI_OVERLAY_OFF       (0x2<<21)
 #define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0)
+#define MI_DISPLAY_FLIP                MI_INSTR(0x14, 2)
+#define   MI_DISPLAY_FLIP_PLANE(n) ((n) << 20)
 #define MI_STORE_DWORD_IMM     MI_INSTR(0x20, 1)
 #define   MI_MEM_VIRTUAL       (1 << 22) /* 965+ only */
 #define MI_STORE_DWORD_INDEX   MI_INSTR(0x21, 1)
 #define HWS_PGA                0x02080
 #define HWS_ADDRESS_MASK       0xfffff000
 #define HWS_START_ADDRESS_SHIFT        4
+#define PWRCTXA                0x2088 /* 965GM+ only */
+#define   PWRCTX_EN    (1<<0)
 #define IPEIR          0x02088
 #define IPEHR          0x0208c
 #define INSTDONE       0x02090
 # define GPIO_DATA_VAL_IN              (1 << 12)
 # define GPIO_DATA_PULLUP_DISABLE      (1 << 13)
 
+#define GMBUS0                 0x5100
+#define GMBUS1                 0x5104
+#define GMBUS2                 0x5108
+#define GMBUS3                 0x510c
+#define GMBUS4                 0x5110
+#define GMBUS5                 0x5120
+
 /*
  * Clock control & power management
  */
 #define   DPLLB_LVDS_P2_CLOCK_DIV_7    (1 << 24) /* i915 */
 #define   DPLL_P2_CLOCK_DIV_MASK       0x03000000 /* i915 */
 #define   DPLL_FPA01_P1_POST_DIV_MASK  0x00ff0000 /* i915 */
-#define   DPLL_FPA01_P1_POST_DIV_MASK_IGD      0x00ff8000 /* IGD */
+#define   DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */
 
 #define I915_FIFO_UNDERRUN_STATUS              (1UL<<31)
 #define I915_CRC_ERROR_ENABLE                  (1UL<<29)
  */
 #define   DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS        0x003f0000
 #define   DPLL_FPA01_P1_POST_DIV_SHIFT 16
-#define   DPLL_FPA01_P1_POST_DIV_SHIFT_IGD 15
+#define   DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW 15
 /* i830, required in DVO non-gang */
 #define   PLL_P2_DIVIDE_BY_4           (1 << 23)
 #define   PLL_P1_DIVIDE_BY_TWO         (1 << 21) /* i830 */
 #define   PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13)
 #define   PLL_REF_INPUT_MASK           (3 << 13)
 #define   PLL_LOAD_PULSE_PHASE_SHIFT           9
-/* IGDNG */
+/* Ironlake */
 # 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 FPB0   0x06048
 #define FPB1   0x0604c
 #define   FP_N_DIV_MASK                0x003f0000
-#define   FP_N_IGD_DIV_MASK    0x00ff0000
+#define   FP_N_PINEVIEW_DIV_MASK       0x00ff0000
 #define   FP_N_DIV_SHIFT               16
 #define   FP_M1_DIV_MASK       0x00003f00
 #define   FP_M1_DIV_SHIFT               8
 #define   FP_M2_DIV_MASK       0x0000003f
-#define   FP_M2_IGD_DIV_MASK   0x000000ff
+#define   FP_M2_PINEVIEW_DIV_MASK      0x000000ff
 #define   FP_M2_DIV_SHIFT               0
 #define DPLL_TEST      0x606c
 #define   DPLLB_TEST_SDVO_DIV_1                (0 << 22)
 
 /** GM965 GM45 render standby register */
 #define MCHBAR_RENDER_STANDBY  0x111B8
-
+#define   RCX_SW_EXIT          (1<<23)
+#define   RSX_STATUS_MASK      0x00700000
 #define PEG_BAND_GAP_DATA      0x14d68
 
 /*
 #define   SDVOB_HOTPLUG_INT_EN                 (1 << 26)
 #define   SDVOC_HOTPLUG_INT_EN                 (1 << 25)
 #define   TV_HOTPLUG_INT_EN                    (1 << 18)
-#define   CRT_EOS_INT_EN                       (1 << 10)
 #define   CRT_HOTPLUG_INT_EN                   (1 << 9)
 #define   CRT_HOTPLUG_FORCE_DETECT             (1 << 3)
 #define CRT_HOTPLUG_ACTIVATION_PERIOD_32       (0 << 8)
                         HDMID_HOTPLUG_INT_EN |   \
                         SDVOB_HOTPLUG_INT_EN |   \
                         SDVOC_HOTPLUG_INT_EN |   \
-                        TV_HOTPLUG_INT_EN |      \
                         CRT_HOTPLUG_INT_EN)
 
 
 #define   DPC_HOTPLUG_INT_STATUS               (1 << 28)
 #define   HDMID_HOTPLUG_INT_STATUS             (1 << 27)
 #define   DPD_HOTPLUG_INT_STATUS               (1 << 27)
-#define   CRT_EOS_INT_STATUS                   (1 << 12)
 #define   CRT_HOTPLUG_INT_STATUS               (1 << 11)
 #define   TV_HOTPLUG_INT_STATUS                        (1 << 10)
 #define   CRT_HOTPLUG_MONITOR_MASK             (3 << 8)
 #define   DP_CLOCK_OUTPUT_ENABLE       (1 << 13)
 
 #define   DP_SCRAMBLING_DISABLE                (1 << 12)
-#define   DP_SCRAMBLING_DISABLE_IGDNG  (1 << 7)
+#define   DP_SCRAMBLING_DISABLE_IRONLAKE       (1 << 7)
 
 /** limit RGB values to avoid confusing TVs */
 #define   DP_COLOR_RANGE_16_235                (1 << 8)
 #define DSPFW3                 0x7003c
 #define   DSPFW_HPLL_SR_EN     (1<<31)
 #define   DSPFW_CURSOR_SR_SHIFT        24
-#define   IGD_SELF_REFRESH_EN  (1<<30)
+#define   PINEVIEW_SELF_REFRESH_EN     (1<<30)
 
 /* FIFO watermark sizes etc */
 #define G4X_FIFO_LINE_SIZE     64
 #define G4X_MAX_WM             0x3f
 #define I915_MAX_WM            0x3f
 
-#define IGD_DISPLAY_FIFO       512 /* in 64byte unit */
-#define IGD_FIFO_LINE_SIZE     64
-#define IGD_MAX_WM             0x1ff
-#define IGD_DFT_WM             0x3f
-#define IGD_DFT_HPLLOFF_WM     0
-#define IGD_GUARD_WM           10
-#define IGD_CURSOR_FIFO                64
-#define IGD_CURSOR_MAX_WM      0x3f
-#define IGD_CURSOR_DFT_WM      0
-#define IGD_CURSOR_GUARD_WM    5
+#define PINEVIEW_DISPLAY_FIFO  512 /* in 64byte unit */
+#define PINEVIEW_FIFO_LINE_SIZE        64
+#define PINEVIEW_MAX_WM                0x1ff
+#define PINEVIEW_DFT_WM                0x3f
+#define PINEVIEW_DFT_HPLLOFF_WM        0
+#define PINEVIEW_GUARD_WM              10
+#define PINEVIEW_CURSOR_FIFO           64
+#define PINEVIEW_CURSOR_MAX_WM 0x3f
+#define PINEVIEW_CURSOR_DFT_WM 0
+#define PINEVIEW_CURSOR_GUARD_WM       5
 
 /*
  * The two pipe frame counter registers are not synchronized, so
 #define   DISPPLANE_16BPP                      (0x5<<26)
 #define   DISPPLANE_32BPP_NO_ALPHA             (0x6<<26)
 #define   DISPPLANE_32BPP                      (0x7<<26)
+#define   DISPPLANE_32BPP_30BIT_NO_ALPHA       (0xa<<26)
 #define   DISPPLANE_STEREO_ENABLE              (1<<25)
 #define   DISPPLANE_STEREO_DISABLE             0
 #define   DISPPLANE_SEL_PIPE_MASK              (1<<24)
 #define   DISPPLANE_NO_LINE_DOUBLE             0
 #define   DISPPLANE_STEREO_POLARITY_FIRST      0
 #define   DISPPLANE_STEREO_POLARITY_SECOND     (1<<18)
-#define   DISPPLANE_TRICKLE_FEED_DISABLE       (1<<14) /* IGDNG */
+#define   DISPPLANE_TRICKLE_FEED_DISABLE       (1<<14) /* Ironlake */
 #define   DISPPLANE_TILED                      (1<<10)
 #define DSPAADDR               0x70184
 #define DSPASTRIDE             0x70188
 # define VGA_2X_MODE                           (1 << 30)
 # define VGA_PIPE_B_SELECT                     (1 << 29)
 
-/* IGDNG */
+/* Ironlake */
 
 #define CPU_VGACNTRL   0x41000
 
 #define SDE_PORTC_HOTPLUG       (1 << 9)
 #define SDE_PORTB_HOTPLUG       (1 << 8)
 #define SDE_SDVOB_HOTPLUG       (1 << 6)
+#define SDE_HOTPLUG_MASK       (0xf << 8)
 
 #define SDEISR  0xc4000
 #define SDEIMR  0xc4004
 #define PCH_GPIOE               0xc5020
 #define PCH_GPIOF               0xc5024
 
+#define PCH_GMBUS0             0xc5100
+#define PCH_GMBUS1             0xc5104
+#define PCH_GMBUS2             0xc5108
+#define PCH_GMBUS3             0xc510c
+#define PCH_GMBUS4             0xc5110
+#define PCH_GMBUS5             0xc5120
+
 #define PCH_DPLL_A              0xc6014
 #define PCH_DPLL_B              0xc6018
 
 #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 */
+/* Ironlake: hardwired to 1 */
 #define  FDI_TX_PLL_ENABLE              (1<<14)
 /* both Tx and Rx */
 #define  FDI_SCRAMBLING_ENABLE          (0<<7)
index 6eec817..d5ebb00 100644 (file)
 #include "drmP.h"
 #include "drm.h"
 #include "i915_drm.h"
-#include "i915_drv.h"
+#include "intel_drv.h"
 
 static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32     dpll_reg;
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dpll_reg = (pipe == PIPE_A) ? PCH_DPLL_A: PCH_DPLL_B;
        } else {
                dpll_reg = (pipe == PIPE_A) ? DPLL_A: DPLL_B;
@@ -53,7 +53,7 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
        if (!i915_pipe_enabled(dev, pipe))
                return;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B;
 
        if (pipe == PIPE_A)
@@ -75,7 +75,7 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
        if (!i915_pipe_enabled(dev, pipe))
                return;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B;
 
        if (pipe == PIPE_A)
@@ -239,7 +239,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL);
                dev_priv->saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL);
        }
@@ -247,7 +247,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
        /* Pipe & plane A info */
        dev_priv->savePIPEACONF = I915_READ(PIPEACONF);
        dev_priv->savePIPEASRC = I915_READ(PIPEASRC);
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->saveFPA0 = I915_READ(PCH_FPA0);
                dev_priv->saveFPA1 = I915_READ(PCH_FPA1);
                dev_priv->saveDPLL_A = I915_READ(PCH_DPLL_A);
@@ -256,7 +256,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
                dev_priv->saveFPA1 = I915_READ(FPA1);
                dev_priv->saveDPLL_A = I915_READ(DPLL_A);
        }
-       if (IS_I965G(dev) && !IS_IGDNG(dev))
+       if (IS_I965G(dev) && !IS_IRONLAKE(dev))
                dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD);
        dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A);
        dev_priv->saveHBLANK_A = I915_READ(HBLANK_A);
@@ -264,10 +264,10 @@ static void i915_save_modeset_reg(struct drm_device *dev)
        dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A);
        dev_priv->saveVBLANK_A = I915_READ(VBLANK_A);
        dev_priv->saveVSYNC_A = I915_READ(VSYNC_A);
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->savePIPEA_DATA_M1 = I915_READ(PIPEA_DATA_M1);
                dev_priv->savePIPEA_DATA_N1 = I915_READ(PIPEA_DATA_N1);
                dev_priv->savePIPEA_LINK_M1 = I915_READ(PIPEA_LINK_M1);
@@ -304,7 +304,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
        /* Pipe & plane B info */
        dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF);
        dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC);
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->saveFPB0 = I915_READ(PCH_FPB0);
                dev_priv->saveFPB1 = I915_READ(PCH_FPB1);
                dev_priv->saveDPLL_B = I915_READ(PCH_DPLL_B);
@@ -313,7 +313,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
                dev_priv->saveFPB1 = I915_READ(FPB1);
                dev_priv->saveDPLL_B = I915_READ(DPLL_B);
        }
-       if (IS_I965G(dev) && !IS_IGDNG(dev))
+       if (IS_I965G(dev) && !IS_IRONLAKE(dev))
                dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD);
        dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B);
        dev_priv->saveHBLANK_B = I915_READ(HBLANK_B);
@@ -321,10 +321,10 @@ static void i915_save_modeset_reg(struct drm_device *dev)
        dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B);
        dev_priv->saveVBLANK_B = I915_READ(VBLANK_B);
        dev_priv->saveVSYNC_B = I915_READ(VSYNC_B);
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                dev_priv->saveBCLRPAT_B = I915_READ(BCLRPAT_B);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->savePIPEB_DATA_M1 = I915_READ(PIPEB_DATA_M1);
                dev_priv->savePIPEB_DATA_N1 = I915_READ(PIPEB_DATA_N1);
                dev_priv->savePIPEB_LINK_M1 = I915_READ(PIPEB_LINK_M1);
@@ -369,7 +369,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
        if (drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dpll_a_reg = PCH_DPLL_A;
                dpll_b_reg = PCH_DPLL_B;
                fpa0_reg = PCH_FPA0;
@@ -385,7 +385,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
                fpb1_reg = FPB1;
        }
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(PCH_DREF_CONTROL, dev_priv->savePCH_DREF_CONTROL);
                I915_WRITE(DISP_ARB_CTL, dev_priv->saveDISP_ARB_CTL);
        }
@@ -402,7 +402,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
        /* Actually enable it */
        I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A);
        DRM_UDELAY(150);
-       if (IS_I965G(dev) && !IS_IGDNG(dev))
+       if (IS_I965G(dev) && !IS_IRONLAKE(dev))
                I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD);
        DRM_UDELAY(150);
 
@@ -413,10 +413,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
        I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A);
        I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A);
        I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A);
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(PIPEA_DATA_M1, dev_priv->savePIPEA_DATA_M1);
                I915_WRITE(PIPEA_DATA_N1, dev_priv->savePIPEA_DATA_N1);
                I915_WRITE(PIPEA_LINK_M1, dev_priv->savePIPEA_LINK_M1);
@@ -467,7 +467,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
        /* Actually enable it */
        I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B);
        DRM_UDELAY(150);
-       if (IS_I965G(dev) && !IS_IGDNG(dev))
+       if (IS_I965G(dev) && !IS_IRONLAKE(dev))
                I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD);
        DRM_UDELAY(150);
 
@@ -478,10 +478,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
        I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B);
        I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B);
        I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B);
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(PIPEB_DATA_M1, dev_priv->savePIPEB_DATA_M1);
                I915_WRITE(PIPEB_DATA_N1, dev_priv->savePIPEB_DATA_N1);
                I915_WRITE(PIPEB_LINK_M1, dev_priv->savePIPEB_LINK_M1);
@@ -546,14 +546,14 @@ void i915_save_display(struct drm_device *dev)
                dev_priv->saveCURSIZE = I915_READ(CURSIZE);
 
        /* CRT state */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->saveADPA = I915_READ(PCH_ADPA);
        } else {
                dev_priv->saveADPA = I915_READ(ADPA);
        }
 
        /* LVDS state */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->savePP_CONTROL = I915_READ(PCH_PP_CONTROL);
                dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_PCH_CTL1);
                dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2);
@@ -571,10 +571,10 @@ void i915_save_display(struct drm_device *dev)
                        dev_priv->saveLVDS = I915_READ(LVDS);
        }
 
-       if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev))
+       if (!IS_I830(dev) && !IS_845G(dev) && !IS_IRONLAKE(dev))
                dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS);
                dev_priv->savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS);
                dev_priv->savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR);
@@ -614,7 +614,7 @@ void i915_save_display(struct drm_device *dev)
        dev_priv->saveVGA0 = I915_READ(VGA0);
        dev_priv->saveVGA1 = I915_READ(VGA1);
        dev_priv->saveVGA_PD = I915_READ(VGA_PD);
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                dev_priv->saveVGACNTRL = I915_READ(CPU_VGACNTRL);
        else
                dev_priv->saveVGACNTRL = I915_READ(VGACNTRL);
@@ -656,24 +656,24 @@ void i915_restore_display(struct drm_device *dev)
                I915_WRITE(CURSIZE, dev_priv->saveCURSIZE);
 
        /* CRT state */
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                I915_WRITE(PCH_ADPA, dev_priv->saveADPA);
        else
                I915_WRITE(ADPA, dev_priv->saveADPA);
 
        /* LVDS state */
-       if (IS_I965G(dev) && !IS_IGDNG(dev))
+       if (IS_I965G(dev) && !IS_IRONLAKE(dev))
                I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(PCH_LVDS, dev_priv->saveLVDS);
        } else if (IS_MOBILE(dev) && !IS_I830(dev))
                I915_WRITE(LVDS, dev_priv->saveLVDS);
 
-       if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev))
+       if (!IS_I830(dev) && !IS_845G(dev) && !IS_IRONLAKE(dev))
                I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(BLC_PWM_PCH_CTL1, dev_priv->saveBLC_PWM_CTL);
                I915_WRITE(BLC_PWM_PCH_CTL2, dev_priv->saveBLC_PWM_CTL2);
                I915_WRITE(BLC_PWM_CPU_CTL, dev_priv->saveBLC_CPU_PWM_CTL);
@@ -713,7 +713,7 @@ void i915_restore_display(struct drm_device *dev)
        }
 
        /* VGA state */
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                I915_WRITE(CPU_VGACNTRL, dev_priv->saveVGACNTRL);
        else
                I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL);
@@ -733,8 +733,10 @@ int i915_save_state(struct drm_device *dev)
        pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB);
 
        /* Render Standby */
-       if (IS_I965G(dev) && IS_MOBILE(dev))
+       if (I915_HAS_RC6(dev)) {
                dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY);
+               dev_priv->savePWRCTXA = I915_READ(PWRCTXA);
+       }
 
        /* Hardware status page */
        dev_priv->saveHWS = I915_READ(HWS_PGA);
@@ -742,7 +744,7 @@ int i915_save_state(struct drm_device *dev)
        i915_save_display(dev);
 
        /* Interrupt state */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                dev_priv->saveDEIER = I915_READ(DEIER);
                dev_priv->saveDEIMR = I915_READ(DEIMR);
                dev_priv->saveGTIER = I915_READ(GTIER);
@@ -754,10 +756,6 @@ int i915_save_state(struct drm_device *dev)
                dev_priv->saveIMR = I915_READ(IMR);
        }
 
-       /* Clock gating state */
-       dev_priv->saveD_STATE = I915_READ(D_STATE);
-       dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); /* Not sure about this */
-
        /* Cache mode state */
        dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
 
@@ -796,8 +794,10 @@ int i915_restore_state(struct drm_device *dev)
        pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB);
 
        /* Render Standby */
-       if (IS_I965G(dev) && IS_MOBILE(dev))
+       if (I915_HAS_RC6(dev)) {
                I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY);
+               I915_WRITE(PWRCTXA, dev_priv->savePWRCTXA);
+       }
 
        /* Hardware status page */
        I915_WRITE(HWS_PGA, dev_priv->saveHWS);
@@ -817,7 +817,7 @@ int i915_restore_state(struct drm_device *dev)
        i915_restore_display(dev);
 
        /* Interrupt state */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(DEIER, dev_priv->saveDEIER);
                I915_WRITE(DEIMR, dev_priv->saveDEIMR);
                I915_WRITE(GTIER, dev_priv->saveGTIER);
@@ -830,8 +830,7 @@ int i915_restore_state(struct drm_device *dev)
        }
 
        /* Clock gating state */
-       I915_WRITE (D_STATE, dev_priv->saveD_STATE);
-       I915_WRITE (DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D);
+       intel_init_clock_gating(dev);
 
        /* Cache mode state */
        I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
@@ -846,6 +845,9 @@ int i915_restore_state(struct drm_device *dev)
        for (i = 0; i < 3; i++)
                I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
 
+       /* I2C state */
+       intel_i2c_reset_gmbus(dev);
+
        return 0;
 }
 
index 96cd256..f275677 100644 (file)
@@ -114,6 +114,8 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
        struct lvds_dvo_timing *dvo_timing;
        struct drm_display_mode *panel_fixed_mode;
        int lfp_data_size, dvo_timing_offset;
+       int i, temp_downclock;
+       struct drm_display_mode *temp_mode;
 
        /* Defaults if we can't find VBT info */
        dev_priv->lvds_dither = 0;
@@ -159,9 +161,49 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
 
        dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
 
-       DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
+       DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
        drm_mode_debug_printmodeline(panel_fixed_mode);
 
+       temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL);
+       temp_downclock = panel_fixed_mode->clock;
+       /*
+        * enumerate the LVDS panel timing info entry in VBT to check whether
+        * the LVDS downclock is found.
+        */
+       for (i = 0; i < 16; i++) {
+               entry = (struct bdb_lvds_lfp_data_entry *)
+                       ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
+               dvo_timing = (struct lvds_dvo_timing *)
+                       ((unsigned char *)entry + dvo_timing_offset);
+
+               fill_detail_timing_data(temp_mode, dvo_timing);
+
+               if (temp_mode->hdisplay == panel_fixed_mode->hdisplay &&
+               temp_mode->hsync_start == panel_fixed_mode->hsync_start &&
+               temp_mode->hsync_end == panel_fixed_mode->hsync_end &&
+               temp_mode->htotal == panel_fixed_mode->htotal &&
+               temp_mode->vdisplay == panel_fixed_mode->vdisplay &&
+               temp_mode->vsync_start == panel_fixed_mode->vsync_start &&
+               temp_mode->vsync_end == panel_fixed_mode->vsync_end &&
+               temp_mode->vtotal == panel_fixed_mode->vtotal &&
+               temp_mode->clock < temp_downclock) {
+                       /*
+                        * downclock is already found. But we expect
+                        * to find the lower downclock.
+                        */
+                       temp_downclock = temp_mode->clock;
+               }
+               /* clear it to zero */
+               memset(temp_mode, 0, sizeof(*temp_mode));
+       }
+       kfree(temp_mode);
+       if (temp_downclock < panel_fixed_mode->clock) {
+               dev_priv->lvds_downclock_avail = 1;
+               dev_priv->lvds_downclock = temp_downclock;
+               DRM_DEBUG_KMS("LVDS downclock is found in VBT. ",
+                               "Normal Clock %dKHz, downclock %dKHz\n",
+                               temp_downclock, panel_fixed_mode->clock);
+       }
        return;
 }
 
@@ -217,7 +259,7 @@ parse_general_features(struct drm_i915_private *dev_priv,
                        if (IS_I85X(dev_priv->dev))
                                dev_priv->lvds_ssc_freq =
                                        general->ssc_freq ? 66 : 48;
-                       else if (IS_IGDNG(dev_priv->dev))
+                       else if (IS_IRONLAKE(dev_priv->dev))
                                dev_priv->lvds_ssc_freq =
                                        general->ssc_freq ? 100 : 120;
                        else
@@ -241,22 +283,18 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
                GPIOF,
        };
 
-       /* Set sensible defaults in case we can't find the general block
-          or it is the wrong chipset */
-       dev_priv->crt_ddc_bus = -1;
-
        general = find_section(bdb, BDB_GENERAL_DEFINITIONS);
        if (general) {
                u16 block_size = get_blocksize(general);
                if (block_size >= sizeof(*general)) {
                        int bus_pin = general->crt_ddc_gmbus_pin;
-                       DRM_DEBUG("crt_ddc_bus_pin: %d\n", bus_pin);
+                       DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin);
                        if ((bus_pin >= 1) && (bus_pin <= 6)) {
                                dev_priv->crt_ddc_bus =
                                        crt_bus_map_table[bus_pin-1];
                        }
                } else {
-                       DRM_DEBUG("BDB_GD too small (%d). Invalid.\n",
+                       DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n",
                                  block_size);
                }
        }
@@ -274,7 +312,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
 
        p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
        if (!p_defs) {
-               DRM_DEBUG("No general definition block is found\n");
+               DRM_DEBUG_KMS("No general definition block is found\n");
                return;
        }
        /* judge whether the size of child device meets the requirements.
@@ -284,7 +322,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
         */
        if (p_defs->child_dev_size != sizeof(*p_child)) {
                /* different child dev size . Ignore it */
-               DRM_DEBUG("different child size is found. Invalid.\n");
+               DRM_DEBUG_KMS("different child size is found. Invalid.\n");
                return;
        }
        /* get the block size of general definitions */
@@ -310,11 +348,11 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
                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");
+                       DRM_DEBUG_KMS("Incorrect SDVO port. Skip it \n");
                        continue;
                }
-               DRM_DEBUG("the SDVO device with slave addr %2x is found on "
-                               "%s port\n",
+               DRM_DEBUG_KMS("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");
@@ -325,21 +363,21 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
                        p_mapping->dvo_wiring = p_child->dvo_wiring;
                        p_mapping->initialized = 1;
                } else {
-                       DRM_DEBUG("Maybe one SDVO port is shared by "
+                       DRM_DEBUG_KMS("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");
+                       DRM_DEBUG_KMS("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");
+               DRM_DEBUG_KMS("No SDVO device info is found in VBT\n");
        }
        return;
 }
@@ -366,6 +404,70 @@ parse_driver_features(struct drm_i915_private *dev_priv,
                dev_priv->render_reclock_avail = true;
 }
 
+static void
+parse_device_mapping(struct drm_i915_private *dev_priv,
+                      struct bdb_header *bdb)
+{
+       struct bdb_general_definitions *p_defs;
+       struct child_device_config *p_child, *child_dev_ptr;
+       int i, child_device_num, count;
+       u16     block_size;
+
+       p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+       if (!p_defs) {
+               DRM_DEBUG_KMS("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_KMS("different child size is found. Invalid.\n");
+               return;
+       }
+       /* get the block size of general definitions */
+       block_size = get_blocksize(p_defs);
+       /* get the number of child device */
+       child_device_num = (block_size - sizeof(*p_defs)) /
+                               sizeof(*p_child);
+       count = 0;
+       /* get the number of child device that is present */
+       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;
+               }
+               count++;
+       }
+       if (!count) {
+               DRM_DEBUG_KMS("no child dev is parsed from VBT \n");
+               return;
+       }
+       dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL);
+       if (!dev_priv->child_dev) {
+               DRM_DEBUG_KMS("No memory space for child device\n");
+               return;
+       }
+
+       dev_priv->child_dev_num = count;
+       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;
+               }
+               child_dev_ptr = dev_priv->child_dev + count;
+               count++;
+               memcpy((void *)child_dev_ptr, (void *)p_child,
+                                       sizeof(*p_child));
+       }
+       return;
+}
 /**
  * intel_init_bios - initialize VBIOS settings & find VBT
  * @dev: DRM device
@@ -417,6 +519,7 @@ intel_init_bios(struct drm_device *dev)
        parse_lfp_panel_data(dev_priv, bdb);
        parse_sdvo_panel_data(dev_priv, bdb);
        parse_sdvo_device_mapping(dev_priv, bdb);
+       parse_device_mapping(dev_priv, bdb);
        parse_driver_features(dev_priv, bdb);
 
        pci_unmap_rom(pdev, bios);
index 0f8e5f6..425ac9d 100644 (file)
@@ -549,4 +549,21 @@ bool intel_init_bios(struct drm_device *dev);
 #define   SWF14_APM_STANDBY    0x1
 #define   SWF14_APM_RESTORE    0x0
 
+/* Add the device class for LFP, TV, HDMI */
+#define         DEVICE_TYPE_INT_LFP    0x1022
+#define         DEVICE_TYPE_INT_TV     0x1009
+#define         DEVICE_TYPE_HDMI       0x60D2
+#define         DEVICE_TYPE_DP         0x68C6
+#define         DEVICE_TYPE_eDP        0x78C6
+
+/* define the DVO port for HDMI output type */
+#define                DVO_B           1
+#define                DVO_C           2
+#define                DVO_D           3
+
+/* define the PORT for DP output type */
+#define                PORT_IDPB       7
+#define                PORT_IDPC       8
+#define                PORT_IDPD       9
+
 #endif /* _I830_BIOS_H_ */
index e505144..9f3d3e5 100644 (file)
@@ -39,7 +39,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 temp, reg;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                reg = PCH_ADPA;
        else
                reg = ADPA;
@@ -64,34 +64,6 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
        }
 
        I915_WRITE(reg, temp);
-
-       if (IS_IGD(dev)) {
-               if (mode == DRM_MODE_DPMS_OFF) {
-                       /* turn off DAC */
-                       temp = I915_READ(PORT_HOTPLUG_EN);
-                       temp &= ~CRT_EOS_INT_EN;
-                       I915_WRITE(PORT_HOTPLUG_EN, temp);
-
-                       temp = I915_READ(PORT_HOTPLUG_STAT);
-                       if (temp & CRT_EOS_INT_STATUS)
-                               I915_WRITE(PORT_HOTPLUG_STAT,
-                                       CRT_EOS_INT_STATUS);
-               } else {
-                       /* turn on DAC. EOS interrupt must be enabled after DAC
-                        * is enabled, so it sounds not good to enable it in
-                        * i915_driver_irq_postinstall()
-                        * wait 12.5ms after DAC is enabled
-                        */
-                       msleep(13);
-                       temp = I915_READ(PORT_HOTPLUG_STAT);
-                       if (temp & CRT_EOS_INT_STATUS)
-                               I915_WRITE(PORT_HOTPLUG_STAT,
-                                       CRT_EOS_INT_STATUS);
-                       temp = I915_READ(PORT_HOTPLUG_EN);
-                       temp |= CRT_EOS_INT_EN;
-                       I915_WRITE(PORT_HOTPLUG_EN, temp);
-               }
-       }
 }
 
 static int intel_crt_mode_valid(struct drm_connector *connector,
@@ -141,7 +113,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
        else
                dpll_md_reg = DPLL_B_MD;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                adpa_reg = PCH_ADPA;
        else
                adpa_reg = ADPA;
@@ -150,7 +122,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
         * 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) && !IS_IGDNG(dev)) {
+       if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
                dpll_md = I915_READ(dpll_md_reg);
                I915_WRITE(dpll_md_reg,
                           dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
@@ -164,18 +136,18 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 
        if (intel_crtc->pipe == 0) {
                adpa |= ADPA_PIPE_A_SELECT;
-               if (!IS_IGDNG(dev))
+               if (!IS_IRONLAKE(dev))
                        I915_WRITE(BCLRPAT_A, 0);
        } else {
                adpa |= ADPA_PIPE_B_SELECT;
-               if (!IS_IGDNG(dev))
+               if (!IS_IRONLAKE(dev))
                        I915_WRITE(BCLRPAT_B, 0);
        }
 
        I915_WRITE(adpa_reg, adpa);
 }
 
-static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
+static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
 {
        struct drm_device *dev = connector->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -194,7 +166,7 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
                        ADPA_CRT_HOTPLUG_ENABLE |
                        ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
 
-       DRM_DEBUG("pch crt adpa 0x%x", adpa);
+       DRM_DEBUG_KMS("pch crt adpa 0x%x", adpa);
        I915_WRITE(PCH_ADPA, adpa);
 
        while ((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) != 0)
@@ -227,8 +199,8 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
        u32 hotplug_en;
        int i, tries = 0;
 
-       if (IS_IGDNG(dev))
-               return intel_igdng_crt_detect_hotplug(connector);
+       if (IS_IRONLAKE(dev))
+               return intel_ironlake_crt_detect_hotplug(connector);
 
        /*
         * On 4 series desktop, CRT detect sequence need to be done twice
@@ -549,12 +521,12 @@ void intel_crt_init(struct drm_device *dev)
                                          &intel_output->enc);
 
        /* Set up the DDC bus. */
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                i2c_reg = PCH_GPIOA;
        else {
                i2c_reg = GPIOA;
                /* Use VBT information for CRT DDC if available */
-               if (dev_priv->crt_ddc_bus != -1)
+               if (dev_priv->crt_ddc_bus != 0)
                        i2c_reg = dev_priv->crt_ddc_bus;
        }
        intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
index 099f420..52cd9b0 100644 (file)
@@ -32,7 +32,7 @@
 #include "intel_drv.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
-#include "intel_dp.h"
+#include "drm_dp_helper.h"
 
 #include "drm_crtc_helper.h"
 
@@ -102,32 +102,32 @@ struct intel_limit {
 #define I9XX_DOT_MAX            400000
 #define I9XX_VCO_MIN           1400000
 #define I9XX_VCO_MAX           2800000
-#define IGD_VCO_MIN            1700000
-#define IGD_VCO_MAX            3500000
+#define PINEVIEW_VCO_MIN               1700000
+#define PINEVIEW_VCO_MAX               3500000
 #define I9XX_N_MIN                   1
 #define I9XX_N_MAX                   6
-/* IGD's Ncounter is a ring counter */
-#define IGD_N_MIN                    3
-#define IGD_N_MAX                    6
+/* Pineview's Ncounter is a ring counter */
+#define PINEVIEW_N_MIN               3
+#define PINEVIEW_N_MAX               6
 #define I9XX_M_MIN                  70
 #define I9XX_M_MAX                 120
-#define IGD_M_MIN                    2
-#define IGD_M_MAX                  256
+#define PINEVIEW_M_MIN               2
+#define PINEVIEW_M_MAX             256
 #define I9XX_M1_MIN                 10
 #define I9XX_M1_MAX                 22
 #define I9XX_M2_MIN                  5
 #define I9XX_M2_MAX                  9
-/* IGD M1 is reserved, and must be 0 */
-#define IGD_M1_MIN                   0
-#define IGD_M1_MAX                   0
-#define IGD_M2_MIN                   0
-#define IGD_M2_MAX                   254
+/* Pineview M1 is reserved, and must be 0 */
+#define PINEVIEW_M1_MIN                      0
+#define PINEVIEW_M1_MAX                      0
+#define PINEVIEW_M2_MIN                      0
+#define PINEVIEW_M2_MAX                      254
 #define I9XX_P_SDVO_DAC_MIN          5
 #define I9XX_P_SDVO_DAC_MAX         80
 #define I9XX_P_LVDS_MIN                      7
 #define I9XX_P_LVDS_MAX                     98
-#define IGD_P_LVDS_MIN               7
-#define IGD_P_LVDS_MAX              112
+#define PINEVIEW_P_LVDS_MIN                  7
+#define PINEVIEW_P_LVDS_MAX                 112
 #define I9XX_P1_MIN                  1
 #define I9XX_P1_MAX                  8
 #define I9XX_P2_SDVO_DAC_SLOW               10
@@ -234,33 +234,33 @@ struct intel_limit {
 #define G4X_P2_DISPLAY_PORT_FAST           10
 #define G4X_P2_DISPLAY_PORT_LIMIT          0
 
-/* IGDNG */
+/* Ironlake */
 /* 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 */
+#define IRONLAKE_DOT_MIN         25000
+#define IRONLAKE_DOT_MAX         350000
+#define IRONLAKE_VCO_MIN         1760000
+#define IRONLAKE_VCO_MAX         3510000
+#define IRONLAKE_N_MIN           1
+#define IRONLAKE_N_MAX           5
+#define IRONLAKE_M_MIN           79
+#define IRONLAKE_M_MAX           118
+#define IRONLAKE_M1_MIN          12
+#define IRONLAKE_M1_MAX          23
+#define IRONLAKE_M2_MIN          5
+#define IRONLAKE_M2_MAX          9
+#define IRONLAKE_P_SDVO_DAC_MIN  5
+#define IRONLAKE_P_SDVO_DAC_MAX  80
+#define IRONLAKE_P_LVDS_MIN      28
+#define IRONLAKE_P_LVDS_MAX      112
+#define IRONLAKE_P1_MIN          1
+#define IRONLAKE_P1_MAX          8
+#define IRONLAKE_P2_SDVO_DAC_SLOW 10
+#define IRONLAKE_P2_SDVO_DAC_FAST 5
+#define IRONLAKE_P2_LVDS_SLOW    14 /* single channel */
+#define IRONLAKE_P2_LVDS_FAST    7  /* double channel */
+#define IRONLAKE_P2_DOT_LIMIT    225000 /* 225Mhz */
 
 static bool
 intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
@@ -272,15 +272,15 @@ 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);
+intel_ironlake_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                            int target, int refclk, intel_clock_t *best_clock);
 
 static bool
 intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
                      int target, int refclk, intel_clock_t *best_clock);
 static bool
-intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc,
-                     int target, int refclk, intel_clock_t *best_clock);
+intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
+                          int target, int refclk, intel_clock_t *best_clock);
 
 static const intel_limit_t intel_limits_i8xx_dvo = {
         .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
@@ -453,13 +453,13 @@ static const intel_limit_t intel_limits_g4x_display_port = {
         .find_pll = intel_find_pll_g4x_dp,
 };
 
-static const intel_limit_t intel_limits_igd_sdvo = {
+static const intel_limit_t intel_limits_pineview_sdvo = {
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX},
-        .vco = { .min = IGD_VCO_MIN,           .max = IGD_VCO_MAX },
-        .n   = { .min = IGD_N_MIN,             .max = IGD_N_MAX },
-        .m   = { .min = IGD_M_MIN,             .max = IGD_M_MAX },
-        .m1  = { .min = IGD_M1_MIN,            .max = IGD_M1_MAX },
-        .m2  = { .min = IGD_M2_MIN,            .max = IGD_M2_MAX },
+        .vco = { .min = PINEVIEW_VCO_MIN,              .max = PINEVIEW_VCO_MAX },
+        .n   = { .min = PINEVIEW_N_MIN,                .max = PINEVIEW_N_MAX },
+        .m   = { .min = PINEVIEW_M_MIN,                .max = PINEVIEW_M_MAX },
+        .m1  = { .min = PINEVIEW_M1_MIN,               .max = PINEVIEW_M1_MAX },
+        .m2  = { .min = PINEVIEW_M2_MIN,               .max = PINEVIEW_M2_MAX },
         .p   = { .min = I9XX_P_SDVO_DAC_MIN,    .max = I9XX_P_SDVO_DAC_MAX },
         .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
        .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
@@ -468,59 +468,59 @@ static const intel_limit_t intel_limits_igd_sdvo = {
        .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
-static const intel_limit_t intel_limits_igd_lvds = {
+static const intel_limit_t intel_limits_pineview_lvds = {
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
-        .vco = { .min = IGD_VCO_MIN,           .max = IGD_VCO_MAX },
-        .n   = { .min = IGD_N_MIN,             .max = IGD_N_MAX },
-        .m   = { .min = IGD_M_MIN,             .max = IGD_M_MAX },
-        .m1  = { .min = IGD_M1_MIN,            .max = IGD_M1_MAX },
-        .m2  = { .min = IGD_M2_MIN,            .max = IGD_M2_MAX },
-        .p   = { .min = IGD_P_LVDS_MIN,        .max = IGD_P_LVDS_MAX },
+        .vco = { .min = PINEVIEW_VCO_MIN,              .max = PINEVIEW_VCO_MAX },
+        .n   = { .min = PINEVIEW_N_MIN,                .max = PINEVIEW_N_MAX },
+        .m   = { .min = PINEVIEW_M_MIN,                .max = PINEVIEW_M_MAX },
+        .m1  = { .min = PINEVIEW_M1_MIN,               .max = PINEVIEW_M1_MAX },
+        .m2  = { .min = PINEVIEW_M2_MIN,               .max = PINEVIEW_M2_MAX },
+        .p   = { .min = PINEVIEW_P_LVDS_MIN,   .max = PINEVIEW_P_LVDS_MAX },
         .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
-       /* IGD only supports single-channel mode. */
+       /* Pineview only supports single-channel mode. */
        .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_SLOW },
        .find_pll = intel_find_best_PLL,
        .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
-static const intel_limit_t intel_limits_igdng_sdvo = {
-       .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,
+static const intel_limit_t intel_limits_ironlake_sdvo = {
+       .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
+       .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
+       .n   = { .min = IRONLAKE_N_MIN,            .max = IRONLAKE_N_MAX },
+       .m   = { .min = IRONLAKE_M_MIN,            .max = IRONLAKE_M_MAX },
+       .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
+       .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
+       .p   = { .min = IRONLAKE_P_SDVO_DAC_MIN,   .max = IRONLAKE_P_SDVO_DAC_MAX },
+       .p1  = { .min = IRONLAKE_P1_MIN,           .max = IRONLAKE_P1_MAX },
+       .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
+                .p2_slow = IRONLAKE_P2_SDVO_DAC_SLOW,
+                .p2_fast = IRONLAKE_P2_SDVO_DAC_FAST },
+       .find_pll = intel_ironlake_find_best_PLL,
 };
 
-static const intel_limit_t intel_limits_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_limits_ironlake_lvds = {
+       .dot = { .min = IRONLAKE_DOT_MIN,          .max = IRONLAKE_DOT_MAX },
+       .vco = { .min = IRONLAKE_VCO_MIN,          .max = IRONLAKE_VCO_MAX },
+       .n   = { .min = IRONLAKE_N_MIN,            .max = IRONLAKE_N_MAX },
+       .m   = { .min = IRONLAKE_M_MIN,            .max = IRONLAKE_M_MAX },
+       .m1  = { .min = IRONLAKE_M1_MIN,           .max = IRONLAKE_M1_MAX },
+       .m2  = { .min = IRONLAKE_M2_MIN,           .max = IRONLAKE_M2_MAX },
+       .p   = { .min = IRONLAKE_P_LVDS_MIN,       .max = IRONLAKE_P_LVDS_MAX },
+       .p1  = { .min = IRONLAKE_P1_MIN,           .max = IRONLAKE_P1_MAX },
+       .p2  = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
+                .p2_slow = IRONLAKE_P2_LVDS_SLOW,
+                .p2_fast = IRONLAKE_P2_LVDS_FAST },
+       .find_pll = intel_ironlake_find_best_PLL,
 };
 
-static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
+static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc)
 {
        const intel_limit_t *limit;
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
-               limit = &intel_limits_igdng_lvds;
+               limit = &intel_limits_ironlake_lvds;
        else
-               limit = &intel_limits_igdng_sdvo;
+               limit = &intel_limits_ironlake_sdvo;
 
        return limit;
 }
@@ -557,20 +557,20 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
        struct drm_device *dev = crtc->dev;
        const intel_limit_t *limit;
 
-       if (IS_IGDNG(dev))
-               limit = intel_igdng_limit(crtc);
+       if (IS_IRONLAKE(dev))
+               limit = intel_ironlake_limit(crtc);
        else if (IS_G4X(dev)) {
                limit = intel_g4x_limit(crtc);
-       } else if (IS_I9XX(dev) && !IS_IGD(dev)) {
+       } else if (IS_I9XX(dev) && !IS_PINEVIEW(dev)) {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
                        limit = &intel_limits_i9xx_lvds;
                else
                        limit = &intel_limits_i9xx_sdvo;
-       } else if (IS_IGD(dev)) {
+       } else if (IS_PINEVIEW(dev)) {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
-                       limit = &intel_limits_igd_lvds;
+                       limit = &intel_limits_pineview_lvds;
                else
-                       limit = &intel_limits_igd_sdvo;
+                       limit = &intel_limits_pineview_sdvo;
        } else {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
                        limit = &intel_limits_i8xx_lvds;
@@ -580,8 +580,8 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
        return limit;
 }
 
-/* m1 is reserved as 0 in IGD, n is a ring counter */
-static void igd_clock(int refclk, intel_clock_t *clock)
+/* m1 is reserved as 0 in Pineview, n is a ring counter */
+static void pineview_clock(int refclk, intel_clock_t *clock)
 {
        clock->m = clock->m2 + 2;
        clock->p = clock->p1 * clock->p2;
@@ -591,8 +591,8 @@ static void igd_clock(int refclk, intel_clock_t *clock)
 
 static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock)
 {
-       if (IS_IGD(dev)) {
-               igd_clock(refclk, clock);
+       if (IS_PINEVIEW(dev)) {
+               pineview_clock(refclk, clock);
                return;
        }
        clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
@@ -657,7 +657,7 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
                INTELPllInvalid ("m2 out of range\n");
        if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1)
                INTELPllInvalid ("m1 out of range\n");
-       if (clock->m1 <= clock->m2 && !IS_IGD(dev))
+       if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
                INTELPllInvalid ("m1 <= m2\n");
        if (clock->m   < limit->m.min   || limit->m.max   < clock->m)
                INTELPllInvalid ("m out of range\n");
@@ -706,16 +706,17 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 
        memset (best_clock, 0, sizeof (*best_clock));
 
-       for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
-               for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
-                    clock.m1++) {
-                       for (clock.m2 = limit->m2.min;
-                            clock.m2 <= limit->m2.max; clock.m2++) {
-                               /* m1 is always 0 in IGD */
-                               if (clock.m2 >= clock.m1 && !IS_IGD(dev))
-                                       break;
-                               for (clock.n = limit->n.min;
-                                    clock.n <= limit->n.max; clock.n++) {
+       for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
+            clock.m1++) {
+               for (clock.m2 = limit->m2.min;
+                    clock.m2 <= limit->m2.max; clock.m2++) {
+                       /* m1 is always 0 in Pineview */
+                       if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev))
+                               break;
+                       for (clock.n = limit->n.min;
+                            clock.n <= limit->n.max; clock.n++) {
+                               for (clock.p1 = limit->p1.min;
+                                       clock.p1 <= limit->p1.max; clock.p1++) {
                                        int this_err;
 
                                        intel_clock(dev, refclk, &clock);
@@ -751,8 +752,8 @@ intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 
        for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
                for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
-                       /* m1 is always 0 in IGD */
-                       if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+                       /* m1 is always 0 in Pineview */
+                       if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev))
                                break;
                        for (clock.n = limit->n.min; clock.n <= limit->n.max;
                             clock.n++) {
@@ -833,8 +834,8 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 }
 
 static bool
-intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
-                     int target, int refclk, intel_clock_t *best_clock)
+intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+                          int target, int refclk, intel_clock_t *best_clock)
 {
        struct drm_device *dev = crtc->dev;
        intel_clock_t clock;
@@ -857,8 +858,8 @@ intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
 }
 
 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)
+intel_ironlake_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;
@@ -871,7 +872,7 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                return true;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
-               return intel_find_pll_igdng_dp(limit, crtc, target,
+               return intel_find_pll_ironlake_dp(limit, crtc, target,
                                               refclk, best_clock);
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
@@ -949,7 +950,7 @@ void
 intel_wait_for_vblank(struct drm_device *dev)
 {
        /* Wait for 20ms, i.e. one cycle at 50hz. */
-       mdelay(20);
+       msleep(20);
 }
 
 /* Parameters have changed, update FBC info */
@@ -994,7 +995,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
                fbc_ctl |= dev_priv->cfb_fence;
        I915_WRITE(FBC_CONTROL, fbc_ctl);
 
-       DRM_DEBUG("enabled FBC, pitch %ld, yoff %d, plane %d, ",
+       DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
                  dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
 }
 
@@ -1017,7 +1018,7 @@ void i8xx_disable_fbc(struct drm_device *dev)
 
        intel_wait_for_vblank(dev);
 
-       DRM_DEBUG("disabled FBC\n");
+       DRM_DEBUG_KMS("disabled FBC\n");
 }
 
 static bool i8xx_fbc_enabled(struct drm_crtc *crtc)
@@ -1062,7 +1063,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        /* enable it... */
        I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN);
 
-       DRM_DEBUG("enabled fbc on plane %d\n", intel_crtc->plane);
+       DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
 }
 
 void g4x_disable_fbc(struct drm_device *dev)
@@ -1076,7 +1077,7 @@ void g4x_disable_fbc(struct drm_device *dev)
        I915_WRITE(DPFC_CONTROL, dpfc_ctl);
        intel_wait_for_vblank(dev);
 
-       DRM_DEBUG("disabled FBC\n");
+       DRM_DEBUG_KMS("disabled FBC\n");
 }
 
 static bool g4x_fbc_enabled(struct drm_crtc *crtc)
@@ -1141,25 +1142,27 @@ static void intel_update_fbc(struct drm_crtc *crtc,
         *   - going to an unsupported config (interlace, pixel multiply, etc.)
         */
        if (intel_fb->obj->size > dev_priv->cfb_size) {
-               DRM_DEBUG("framebuffer too large, disabling compression\n");
+               DRM_DEBUG_KMS("framebuffer too large, disabling "
+                               "compression\n");
                goto out_disable;
        }
        if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
            (mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
-               DRM_DEBUG("mode incompatible with compression, disabling\n");
+               DRM_DEBUG_KMS("mode incompatible with compression, "
+                               "disabling\n");
                goto out_disable;
        }
        if ((mode->hdisplay > 2048) ||
            (mode->vdisplay > 1536)) {
-               DRM_DEBUG("mode too large for compression, disabling\n");
+               DRM_DEBUG_KMS("mode too large for compression, disabling\n");
                goto out_disable;
        }
        if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) {
-               DRM_DEBUG("plane not 0, disabling compression\n");
+               DRM_DEBUG_KMS("plane not 0, disabling compression\n");
                goto out_disable;
        }
        if (obj_priv->tiling_mode != I915_TILING_X) {
-               DRM_DEBUG("framebuffer not tiled, disabling compression\n");
+               DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
                goto out_disable;
        }
 
@@ -1181,12 +1184,56 @@ static void intel_update_fbc(struct drm_crtc *crtc,
        return;
 
 out_disable:
-       DRM_DEBUG("unsupported config, disabling FBC\n");
+       DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
        /* Multiple disables should be harmless */
        if (dev_priv->display.fbc_enabled(crtc))
                dev_priv->display.disable_fbc(dev);
 }
 
+static int
+intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
+{
+       struct drm_i915_gem_object *obj_priv = obj->driver_private;
+       u32 alignment;
+       int ret;
+
+       switch (obj_priv->tiling_mode) {
+       case I915_TILING_NONE:
+               alignment = 64 * 1024;
+               break;
+       case I915_TILING_X:
+               /* pin() will align the object as required by fence */
+               alignment = 0;
+               break;
+       case I915_TILING_Y:
+               /* FIXME: Is this true? */
+               DRM_ERROR("Y tiled not allowed for scan out buffers\n");
+               return -EINVAL;
+       default:
+               BUG();
+       }
+
+       ret = i915_gem_object_pin(obj, alignment);
+       if (ret != 0)
+               return ret;
+
+       /* Install a fence for tiled scan-out. Pre-i965 always needs a
+        * fence, whereas 965+ only requires a fence if using
+        * framebuffer compression.  For simplicity, we always install
+        * a fence as the cost is not that onerous.
+        */
+       if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
+           obj_priv->tiling_mode != I915_TILING_NONE) {
+               ret = i915_gem_object_get_fence_reg(obj);
+               if (ret != 0) {
+                       i915_gem_object_unpin(obj);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                    struct drm_framebuffer *old_fb)
@@ -1206,12 +1253,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
        int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
        int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
-       u32 dspcntr, alignment;
+       u32 dspcntr;
        int ret;
 
        /* no fb bound */
        if (!crtc->fb) {
-               DRM_DEBUG("No FB bound\n");
+               DRM_DEBUG_KMS("No FB bound\n");
                return 0;
        }
 
@@ -1228,24 +1275,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        obj = intel_fb->obj;
        obj_priv = obj->driver_private;
 
-       switch (obj_priv->tiling_mode) {
-       case I915_TILING_NONE:
-               alignment = 64 * 1024;
-               break;
-       case I915_TILING_X:
-               /* pin() will align the object as required by fence */
-               alignment = 0;
-               break;
-       case I915_TILING_Y:
-               /* FIXME: Is this true? */
-               DRM_ERROR("Y tiled not allowed for scan out buffers\n");
-               return -EINVAL;
-       default:
-               BUG();
-       }
-
        mutex_lock(&dev->struct_mutex);
-       ret = i915_gem_object_pin(obj, alignment);
+       ret = intel_pin_and_fence_fb_obj(dev, obj);
        if (ret != 0) {
                mutex_unlock(&dev->struct_mutex);
                return ret;
@@ -1258,20 +1289,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                return ret;
        }
 
-       /* Install a fence for tiled scan-out. Pre-i965 always needs a fence,
-        * whereas 965+ only requires a fence if using framebuffer compression.
-        * For simplicity, we always install a fence as the cost is not that onerous.
-        */
-       if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
-           obj_priv->tiling_mode != I915_TILING_NONE) {
-               ret = i915_gem_object_get_fence_reg(obj);
-               if (ret != 0) {
-                       i915_gem_object_unpin(obj);
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
-       }
-
        dspcntr = I915_READ(dspcntr_reg);
        /* Mask out pixel format bits in case we change it */
        dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
@@ -1287,7 +1304,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                break;
        case 24:
        case 32:
-               dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+               if (crtc->fb->depth == 30)
+                       dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA;
+               else
+                       dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
                break;
        default:
                DRM_ERROR("Unknown color depth\n");
@@ -1302,7 +1322,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                        dspcntr &= ~DISPPLANE_TILED;
        }
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                /* must disable */
                dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
@@ -1311,7 +1331,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        Start = obj_priv->gtt_offset;
        Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
 
-       DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+       DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
        I915_WRITE(dspstride, crtc->fb->pitch);
        if (IS_I965G(dev)) {
                I915_WRITE(dspbase, Offset);
@@ -1363,7 +1383,7 @@ static void i915_disable_vga (struct drm_device *dev)
        u8 sr1;
        u32 vga_reg;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                vga_reg = CPU_VGACNTRL;
        else
                vga_reg = VGACNTRL;
@@ -1379,19 +1399,19 @@ static void i915_disable_vga (struct drm_device *dev)
        I915_WRITE(vga_reg, VGA_DISP_DISABLE);
 }
 
-static void igdng_disable_pll_edp (struct drm_crtc *crtc)
+static void ironlake_disable_pll_edp (struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpa_ctl;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
        dpa_ctl = I915_READ(DP_A);
        dpa_ctl &= ~DP_PLL_ENABLE;
        I915_WRITE(DP_A, dpa_ctl);
 }
 
-static void igdng_enable_pll_edp (struct drm_crtc *crtc)
+static void ironlake_enable_pll_edp (struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1404,13 +1424,13 @@ static void igdng_enable_pll_edp (struct drm_crtc *crtc)
 }
 
 
-static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
+static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpa_ctl;
 
-       DRM_DEBUG("eDP PLL enable for clock %d\n", clock);
+       DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock);
        dpa_ctl = I915_READ(DP_A);
        dpa_ctl &= ~DP_PLL_FREQ_MASK;
 
@@ -1440,7 +1460,7 @@ static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
        udelay(500);
 }
 
-static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1481,10 +1501,19 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_ON:
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
-               DRM_DEBUG("crtc %d dpms on\n", pipe);
+               DRM_DEBUG_KMS("crtc %d dpms on\n", pipe);
+
+               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+                       temp = I915_READ(PCH_LVDS);
+                       if ((temp & LVDS_PORT_EN) == 0) {
+                               I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
+                               POSTING_READ(PCH_LVDS);
+                       }
+               }
+
                if (HAS_eDP) {
                        /* enable eDP PLL */
-                       igdng_enable_pll_edp(crtc);
+                       ironlake_enable_pll_edp(crtc);
                } else {
                        /* enable PCH DPLL */
                        temp = I915_READ(pch_dpll_reg);
@@ -1501,7 +1530,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        I915_READ(fdi_rx_reg);
                        udelay(200);
 
-                       /* Enable CPU FDI TX PLL, always on for IGDNG */
+                       /* Enable CPU FDI TX PLL, always on for Ironlake */
                        temp = I915_READ(fdi_tx_reg);
                        if ((temp & FDI_TX_PLL_ENABLE) == 0) {
                                I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
@@ -1568,12 +1597,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        udelay(150);
 
                        temp = I915_READ(fdi_rx_iir_reg);
-                       DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                       DRM_DEBUG_KMS("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);
+                                       DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
+                                                               temp);
                                        if (temp & FDI_RX_BIT_LOCK)
                                                break;
                                        udelay(200);
@@ -1582,11 +1612,11 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                                        I915_WRITE(fdi_rx_iir_reg,
                                                        temp | FDI_RX_BIT_LOCK);
                                else
-                                       DRM_DEBUG("train 1 fail\n");
+                                       DRM_DEBUG_KMS("train 1 fail\n");
                        } else {
                                I915_WRITE(fdi_rx_iir_reg,
                                                temp | FDI_RX_BIT_LOCK);
-                               DRM_DEBUG("train 1 ok 2!\n");
+                               DRM_DEBUG_KMS("train 1 ok 2!\n");
                        }
                        temp = I915_READ(fdi_tx_reg);
                        temp &= ~FDI_LINK_TRAIN_NONE;
@@ -1601,12 +1631,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        udelay(150);
 
                        temp = I915_READ(fdi_rx_iir_reg);
-                       DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                       DRM_DEBUG_KMS("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);
+                                       DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
+                                                               temp);
                                        if (temp & FDI_RX_SYMBOL_LOCK)
                                                break;
                                        udelay(200);
@@ -1614,15 +1645,15 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                                if (j != tries) {
                                        I915_WRITE(fdi_rx_iir_reg,
                                                        temp | FDI_RX_SYMBOL_LOCK);
-                                       DRM_DEBUG("train 2 ok 1!\n");
+                                       DRM_DEBUG_KMS("train 2 ok 1!\n");
                                } else
-                                       DRM_DEBUG("train 2 fail\n");
+                                       DRM_DEBUG_KMS("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_KMS("train 2 ok 2!\n");
                        }
-                       DRM_DEBUG("train done\n");
+                       DRM_DEBUG_KMS("train done\n");
 
                        /* set transcoder timing */
                        I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
@@ -1664,9 +1695,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
 
        break;
        case DRM_MODE_DPMS_OFF:
-               DRM_DEBUG("crtc %d dpms off\n", pipe);
-
-               i915_disable_vga(dev);
+               DRM_DEBUG_KMS("crtc %d dpms off\n", pipe);
 
                /* Disable display plane */
                temp = I915_READ(dspcntr_reg);
@@ -1677,6 +1706,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        I915_READ(dspbase_reg);
                }
 
+               i915_disable_vga(dev);
+
                /* disable cpu pipe, disable after all planes disabled */
                temp = I915_READ(pipeconf_reg);
                if ((temp & PIPEACONF_ENABLE) != 0) {
@@ -1690,16 +1721,23 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                                        udelay(500);
                                        continue;
                                } else {
-                                       DRM_DEBUG("pipe %d off delay\n", pipe);
+                                       DRM_DEBUG_KMS("pipe %d off delay\n",
+                                                               pipe);
                                        break;
                                }
                        }
                } else
-                       DRM_DEBUG("crtc %d is disabled\n", pipe);
+                       DRM_DEBUG_KMS("crtc %d is disabled\n", pipe);
 
-               if (HAS_eDP) {
-                       igdng_disable_pll_edp(crtc);
+               udelay(100);
+
+               /* Disable PF */
+               temp = I915_READ(pf_ctl_reg);
+               if ((temp & PF_ENABLE) != 0) {
+                       I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+                       I915_READ(pf_ctl_reg);
                }
+               I915_WRITE(pf_win_size, 0);
 
                /* disable CPU FDI tx and PCH FDI rx */
                temp = I915_READ(fdi_tx_reg);
@@ -1725,6 +1763,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                udelay(100);
 
+               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+                       temp = I915_READ(PCH_LVDS);
+                       I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
+                       I915_READ(PCH_LVDS);
+                       udelay(100);
+               }
+
                /* disable PCH transcoder */
                temp = I915_READ(transconf_reg);
                if ((temp & TRANS_ENABLE) != 0) {
@@ -1738,12 +1783,15 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                                        udelay(500);
                                        continue;
                                } else {
-                                       DRM_DEBUG("transcoder %d off delay\n", pipe);
+                                       DRM_DEBUG_KMS("transcoder %d off "
+                                                       "delay\n", pipe);
                                        break;
                                }
                        }
                }
 
+               udelay(100);
+
                /* disable PCH DPLL */
                temp = I915_READ(pch_dpll_reg);
                if ((temp & DPLL_VCO_ENABLE) != 0) {
@@ -1751,14 +1799,20 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        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);
+               if (HAS_eDP) {
+                       ironlake_disable_pll_edp(crtc);
                }
 
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_SEL_PCDCLK;
+               I915_WRITE(fdi_rx_reg, temp);
+               I915_READ(fdi_rx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_RX_PLL_ENABLE;
+               I915_WRITE(fdi_rx_reg, temp);
+               I915_READ(fdi_rx_reg);
+
                /* Disable CPU FDI TX PLL */
                temp = I915_READ(fdi_tx_reg);
                if ((temp & FDI_TX_PLL_ENABLE) != 0) {
@@ -1767,20 +1821,43 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        udelay(100);
                }
 
-               /* Disable PF */
-               temp = I915_READ(pf_ctl_reg);
-               if ((temp & PF_ENABLE) != 0) {
-                       I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
-                       I915_READ(pf_ctl_reg);
-               }
-               I915_WRITE(pf_win_size, 0);
-
                /* Wait for the clocks to turn off. */
-               udelay(150);
+               udelay(100);
                break;
        }
 }
 
+static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
+{
+       struct intel_overlay *overlay;
+       int ret;
+
+       if (!enable && intel_crtc->overlay) {
+               overlay = intel_crtc->overlay;
+               mutex_lock(&overlay->dev->struct_mutex);
+               for (;;) {
+                       ret = intel_overlay_switch_off(overlay);
+                       if (ret == 0)
+                               break;
+
+                       ret = intel_overlay_recover_from_interrupt(overlay, 0);
+                       if (ret != 0) {
+                               /* overlay doesn't react anymore. Usually
+                                * results in a black screen and an unkillable
+                                * X server. */
+                               BUG();
+                               overlay->hw_wedged = HW_WEDGED;
+                               break;
+                       }
+               }
+               mutex_unlock(&overlay->dev->struct_mutex);
+       }
+       /* Let userspace switch the overlay on again. In most cases userspace
+        * has to recompute where to put it anyway. */
+
+       return;
+}
+
 static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
@@ -1839,12 +1916,14 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
                        intel_update_fbc(crtc, &crtc->mode);
 
                /* Give the overlay scaler a chance to enable if it's on this pipe */
-               //intel_crtc_dpms_video(crtc, true); TODO
+               intel_crtc_dpms_overlay(intel_crtc, true);
        break;
        case DRM_MODE_DPMS_OFF:
                intel_update_watermarks(dev);
+
                /* Give the overlay scaler a chance to disable if it's on this pipe */
-               //intel_crtc_dpms_video(crtc, FALSE); TODO
+               intel_crtc_dpms_overlay(intel_crtc, false);
+               drm_vblank_off(dev, pipe);
 
                if (dev_priv->cfb_plane == plane &&
                    dev_priv->display.disable_fbc)
@@ -1963,7 +2042,7 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
                                  struct drm_display_mode *adjusted_mode)
 {
        struct drm_device *dev = crtc->dev;
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                /* FDI link clock is fixed at 2.7G */
                if (mode->clock * 3 > 27000 * 4)
                        return MODE_CLOCK_HIGH;
@@ -2039,7 +2118,7 @@ static int i830_get_display_clock_speed(struct drm_device *dev)
  * Return the pipe currently connected to the panel fitter,
  * or -1 if the panel fitter is not present or not in use
  */
-static int intel_panel_fitter_pipe (struct drm_device *dev)
+int intel_panel_fitter_pipe (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32  pfit_control;
@@ -2083,9 +2162,8 @@ fdi_reduce_ratio(u32 *num, u32 *den)
 #define LINK_N 0x80000
 
 static void
-igdng_compute_m_n(int bits_per_pixel, int nlanes,
-               int pixel_clock, int link_clock,
-               struct fdi_m_n *m_n)
+ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock,
+                    int link_clock, struct fdi_m_n *m_n)
 {
        u64 temp;
 
@@ -2113,34 +2191,34 @@ struct intel_watermark_params {
        unsigned long cacheline_size;
 };
 
-/* IGD has different values for various configs */
-static struct intel_watermark_params igd_display_wm = {
-       IGD_DISPLAY_FIFO,
-       IGD_MAX_WM,
-       IGD_DFT_WM,
-       IGD_GUARD_WM,
-       IGD_FIFO_LINE_SIZE
+/* Pineview has different values for various configs */
+static struct intel_watermark_params pineview_display_wm = {
+       PINEVIEW_DISPLAY_FIFO,
+       PINEVIEW_MAX_WM,
+       PINEVIEW_DFT_WM,
+       PINEVIEW_GUARD_WM,
+       PINEVIEW_FIFO_LINE_SIZE
 };
-static struct intel_watermark_params igd_display_hplloff_wm = {
-       IGD_DISPLAY_FIFO,
-       IGD_MAX_WM,
-       IGD_DFT_HPLLOFF_WM,
-       IGD_GUARD_WM,
-       IGD_FIFO_LINE_SIZE
+static struct intel_watermark_params pineview_display_hplloff_wm = {
+       PINEVIEW_DISPLAY_FIFO,
+       PINEVIEW_MAX_WM,
+       PINEVIEW_DFT_HPLLOFF_WM,
+       PINEVIEW_GUARD_WM,
+       PINEVIEW_FIFO_LINE_SIZE
 };
-static struct intel_watermark_params igd_cursor_wm = {
-       IGD_CURSOR_FIFO,
-       IGD_CURSOR_MAX_WM,
-       IGD_CURSOR_DFT_WM,
-       IGD_CURSOR_GUARD_WM,
-       IGD_FIFO_LINE_SIZE,
+static struct intel_watermark_params pineview_cursor_wm = {
+       PINEVIEW_CURSOR_FIFO,
+       PINEVIEW_CURSOR_MAX_WM,
+       PINEVIEW_CURSOR_DFT_WM,
+       PINEVIEW_CURSOR_GUARD_WM,
+       PINEVIEW_FIFO_LINE_SIZE,
 };
-static struct intel_watermark_params igd_cursor_hplloff_wm = {
-       IGD_CURSOR_FIFO,
-       IGD_CURSOR_MAX_WM,
-       IGD_CURSOR_DFT_WM,
-       IGD_CURSOR_GUARD_WM,
-       IGD_FIFO_LINE_SIZE
+static struct intel_watermark_params pineview_cursor_hplloff_wm = {
+       PINEVIEW_CURSOR_FIFO,
+       PINEVIEW_CURSOR_MAX_WM,
+       PINEVIEW_CURSOR_DFT_WM,
+       PINEVIEW_CURSOR_GUARD_WM,
+       PINEVIEW_FIFO_LINE_SIZE
 };
 static struct intel_watermark_params g4x_wm_info = {
        G4X_FIFO_SIZE,
@@ -2213,11 +2291,11 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
                1000;
        entries_required /= wm->cacheline_size;
 
-       DRM_DEBUG("FIFO entries required for mode: %d\n", entries_required);
+       DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries_required);
 
        wm_size = wm->fifo_size - (entries_required + wm->guard_size);
 
-       DRM_DEBUG("FIFO watermark level: %d\n", wm_size);
+       DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size);
 
        /* Don't promote wm_size to unsigned... */
        if (wm_size > (long)wm->max_wm)
@@ -2279,50 +2357,50 @@ static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb,
                        return latency;
        }
 
-       DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+       DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
 
        return NULL;
 }
 
-static void igd_disable_cxsr(struct drm_device *dev)
+static void pineview_disable_cxsr(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
 
        /* deactivate cxsr */
        reg = I915_READ(DSPFW3);
-       reg &= ~(IGD_SELF_REFRESH_EN);
+       reg &= ~(PINEVIEW_SELF_REFRESH_EN);
        I915_WRITE(DSPFW3, reg);
        DRM_INFO("Big FIFO is disabled\n");
 }
 
-static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
-                           int pixel_size)
+static void pineview_enable_cxsr(struct drm_device *dev, unsigned long clock,
+                                int pixel_size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
        unsigned long wm;
        struct cxsr_latency *latency;
 
-       latency = intel_get_cxsr_latency(IS_IGDG(dev), dev_priv->fsb_freq,
+       latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->fsb_freq,
                dev_priv->mem_freq);
        if (!latency) {
-               DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
-               igd_disable_cxsr(dev);
+               DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
+               pineview_disable_cxsr(dev);
                return;
        }
 
        /* Display SR */
-       wm = intel_calculate_wm(clock, &igd_display_wm, pixel_size,
+       wm = intel_calculate_wm(clock, &pineview_display_wm, pixel_size,
                                latency->display_sr);
        reg = I915_READ(DSPFW1);
        reg &= 0x7fffff;
        reg |= wm << 23;
        I915_WRITE(DSPFW1, reg);
-       DRM_DEBUG("DSPFW1 register is %x\n", reg);
+       DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg);
 
        /* cursor SR */
-       wm = intel_calculate_wm(clock, &igd_cursor_wm, pixel_size,
+       wm = intel_calculate_wm(clock, &pineview_cursor_wm, pixel_size,
                                latency->cursor_sr);
        reg = I915_READ(DSPFW3);
        reg &= ~(0x3f << 24);
@@ -2330,7 +2408,7 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
        I915_WRITE(DSPFW3, reg);
 
        /* Display HPLL off SR */
-       wm = intel_calculate_wm(clock, &igd_display_hplloff_wm,
+       wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm,
                latency->display_hpll_disable, I915_FIFO_LINE_SIZE);
        reg = I915_READ(DSPFW3);
        reg &= 0xfffffe00;
@@ -2338,17 +2416,17 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
        I915_WRITE(DSPFW3, reg);
 
        /* cursor HPLL off SR */
-       wm = intel_calculate_wm(clock, &igd_cursor_hplloff_wm, pixel_size,
+       wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm, pixel_size,
                                latency->cursor_hpll_disable);
        reg = I915_READ(DSPFW3);
        reg &= ~(0x3f << 16);
        reg |= (wm & 0x3f) << 16;
        I915_WRITE(DSPFW3, reg);
-       DRM_DEBUG("DSPFW3 register is %x\n", reg);
+       DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
 
        /* activate cxsr */
        reg = I915_READ(DSPFW3);
-       reg |= IGD_SELF_REFRESH_EN;
+       reg |= PINEVIEW_SELF_REFRESH_EN;
        I915_WRITE(DSPFW3, reg);
 
        DRM_INFO("Big FIFO is enabled\n");
@@ -2384,8 +2462,8 @@ static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
                size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) -
                        (dsparb & 0x7f);
 
-       DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
-                 size);
+       DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
+                       plane ? "B" : "A", size);
 
        return size;
 }
@@ -2403,8 +2481,8 @@ static int i85x_get_fifo_size(struct drm_device *dev, int plane)
                        (dsparb & 0x1ff);
        size >>= 1; /* Convert to cachelines */
 
-       DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
-                 size);
+       DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
+                       plane ? "B" : "A", size);
 
        return size;
 }
@@ -2418,7 +2496,8 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane)
        size = dsparb & 0x7f;
        size >>= 2; /* Convert to cachelines */
 
-       DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
+       DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
+                       plane ? "B" : "A",
                  size);
 
        return size;
@@ -2433,8 +2512,8 @@ static int i830_get_fifo_size(struct drm_device *dev, int plane)
        size = dsparb & 0x7f;
        size >>= 1; /* Convert to cachelines */
 
-       DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
-                 size);
+       DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
+                       plane ? "B" : "A", size);
 
        return size;
 }
@@ -2509,15 +2588,39 @@ static void g4x_update_wm(struct drm_device *dev,  int planea_clock,
                   (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
 }
 
-static void i965_update_wm(struct drm_device *dev, int unused, int unused2,
-                          int unused3, int unused4)
+static void i965_update_wm(struct drm_device *dev, int planea_clock,
+                          int planeb_clock, int sr_hdisplay, int pixel_size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       unsigned long line_time_us;
+       int sr_clock, sr_entries, srwm = 1;
+
+       /* Calc sr entries for one plane configs */
+       if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
+               /* self-refresh has much higher latency */
+               const static int sr_latency_ns = 12000;
+
+               sr_clock = planea_clock ? planea_clock : planeb_clock;
+               line_time_us = ((sr_hdisplay * 1000) / sr_clock);
+
+               /* Use ns/us then divide to preserve precision */
+               sr_entries = (((sr_latency_ns / line_time_us) + 1) *
+                             pixel_size * sr_hdisplay) / 1000;
+               sr_entries = roundup(sr_entries / I915_FIFO_LINE_SIZE, 1);
+               DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
+               srwm = I945_FIFO_SIZE - sr_entries;
+               if (srwm < 0)
+                       srwm = 1;
+               srwm &= 0x3f;
+               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
+       }
 
-       DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n");
+       DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
+                     srwm);
 
        /* 965 has limitations... */
-       I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0));
+       I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | (8 << 16) | (8 << 8) |
+                  (8 << 0));
        I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
 }
 
@@ -2553,7 +2656,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
                                       pixel_size, latency_ns);
        planeb_wm = intel_calculate_wm(planeb_clock, &planeb_params,
                                       pixel_size, latency_ns);
-       DRM_DEBUG("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
+       DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
 
        /*
         * Overlay gets an aggressive default since video jitter is bad.
@@ -2573,14 +2676,14 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
                sr_entries = (((sr_latency_ns / line_time_us) + 1) *
                              pixel_size * sr_hdisplay) / 1000;
                sr_entries = roundup(sr_entries / cacheline_size, 1);
-               DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
+               DRM_DEBUG_KMS("self-refresh entries: %d\n", sr_entries);
                srwm = total_size - sr_entries;
                if (srwm < 0)
                        srwm = 1;
                I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
        }
 
-       DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
+       DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
                  planea_wm, planeb_wm, cwm, srwm);
 
        fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f);
@@ -2607,7 +2710,7 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused,
                                       pixel_size, latency_ns);
        fwater_lo |= (3<<8) | planea_wm;
 
-       DRM_DEBUG("Setting FIFO watermarks - A: %d\n", planea_wm);
+       DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d\n", planea_wm);
 
        I915_WRITE(FW_BLC, fwater_lo);
 }
@@ -2661,11 +2764,11 @@ static void intel_update_watermarks(struct drm_device *dev)
                if (crtc->enabled) {
                        enabled++;
                        if (intel_crtc->plane == 0) {
-                               DRM_DEBUG("plane A (pipe %d) clock: %d\n",
+                               DRM_DEBUG_KMS("plane A (pipe %d) clock: %d\n",
                                          intel_crtc->pipe, crtc->mode.clock);
                                planea_clock = crtc->mode.clock;
                        } else {
-                               DRM_DEBUG("plane B (pipe %d) clock: %d\n",
+                               DRM_DEBUG_KMS("plane B (pipe %d) clock: %d\n",
                                          intel_crtc->pipe, crtc->mode.clock);
                                planeb_clock = crtc->mode.clock;
                        }
@@ -2682,10 +2785,10 @@ static void intel_update_watermarks(struct drm_device *dev)
                return;
 
        /* Single plane configs can enable self refresh */
-       if (enabled == 1 && IS_IGD(dev))
-               igd_enable_cxsr(dev, sr_clock, pixel_size);
-       else if (IS_IGD(dev))
-               igd_disable_cxsr(dev);
+       if (enabled == 1 && IS_PINEVIEW(dev))
+               pineview_enable_cxsr(dev, sr_clock, pixel_size);
+       else if (IS_PINEVIEW(dev))
+               pineview_disable_cxsr(dev);
 
        dev_priv->display.update_wm(dev, planea_clock, planeb_clock,
                                    sr_hdisplay, pixel_size);
@@ -2779,10 +2882,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) {
                refclk = dev_priv->lvds_ssc_freq * 1000;
-               DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
+               DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
+                                       refclk / 1000);
        } else if (IS_I9XX(dev)) {
                refclk = 96000;
-               if (IS_IGDNG(dev))
+               if (IS_IRONLAKE(dev))
                        refclk = 120000; /* 120Mhz refclk */
        } else {
                refclk = 48000;
@@ -2802,14 +2906,25 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                return -EINVAL;
        }
 
-       if (limit->find_reduced_pll && dev_priv->lvds_downclock_avail) {
+       if (is_lvds && limit->find_reduced_pll &&
+                       dev_priv->lvds_downclock_avail) {
                memcpy(&reduced_clock, &clock, sizeof(intel_clock_t));
                has_reduced_clock = limit->find_reduced_pll(limit, crtc,
-                                                           (adjusted_mode->clock*3/4),
+                                                           dev_priv->lvds_downclock,
                                                            refclk,
                                                            &reduced_clock);
+               if (has_reduced_clock && (clock.p != reduced_clock.p)) {
+                       /*
+                        * If the different P is found, it means that we can't
+                        * switch the display clock by using the FP0/FP1.
+                        * In such case we will disable the LVDS downclock
+                        * feature.
+                        */
+                       DRM_DEBUG_KMS("Different P is found for "
+                                               "LVDS clock/downclock\n");
+                       has_reduced_clock = 0;
+               }
        }
-
        /* SDVO TV has fixed PLL values depend on its clock range,
           this mirrors vbios setting. */
        if (is_sdvo && is_tv) {
@@ -2831,7 +2946,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        /* FDI link */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                int lane, link_bw, bpp;
                /* eDP doesn't require FDI link, so just set DP M/N
                   according to current link config */
@@ -2873,8 +2988,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        bpp = 24;
                }
 
-               igdng_compute_m_n(bpp, lane, target_clock,
-                                 link_bw, &m_n);
+               ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
        }
 
        /* Ironlake: try to setup display ref clock before DPLL
@@ -2882,7 +2996,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
         * PCH B stepping, previous chipset stepping should be
         * ignoring this setting.
         */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                temp = I915_READ(PCH_DREF_CONTROL);
                /* Always enable nonspread source */
                temp &= ~DREF_NONSPREAD_SOURCE_MASK;
@@ -2917,7 +3031,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
-       if (IS_IGD(dev)) {
+       if (IS_PINEVIEW(dev)) {
                fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
                if (has_reduced_clock)
                        fp2 = (1 << reduced_clock.n) << 16 |
@@ -2929,7 +3043,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                                reduced_clock.m2;
        }
 
-       if (!IS_IGDNG(dev))
+       if (!IS_IRONLAKE(dev))
                dpll = DPLL_VGA_MODE_DIS;
 
        if (IS_I9XX(dev)) {
@@ -2942,19 +3056,19 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
                        if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-                       else if (IS_IGDNG(dev))
+                       else if (IS_IRONLAKE(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
                }
                if (is_dp)
                        dpll |= DPLL_DVO_HIGH_SPEED;
 
                /* compute bitmask from p1 value */
-               if (IS_IGD(dev))
-                       dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
+               if (IS_PINEVIEW(dev))
+                       dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW;
                else {
                        dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
                        /* also FPA1 */
-                       if (IS_IGDNG(dev))
+                       if (IS_IRONLAKE(dev))
                                dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
                        if (IS_G4X(dev) && has_reduced_clock)
                                dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
@@ -2973,7 +3087,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
                        break;
                }
-               if (IS_I965G(dev) && !IS_IGDNG(dev))
+               if (IS_I965G(dev) && !IS_IRONLAKE(dev))
                        dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
        } else {
                if (is_lvds) {
@@ -3005,9 +3119,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        /* Set up the display plane register */
        dspcntr = DISPPLANE_GAMMA_ENABLE;
 
-       /* IGDNG's plane is forced to pipe, bit 24 is to
+       /* Ironlake's plane is forced to pipe, bit 24 is to
           enable color space conversion */
-       if (!IS_IGDNG(dev)) {
+       if (!IS_IRONLAKE(dev)) {
                if (pipe == 0)
                        dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
                else
@@ -3034,20 +3148,20 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
 
        /* Disable the panel fitter if it was on our pipe */
-       if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
+       if (!IS_IRONLAKE(dev) && intel_panel_fitter_pipe(dev) == pipe)
                I915_WRITE(PFIT_CONTROL, 0);
 
-       DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+       DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
        drm_mode_debug_printmodeline(mode);
 
-       /* assign to IGDNG registers */
-       if (IS_IGDNG(dev)) {
+       /* assign to Ironlake registers */
+       if (IS_IRONLAKE(dev)) {
                fp_reg = pch_fp_reg;
                dpll_reg = pch_dpll_reg;
        }
 
        if (is_edp) {
-               igdng_disable_pll_edp(crtc);
+               ironlake_disable_pll_edp(crtc);
        } else if ((dpll & DPLL_VCO_ENABLE)) {
                I915_WRITE(fp_reg, fp);
                I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
@@ -3062,7 +3176,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        if (is_lvds) {
                u32 lvds;
 
-               if (IS_IGDNG(dev))
+               if (IS_IRONLAKE(dev))
                        lvds_reg = PCH_LVDS;
 
                lvds = I915_READ(lvds_reg);
@@ -3095,7 +3209,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                /* Wait for the clocks to stabilize. */
                udelay(150);
 
-               if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+               if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
                        if (is_sdvo) {
                                sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
                                I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
@@ -3115,14 +3229,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                I915_WRITE(fp_reg + 4, fp2);
                intel_crtc->lowfreq_avail = true;
                if (HAS_PIPE_CXSR(dev)) {
-                       DRM_DEBUG("enabling CxSR downclocking\n");
+                       DRM_DEBUG_KMS("enabling CxSR downclocking\n");
                        pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
                }
        } else {
                I915_WRITE(fp_reg + 4, fp);
                intel_crtc->lowfreq_avail = false;
                if (HAS_PIPE_CXSR(dev)) {
-                       DRM_DEBUG("disabling CxSR downclocking\n");
+                       DRM_DEBUG_KMS("disabling CxSR downclocking\n");
                        pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
                }
        }
@@ -3142,21 +3256,21 @@ 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.
         */
-       if (!IS_IGDNG(dev)) {
+       if (!IS_IRONLAKE(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)) {
+       if (IS_IRONLAKE(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);
 
                if (is_edp) {
-                       igdng_set_pll_edp(crtc, adjusted_mode->clock);
+                       ironlake_set_pll_edp(crtc, adjusted_mode->clock);
                } else {
                        /* enable FDI RX PLL too */
                        temp = I915_READ(fdi_rx_reg);
@@ -3170,7 +3284,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        intel_wait_for_vblank(dev);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                /* enable address swizzle for tiling buffer */
                temp = I915_READ(DISP_ARB_CTL);
                I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING);
@@ -3204,8 +3318,8 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
        if (!crtc->enabled)
                return;
 
-       /* use legacy palette for IGDNG */
-       if (IS_IGDNG(dev))
+       /* use legacy palette for Ironlake */
+       if (IS_IRONLAKE(dev))
                palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
                                                   LGC_PALETTE_B;
 
@@ -3234,11 +3348,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        size_t addr;
        int ret;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
 
        /* if we want to turn off the cursor ignore width and height */
        if (!handle) {
-               DRM_DEBUG("cursor off\n");
+               DRM_DEBUG_KMS("cursor off\n");
                if (IS_MOBILE(dev) || IS_I9XX(dev)) {
                        temp &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
                        temp |= CURSOR_MODE_DISABLE;
@@ -3546,18 +3660,18 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
 
        clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
-       if (IS_IGD(dev)) {
-               clock.n = ffs((fp & FP_N_IGD_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
-               clock.m2 = (fp & FP_M2_IGD_DIV_MASK) >> FP_M2_DIV_SHIFT;
+       if (IS_PINEVIEW(dev)) {
+               clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
+               clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT;
        } else {
                clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
                clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
        }
 
        if (IS_I9XX(dev)) {
-               if (IS_IGD(dev))
-                       clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_IGD) >>
-                               DPLL_FPA01_P1_POST_DIV_SHIFT_IGD);
+               if (IS_PINEVIEW(dev))
+                       clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >>
+                               DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW);
                else
                        clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
                               DPLL_FPA01_P1_POST_DIV_SHIFT);
@@ -3572,7 +3686,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                                7 : 14;
                        break;
                default:
-                       DRM_DEBUG("Unknown DPLL mode %08x in programmed "
+                       DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed "
                                  "mode\n", (int)(dpll & DPLL_MODE_MASK));
                        return 0;
                }
@@ -3658,7 +3772,7 @@ static void intel_gpu_idle_timer(unsigned long arg)
        struct drm_device *dev = (struct drm_device *)arg;
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       DRM_DEBUG("idle timer fired, downclocking\n");
+       DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
 
        dev_priv->busy = false;
 
@@ -3669,11 +3783,11 @@ void intel_increase_renderclock(struct drm_device *dev, bool schedule)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return;
 
        if (!dev_priv->render_reclock_avail) {
-               DRM_DEBUG("not reclocking render clock\n");
+               DRM_DEBUG_DRIVER("not reclocking render clock\n");
                return;
        }
 
@@ -3682,7 +3796,7 @@ void intel_increase_renderclock(struct drm_device *dev, bool schedule)
                pci_write_config_word(dev->pdev, GCFGC, dev_priv->orig_clock);
        else if (IS_I85X(dev))
                pci_write_config_word(dev->pdev, HPLLCC, dev_priv->orig_clock);
-       DRM_DEBUG("increasing render clock frequency\n");
+       DRM_DEBUG_DRIVER("increasing render clock frequency\n");
 
        /* Schedule downclock */
        if (schedule)
@@ -3694,11 +3808,11 @@ void intel_decrease_renderclock(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return;
 
        if (!dev_priv->render_reclock_avail) {
-               DRM_DEBUG("not reclocking render clock\n");
+               DRM_DEBUG_DRIVER("not reclocking render clock\n");
                return;
        }
 
@@ -3758,7 +3872,7 @@ void intel_decrease_renderclock(struct drm_device *dev)
 
                pci_write_config_word(dev->pdev, HPLLCC, hpllcc);
        }
-       DRM_DEBUG("decreasing render clock frequency\n");
+       DRM_DEBUG_DRIVER("decreasing render clock frequency\n");
 }
 
 /* Note that no increase function is needed for this - increase_renderclock()
@@ -3766,7 +3880,7 @@ void intel_decrease_renderclock(struct drm_device *dev)
  */
 void intel_decrease_displayclock(struct drm_device *dev)
 {
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return;
 
        if (IS_I945G(dev) || IS_I945GM(dev) || IS_I915G(dev) ||
@@ -3792,7 +3906,7 @@ static void intel_crtc_idle_timer(unsigned long arg)
        struct drm_crtc *crtc = &intel_crtc->base;
        drm_i915_private_t *dev_priv = crtc->dev->dev_private;
 
-       DRM_DEBUG("idle timer fired, downclocking\n");
+       DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
 
        intel_crtc->busy = false;
 
@@ -3808,14 +3922,14 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
        int dpll = I915_READ(dpll_reg);
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return;
 
        if (!dev_priv->lvds_downclock_avail)
                return;
 
        if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
-               DRM_DEBUG("upclocking LVDS\n");
+               DRM_DEBUG_DRIVER("upclocking LVDS\n");
 
                /* Unlock panel regs */
                I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
@@ -3826,7 +3940,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
                intel_wait_for_vblank(dev);
                dpll = I915_READ(dpll_reg);
                if (dpll & DISPLAY_RATE_SELECT_FPA1)
-                       DRM_DEBUG("failed to upclock LVDS!\n");
+                       DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
 
                /* ...and lock them again */
                I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
@@ -3847,7 +3961,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
        int dpll = I915_READ(dpll_reg);
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return;
 
        if (!dev_priv->lvds_downclock_avail)
@@ -3858,7 +3972,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
         * the manual case.
         */
        if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
-               DRM_DEBUG("downclocking LVDS\n");
+               DRM_DEBUG_DRIVER("downclocking LVDS\n");
 
                /* Unlock panel regs */
                I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
@@ -3869,7 +3983,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
                intel_wait_for_vblank(dev);
                dpll = I915_READ(dpll_reg);
                if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
-                       DRM_DEBUG("failed to downclock LVDS!\n");
+                       DRM_DEBUG_DRIVER("failed to downclock LVDS!\n");
 
                /* ...and lock them again */
                I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
@@ -3936,8 +4050,13 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       dev_priv->busy = true;
-       intel_increase_renderclock(dev, true);
+       if (!dev_priv->busy) {
+               dev_priv->busy = true;
+               intel_increase_renderclock(dev, true);
+       } else {
+               mod_timer(&dev_priv->idle_timer, jiffies +
+                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
+       }
 
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                if (!crtc->fb)
@@ -3967,6 +4086,158 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
        kfree(intel_crtc);
 }
 
+struct intel_unpin_work {
+       struct work_struct work;
+       struct drm_device *dev;
+       struct drm_gem_object *obj;
+       struct drm_pending_vblank_event *event;
+       int pending;
+};
+
+static void intel_unpin_work_fn(struct work_struct *__work)
+{
+       struct intel_unpin_work *work =
+               container_of(__work, struct intel_unpin_work, work);
+
+       mutex_lock(&work->dev->struct_mutex);
+       i915_gem_object_unpin(work->obj);
+       drm_gem_object_unreference(work->obj);
+       mutex_unlock(&work->dev->struct_mutex);
+       kfree(work);
+}
+
+void intel_finish_page_flip(struct drm_device *dev, int pipe)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_unpin_work *work;
+       struct drm_i915_gem_object *obj_priv;
+       struct drm_pending_vblank_event *e;
+       struct timeval now;
+       unsigned long flags;
+
+       /* Ignore early vblank irqs */
+       if (intel_crtc == NULL)
+               return;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       work = intel_crtc->unpin_work;
+       if (work == NULL || !work->pending) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               return;
+       }
+
+       intel_crtc->unpin_work = NULL;
+       drm_vblank_put(dev, intel_crtc->pipe);
+
+       if (work->event) {
+               e = work->event;
+               do_gettimeofday(&now);
+               e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);
+               e->event.tv_sec = now.tv_sec;
+               e->event.tv_usec = now.tv_usec;
+               list_add_tail(&e->base.link,
+                             &e->base.file_priv->event_list);
+               wake_up_interruptible(&e->base.file_priv->event_wait);
+       }
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       obj_priv = work->obj->driver_private;
+       if (atomic_dec_and_test(&obj_priv->pending_flip))
+               DRM_WAKEUP(&dev_priv->pending_flip_queue);
+       schedule_work(&work->work);
+}
+
+void intel_prepare_page_flip(struct drm_device *dev, int plane)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc =
+               to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (intel_crtc->unpin_work)
+               intel_crtc->unpin_work->pending = 1;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int intel_crtc_page_flip(struct drm_crtc *crtc,
+                               struct drm_framebuffer *fb,
+                               struct drm_pending_vblank_event *event)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_framebuffer *intel_fb;
+       struct drm_i915_gem_object *obj_priv;
+       struct drm_gem_object *obj;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_unpin_work *work;
+       unsigned long flags;
+       int ret;
+       RING_LOCALS;
+
+       work = kzalloc(sizeof *work, GFP_KERNEL);
+       if (work == NULL)
+               return -ENOMEM;
+
+       mutex_lock(&dev->struct_mutex);
+
+       work->event = event;
+       work->dev = crtc->dev;
+       intel_fb = to_intel_framebuffer(crtc->fb);
+       work->obj = intel_fb->obj;
+       INIT_WORK(&work->work, intel_unpin_work_fn);
+
+       /* We borrow the event spin lock for protecting unpin_work */
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (intel_crtc->unpin_work) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               kfree(work);
+               mutex_unlock(&dev->struct_mutex);
+               return -EBUSY;
+       }
+       intel_crtc->unpin_work = work;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       intel_fb = to_intel_framebuffer(fb);
+       obj = intel_fb->obj;
+
+       ret = intel_pin_and_fence_fb_obj(dev, obj);
+       if (ret != 0) {
+               kfree(work);
+               mutex_unlock(&dev->struct_mutex);
+               return ret;
+       }
+
+       /* Reference the old fb object for the scheduled work. */
+       drm_gem_object_reference(work->obj);
+
+       crtc->fb = fb;
+       i915_gem_object_flush_write_domain(obj);
+       drm_vblank_get(dev, intel_crtc->pipe);
+       obj_priv = obj->driver_private;
+       atomic_inc(&obj_priv->pending_flip);
+
+       BEGIN_LP_RING(4);
+       OUT_RING(MI_DISPLAY_FLIP |
+                MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
+       OUT_RING(fb->pitch);
+       if (IS_I965G(dev)) {
+               OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
+               OUT_RING((fb->width << 16) | fb->height);
+       } else {
+               OUT_RING(obj_priv->gtt_offset);
+               OUT_RING(MI_NOOP);
+       }
+       ADVANCE_LP_RING();
+
+       mutex_unlock(&dev->struct_mutex);
+
+       return 0;
+}
+
 static const struct drm_crtc_helper_funcs intel_helper_funcs = {
        .dpms = intel_crtc_dpms,
        .mode_fixup = intel_crtc_mode_fixup,
@@ -3983,11 +4254,13 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
        .gamma_set = intel_crtc_gamma_set,
        .set_config = drm_crtc_helper_set_config,
        .destroy = intel_crtc_destroy,
+       .page_flip = intel_crtc_page_flip,
 };
 
 
 static void intel_crtc_init(struct drm_device *dev, int pipe)
 {
+       drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc;
        int i;
 
@@ -4010,10 +4283,15 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        intel_crtc->pipe = pipe;
        intel_crtc->plane = pipe;
        if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) {
-               DRM_DEBUG("swapping pipes & planes for FBC\n");
+               DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
                intel_crtc->plane = ((pipe == 0) ? 1 : 0);
        }
 
+       BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
+              dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
+       dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
+       dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
+
        intel_crtc->cursor_addr = 0;
        intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
        drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
@@ -4090,7 +4368,7 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (IS_MOBILE(dev) && !IS_I830(dev))
                intel_lvds_init(dev);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                int found;
 
                if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
@@ -4118,7 +4396,7 @@ static void intel_setup_outputs(struct drm_device *dev)
                if (I915_READ(PCH_DP_D) & DP_DETECTED)
                        intel_dp_init(dev, PCH_DP_D);
 
-       } else if (IS_I9XX(dev)) {
+       } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;
 
                if (I915_READ(SDVOB) & SDVO_DETECTED) {
@@ -4145,10 +4423,10 @@ static void intel_setup_outputs(struct drm_device *dev)
 
                if (SUPPORTS_INTEGRATED_DP(dev) && (I915_READ(DP_D) & DP_DETECTED))
                        intel_dp_init(dev, DP_D);
-       } else
+       } else if (IS_I8XX(dev))
                intel_dvo_init(dev);
 
-       if (IS_I9XX(dev) && IS_MOBILE(dev) && !IS_IGDNG(dev))
+       if (SUPPORTS_TV(dev))
                intel_tv_init(dev);
 
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -4257,7 +4535,7 @@ void intel_init_clock_gating(struct drm_device *dev)
         * Disable clock gating reported to work incorrectly according to the
         * specs, but enable as much else as we can.
         */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                return;
        } else if (IS_G4X(dev)) {
                uint32_t dspclk_gate;
@@ -4291,11 +4569,52 @@ void intel_init_clock_gating(struct drm_device *dev)
                dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
                        DSTATE_DOT_CLOCK_GATING;
                I915_WRITE(D_STATE, dstate);
-       } else if (IS_I855(dev) || IS_I865G(dev)) {
+       } else if (IS_I85X(dev) || IS_I865G(dev)) {
                I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
        } else if (IS_I830(dev)) {
                I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
        }
+
+       /*
+        * GPU can automatically power down the render unit if given a page
+        * to save state.
+        */
+       if (I915_HAS_RC6(dev)) {
+               struct drm_gem_object *pwrctx;
+               struct drm_i915_gem_object *obj_priv;
+               int ret;
+
+               if (dev_priv->pwrctx) {
+                       obj_priv = dev_priv->pwrctx->driver_private;
+               } else {
+                       pwrctx = drm_gem_object_alloc(dev, 4096);
+                       if (!pwrctx) {
+                               DRM_DEBUG("failed to alloc power context, "
+                                         "RC6 disabled\n");
+                               goto out;
+                       }
+
+                       ret = i915_gem_object_pin(pwrctx, 4096);
+                       if (ret) {
+                               DRM_ERROR("failed to pin power context: %d\n",
+                                         ret);
+                               drm_gem_object_unreference(pwrctx);
+                               goto out;
+                       }
+
+                       i915_gem_object_set_to_gtt_domain(pwrctx, 1);
+
+                       dev_priv->pwrctx = pwrctx;
+                       obj_priv = pwrctx->driver_private;
+               }
+
+               I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN);
+               I915_WRITE(MCHBAR_RENDER_STANDBY,
+                          I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
+       }
+
+out:
+       return;
 }
 
 /* Set up chip specific display functions */
@@ -4304,8 +4623,8 @@ static void intel_init_display(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* We always want a DPMS function */
-       if (IS_IGDNG(dev))
-               dev_priv->display.dpms = igdng_crtc_dpms;
+       if (IS_IRONLAKE(dev))
+               dev_priv->display.dpms = ironlake_crtc_dpms;
        else
                dev_priv->display.dpms = i9xx_crtc_dpms;
 
@@ -4324,13 +4643,13 @@ static void intel_init_display(struct drm_device *dev)
        }
 
        /* Returns the core display clock speed */
-       if (IS_I945G(dev))
+       if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev)))
                dev_priv->display.get_display_clock_speed =
                        i945_get_display_clock_speed;
        else if (IS_I915G(dev))
                dev_priv->display.get_display_clock_speed =
                        i915_get_display_clock_speed;
-       else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev))
+       else if (IS_I945GM(dev) || IS_845G(dev) || IS_PINEVIEW_M(dev))
                dev_priv->display.get_display_clock_speed =
                        i9xx_misc_get_display_clock_speed;
        else if (IS_I915GM(dev))
@@ -4339,7 +4658,7 @@ static void intel_init_display(struct drm_device *dev)
        else if (IS_I865G(dev))
                dev_priv->display.get_display_clock_speed =
                        i865_get_display_clock_speed;
-       else if (IS_I855(dev))
+       else if (IS_I85X(dev))
                dev_priv->display.get_display_clock_speed =
                        i855_get_display_clock_speed;
        else /* 852, 830 */
@@ -4347,7 +4666,7 @@ static void intel_init_display(struct drm_device *dev)
                        i830_get_display_clock_speed;
 
        /* For FIFO watermark updates */
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                dev_priv->display.update_wm = NULL;
        else if (IS_G4X(dev))
                dev_priv->display.update_wm = g4x_update_wm;
@@ -4403,7 +4722,7 @@ void intel_modeset_init(struct drm_device *dev)
                num_pipe = 2;
        else
                num_pipe = 1;
-       DRM_DEBUG("%d display pipe%s available.\n",
+       DRM_DEBUG_KMS("%d display pipe%s available.\n",
                  num_pipe, num_pipe > 1 ? "s" : "");
 
        if (IS_I85X(dev))
@@ -4422,6 +4741,15 @@ void intel_modeset_init(struct drm_device *dev)
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
                    (unsigned long)dev);
+
+       intel_setup_overlay(dev);
+
+       if (IS_PINEVIEW(dev) && !intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
+                                                       dev_priv->fsb_freq,
+                                                       dev_priv->mem_freq))
+               DRM_INFO("failed to find known CxSR latency "
+                        "(found fsb freq %d, mem freq %d), disabling CxSR\n",
+                        dev_priv->fsb_freq, dev_priv->mem_freq);
 }
 
 void intel_modeset_cleanup(struct drm_device *dev)
@@ -4445,11 +4773,21 @@ void intel_modeset_cleanup(struct drm_device *dev)
        intel_increase_renderclock(dev, false);
        del_timer_sync(&dev_priv->idle_timer);
 
-       mutex_unlock(&dev->struct_mutex);
-
        if (dev_priv->display.disable_fbc)
                dev_priv->display.disable_fbc(dev);
 
+       if (dev_priv->pwrctx) {
+               struct drm_i915_gem_object *obj_priv;
+
+               obj_priv = dev_priv->pwrctx->driver_private;
+               I915_WRITE(PWRCTXA, obj_priv->gtt_offset &~ PWRCTX_EN);
+               I915_READ(PWRCTXA);
+               i915_gem_object_unpin(dev_priv->pwrctx);
+               drm_gem_object_unreference(dev_priv->pwrctx);
+       }
+
+       mutex_unlock(&dev->struct_mutex);
+
        drm_mode_config_cleanup(dev);
 }
 
index d834475..4e7aa8b 100644 (file)
@@ -33,7 +33,8 @@
 #include "intel_drv.h"
 #include "i915_drm.h"
 #include "i915_drv.h"
-#include "intel_dp.h"
+#include "drm_dp_helper.h"
+
 
 #define DP_LINK_STATUS_SIZE    6
 #define DP_LINK_CHECK_TIMEOUT  (10 * 1000)
@@ -223,8 +224,8 @@ intel_dp_aux_ch(struct intel_output *intel_output,
         */
        if (IS_eDP(intel_output))
                aux_clock_divider = 225; /* eDP input clock at 450Mhz */
-       else if (IS_IGDNG(dev))
-               aux_clock_divider = 62; /* IGDNG: input clock fixed at 125Mhz */
+       else if (IS_IRONLAKE(dev))
+               aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */
        else
                aux_clock_divider = intel_hrawclk(dev) / 2;
 
@@ -282,7 +283,7 @@ intel_dp_aux_ch(struct intel_output *intel_output,
        /* Timeouts occur when the device isn't connected, so they're
         * "normal" -- don't fill the kernel log with these */
        if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
-               DRM_DEBUG("dp_aux_ch timeout status 0x%08x\n", status);
+               DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
                return -ETIMEDOUT;
        }
 
@@ -382,17 +383,77 @@ intel_dp_aux_native_read(struct intel_output *intel_output,
 }
 
 static int
-intel_dp_i2c_aux_ch(struct i2c_adapter *adapter,
-                   uint8_t *send, int send_bytes,
-                   uint8_t *recv, int recv_bytes)
+intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+                   uint8_t write_byte, uint8_t *read_byte)
 {
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
        struct intel_dp_priv *dp_priv = container_of(adapter,
                                                     struct intel_dp_priv,
                                                     adapter);
        struct intel_output *intel_output = dp_priv->intel_output;
+       uint16_t address = algo_data->address;
+       uint8_t msg[5];
+       uint8_t reply[2];
+       int msg_bytes;
+       int reply_bytes;
+       int ret;
+
+       /* Set up the command byte */
+       if (mode & MODE_I2C_READ)
+               msg[0] = AUX_I2C_READ << 4;
+       else
+               msg[0] = AUX_I2C_WRITE << 4;
+
+       if (!(mode & MODE_I2C_STOP))
+               msg[0] |= AUX_I2C_MOT << 4;
 
-       return intel_dp_aux_ch(intel_output,
-                              send, send_bytes, recv, recv_bytes);
+       msg[1] = address >> 8;
+       msg[2] = address;
+
+       switch (mode) {
+       case MODE_I2C_WRITE:
+               msg[3] = 0;
+               msg[4] = write_byte;
+               msg_bytes = 5;
+               reply_bytes = 1;
+               break;
+       case MODE_I2C_READ:
+               msg[3] = 0;
+               msg_bytes = 4;
+               reply_bytes = 2;
+               break;
+       default:
+               msg_bytes = 3;
+               reply_bytes = 1;
+               break;
+       }
+
+       for (;;) {
+         ret = intel_dp_aux_ch(intel_output,
+                               msg, msg_bytes,
+                               reply, reply_bytes);
+               if (ret < 0) {
+                       DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
+                       return ret;
+               }
+               switch (reply[0] & AUX_I2C_REPLY_MASK) {
+               case AUX_I2C_REPLY_ACK:
+                       if (mode == MODE_I2C_READ) {
+                               *read_byte = reply[1];
+                       }
+                       return reply_bytes - 1;
+               case AUX_I2C_REPLY_NACK:
+                       DRM_DEBUG_KMS("aux_ch nack\n");
+                       return -EREMOTEIO;
+               case AUX_I2C_REPLY_DEFER:
+                       DRM_DEBUG_KMS("aux_ch defer\n");
+                       udelay(100);
+                       break;
+               default:
+                       DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]);
+                       return -EREMOTEIO;
+               }
+       }
 }
 
 static int
@@ -435,7 +496,8 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
                                dp_priv->link_bw = bws[clock];
                                dp_priv->lane_count = lane_count;
                                adjusted_mode->clock = intel_dp_link_clock(dp_priv->link_bw);
-                               DRM_DEBUG("Display port link bw %02x lane count %d clock %d\n",
+                               DRM_DEBUG_KMS("Display port link bw %02x lane "
+                                               "count %d clock %d\n",
                                       dp_priv->link_bw, dp_priv->lane_count,
                                       adjusted_mode->clock);
                                return true;
@@ -514,7 +576,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
        intel_dp_compute_m_n(3, lane_count,
                             mode->clock, adjusted_mode->clock, &m_n);
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                if (intel_crtc->pipe == 0) {
                        I915_WRITE(TRANSA_DATA_M1,
                                   ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
@@ -606,23 +668,23 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
        }
 }
 
-static void igdng_edp_backlight_on (struct drm_device *dev)
+static void ironlake_edp_backlight_on (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pp;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
        pp = I915_READ(PCH_PP_CONTROL);
        pp |= EDP_BLC_ENABLE;
        I915_WRITE(PCH_PP_CONTROL, pp);
 }
 
-static void igdng_edp_backlight_off (struct drm_device *dev)
+static void ironlake_edp_backlight_off (struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pp;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
        pp = I915_READ(PCH_PP_CONTROL);
        pp &= ~EDP_BLC_ENABLE;
        I915_WRITE(PCH_PP_CONTROL, pp);
@@ -641,13 +703,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
                if (dp_reg & DP_PORT_EN) {
                        intel_dp_link_down(intel_output, dp_priv->DP);
                        if (IS_eDP(intel_output))
-                               igdng_edp_backlight_off(dev);
+                               ironlake_edp_backlight_off(dev);
                }
        } else {
                if (!(dp_reg & DP_PORT_EN)) {
                        intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
                        if (IS_eDP(intel_output))
-                               igdng_edp_backlight_on(dev);
+                               ironlake_edp_backlight_on(dev);
                }
        }
        dp_priv->dpms_mode = mode;
@@ -1010,7 +1072,7 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_dp_priv *dp_priv = intel_output->dev_priv;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
 
        if (IS_eDP(intel_output)) {
                DP &= ~DP_PLL_ENABLE;
@@ -1071,7 +1133,7 @@ intel_dp_check_link_status(struct intel_output *intel_output)
 }
 
 static enum drm_connector_status
-igdng_dp_detect(struct drm_connector *connector)
+ironlake_dp_detect(struct drm_connector *connector)
 {
        struct intel_output *intel_output = to_intel_output(connector);
        struct intel_dp_priv *dp_priv = intel_output->dev_priv;
@@ -1106,8 +1168,8 @@ intel_dp_detect(struct drm_connector *connector)
 
        dp_priv->has_audio = false;
 
-       if (IS_IGDNG(dev))
-               return igdng_dp_detect(connector);
+       if (IS_IRONLAKE(dev))
+               return ironlake_dp_detect(connector);
 
        temp = I915_READ(PORT_HOTPLUG_EN);
 
@@ -1227,7 +1289,53 @@ intel_dp_hot_plug(struct intel_output *intel_output)
        if (dp_priv->dpms_mode == DRM_MODE_DPMS_ON)
                intel_dp_check_link_status(intel_output);
 }
-
+/*
+ * Enumerate the child dev array parsed from VBT to check whether
+ * the given DP is present.
+ * If it is present, return 1.
+ * If it is not present, return false.
+ * If no child dev is parsed from VBT, it is assumed that the given
+ * DP is present.
+ */
+static int dp_is_present_in_vbt(struct drm_device *dev, int dp_reg)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct child_device_config *p_child;
+       int i, dp_port, ret;
+
+       if (!dev_priv->child_dev_num)
+               return 1;
+
+       dp_port = 0;
+       if (dp_reg == DP_B || dp_reg == PCH_DP_B)
+               dp_port = PORT_IDPB;
+       else if (dp_reg == DP_C || dp_reg == PCH_DP_C)
+               dp_port = PORT_IDPC;
+       else if (dp_reg == DP_D || dp_reg == PCH_DP_D)
+               dp_port = PORT_IDPD;
+
+       ret = 0;
+       for (i = 0; i < dev_priv->child_dev_num; i++) {
+               p_child = dev_priv->child_dev + i;
+               /*
+                * If the device type is not DP, continue.
+                */
+               if (p_child->device_type != DEVICE_TYPE_DP &&
+                       p_child->device_type != DEVICE_TYPE_eDP)
+                       continue;
+               /* Find the eDP port */
+               if (dp_reg == DP_A && p_child->device_type == DEVICE_TYPE_eDP) {
+                       ret = 1;
+                       break;
+               }
+               /* Find the DP port */
+               if (p_child->dvo_port == dp_port) {
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
 void
 intel_dp_init(struct drm_device *dev, int output_reg)
 {
@@ -1237,6 +1345,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
        struct intel_dp_priv *dp_priv;
        const char *name = NULL;
 
+       if (!dp_is_present_in_vbt(dev, output_reg)) {
+               DRM_DEBUG_KMS("DP is not present. Ignore it\n");
+               return;
+       }
        intel_output = kcalloc(sizeof(struct intel_output) + 
                               sizeof(struct intel_dp_priv), 1, GFP_KERNEL);
        if (!intel_output)
@@ -1254,11 +1366,11 @@ intel_dp_init(struct drm_device *dev, int output_reg)
        else
                intel_output->type = INTEL_OUTPUT_DISPLAYPORT;
 
-       if (output_reg == DP_B)
+       if (output_reg == DP_B || output_reg == PCH_DP_B)
                intel_output->clone_mask = (1 << INTEL_DP_B_CLONE_BIT);
-       else if (output_reg == DP_C)
+       else if (output_reg == DP_C || output_reg == PCH_DP_C)
                intel_output->clone_mask = (1 << INTEL_DP_C_CLONE_BIT);
-       else if (output_reg == DP_D)
+       else if (output_reg == DP_D || output_reg == PCH_DP_D)
                intel_output->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
 
        if (IS_eDP(intel_output)) {
index ef61fe9..a51573d 100644 (file)
@@ -110,6 +110,32 @@ struct intel_output {
        int clone_mask;
 };
 
+struct intel_crtc;
+struct intel_overlay {
+       struct drm_device *dev;
+       struct intel_crtc *crtc;
+       struct drm_i915_gem_object *vid_bo;
+       struct drm_i915_gem_object *old_vid_bo;
+       int active;
+       int pfit_active;
+       u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
+       u32 color_key;
+       u32 brightness, contrast, saturation;
+       u32 old_xscale, old_yscale;
+       /* register access */
+       u32 flip_addr;
+       struct drm_i915_gem_object *reg_bo;
+       void *virt_addr;
+       /* flip handling */
+       uint32_t last_flip_req;
+       int hw_wedged;
+#define HW_WEDGED              1
+#define NEEDS_WAIT_FOR_FLIP    2
+#define RELEASE_OLD_VID                3
+#define SWITCH_OFF_STAGE_1     4
+#define SWITCH_OFF_STAGE_2     5
+};
+
 struct intel_crtc {
        struct drm_crtc base;
        enum pipe pipe;
@@ -121,6 +147,8 @@ struct intel_crtc {
        bool busy; /* is scanout buffer being updated frequently? */
        struct timer_list idle_timer;
        bool lowfreq_avail;
+       struct intel_overlay *overlay;
+       struct intel_unpin_work *unpin_work;
 };
 
 #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
@@ -134,6 +162,8 @@ void intel_i2c_destroy(struct i2c_adapter *adapter);
 int intel_ddc_get_modes(struct intel_output *intel_output);
 extern bool intel_ddc_probe(struct intel_output *intel_output);
 void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
+void intel_i2c_reset_gmbus(struct drm_device *dev);
+
 extern void intel_crt_init(struct drm_device *dev);
 extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
 extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
@@ -148,6 +178,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
 extern void intel_edp_link_config (struct intel_output *, int *, int *);
 
 
+extern int intel_panel_fitter_pipe (struct drm_device *dev);
 extern void intel_crtc_load_lut(struct drm_crtc *crtc);
 extern void intel_encoder_prepare (struct drm_encoder *encoder);
 extern void intel_encoder_commit (struct drm_encoder *encoder);
@@ -177,10 +208,23 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
                                    u16 blue, int regno);
 extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
                                    u16 *blue, int regno);
+extern void intel_init_clock_gating(struct drm_device *dev);
 
 extern int intel_framebuffer_create(struct drm_device *dev,
                                    struct drm_mode_fb_cmd *mode_cmd,
                                    struct drm_framebuffer **fb,
                                    struct drm_gem_object *obj);
 
+extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
+extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
+
+extern void intel_setup_overlay(struct drm_device *dev);
+extern void intel_cleanup_overlay(struct drm_device *dev);
+extern int intel_overlay_switch_off(struct intel_overlay *overlay);
+extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
+                                               int interruptible);
+extern int intel_overlay_put_image(struct drm_device *dev, void *data,
+                                  struct drm_file *file_priv);
+extern int intel_overlay_attrs(struct drm_device *dev, void *data,
+                              struct drm_file *file_priv);
 #endif /* __INTEL_DRV_H__ */
index 2b0fe54..371d753 100644 (file)
@@ -70,7 +70,7 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
 
 
 /**
- * Curretly it is assumed that the old framebuffer is reused.
+ * Currently it is assumed that the old framebuffer is reused.
  *
  * LOCKING
  * caller should hold the mode config lock.
@@ -230,8 +230,9 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
        par->intel_fb = intel_fb;
 
        /* To allow resizeing without swapping buffers */
-       DRM_DEBUG("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
-                 intel_fb->base.height, obj_priv->gtt_offset, fbo);
+       DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n",
+                       intel_fb->base.width, intel_fb->base.height,
+                       obj_priv->gtt_offset, fbo);
 
        mutex_unlock(&dev->struct_mutex);
        return 0;
@@ -249,7 +250,7 @@ int intelfb_probe(struct drm_device *dev)
 {
        int ret;
 
-       DRM_DEBUG("\n");
+       DRM_DEBUG_KMS("\n");
        ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create);
        return ret;
 }
index c33451a..f04dbbe 100644 (file)
@@ -82,7 +82,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
        /* HW workaround, need to toggle enable bit off and on for 12bpc, but
         * we do this anyway which shows more stable in testing.
         */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE);
                POSTING_READ(hdmi_priv->sdvox_reg);
        }
@@ -99,7 +99,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
        /* HW workaround, need to write this twice for issue that may result
         * in first write getting masked.
         */
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                I915_WRITE(hdmi_priv->sdvox_reg, temp);
                POSTING_READ(hdmi_priv->sdvox_reg);
        }
@@ -225,7 +225,52 @@ static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
        .destroy = intel_hdmi_enc_destroy,
 };
 
-
+/*
+ * Enumerate the child dev array parsed from VBT to check whether
+ * the given HDMI is present.
+ * If it is present, return 1.
+ * If it is not present, return false.
+ * If no child dev is parsed from VBT, it assumes that the given
+ * HDMI is present.
+ */
+static int hdmi_is_present_in_vbt(struct drm_device *dev, int hdmi_reg)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct child_device_config *p_child;
+       int i, hdmi_port, ret;
+
+       if (!dev_priv->child_dev_num)
+               return 1;
+
+       if (hdmi_reg == SDVOB)
+               hdmi_port = DVO_B;
+       else if (hdmi_reg == SDVOC)
+               hdmi_port = DVO_C;
+       else if (hdmi_reg == HDMIB)
+               hdmi_port = DVO_B;
+       else if (hdmi_reg == HDMIC)
+               hdmi_port = DVO_C;
+       else if (hdmi_reg == HDMID)
+               hdmi_port = DVO_D;
+       else
+               return 0;
+
+       ret = 0;
+       for (i = 0; i < dev_priv->child_dev_num; i++) {
+               p_child = dev_priv->child_dev + i;
+               /*
+                * If the device type is not HDMI, continue.
+                */
+               if (p_child->device_type != DEVICE_TYPE_HDMI)
+                       continue;
+               /* Find the HDMI port */
+               if (p_child->dvo_port == hdmi_port) {
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
 void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -233,6 +278,10 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
        struct intel_output *intel_output;
        struct intel_hdmi_priv *hdmi_priv;
 
+       if (!hdmi_is_present_in_vbt(dev, sdvox_reg)) {
+               DRM_DEBUG_KMS("HDMI is not present. Ignored it \n");
+               return;
+       }
        intel_output = kcalloc(sizeof(struct intel_output) +
                               sizeof(struct intel_hdmi_priv), 1, GFP_KERNEL);
        if (!intel_output)
index c7eab72..8673c73 100644 (file)
@@ -39,7 +39,7 @@ void intel_i2c_quirk_set(struct drm_device *dev, bool enable)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        /* When using bit bashing for I2C, this bit needs to be set to 1 */
-       if (!IS_IGD(dev))
+       if (!IS_PINEVIEW(dev))
                return;
        if (enable)
                I915_WRITE(DSPCLK_GATE_D,
@@ -118,6 +118,23 @@ static void set_data(void *data, int state_high)
        udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
 }
 
+/* Clears the GMBUS setup.  Our driver doesn't make use of the GMBUS I2C
+ * engine, but if the BIOS leaves it enabled, then that can break our use
+ * of the bit-banging I2C interfaces.  This is notably the case with the
+ * Mac Mini in EFI mode.
+ */
+void
+intel_i2c_reset_gmbus(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (IS_IRONLAKE(dev)) {
+               I915_WRITE(PCH_GMBUS0, 0);
+       } else {
+               I915_WRITE(GMBUS0, 0);
+       }
+}
+
 /**
  * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
  * @dev: DRM device
@@ -168,6 +185,8 @@ struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg,
        if(i2c_bit_add_bus(&chan->adapter))
                goto out_free;
 
+       intel_i2c_reset_gmbus(dev);
+
        /* JJJ:  raise SCL and SDA? */
        intel_i2c_quirk_set(dev, true);
        set_data(chan, 1);
index 05598ae..3118ce2 100644 (file)
@@ -56,7 +56,7 @@ 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, reg;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                reg = BLC_PWM_CPU_CTL;
        else
                reg = BLC_PWM_CTL;
@@ -74,7 +74,7 @@ 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))
+       if (IS_IRONLAKE(dev))
                reg = BLC_PWM_PCH_CTL2;
        else
                reg = BLC_PWM_CTL;
@@ -91,7 +91,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 pp_status, ctl_reg, status_reg;
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                ctl_reg = PCH_PP_CONTROL;
                status_reg = PCH_PP_STATUS;
        } else {
@@ -137,7 +137,7 @@ static void intel_lvds_save(struct drm_connector *connector)
        u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
        u32 pwm_ctl_reg;
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                pp_on_reg = PCH_PP_ON_DELAYS;
                pp_off_reg = PCH_PP_OFF_DELAYS;
                pp_ctl_reg = PCH_PP_CONTROL;
@@ -174,7 +174,7 @@ static void intel_lvds_restore(struct drm_connector *connector)
        u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
        u32 pwm_ctl_reg;
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                pp_on_reg = PCH_PP_ON_DELAYS;
                pp_off_reg = PCH_PP_OFF_DELAYS;
                pp_ctl_reg = PCH_PP_CONTROL;
@@ -297,7 +297,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
        }
 
        /* full screen scale for now */
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                goto out;
 
        /* 965+ wants fuzzy fitting */
@@ -327,7 +327,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
         * to register description and PRM.
         * Change the value here to see the borders for debugging
         */
-       if (!IS_IGDNG(dev)) {
+       if (!IS_IRONLAKE(dev)) {
                I915_WRITE(BCLRPAT_A, 0);
                I915_WRITE(BCLRPAT_B, 0);
        }
@@ -548,7 +548,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                reg = BLC_PWM_CPU_CTL;
        else
                reg = BLC_PWM_CTL;
@@ -587,7 +587,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
         * settings.
         */
 
-       if (IS_IGDNG(dev))
+       if (IS_IRONLAKE(dev))
                return;
 
        /*
@@ -899,7 +899,7 @@ static int intel_lid_present(void)
 
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
                                ACPI_UINT32_MAX,
-                               check_lid_device, &lid_present, NULL);
+                               check_lid_device, NULL, &lid_present, NULL);
 
        return lid_present;
 }
@@ -913,6 +913,101 @@ static int intel_lid_present(void)
 }
 #endif
 
+/**
+ * intel_find_lvds_downclock - find the reduced downclock for LVDS in EDID
+ * @dev: drm device
+ * @connector: LVDS connector
+ *
+ * Find the reduced downclock for LVDS in EDID.
+ */
+static void intel_find_lvds_downclock(struct drm_device *dev,
+                               struct drm_connector *connector)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_display_mode *scan, *panel_fixed_mode;
+       int temp_downclock;
+
+       panel_fixed_mode = dev_priv->panel_fixed_mode;
+       temp_downclock = panel_fixed_mode->clock;
+
+       mutex_lock(&dev->mode_config.mutex);
+       list_for_each_entry(scan, &connector->probed_modes, head) {
+               /*
+                * If one mode has the same resolution with the fixed_panel
+                * mode while they have the different refresh rate, it means
+                * that the reduced downclock is found for the LVDS. In such
+                * case we can set the different FPx0/1 to dynamically select
+                * between low and high frequency.
+                */
+               if (scan->hdisplay == panel_fixed_mode->hdisplay &&
+                       scan->hsync_start == panel_fixed_mode->hsync_start &&
+                       scan->hsync_end == panel_fixed_mode->hsync_end &&
+                       scan->htotal == panel_fixed_mode->htotal &&
+                       scan->vdisplay == panel_fixed_mode->vdisplay &&
+                       scan->vsync_start == panel_fixed_mode->vsync_start &&
+                       scan->vsync_end == panel_fixed_mode->vsync_end &&
+                       scan->vtotal == panel_fixed_mode->vtotal) {
+                       if (scan->clock < temp_downclock) {
+                               /*
+                                * The downclock is already found. But we
+                                * expect to find the lower downclock.
+                                */
+                               temp_downclock = scan->clock;
+                       }
+               }
+       }
+       mutex_unlock(&dev->mode_config.mutex);
+       if (temp_downclock < panel_fixed_mode->clock) {
+               /* We found the downclock for LVDS. */
+               dev_priv->lvds_downclock_avail = 1;
+               dev_priv->lvds_downclock = temp_downclock;
+               DRM_DEBUG_KMS("LVDS downclock is found in EDID. "
+                               "Normal clock %dKhz, downclock %dKhz\n",
+                               panel_fixed_mode->clock, temp_downclock);
+       }
+       return;
+}
+
+/*
+ * Enumerate the child dev array parsed from VBT to check whether
+ * the LVDS is present.
+ * If it is present, return 1.
+ * If it is not present, return false.
+ * If no child dev is parsed from VBT, it assumes that the LVDS is present.
+ * Note: The addin_offset should also be checked for LVDS panel.
+ * Only when it is non-zero, it is assumed that it is present.
+ */
+static int lvds_is_present_in_vbt(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct child_device_config *p_child;
+       int i, ret;
+
+       if (!dev_priv->child_dev_num)
+               return 1;
+
+       ret = 0;
+       for (i = 0; i < dev_priv->child_dev_num; i++) {
+               p_child = dev_priv->child_dev + i;
+               /*
+                * If the device type is not LFP, continue.
+                * If the device type is 0x22, it is also regarded as LFP.
+                */
+               if (p_child->device_type != DEVICE_TYPE_INT_LFP &&
+                       p_child->device_type != DEVICE_TYPE_LFP)
+                       continue;
+
+               /* The addin_offset should be checked. Only when it is
+                * non-zero, it is regarded as present.
+                */
+               if (p_child->addin_offset) {
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
+
 /**
  * intel_lvds_init - setup LVDS connectors on this device
  * @dev: drm device
@@ -936,21 +1031,20 @@ void intel_lvds_init(struct drm_device *dev)
        if (dmi_check_system(intel_no_lvds))
                return;
 
-       /* Assume that any device without an ACPI LID device also doesn't
-        * have an integrated LVDS.  We would be better off parsing the BIOS
-        * to get a reliable indicator, but that code isn't written yet.
-        *
-        * In the case of all-in-one desktops using LVDS that we've seen,
-        * they're using SDVO LVDS.
+       /*
+        * Assume LVDS is present if there's an ACPI lid device or if the
+        * device is present in the VBT.
         */
-       if (!intel_lid_present())
+       if (!lvds_is_present_in_vbt(dev) && !intel_lid_present()) {
+               DRM_DEBUG_KMS("LVDS is not present in VBT and no lid detected\n");
                return;
+       }
 
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
                        return;
                if (dev_priv->edp_support) {
-                       DRM_DEBUG("disable LVDS for eDP support\n");
+                       DRM_DEBUG_KMS("disable LVDS for eDP support\n");
                        return;
                }
                gpio = PCH_GPIOC;
@@ -1023,6 +1117,7 @@ void intel_lvds_init(struct drm_device *dev)
                        dev_priv->panel_fixed_mode =
                                drm_mode_duplicate(dev, scan);
                        mutex_unlock(&dev->mode_config.mutex);
+                       intel_find_lvds_downclock(dev, connector);
                        goto out;
                }
                mutex_unlock(&dev->mode_config.mutex);
@@ -1047,8 +1142,8 @@ void intel_lvds_init(struct drm_device *dev)
         * correct mode.
         */
 
-       /* IGDNG: FIXME if still fail, not try pipe mode now */
-       if (IS_IGDNG(dev))
+       /* Ironlake: FIXME if still fail, not try pipe mode now */
+       if (IS_IRONLAKE(dev))
                goto failed;
 
        lvds = I915_READ(LVDS);
@@ -1069,7 +1164,7 @@ void intel_lvds_init(struct drm_device *dev)
                goto failed;
 
 out:
-       if (IS_IGDNG(dev)) {
+       if (IS_IRONLAKE(dev)) {
                u32 pwm;
                /* make sure PWM is enabled */
                pwm = I915_READ(BLC_PWM_CPU_CTL2);
@@ -1082,7 +1177,7 @@ out:
        }
        dev_priv->lid_notifier.notifier_call = intel_lid_notify;
        if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
-               DRM_DEBUG("lid notifier registration failed\n");
+               DRM_DEBUG_KMS("lid notifier registration failed\n");
                dev_priv->lid_notifier.notifier_call = NULL;
        }
        drm_sysfs_connector_add(connector);
@@ -1093,5 +1188,6 @@ failed:
        if (intel_output->ddc_bus)
                intel_i2c_destroy(intel_output->ddc_bus);
        drm_connector_cleanup(connector);
+       drm_encoder_cleanup(encoder);
        kfree(intel_output);
 }
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
new file mode 100644 (file)
index 0000000..2639591
--- /dev/null
@@ -0,0 +1,1416 @@
+/*
+ * Copyright © 2009
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *    Daniel Vetter <daniel@ffwll.ch>
+ *
+ * Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "i915_reg.h"
+#include "intel_drv.h"
+
+/* Limits for overlay size. According to intel doc, the real limits are:
+ * Y width: 4095, UV width (planar): 2047, Y height: 2047,
+ * UV width (planar): * 1023. But the xorg thinks 2048 for height and width. Use
+ * the mininum of both.  */
+#define IMAGE_MAX_WIDTH                2048
+#define IMAGE_MAX_HEIGHT       2046 /* 2 * 1023 */
+/* on 830 and 845 these large limits result in the card hanging */
+#define IMAGE_MAX_WIDTH_LEGACY 1024
+#define IMAGE_MAX_HEIGHT_LEGACY        1088
+
+/* overlay register definitions */
+/* OCMD register */
+#define OCMD_TILED_SURFACE     (0x1<<19)
+#define OCMD_MIRROR_MASK       (0x3<<17)
+#define OCMD_MIRROR_MODE       (0x3<<17)
+#define OCMD_MIRROR_HORIZONTAL (0x1<<17)
+#define OCMD_MIRROR_VERTICAL   (0x2<<17)
+#define OCMD_MIRROR_BOTH       (0x3<<17)
+#define OCMD_BYTEORDER_MASK    (0x3<<14) /* zero for YUYV or FOURCC YUY2 */
+#define OCMD_UV_SWAP           (0x1<<14) /* YVYU */
+#define OCMD_Y_SWAP            (0x2<<14) /* UYVY or FOURCC UYVY */
+#define OCMD_Y_AND_UV_SWAP     (0x3<<14) /* VYUY */
+#define OCMD_SOURCE_FORMAT_MASK (0xf<<10)
+#define OCMD_RGB_888           (0x1<<10) /* not in i965 Intel docs */
+#define OCMD_RGB_555           (0x2<<10) /* not in i965 Intel docs */
+#define OCMD_RGB_565           (0x3<<10) /* not in i965 Intel docs */
+#define OCMD_YUV_422_PACKED    (0x8<<10)
+#define OCMD_YUV_411_PACKED    (0x9<<10) /* not in i965 Intel docs */
+#define OCMD_YUV_420_PLANAR    (0xc<<10)
+#define OCMD_YUV_422_PLANAR    (0xd<<10)
+#define OCMD_YUV_410_PLANAR    (0xe<<10) /* also 411 */
+#define OCMD_TVSYNCFLIP_PARITY (0x1<<9)
+#define OCMD_TVSYNCFLIP_ENABLE (0x1<<7)
+#define OCMD_BUF_TYPE_MASK     (Ox1<<5)
+#define OCMD_BUF_TYPE_FRAME    (0x0<<5)
+#define OCMD_BUF_TYPE_FIELD    (0x1<<5)
+#define OCMD_TEST_MODE         (0x1<<4)
+#define OCMD_BUFFER_SELECT     (0x3<<2)
+#define OCMD_BUFFER0           (0x0<<2)
+#define OCMD_BUFFER1           (0x1<<2)
+#define OCMD_FIELD_SELECT      (0x1<<2)
+#define OCMD_FIELD0            (0x0<<1)
+#define OCMD_FIELD1            (0x1<<1)
+#define OCMD_ENABLE            (0x1<<0)
+
+/* OCONFIG register */
+#define OCONF_PIPE_MASK                (0x1<<18)
+#define OCONF_PIPE_A           (0x0<<18)
+#define OCONF_PIPE_B           (0x1<<18)
+#define OCONF_GAMMA2_ENABLE    (0x1<<16)
+#define OCONF_CSC_MODE_BT601   (0x0<<5)
+#define OCONF_CSC_MODE_BT709   (0x1<<5)
+#define OCONF_CSC_BYPASS       (0x1<<4)
+#define OCONF_CC_OUT_8BIT      (0x1<<3)
+#define OCONF_TEST_MODE                (0x1<<2)
+#define OCONF_THREE_LINE_BUFFER        (0x1<<0)
+#define OCONF_TWO_LINE_BUFFER  (0x0<<0)
+
+/* DCLRKM (dst-key) register */
+#define DST_KEY_ENABLE         (0x1<<31)
+#define CLK_RGB24_MASK         0x0
+#define CLK_RGB16_MASK         0x070307
+#define CLK_RGB15_MASK         0x070707
+#define CLK_RGB8I_MASK         0xffffff
+
+#define RGB16_TO_COLORKEY(c) \
+       (((c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x001F) << 3))
+#define RGB15_TO_COLORKEY(c) \
+       (((c & 0x7c00) << 9) | ((c & 0x03E0) << 6) | ((c & 0x001F) << 3))
+
+/* overlay flip addr flag */
+#define OFC_UPDATE             0x1
+
+/* polyphase filter coefficients */
+#define N_HORIZ_Y_TAPS          5
+#define N_VERT_Y_TAPS           3
+#define N_HORIZ_UV_TAPS         3
+#define N_VERT_UV_TAPS          3
+#define N_PHASES                17
+#define MAX_TAPS                5
+
+/* memory bufferd overlay registers */
+struct overlay_registers {
+    u32 OBUF_0Y;
+    u32 OBUF_1Y;
+    u32 OBUF_0U;
+    u32 OBUF_0V;
+    u32 OBUF_1U;
+    u32 OBUF_1V;
+    u32 OSTRIDE;
+    u32 YRGB_VPH;
+    u32 UV_VPH;
+    u32 HORZ_PH;
+    u32 INIT_PHS;
+    u32 DWINPOS;
+    u32 DWINSZ;
+    u32 SWIDTH;
+    u32 SWIDTHSW;
+    u32 SHEIGHT;
+    u32 YRGBSCALE;
+    u32 UVSCALE;
+    u32 OCLRC0;
+    u32 OCLRC1;
+    u32 DCLRKV;
+    u32 DCLRKM;
+    u32 SCLRKVH;
+    u32 SCLRKVL;
+    u32 SCLRKEN;
+    u32 OCONFIG;
+    u32 OCMD;
+    u32 RESERVED1; /* 0x6C */
+    u32 OSTART_0Y;
+    u32 OSTART_1Y;
+    u32 OSTART_0U;
+    u32 OSTART_0V;
+    u32 OSTART_1U;
+    u32 OSTART_1V;
+    u32 OTILEOFF_0Y;
+    u32 OTILEOFF_1Y;
+    u32 OTILEOFF_0U;
+    u32 OTILEOFF_0V;
+    u32 OTILEOFF_1U;
+    u32 OTILEOFF_1V;
+    u32 FASTHSCALE; /* 0xA0 */
+    u32 UVSCALEV; /* 0xA4 */
+    u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */
+    u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */
+    u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
+    u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */
+    u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
+    u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */
+    u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
+    u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */
+    u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
+};
+
+/* overlay flip addr flag */
+#define OFC_UPDATE             0x1
+
+#define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev))
+#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev))
+
+
+static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
+{
+        drm_i915_private_t *dev_priv = overlay->dev->dev_private;
+       struct overlay_registers *regs;
+
+       /* no recursive mappings */
+       BUG_ON(overlay->virt_addr);
+
+       if (OVERLAY_NONPHYSICAL(overlay->dev)) {
+               regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+                               overlay->reg_bo->gtt_offset);
+
+               if (!regs) {
+                       DRM_ERROR("failed to map overlay regs in GTT\n");
+                       return NULL;
+               }
+       } else
+               regs = overlay->reg_bo->phys_obj->handle->vaddr;
+
+       return overlay->virt_addr = regs;
+}
+
+static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay)
+{
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+
+       if (OVERLAY_NONPHYSICAL(overlay->dev))
+               io_mapping_unmap_atomic(overlay->virt_addr);
+
+       overlay->virt_addr = NULL;
+
+       I915_READ(OVADD); /* flush wc cashes */
+
+       return;
+}
+
+/* overlay needs to be disable in OCMD reg */
+static int intel_overlay_on(struct intel_overlay *overlay)
+{
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       int ret;
+       RING_LOCALS;
+
+       BUG_ON(overlay->active);
+
+       overlay->active = 1;
+       overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP;
+
+       BEGIN_LP_RING(6);
+       OUT_RING(MI_FLUSH);
+       OUT_RING(MI_NOOP);
+       OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON);
+       OUT_RING(overlay->flip_addr | OFC_UPDATE);
+       OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+       OUT_RING(MI_NOOP);
+       ADVANCE_LP_RING();
+
+       overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+       if (overlay->last_flip_req == 0)
+               return -ENOMEM;
+
+       ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
+       if (ret != 0)
+               return ret;
+
+       overlay->hw_wedged = 0;
+       overlay->last_flip_req = 0;
+       return 0;
+}
+
+/* overlay needs to be enabled in OCMD reg */
+static void intel_overlay_continue(struct intel_overlay *overlay,
+                           bool load_polyphase_filter)
+{
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 flip_addr = overlay->flip_addr;
+       u32 tmp;
+       RING_LOCALS;
+
+       BUG_ON(!overlay->active);
+
+       if (load_polyphase_filter)
+               flip_addr |= OFC_UPDATE;
+
+       /* check for underruns */
+       tmp = I915_READ(DOVSTA);
+       if (tmp & (1 << 17))
+               DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
+
+       BEGIN_LP_RING(4);
+       OUT_RING(MI_FLUSH);
+       OUT_RING(MI_NOOP);
+       OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
+       OUT_RING(flip_addr);
+        ADVANCE_LP_RING();
+
+       overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+}
+
+static int intel_overlay_wait_flip(struct intel_overlay *overlay)
+{
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       int ret;
+       u32 tmp;
+       RING_LOCALS;
+
+       if (overlay->last_flip_req != 0) {
+               ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
+               if (ret == 0) {
+                       overlay->last_flip_req = 0;
+
+                       tmp = I915_READ(ISR);
+
+                       if (!(tmp & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT))
+                               return 0;
+               }
+       }
+
+       /* synchronous slowpath */
+       overlay->hw_wedged = RELEASE_OLD_VID;
+
+       BEGIN_LP_RING(2);
+        OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+        OUT_RING(MI_NOOP);
+        ADVANCE_LP_RING();
+
+       overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+       if (overlay->last_flip_req == 0)
+               return -ENOMEM;
+
+       ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
+       if (ret != 0)
+               return ret;
+
+       overlay->hw_wedged = 0;
+       overlay->last_flip_req = 0;
+       return 0;
+}
+
+/* overlay needs to be disabled in OCMD reg */
+static int intel_overlay_off(struct intel_overlay *overlay)
+{
+       u32 flip_addr = overlay->flip_addr;
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       int ret;
+       RING_LOCALS;
+
+       BUG_ON(!overlay->active);
+
+       /* According to intel docs the overlay hw may hang (when switching
+        * off) without loading the filter coeffs. It is however unclear whether
+        * this applies to the disabling of the overlay or to the switching off
+        * of the hw. Do it in both cases */
+       flip_addr |= OFC_UPDATE;
+
+       /* wait for overlay to go idle */
+       overlay->hw_wedged = SWITCH_OFF_STAGE_1;
+
+       BEGIN_LP_RING(6);
+       OUT_RING(MI_FLUSH);
+       OUT_RING(MI_NOOP);
+       OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
+       OUT_RING(flip_addr);
+        OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+        OUT_RING(MI_NOOP);
+        ADVANCE_LP_RING();
+
+       overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+       if (overlay->last_flip_req == 0)
+               return -ENOMEM;
+
+       ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
+       if (ret != 0)
+               return ret;
+
+       /* turn overlay off */
+       overlay->hw_wedged = SWITCH_OFF_STAGE_2;
+
+       BEGIN_LP_RING(6);
+        OUT_RING(MI_FLUSH);
+        OUT_RING(MI_NOOP);
+        OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
+       OUT_RING(flip_addr);
+        OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+        OUT_RING(MI_NOOP);
+       ADVANCE_LP_RING();
+
+       overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+       if (overlay->last_flip_req == 0)
+               return -ENOMEM;
+
+       ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
+       if (ret != 0)
+               return ret;
+
+       overlay->hw_wedged = 0;
+       overlay->last_flip_req = 0;
+       return ret;
+}
+
+static void intel_overlay_off_tail(struct intel_overlay *overlay)
+{
+       struct drm_gem_object *obj;
+
+       /* never have the overlay hw on without showing a frame */
+       BUG_ON(!overlay->vid_bo);
+       obj = overlay->vid_bo->obj;
+
+       i915_gem_object_unpin(obj);
+       drm_gem_object_unreference(obj);
+       overlay->vid_bo = NULL;
+
+       overlay->crtc->overlay = NULL;
+       overlay->crtc = NULL;
+       overlay->active = 0;
+}
+
+/* recover from an interruption due to a signal
+ * We have to be careful not to repeat work forever an make forward progess. */
+int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
+                                        int interruptible)
+{
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_gem_object *obj;
+       u32 flip_addr;
+       int ret;
+       RING_LOCALS;
+
+       if (overlay->hw_wedged == HW_WEDGED)
+               return -EIO;
+
+       if (overlay->last_flip_req == 0) {
+               overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+               if (overlay->last_flip_req == 0)
+                       return -ENOMEM;
+       }
+
+       ret = i915_do_wait_request(dev, overlay->last_flip_req, interruptible);
+       if (ret != 0)
+               return ret;
+
+       switch (overlay->hw_wedged) {
+               case RELEASE_OLD_VID:
+                       obj = overlay->old_vid_bo->obj;
+                       i915_gem_object_unpin(obj);
+                       drm_gem_object_unreference(obj);
+                       overlay->old_vid_bo = NULL;
+                       break;
+               case SWITCH_OFF_STAGE_1:
+                       flip_addr = overlay->flip_addr;
+                       flip_addr |= OFC_UPDATE;
+
+                       overlay->hw_wedged = SWITCH_OFF_STAGE_2;
+
+                       BEGIN_LP_RING(6);
+                       OUT_RING(MI_FLUSH);
+                       OUT_RING(MI_NOOP);
+                       OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
+                       OUT_RING(flip_addr);
+                       OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+                       OUT_RING(MI_NOOP);
+                       ADVANCE_LP_RING();
+
+                       overlay->last_flip_req = i915_add_request(dev, NULL, 0);
+                       if (overlay->last_flip_req == 0)
+                               return -ENOMEM;
+
+                       ret = i915_do_wait_request(dev, overlay->last_flip_req,
+                                       interruptible);
+                       if (ret != 0)
+                               return ret;
+
+               case SWITCH_OFF_STAGE_2:
+                       intel_overlay_off_tail(overlay);
+                       break;
+               default:
+                       BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP);
+       }
+
+       overlay->hw_wedged = 0;
+       overlay->last_flip_req = 0;
+       return 0;
+}
+
+/* Wait for pending overlay flip and release old frame.
+ * Needs to be called before the overlay register are changed
+ * via intel_overlay_(un)map_regs_atomic */
+static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
+{
+       int ret;
+       struct drm_gem_object *obj;
+
+       /* only wait if there is actually an old frame to release to
+        * guarantee forward progress */
+       if (!overlay->old_vid_bo)
+               return 0;
+
+       ret = intel_overlay_wait_flip(overlay);
+       if (ret != 0)
+               return ret;
+
+       obj = overlay->old_vid_bo->obj;
+       i915_gem_object_unpin(obj);
+       drm_gem_object_unreference(obj);
+       overlay->old_vid_bo = NULL;
+
+       return 0;
+}
+
+struct put_image_params {
+       int format;
+       short dst_x;
+       short dst_y;
+       short dst_w;
+       short dst_h;
+       short src_w;
+       short src_scan_h;
+       short src_scan_w;
+       short src_h;
+       short stride_Y;
+       short stride_UV;
+       int offset_Y;
+       int offset_U;
+       int offset_V;
+};
+
+static int packed_depth_bytes(u32 format)
+{
+       switch (format & I915_OVERLAY_DEPTH_MASK) {
+               case I915_OVERLAY_YUV422:
+                       return 4;
+               case I915_OVERLAY_YUV411:
+                       /* return 6; not implemented */
+               default:
+                       return -EINVAL;
+       }
+}
+
+static int packed_width_bytes(u32 format, short width)
+{
+       switch (format & I915_OVERLAY_DEPTH_MASK) {
+               case I915_OVERLAY_YUV422:
+                       return width << 1;
+               default:
+                       return -EINVAL;
+       }
+}
+
+static int uv_hsubsampling(u32 format)
+{
+       switch (format & I915_OVERLAY_DEPTH_MASK) {
+               case I915_OVERLAY_YUV422:
+               case I915_OVERLAY_YUV420:
+                       return 2;
+               case I915_OVERLAY_YUV411:
+               case I915_OVERLAY_YUV410:
+                       return 4;
+               default:
+                       return -EINVAL;
+       }
+}
+
+static int uv_vsubsampling(u32 format)
+{
+       switch (format & I915_OVERLAY_DEPTH_MASK) {
+               case I915_OVERLAY_YUV420:
+               case I915_OVERLAY_YUV410:
+                       return 2;
+               case I915_OVERLAY_YUV422:
+               case I915_OVERLAY_YUV411:
+                       return 1;
+               default:
+                       return -EINVAL;
+       }
+}
+
+static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
+{
+       u32 mask, shift, ret;
+       if (IS_I9XX(dev)) {
+               mask = 0x3f;
+               shift = 6;
+       } else {
+               mask = 0x1f;
+               shift = 5;
+       }
+       ret = ((offset + width + mask) >> shift) - (offset >> shift);
+       if (IS_I9XX(dev))
+               ret <<= 1;
+       ret -=1;
+       return ret << 2;
+}
+
+static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = {
+       0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0,
+       0x3000, 0xb500, 0x19d0, 0x1880, 0xb440,
+       0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0,
+       0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380,
+       0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320,
+       0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0,
+       0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260,
+       0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200,
+       0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0,
+       0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160,
+       0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120,
+       0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0,
+       0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0,
+       0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
+       0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
+       0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
+       0xb000, 0x3000, 0x0800, 0x3000, 0xb000};
+static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
+       0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
+       0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
+       0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880,
+       0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00,
+       0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0,
+       0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
+       0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
+       0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
+       0x3000, 0x0800, 0x3000};
+
+static void update_polyphase_filter(struct overlay_registers *regs)
+{
+       memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
+       memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs));
+}
+
+static bool update_scaling_factors(struct intel_overlay *overlay,
+                                  struct overlay_registers *regs,
+                                  struct put_image_params *params)
+{
+       /* fixed point with a 12 bit shift */
+       u32 xscale, yscale, xscale_UV, yscale_UV;
+#define FP_SHIFT 12
+#define FRACT_MASK 0xfff
+       bool scale_changed = false;
+       int uv_hscale = uv_hsubsampling(params->format);
+       int uv_vscale = uv_vsubsampling(params->format);
+
+       if (params->dst_w > 1)
+               xscale = ((params->src_scan_w - 1) << FP_SHIFT)
+                       /(params->dst_w);
+       else
+               xscale = 1 << FP_SHIFT;
+
+       if (params->dst_h > 1)
+               yscale = ((params->src_scan_h - 1) << FP_SHIFT)
+                       /(params->dst_h);
+       else
+               yscale = 1 << FP_SHIFT;
+
+       /*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
+               xscale_UV = xscale/uv_hscale;
+               yscale_UV = yscale/uv_vscale;
+               /* make the Y scale to UV scale ratio an exact multiply */
+               xscale = xscale_UV * uv_hscale;
+               yscale = yscale_UV * uv_vscale;
+       /*} else {
+               xscale_UV = 0;
+               yscale_UV = 0;
+       }*/
+
+       if (xscale != overlay->old_xscale || yscale != overlay->old_yscale)
+               scale_changed = true;
+       overlay->old_xscale = xscale;
+       overlay->old_yscale = yscale;
+
+       regs->YRGBSCALE = ((yscale & FRACT_MASK) << 20)
+               | ((xscale >> FP_SHIFT) << 16)
+               | ((xscale & FRACT_MASK) << 3);
+       regs->UVSCALE = ((yscale_UV & FRACT_MASK) << 20)
+               | ((xscale_UV >> FP_SHIFT) << 16)
+               | ((xscale_UV & FRACT_MASK) << 3);
+       regs->UVSCALEV = ((yscale >> FP_SHIFT) << 16)
+               | ((yscale_UV >> FP_SHIFT) << 0);
+
+       if (scale_changed)
+               update_polyphase_filter(regs);
+
+       return scale_changed;
+}
+
+static void update_colorkey(struct intel_overlay *overlay,
+                           struct overlay_registers *regs)
+{
+       u32 key = overlay->color_key;
+       switch (overlay->crtc->base.fb->bits_per_pixel) {
+               case 8:
+                       regs->DCLRKV = 0;
+                       regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
+               case 16:
+                       if (overlay->crtc->base.fb->depth == 15) {
+                               regs->DCLRKV = RGB15_TO_COLORKEY(key);
+                               regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
+                       } else {
+                               regs->DCLRKV = RGB16_TO_COLORKEY(key);
+                               regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
+                       }
+               case 24:
+               case 32:
+                       regs->DCLRKV = key;
+                       regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
+       }
+}
+
+static u32 overlay_cmd_reg(struct put_image_params *params)
+{
+       u32 cmd = OCMD_ENABLE | OCMD_BUF_TYPE_FRAME | OCMD_BUFFER0;
+
+       if (params->format & I915_OVERLAY_YUV_PLANAR) {
+               switch (params->format & I915_OVERLAY_DEPTH_MASK) {
+                       case I915_OVERLAY_YUV422:
+                               cmd |= OCMD_YUV_422_PLANAR;
+                               break;
+                       case I915_OVERLAY_YUV420:
+                               cmd |= OCMD_YUV_420_PLANAR;
+                               break;
+                       case I915_OVERLAY_YUV411:
+                       case I915_OVERLAY_YUV410:
+                               cmd |= OCMD_YUV_410_PLANAR;
+                               break;
+               }
+       } else { /* YUV packed */
+               switch (params->format & I915_OVERLAY_DEPTH_MASK) {
+                       case I915_OVERLAY_YUV422:
+                               cmd |= OCMD_YUV_422_PACKED;
+                               break;
+                       case I915_OVERLAY_YUV411:
+                               cmd |= OCMD_YUV_411_PACKED;
+                               break;
+               }
+
+               switch (params->format & I915_OVERLAY_SWAP_MASK) {
+                       case I915_OVERLAY_NO_SWAP:
+                               break;
+                       case I915_OVERLAY_UV_SWAP:
+                               cmd |= OCMD_UV_SWAP;
+                               break;
+                       case I915_OVERLAY_Y_SWAP:
+                               cmd |= OCMD_Y_SWAP;
+                               break;
+                       case I915_OVERLAY_Y_AND_UV_SWAP:
+                               cmd |= OCMD_Y_AND_UV_SWAP;
+                               break;
+               }
+       }
+
+       return cmd;
+}
+
+int intel_overlay_do_put_image(struct intel_overlay *overlay,
+                              struct drm_gem_object *new_bo,
+                              struct put_image_params *params)
+{
+       int ret, tmp_width;
+       struct overlay_registers *regs;
+       bool scale_changed = false;
+       struct drm_i915_gem_object *bo_priv = new_bo->driver_private;
+       struct drm_device *dev = overlay->dev;
+
+       BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+       BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+       BUG_ON(!overlay);
+
+       ret = intel_overlay_release_old_vid(overlay);
+       if (ret != 0)
+               return ret;
+
+       ret = i915_gem_object_pin(new_bo, PAGE_SIZE);
+       if (ret != 0)
+               return ret;
+
+       ret = i915_gem_object_set_to_gtt_domain(new_bo, 0);
+       if (ret != 0)
+               goto out_unpin;
+
+       if (!overlay->active) {
+               regs = intel_overlay_map_regs_atomic(overlay);
+               if (!regs) {
+                       ret = -ENOMEM;
+                       goto out_unpin;
+               }
+               regs->OCONFIG = OCONF_CC_OUT_8BIT;
+               if (IS_I965GM(overlay->dev))
+                       regs->OCONFIG |= OCONF_CSC_MODE_BT709;
+               regs->OCONFIG |= overlay->crtc->pipe == 0 ?
+                       OCONF_PIPE_A : OCONF_PIPE_B;
+               intel_overlay_unmap_regs_atomic(overlay);
+
+               ret = intel_overlay_on(overlay);
+               if (ret != 0)
+                       goto out_unpin;
+       }
+
+       regs = intel_overlay_map_regs_atomic(overlay);
+       if (!regs) {
+               ret = -ENOMEM;
+               goto out_unpin;
+       }
+
+       regs->DWINPOS = (params->dst_y << 16) | params->dst_x;
+       regs->DWINSZ = (params->dst_h << 16) | params->dst_w;
+
+       if (params->format & I915_OVERLAY_YUV_PACKED)
+               tmp_width = packed_width_bytes(params->format, params->src_w);
+       else
+               tmp_width = params->src_w;
+
+       regs->SWIDTH = params->src_w;
+       regs->SWIDTHSW = calc_swidthsw(overlay->dev,
+                       params->offset_Y, tmp_width);
+       regs->SHEIGHT = params->src_h;
+       regs->OBUF_0Y = bo_priv->gtt_offset + params-> offset_Y;
+       regs->OSTRIDE = params->stride_Y;
+
+       if (params->format & I915_OVERLAY_YUV_PLANAR) {
+               int uv_hscale = uv_hsubsampling(params->format);
+               int uv_vscale = uv_vsubsampling(params->format);
+               u32 tmp_U, tmp_V;
+               regs->SWIDTH |= (params->src_w/uv_hscale) << 16;
+               tmp_U = calc_swidthsw(overlay->dev, params->offset_U,
+                               params->src_w/uv_hscale);
+               tmp_V = calc_swidthsw(overlay->dev, params->offset_V,
+                               params->src_w/uv_hscale);
+               regs->SWIDTHSW |= max_t(u32, tmp_U, tmp_V) << 16;
+               regs->SHEIGHT |= (params->src_h/uv_vscale) << 16;
+               regs->OBUF_0U = bo_priv->gtt_offset + params->offset_U;
+               regs->OBUF_0V = bo_priv->gtt_offset + params->offset_V;
+               regs->OSTRIDE |= params->stride_UV << 16;
+       }
+
+       scale_changed = update_scaling_factors(overlay, regs, params);
+
+       update_colorkey(overlay, regs);
+
+       regs->OCMD = overlay_cmd_reg(params);
+
+       intel_overlay_unmap_regs_atomic(overlay);
+
+       intel_overlay_continue(overlay, scale_changed);
+
+       overlay->old_vid_bo = overlay->vid_bo;
+       overlay->vid_bo = new_bo->driver_private;
+
+       return 0;
+
+out_unpin:
+       i915_gem_object_unpin(new_bo);
+       return ret;
+}
+
+int intel_overlay_switch_off(struct intel_overlay *overlay)
+{
+       int ret;
+       struct overlay_registers *regs;
+       struct drm_device *dev = overlay->dev;
+
+       BUG_ON(!mutex_is_locked(&dev->struct_mutex));
+       BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
+       if (overlay->hw_wedged) {
+               ret = intel_overlay_recover_from_interrupt(overlay, 1);
+               if (ret != 0)
+                       return ret;
+       }
+
+       if (!overlay->active)
+               return 0;
+
+       ret = intel_overlay_release_old_vid(overlay);
+       if (ret != 0)
+               return ret;
+
+       regs = intel_overlay_map_regs_atomic(overlay);
+       regs->OCMD = 0;
+       intel_overlay_unmap_regs_atomic(overlay);
+
+       ret = intel_overlay_off(overlay);
+       if (ret != 0)
+               return ret;
+
+       intel_overlay_off_tail(overlay);
+
+       return 0;
+}
+
+static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
+                                         struct intel_crtc *crtc)
+{
+        drm_i915_private_t *dev_priv = overlay->dev->dev_private;
+       u32 pipeconf;
+       int pipeconf_reg = (crtc->pipe == 0) ? PIPEACONF : PIPEBCONF;
+
+       if (!crtc->base.enabled || crtc->dpms_mode != DRM_MODE_DPMS_ON)
+               return -EINVAL;
+
+       pipeconf = I915_READ(pipeconf_reg);
+
+       /* can't use the overlay with double wide pipe */
+       if (!IS_I965G(overlay->dev) && pipeconf & PIPEACONF_DOUBLE_WIDE)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
+{
+       struct drm_device *dev = overlay->dev;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 ratio;
+       u32 pfit_control = I915_READ(PFIT_CONTROL);
+
+       /* XXX: This is not the same logic as in the xorg driver, but more in
+        * line with the intel documentation for the i965 */
+       if (!IS_I965G(dev) && (pfit_control & VERT_AUTO_SCALE)) {
+               ratio = I915_READ(PFIT_AUTO_RATIOS) >> PFIT_VERT_SCALE_SHIFT;
+       } else { /* on i965 use the PGM reg to read out the autoscaler values */
+               ratio = I915_READ(PFIT_PGM_RATIOS);
+               if (IS_I965G(dev))
+                       ratio >>= PFIT_VERT_SCALE_SHIFT_965;
+               else
+                       ratio >>= PFIT_VERT_SCALE_SHIFT;
+       }
+
+       overlay->pfit_vscale_ratio = ratio;
+}
+
+static int check_overlay_dst(struct intel_overlay *overlay,
+                            struct drm_intel_overlay_put_image *rec)
+{
+       struct drm_display_mode *mode = &overlay->crtc->base.mode;
+
+       if ((rec->dst_x < mode->crtc_hdisplay)
+           && (rec->dst_x + rec->dst_width
+                   <= mode->crtc_hdisplay)
+           && (rec->dst_y < mode->crtc_vdisplay)
+           && (rec->dst_y + rec->dst_height
+                   <= mode->crtc_vdisplay))
+               return 0;
+       else
+               return -EINVAL;
+}
+
+static int check_overlay_scaling(struct put_image_params *rec)
+{
+       u32 tmp;
+
+       /* downscaling limit is 8.0 */
+       tmp = ((rec->src_scan_h << 16) / rec->dst_h) >> 16;
+       if (tmp > 7)
+               return -EINVAL;
+       tmp = ((rec->src_scan_w << 16) / rec->dst_w) >> 16;
+       if (tmp > 7)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int check_overlay_src(struct drm_device *dev,
+                            struct drm_intel_overlay_put_image *rec,
+                            struct drm_gem_object *new_bo)
+{
+       u32 stride_mask;
+       int depth;
+       int uv_hscale = uv_hsubsampling(rec->flags);
+       int uv_vscale = uv_vsubsampling(rec->flags);
+       size_t tmp;
+
+       /* check src dimensions */
+       if (IS_845G(dev) || IS_I830(dev)) {
+               if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY
+                   || rec->src_width > IMAGE_MAX_WIDTH_LEGACY)
+                       return -EINVAL;
+       } else {
+               if (rec->src_height > IMAGE_MAX_HEIGHT
+                   || rec->src_width > IMAGE_MAX_WIDTH)
+                       return -EINVAL;
+       }
+       /* better safe than sorry, use 4 as the maximal subsampling ratio */
+       if (rec->src_height < N_VERT_Y_TAPS*4
+           || rec->src_width < N_HORIZ_Y_TAPS*4)
+               return -EINVAL;
+
+       /* check alingment constrains */
+       switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
+               case I915_OVERLAY_RGB:
+                       /* not implemented */
+                       return -EINVAL;
+               case I915_OVERLAY_YUV_PACKED:
+                       depth = packed_depth_bytes(rec->flags);
+                       if (uv_vscale != 1)
+                               return -EINVAL;
+                       if (depth < 0)
+                               return depth;
+                       /* ignore UV planes */
+                       rec->stride_UV = 0;
+                       rec->offset_U = 0;
+                       rec->offset_V = 0;
+                       /* check pixel alignment */
+                       if (rec->offset_Y % depth)
+                               return -EINVAL;
+                       break;
+               case I915_OVERLAY_YUV_PLANAR:
+                       if (uv_vscale < 0 || uv_hscale < 0)
+                               return -EINVAL;
+                       /* no offset restrictions for planar formats */
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       if (rec->src_width % uv_hscale)
+               return -EINVAL;
+
+       /* stride checking */
+       stride_mask = 63;
+
+       if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask)
+               return -EINVAL;
+       if (IS_I965G(dev) && rec->stride_Y < 512)
+               return -EINVAL;
+
+       tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ?
+               4 : 8;
+       if (rec->stride_Y > tmp*1024 || rec->stride_UV > 2*1024)
+               return -EINVAL;
+
+       /* check buffer dimensions */
+       switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
+               case I915_OVERLAY_RGB:
+               case I915_OVERLAY_YUV_PACKED:
+                       /* always 4 Y values per depth pixels */
+                       if (packed_width_bytes(rec->flags, rec->src_width)
+                                       > rec->stride_Y)
+                               return -EINVAL;
+
+                       tmp = rec->stride_Y*rec->src_height;
+                       if (rec->offset_Y + tmp > new_bo->size)
+                               return -EINVAL;
+                       break;
+               case I915_OVERLAY_YUV_PLANAR:
+                       if (rec->src_width > rec->stride_Y)
+                               return -EINVAL;
+                       if (rec->src_width/uv_hscale > rec->stride_UV)
+                               return -EINVAL;
+
+                       tmp = rec->stride_Y*rec->src_height;
+                       if (rec->offset_Y + tmp > new_bo->size)
+                               return -EINVAL;
+                       tmp = rec->stride_UV*rec->src_height;
+                       tmp /= uv_vscale;
+                       if (rec->offset_U + tmp > new_bo->size
+                           || rec->offset_V + tmp > new_bo->size)
+                               return -EINVAL;
+                       break;
+       }
+
+       return 0;
+}
+
+int intel_overlay_put_image(struct drm_device *dev, void *data,
+                            struct drm_file *file_priv)
+{
+       struct drm_intel_overlay_put_image *put_image_rec = data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_overlay *overlay;
+       struct drm_mode_object *drmmode_obj;
+       struct intel_crtc *crtc;
+       struct drm_gem_object *new_bo;
+       struct put_image_params *params;
+       int ret;
+
+       if (!dev_priv) {
+               DRM_ERROR("called with no initialization\n");
+               return -EINVAL;
+       }
+
+       overlay = dev_priv->overlay;
+       if (!overlay) {
+               DRM_DEBUG("userspace bug: no overlay\n");
+               return -ENODEV;
+       }
+
+       if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) {
+               mutex_lock(&dev->mode_config.mutex);
+               mutex_lock(&dev->struct_mutex);
+
+               ret = intel_overlay_switch_off(overlay);
+
+               mutex_unlock(&dev->struct_mutex);
+               mutex_unlock(&dev->mode_config.mutex);
+
+               return ret;
+       }
+
+       params = kmalloc(sizeof(struct put_image_params), GFP_KERNEL);
+       if (!params)
+               return -ENOMEM;
+
+       drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
+                        DRM_MODE_OBJECT_CRTC);
+       if (!drmmode_obj)
+               return -ENOENT;
+       crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
+
+       new_bo = drm_gem_object_lookup(dev, file_priv,
+                       put_image_rec->bo_handle);
+       if (!new_bo)
+               return -ENOENT;
+
+       mutex_lock(&dev->mode_config.mutex);
+       mutex_lock(&dev->struct_mutex);
+
+       if (overlay->hw_wedged) {
+               ret = intel_overlay_recover_from_interrupt(overlay, 1);
+               if (ret != 0)
+                       goto out_unlock;
+       }
+
+       if (overlay->crtc != crtc) {
+               struct drm_display_mode *mode = &crtc->base.mode;
+               ret = intel_overlay_switch_off(overlay);
+               if (ret != 0)
+                       goto out_unlock;
+
+               ret = check_overlay_possible_on_crtc(overlay, crtc);
+               if (ret != 0)
+                       goto out_unlock;
+
+               overlay->crtc = crtc;
+               crtc->overlay = overlay;
+
+               if (intel_panel_fitter_pipe(dev) == crtc->pipe
+                   /* and line to wide, i.e. one-line-mode */
+                   && mode->hdisplay > 1024) {
+                       overlay->pfit_active = 1;
+                       update_pfit_vscale_ratio(overlay);
+               } else
+                       overlay->pfit_active = 0;
+       }
+
+       ret = check_overlay_dst(overlay, put_image_rec);
+       if (ret != 0)
+               goto out_unlock;
+
+       if (overlay->pfit_active) {
+               params->dst_y = ((((u32)put_image_rec->dst_y) << 12) /
+                       overlay->pfit_vscale_ratio);
+               /* shifting right rounds downwards, so add 1 */
+               params->dst_h = ((((u32)put_image_rec->dst_height) << 12) /
+                       overlay->pfit_vscale_ratio) + 1;
+       } else {
+               params->dst_y = put_image_rec->dst_y;
+               params->dst_h = put_image_rec->dst_height;
+       }
+       params->dst_x = put_image_rec->dst_x;
+       params->dst_w = put_image_rec->dst_width;
+
+       params->src_w = put_image_rec->src_width;
+       params->src_h = put_image_rec->src_height;
+       params->src_scan_w = put_image_rec->src_scan_width;
+       params->src_scan_h = put_image_rec->src_scan_height;
+       if (params->src_scan_h > params->src_h
+           || params->src_scan_w > params->src_w) {
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       ret = check_overlay_src(dev, put_image_rec, new_bo);
+       if (ret != 0)
+               goto out_unlock;
+       params->format = put_image_rec->flags & ~I915_OVERLAY_FLAGS_MASK;
+       params->stride_Y = put_image_rec->stride_Y;
+       params->stride_UV = put_image_rec->stride_UV;
+       params->offset_Y = put_image_rec->offset_Y;
+       params->offset_U = put_image_rec->offset_U;
+       params->offset_V = put_image_rec->offset_V;
+
+       /* Check scaling after src size to prevent a divide-by-zero. */
+       ret = check_overlay_scaling(params);
+       if (ret != 0)
+               goto out_unlock;
+
+       ret = intel_overlay_do_put_image(overlay, new_bo, params);
+       if (ret != 0)
+               goto out_unlock;
+
+       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       kfree(params);
+
+       return 0;
+
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->mode_config.mutex);
+       drm_gem_object_unreference(new_bo);
+       kfree(params);
+
+       return ret;
+}
+
+static void update_reg_attrs(struct intel_overlay *overlay,
+                            struct overlay_registers *regs)
+{
+       regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff);
+       regs->OCLRC1 = overlay->saturation;
+}
+
+static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
+{
+       int i;
+
+       if (gamma1 & 0xff000000 || gamma2 & 0xff000000)
+               return false;
+
+       for (i = 0; i < 3; i++) {
+               if (((gamma1 >> i * 8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
+                       return false;
+       }
+
+       return true;
+}
+
+static bool check_gamma5_errata(u32 gamma5)
+{
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               if (((gamma5 >> i*8) & 0xff) == 0x80)
+                       return false;
+       }
+
+       return true;
+}
+
+static int check_gamma(struct drm_intel_overlay_attrs *attrs)
+{
+       if (!check_gamma_bounds(0, attrs->gamma0)
+           || !check_gamma_bounds(attrs->gamma0, attrs->gamma1)
+           || !check_gamma_bounds(attrs->gamma1, attrs->gamma2)
+           || !check_gamma_bounds(attrs->gamma2, attrs->gamma3)
+           || !check_gamma_bounds(attrs->gamma3, attrs->gamma4)
+           || !check_gamma_bounds(attrs->gamma4, attrs->gamma5)
+           || !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
+               return -EINVAL;
+       if (!check_gamma5_errata(attrs->gamma5))
+               return -EINVAL;
+       return 0;
+}
+
+int intel_overlay_attrs(struct drm_device *dev, void *data,
+                        struct drm_file *file_priv)
+{
+       struct drm_intel_overlay_attrs *attrs = data;
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_overlay *overlay;
+       struct overlay_registers *regs;
+       int ret;
+
+       if (!dev_priv) {
+               DRM_ERROR("called with no initialization\n");
+               return -EINVAL;
+       }
+
+       overlay = dev_priv->overlay;
+       if (!overlay) {
+               DRM_DEBUG("userspace bug: no overlay\n");
+               return -ENODEV;
+       }
+
+       mutex_lock(&dev->mode_config.mutex);
+       mutex_lock(&dev->struct_mutex);
+
+       if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) {
+               attrs->color_key = overlay->color_key;
+               attrs->brightness = overlay->brightness;
+               attrs->contrast = overlay->contrast;
+               attrs->saturation = overlay->saturation;
+
+               if (IS_I9XX(dev)) {
+                       attrs->gamma0 = I915_READ(OGAMC0);
+                       attrs->gamma1 = I915_READ(OGAMC1);
+                       attrs->gamma2 = I915_READ(OGAMC2);
+                       attrs->gamma3 = I915_READ(OGAMC3);
+                       attrs->gamma4 = I915_READ(OGAMC4);
+                       attrs->gamma5 = I915_READ(OGAMC5);
+               }
+               ret = 0;
+       } else {
+               overlay->color_key = attrs->color_key;
+               if (attrs->brightness >= -128 && attrs->brightness <= 127) {
+                       overlay->brightness = attrs->brightness;
+               } else {
+                       ret = -EINVAL;
+                       goto out_unlock;
+               }
+               if (attrs->contrast <= 255) {
+                       overlay->contrast = attrs->contrast;
+               } else {
+                       ret = -EINVAL;
+                       goto out_unlock;
+               }
+               if (attrs->saturation <= 1023) {
+                       overlay->saturation = attrs->saturation;
+               } else {
+                       ret = -EINVAL;
+                       goto out_unlock;
+               }
+
+               regs = intel_overlay_map_regs_atomic(overlay);
+               if (!regs) {
+                       ret = -ENOMEM;
+                       goto out_unlock;
+               }
+
+               update_reg_attrs(overlay, regs);
+
+               intel_overlay_unmap_regs_atomic(overlay);
+
+               if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) {
+                       if (!IS_I9XX(dev)) {
+                               ret = -EINVAL;
+                               goto out_unlock;
+                       }
+
+                       if (overlay->active) {
+                               ret = -EBUSY;
+                               goto out_unlock;
+                       }
+
+                       ret = check_gamma(attrs);
+                       if (ret != 0)
+                               goto out_unlock;
+
+                       I915_WRITE(OGAMC0, attrs->gamma0);
+                       I915_WRITE(OGAMC1, attrs->gamma1);
+                       I915_WRITE(OGAMC2, attrs->gamma2);
+                       I915_WRITE(OGAMC3, attrs->gamma3);
+                       I915_WRITE(OGAMC4, attrs->gamma4);
+                       I915_WRITE(OGAMC5, attrs->gamma5);
+               }
+               ret = 0;
+       }
+
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
+}
+
+void intel_setup_overlay(struct drm_device *dev)
+{
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_overlay *overlay;
+       struct drm_gem_object *reg_bo;
+       struct overlay_registers *regs;
+       int ret;
+
+       if (!OVERLAY_EXISTS(dev))
+               return;
+
+       overlay = kzalloc(sizeof(struct intel_overlay), GFP_KERNEL);
+       if (!overlay)
+               return;
+       overlay->dev = dev;
+
+       reg_bo = drm_gem_object_alloc(dev, PAGE_SIZE);
+       if (!reg_bo)
+               goto out_free;
+       overlay->reg_bo = reg_bo->driver_private;
+
+       if (OVERLAY_NONPHYSICAL(dev)) {
+               ret = i915_gem_object_pin(reg_bo, PAGE_SIZE);
+               if (ret) {
+                        DRM_ERROR("failed to pin overlay register bo\n");
+                        goto out_free_bo;
+                }
+               overlay->flip_addr = overlay->reg_bo->gtt_offset;
+       } else {
+               ret = i915_gem_attach_phys_object(dev, reg_bo,
+                               I915_GEM_PHYS_OVERLAY_REGS);
+                if (ret) {
+                        DRM_ERROR("failed to attach phys overlay regs\n");
+                        goto out_free_bo;
+                }
+               overlay->flip_addr = overlay->reg_bo->phys_obj->handle->busaddr;
+       }
+
+       /* init all values */
+       overlay->color_key = 0x0101fe;
+       overlay->brightness = -19;
+       overlay->contrast = 75;
+       overlay->saturation = 146;
+
+       regs = intel_overlay_map_regs_atomic(overlay);
+       if (!regs)
+               goto out_free_bo;
+
+       memset(regs, 0, sizeof(struct overlay_registers));
+       update_polyphase_filter(regs);
+
+       update_reg_attrs(overlay, regs);
+
+       intel_overlay_unmap_regs_atomic(overlay);
+
+       dev_priv->overlay = overlay;
+       DRM_INFO("initialized overlay support\n");
+       return;
+
+out_free_bo:
+       drm_gem_object_unreference(reg_bo);
+out_free:
+       kfree(overlay);
+       return;
+}
+
+void intel_cleanup_overlay(struct drm_device *dev)
+{
+        drm_i915_private_t *dev_priv = dev->dev_private;
+
+       if (dev_priv->overlay) {
+               /* The bo's should be free'd by the generic code already.
+                * Furthermore modesetting teardown happens beforehand so the
+                * hardware should be off already */
+               BUG_ON(dev_priv->overlay->active);
+
+               kfree(dev_priv->overlay);
+       }
+}
index 083bec2..24a3dc9 100644 (file)
@@ -36,8 +36,6 @@
 #include "i915_drv.h"
 #include "intel_sdvo_regs.h"
 
-#undef SDVO_DEBUG
-
 static char *tv_format_names[] = {
        "NTSC_M"   , "NTSC_J"  , "NTSC_443",
        "PAL_B"    , "PAL_D"   , "PAL_G"   ,
@@ -356,7 +354,6 @@ static const struct _sdvo_cmd_name {
 #define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
 #define SDVO_PRIV(output)   ((struct intel_sdvo_priv *) (output)->dev_priv)
 
-#ifdef SDVO_DEBUG
 static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
                                   void *args, int args_len)
 {
@@ -379,9 +376,6 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
                DRM_LOG_KMS("(%02X)", cmd);
        DRM_LOG_KMS("\n");
 }
-#else
-#define intel_sdvo_debug_write(o, c, a, l)
-#endif
 
 static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
                                 void *args, int args_len)
@@ -398,7 +392,6 @@ static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
        intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
 }
 
-#ifdef SDVO_DEBUG
 static const char *cmd_status_names[] = {
        "Power on",
        "Success",
@@ -427,9 +420,6 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
                DRM_LOG_KMS("(??? %d)", status);
        DRM_LOG_KMS("\n");
 }
-#else
-#define intel_sdvo_debug_response(o, r, l, s)
-#endif
 
 static u8 intel_sdvo_read_response(struct intel_output *intel_output,
                                   void *response, int response_len)
@@ -1627,6 +1617,10 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
 
        intel_sdvo_write_cmd(intel_output,
                             SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
+       if (sdvo_priv->is_tv) {
+               /* add 30ms delay when the output type is SDVO-TV */
+               mdelay(30);
+       }
        status = intel_sdvo_read_response(intel_output, &response, 2);
 
        DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8);
@@ -2726,7 +2720,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
        /* Wrap with our custom algo which switches to DDC mode */
        intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
 
-       /* In defaut case sdvo lvds is false */
+       /* In default case sdvo lvds is false */
        intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
 
        if (intel_sdvo_output_setup(intel_output,
index 9ca9179..552ec11 100644 (file)
@@ -1213,20 +1213,17 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
                tv_ctl |= TV_TRILEVEL_SYNC;
        if (tv_mode->pal_burst)
                tv_ctl |= TV_PAL_BURST;
+
        scctl1 = 0;
-       /* dda1 implies valid video levels */
-       if (tv_mode->dda1_inc) {
+       if (tv_mode->dda1_inc)
                scctl1 |= TV_SC_DDA1_EN;
-       }
-
        if (tv_mode->dda2_inc)
                scctl1 |= TV_SC_DDA2_EN;
-
        if (tv_mode->dda3_inc)
                scctl1 |= TV_SC_DDA3_EN;
-
        scctl1 |= tv_mode->sc_reset;
-       scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
+       if (video_levels)
+               scctl1 |= video_levels->burst << TV_BURST_LEVEL_SHIFT;
        scctl1 |= tv_mode->dda1_inc << TV_SCDDA1_INC_SHIFT;
 
        scctl2 = tv_mode->dda2_size << TV_SCDDA2_SIZE_SHIFT |
@@ -1416,16 +1413,16 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
         *  0 0 0 Component
         */
        if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
-               DRM_DEBUG("Detected Composite TV connection\n");
+               DRM_DEBUG_KMS("Detected Composite TV connection\n");
                type = DRM_MODE_CONNECTOR_Composite;
        } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
-               DRM_DEBUG("Detected S-Video TV connection\n");
+               DRM_DEBUG_KMS("Detected S-Video TV connection\n");
                type = DRM_MODE_CONNECTOR_SVIDEO;
        } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
-               DRM_DEBUG("Detected Component TV connection\n");
+               DRM_DEBUG_KMS("Detected Component TV connection\n");
                type = DRM_MODE_CONNECTOR_Component;
        } else {
-               DRM_DEBUG("No TV connection detected\n");
+               DRM_DEBUG_KMS("No TV connection detected\n");
                type = -1;
        }
 
@@ -1702,6 +1699,41 @@ static const struct drm_encoder_funcs intel_tv_enc_funcs = {
        .destroy = intel_tv_enc_destroy,
 };
 
+/*
+ * Enumerate the child dev array parsed from VBT to check whether
+ * the integrated TV is present.
+ * If it is present, return 1.
+ * If it is not present, return false.
+ * If no child dev is parsed from VBT, it assumes that the TV is present.
+ */
+static int tv_is_present_in_vbt(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct child_device_config *p_child;
+       int i, ret;
+
+       if (!dev_priv->child_dev_num)
+               return 1;
+
+       ret = 0;
+       for (i = 0; i < dev_priv->child_dev_num; i++) {
+               p_child = dev_priv->child_dev + i;
+               /*
+                * If the device type is not TV, continue.
+                */
+               if (p_child->device_type != DEVICE_TYPE_INT_TV &&
+                       p_child->device_type != DEVICE_TYPE_TV)
+                       continue;
+               /* Only when the addin_offset is non-zero, it is regarded
+                * as present.
+                */
+               if (p_child->addin_offset) {
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
 
 void
 intel_tv_init(struct drm_device *dev)
@@ -1717,6 +1749,10 @@ intel_tv_init(struct drm_device *dev)
        if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
                return;
 
+       if (!tv_is_present_in_vbt(dev)) {
+               DRM_DEBUG_KMS("Integrated TV is not present.\n");
+               return;
+       }
        /* Even if we have an encoder we may not have a connector */
        if (!dev_priv->int_tv_support)
                return;
index b5713ee..feb52ee 100644 (file)
@@ -49,7 +49,7 @@ radeon-y += radeon_device.o radeon_kms.o \
        radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
        rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
        r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
-       r600_blit_kms.o radeon_pm.o
+       r600_blit_kms.o radeon_pm.o atombios_dp.o
 
 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
 
index d67c425..6578d19 100644 (file)
@@ -263,10 +263,10 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
        case ATOM_ARG_FB:
                idx = U8(*ptr);
                (*ptr)++;
+               val = gctx->scratch[((gctx->fb_base + idx) / 4)];
                if (print)
                        DEBUG("FB[0x%02X]", idx);
-               printk(KERN_INFO "FB access is not implemented.\n");
-               return 0;
+               break;
        case ATOM_ARG_IMM:
                switch (align) {
                case ATOM_SRC_DWORD:
@@ -488,9 +488,9 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
        case ATOM_ARG_FB:
                idx = U8(*ptr);
                (*ptr)++;
+               gctx->scratch[((gctx->fb_base + idx) / 4)] = val;
                DEBUG("FB[0x%02X]", idx);
-               printk(KERN_INFO "FB access is not implemented.\n");
-               return;
+               break;
        case ATOM_ARG_PLL:
                idx = U8(*ptr);
                (*ptr)++;
@@ -1214,3 +1214,28 @@ void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,
                *crev = CU8(idx + 3);
        return;
 }
+
+int atom_allocate_fb_scratch(struct atom_context *ctx)
+{
+       int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
+       uint16_t data_offset;
+       int usage_bytes;
+       struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage;
+
+       atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset);
+
+       firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset);
+
+       DRM_DEBUG("atom firmware requested %08x %dkb\n",
+                 firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware,
+                 firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb);
+
+       usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024;
+       if (usage_bytes == 0)
+               usage_bytes = 20 * 1024;
+       /* allocate some scratch memory */
+       ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL);
+       if (!ctx->scratch)
+               return -ENOMEM;
+       return 0;
+}
index e6eb38f..6671848 100644 (file)
@@ -132,6 +132,7 @@ struct atom_context {
        uint8_t shift;
        int cs_equal, cs_above;
        int io_mode;
+       uint32_t *scratch;
 };
 
 extern int atom_debug;
@@ -142,6 +143,7 @@ int atom_asic_init(struct atom_context *);
 void atom_destroy(struct atom_context *);
 void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start);
 void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev);
+int atom_allocate_fb_scratch(struct atom_context *ctx);
 #include "atom-types.h"
 #include "atombios.h"
 #include "ObjectID.h"
index c11dddd..5f48515 100644 (file)
@@ -1141,7 +1141,7 @@ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS {
 /* ucTableFormatRevision=1,ucTableContentRevision=2 */
 typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 {
        USHORT usPixelClock;    /*  in 10KHz; for bios convenient */
-       UCHAR ucMisc;           /*  see PANEL_ENCODER_MISC_xx defintions below */
+       UCHAR ucMisc;           /*  see PANEL_ENCODER_MISC_xx definitions below */
        UCHAR ucAction;         /*  0: turn off encoder */
        /*  1: setup and turn on encoder */
        UCHAR ucTruncate;       /*  bit0=0: Disable truncate */
@@ -1424,7 +1424,7 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO {
 /*  Structures used in FirmwareInfoTable */
 /****************************************************************************/
 
-/*  usBIOSCapability Defintion: */
+/*  usBIOSCapability Definition: */
 /*  Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; */
 /*  Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; */
 /*  Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; */
@@ -2386,7 +2386,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
 } ATOM_ANALOG_TV_INFO_V1_2;
 
 /**************************************************************************/
-/*  VRAM usage and their defintions */
+/*  VRAM usage and their definitions */
 
 /*  One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. */
 /*  Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. */
@@ -2680,7 +2680,7 @@ typedef struct _ATOM_I2C_RECORD {
 typedef struct _ATOM_HPD_INT_RECORD {
        ATOM_COMMON_RECORD_HEADER sheader;
        UCHAR ucHPDIntGPIOID;   /* Corresponding block in GPIO_PIN_INFO table gives the pin info */
-       UCHAR ucPluggged_PinState;
+       UCHAR ucPlugged_PinState;
 } ATOM_HPD_INT_RECORD;
 
 typedef struct _ATOM_OUTPUT_PROTECTION_RECORD {
@@ -3046,7 +3046,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC     2
 #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3
 
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
 #define ATOM_S0_CRT1_MONOb0             0x01
 #define ATOM_S0_CRT1_COLORb0            0x02
 #define ATOM_S0_CRT1_MASKb0             (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0)
@@ -3131,7 +3131,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30
 #define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK   0xC0000000L
 
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
 #define ATOM_S2_TV1_STANDARD_MASKb0     0x0F
 #define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF
 #define ATOM_S2_CRT1_DPMS_STATEb2       0x01
@@ -3190,7 +3190,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S3_ALLOW_FAST_PWR_SWITCH   0x40000000L
 #define ATOM_S3_RQST_GPU_USE_MIN_PWR    0x80000000L
 
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
 #define ATOM_S3_CRT1_ACTIVEb0           0x01
 #define ATOM_S3_LCD1_ACTIVEb0           0x02
 #define ATOM_S3_TV1_ACTIVEb0            0x04
@@ -3230,7 +3230,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S4_LCD1_REFRESH_MASK       0x0000FF00L
 #define ATOM_S4_LCD1_REFRESH_SHIFT      8
 
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
 #define ATOM_S4_LCD1_PANEL_ID_MASKb0     0x0FF
 #define ATOM_S4_LCD1_REFRESH_MASKb1              ATOM_S4_LCD1_PANEL_ID_MASKb0
 #define ATOM_S4_VRAM_INFO_MASKb2        ATOM_S4_LCD1_PANEL_ID_MASKb0
@@ -3310,7 +3310,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
 #define ATOM_S6_VRI_BRIGHTNESS_CHANGE       0x40000000L
 #define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK  0x80000000L
 
-/* Byte aligned defintion for BIOS usage */
+/* Byte aligned definition for BIOS usage */
 #define ATOM_S6_DEVICE_CHANGEb0         0x01
 #define ATOM_S6_SCALER_CHANGEb0         0x02
 #define ATOM_S6_LID_CHANGEb0            0x04
index c15287a..260fcf5 100644 (file)
@@ -241,6 +241,7 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
        struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
 
        switch (mode) {
        case DRM_MODE_DPMS_ON:
@@ -248,20 +249,19 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
                if (ASIC_IS_DCE3(rdev))
                        atombios_enable_crtc_memreq(crtc, 1);
                atombios_blank_crtc(crtc, 0);
+               drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+               radeon_crtc_load_lut(crtc);
                break;
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
        case DRM_MODE_DPMS_OFF:
+               drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
                atombios_blank_crtc(crtc, 1);
                if (ASIC_IS_DCE3(rdev))
                        atombios_enable_crtc_memreq(crtc, 0);
                atombios_enable_crtc(crtc, 0);
                break;
        }
-
-       if (mode != DRM_MODE_DPMS_OFF) {
-               radeon_crtc_load_lut(crtc);
-       }
 }
 
 static void
@@ -457,9 +457,8 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
                                if (encoder->encoder_type !=
                                    DRM_MODE_ENCODER_DAC)
                                        pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
-                               if (!ASIC_IS_AVIVO(rdev)
-                                   && (encoder->encoder_type ==
-                                       DRM_MODE_ENCODER_LVDS))
+                               if (encoder->encoder_type ==
+                                       DRM_MODE_ENCODER_LVDS)
                                        pll_flags |= RADEON_PLL_USE_REF_DIV;
                        }
                        radeon_encoder = to_radeon_encoder(encoder);
@@ -500,8 +499,18 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
        else
                pll = &rdev->clock.p2pll;
 
-       radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
-                          &ref_div, &post_div, pll_flags);
+       if (ASIC_IS_AVIVO(rdev)) {
+               if (radeon_new_pll)
+                       radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock,
+                                                &fb_div, &frac_fb_div,
+                                                &ref_div, &post_div, pll_flags);
+               else
+                       radeon_compute_pll(pll, adjusted_clock, &pll_clock,
+                                          &fb_div, &frac_fb_div,
+                                          &ref_div, &post_div, pll_flags);
+       } else
+               radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
+                                  &ref_div, &post_div, pll_flags);
 
        index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
        atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
@@ -574,21 +583,32 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_framebuffer *radeon_fb;
        struct drm_gem_object *obj;
-       struct drm_radeon_gem_object *obj_priv;
+       struct radeon_bo *rbo;
        uint64_t fb_location;
        uint32_t fb_format, fb_pitch_pixels, tiling_flags;
+       int r;
 
-       if (!crtc->fb)
-               return -EINVAL;
+       /* no fb bound */
+       if (!crtc->fb) {
+               DRM_DEBUG("No FB bound\n");
+               return 0;
+       }
 
        radeon_fb = to_radeon_framebuffer(crtc->fb);
 
+       /* Pin framebuffer & get tilling informations */
        obj = radeon_fb->obj;
-       obj_priv = obj->driver_private;
-
-       if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &fb_location)) {
+       rbo = obj->driver_private;
+       r = radeon_bo_reserve(rbo, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
+       if (unlikely(r != 0)) {
+               radeon_bo_unreserve(rbo);
                return -EINVAL;
        }
+       radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
+       radeon_bo_unreserve(rbo);
 
        switch (crtc->fb->bits_per_pixel) {
        case 8:
@@ -618,8 +638,6 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
                return -EINVAL;
        }
 
-       radeon_object_get_tiling_flags(obj->driver_private,
-                                      &tiling_flags, NULL);
        if (tiling_flags & RADEON_TILING_MACRO)
                fb_format |= AVIVO_D1GRPH_MACRO_ADDRESS_MODE;
 
@@ -674,7 +692,12 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
 
        if (old_fb && old_fb != crtc->fb) {
                radeon_fb = to_radeon_framebuffer(old_fb);
-               radeon_gem_object_unpin(radeon_fb->obj);
+               rbo = radeon_fb->obj->driver_private;
+               r = radeon_bo_reserve(rbo, false);
+               if (unlikely(r != 0))
+                       return r;
+               radeon_bo_unpin(rbo);
+               radeon_bo_unreserve(rbo);
        }
 
        /* Bytes per pixel may have changed */
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
new file mode 100644 (file)
index 0000000..0d63c44
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * Copyright 2007-8 Advanced Micro Devices, Inc.
+ * Copyright 2008 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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: Dave Airlie
+ *          Alex Deucher
+ */
+#include "drmP.h"
+#include "radeon_drm.h"
+#include "radeon.h"
+
+#include "atom.h"
+#include "atom-bits.h"
+#include "drm_dp_helper.h"
+
+/* move these to drm_dp_helper.c/h */
+#define DP_LINK_CONFIGURATION_SIZE 9
+#define DP_LINK_STATUS_SIZE       6
+#define DP_DPCD_SIZE              8
+
+static char *voltage_names[] = {
+        "0.4V", "0.6V", "0.8V", "1.2V"
+};
+static char *pre_emph_names[] = {
+        "0dB", "3.5dB", "6dB", "9.5dB"
+};
+
+static const int dp_clocks[] = {
+       54000,  /* 1 lane, 1.62 Ghz */
+       90000,  /* 1 lane, 2.70 Ghz */
+       108000, /* 2 lane, 1.62 Ghz */
+       180000, /* 2 lane, 2.70 Ghz */
+       216000, /* 4 lane, 1.62 Ghz */
+       360000, /* 4 lane, 2.70 Ghz */
+};
+
+static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int);
+
+/* common helper functions */
+static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+{
+       int i;
+       u8 max_link_bw;
+       u8 max_lane_count;
+
+       if (!dpcd)
+               return 0;
+
+       max_link_bw = dpcd[DP_MAX_LINK_RATE];
+       max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+
+       switch (max_link_bw) {
+       case DP_LINK_BW_1_62:
+       default:
+               for (i = 0; i < num_dp_clocks; i++) {
+                       if (i % 2)
+                               continue;
+                       switch (max_lane_count) {
+                       case 1:
+                               if (i > 1)
+                                       return 0;
+                               break;
+                       case 2:
+                               if (i > 3)
+                                       return 0;
+                               break;
+                       case 4:
+                       default:
+                               break;
+                       }
+                       if (dp_clocks[i] > mode_clock) {
+                               if (i < 2)
+                                       return 1;
+                               else if (i < 4)
+                                       return 2;
+                               else
+                                       return 4;
+                       }
+               }
+               break;
+       case DP_LINK_BW_2_7:
+               for (i = 0; i < num_dp_clocks; i++) {
+                       switch (max_lane_count) {
+                       case 1:
+                               if (i > 1)
+                                       return 0;
+                               break;
+                       case 2:
+                               if (i > 3)
+                                       return 0;
+                               break;
+                       case 4:
+                       default:
+                               break;
+                       }
+                       if (dp_clocks[i] > mode_clock) {
+                               if (i < 2)
+                                       return 1;
+                               else if (i < 4)
+                                       return 2;
+                               else
+                                       return 4;
+                       }
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+{
+       int i;
+       u8 max_link_bw;
+       u8 max_lane_count;
+
+       if (!dpcd)
+               return 0;
+
+       max_link_bw = dpcd[DP_MAX_LINK_RATE];
+       max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
+
+       switch (max_link_bw) {
+       case DP_LINK_BW_1_62:
+       default:
+               for (i = 0; i < num_dp_clocks; i++) {
+                       if (i % 2)
+                               continue;
+                       switch (max_lane_count) {
+                       case 1:
+                               if (i > 1)
+                                       return 0;
+                               break;
+                       case 2:
+                               if (i > 3)
+                                       return 0;
+                               break;
+                       case 4:
+                       default:
+                               break;
+                       }
+                       if (dp_clocks[i] > mode_clock)
+                               return 162000;
+               }
+               break;
+       case DP_LINK_BW_2_7:
+               for (i = 0; i < num_dp_clocks; i++) {
+                       switch (max_lane_count) {
+                       case 1:
+                               if (i > 1)
+                                       return 0;
+                               break;
+                       case 2:
+                               if (i > 3)
+                                       return 0;
+                               break;
+                       case 4:
+                       default:
+                               break;
+                       }
+                       if (dp_clocks[i] > mode_clock)
+                               return (i % 2) ? 270000 : 162000;
+               }
+       }
+
+       return 0;
+}
+
+int dp_mode_valid(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
+{
+       int lanes = dp_lanes_for_mode_clock(dpcd, mode_clock);
+       int bw = dp_lanes_for_mode_clock(dpcd, mode_clock);
+
+       if ((lanes == 0) || (bw == 0))
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+       return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE],
+                            int lane)
+{
+       int i = DP_LANE0_1_STATUS + (lane >> 1);
+       int s = (lane & 1) * 4;
+       u8 l = dp_link_status(link_status, i);
+       return (l >> s) & 0xf;
+}
+
+static bool dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE],
+                                int lane_count)
+{
+       int lane;
+       u8 lane_status;
+
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = dp_get_lane_status(link_status, lane);
+               if ((lane_status & DP_LANE_CR_DONE) == 0)
+                       return false;
+       }
+       return true;
+}
+
+static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
+                            int lane_count)
+{
+       u8 lane_align;
+       u8 lane_status;
+       int lane;
+
+       lane_align = dp_link_status(link_status,
+                                   DP_LANE_ALIGN_STATUS_UPDATED);
+       if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
+               return false;
+       for (lane = 0; lane < lane_count; lane++) {
+               lane_status = dp_get_lane_status(link_status, lane);
+               if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
+                       return false;
+       }
+       return true;
+}
+
+static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                                       int lane)
+
+{
+       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int s = ((lane & 1) ?
+                DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+                DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+       u8 l = dp_link_status(link_status, i);
+
+       return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
+                                            int lane)
+{
+       int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+       int s = ((lane & 1) ?
+                DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+                DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+       u8 l = dp_link_status(link_status, i);
+
+       return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+/* XXX fix me -- chip specific */
+#define DP_VOLTAGE_MAX         DP_TRAIN_VOLTAGE_SWING_1200
+static u8 dp_pre_emphasis_max(u8 voltage_swing)
+{
+       switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+       case DP_TRAIN_VOLTAGE_SWING_400:
+               return DP_TRAIN_PRE_EMPHASIS_6;
+       case DP_TRAIN_VOLTAGE_SWING_600:
+               return DP_TRAIN_PRE_EMPHASIS_6;
+       case DP_TRAIN_VOLTAGE_SWING_800:
+               return DP_TRAIN_PRE_EMPHASIS_3_5;
+       case DP_TRAIN_VOLTAGE_SWING_1200:
+       default:
+               return DP_TRAIN_PRE_EMPHASIS_0;
+       }
+}
+
+static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
+                               int lane_count,
+                               u8 train_set[4])
+{
+       u8 v = 0;
+       u8 p = 0;
+       int lane;
+
+       for (lane = 0; lane < lane_count; lane++) {
+               u8 this_v = dp_get_adjust_request_voltage(link_status, lane);
+               u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane);
+
+               DRM_DEBUG("requested signal parameters: lane %d voltage %s pre_emph %s\n",
+                         lane,
+                         voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
+                         pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
+
+               if (this_v > v)
+                       v = this_v;
+               if (this_p > p)
+                       p = this_p;
+       }
+
+       if (v >= DP_VOLTAGE_MAX)
+               v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
+
+       if (p >= dp_pre_emphasis_max(v))
+               p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+       DRM_DEBUG("using signal parameters: voltage %s pre_emph %s\n",
+                 voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
+                 pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
+
+       for (lane = 0; lane < 4; lane++)
+               train_set[lane] = v | p;
+}
+
+
+/* radeon aux chan functions */
+bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
+                          int num_bytes, u8 *read_byte,
+                          u8 read_buf_len, u8 delay)
+{
+       struct drm_device *dev = chan->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION args;
+       int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
+       unsigned char *base;
+
+       memset(&args, 0, sizeof(args));
+
+       base = (unsigned char *)rdev->mode_info.atom_context->scratch;
+
+       memcpy(base, req_bytes, num_bytes);
+
+       args.lpAuxRequest = 0;
+       args.lpDataOut = 16;
+       args.ucDataOutLen = 0;
+       args.ucChannelID = chan->rec.i2c_id;
+       args.ucDelay = delay / 10;
+
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+       if (args.ucReplyStatus) {
+               DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x\n",
+                         req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
+                         chan->rec.i2c_id, args.ucReplyStatus);
+               return false;
+       }
+
+       if (args.ucDataOutLen && read_byte && read_buf_len) {
+               if (read_buf_len < args.ucDataOutLen) {
+                       DRM_ERROR("Buffer to small for return answer %d %d\n",
+                                 read_buf_len, args.ucDataOutLen);
+                       return false;
+               }
+               {
+                       int len = min(read_buf_len, args.ucDataOutLen);
+                       memcpy(read_byte, base + 16, len);
+               }
+       }
+       return true;
+}
+
+bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address,
+                               uint8_t send_bytes, uint8_t *send)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       u8 msg[20];
+       u8 msg_len, dp_msg_len;
+       bool ret;
+
+       dp_msg_len = 4;
+       msg[0] = address;
+       msg[1] = address >> 8;
+       msg[2] = AUX_NATIVE_WRITE << 4;
+       dp_msg_len += send_bytes;
+       msg[3] = (dp_msg_len << 4) | (send_bytes - 1);
+
+       if (send_bytes > 16)
+               return false;
+
+       memcpy(&msg[4], send, send_bytes);
+       msg_len = 4 + send_bytes;
+       ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0);
+       return ret;
+}
+
+bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16_t address,
+                              uint8_t delay, uint8_t expected_bytes,
+                              uint8_t *read_p)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       u8 msg[20];
+       u8 msg_len, dp_msg_len;
+       bool ret = false;
+       msg_len = 4;
+       dp_msg_len = 4;
+       msg[0] = address;
+       msg[1] = address >> 8;
+       msg[2] = AUX_NATIVE_READ << 4;
+       msg[3] = (dp_msg_len) << 4;
+       msg[3] |= expected_bytes - 1;
+
+       ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay);
+       return ret;
+}
+
+/* radeon dp functions */
+static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock,
+                                   uint8_t ucconfig, uint8_t lane_num)
+{
+       DP_ENCODER_SERVICE_PARAMETERS args;
+       int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
+
+       memset(&args, 0, sizeof(args));
+       args.ucLinkClock = dp_clock / 10;
+       args.ucConfig = ucconfig;
+       args.ucAction = action;
+       args.ucLaneNum = lane_num;
+       args.ucStatus = 0;
+
+       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+       return args.ucStatus;
+}
+
+u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       struct drm_device *dev = radeon_connector->base.dev;
+       struct radeon_device *rdev = dev->dev_private;
+
+       return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0,
+                                        dig_connector->dp_i2c_bus->rec.i2c_id, 0);
+}
+
+bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       u8 msg[25];
+       int ret;
+
+       ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg);
+       if (ret) {
+               memcpy(dig_connector->dpcd, msg, 8);
+               {
+                       int i;
+                       DRM_DEBUG("DPCD: ");
+                       for (i = 0; i < 8; i++)
+                               DRM_DEBUG("%02x ", msg[i]);
+                       DRM_DEBUG("\n");
+               }
+               return true;
+       }
+       dig_connector->dpcd[0] = 0;
+       return false;
+}
+
+void radeon_dp_set_link_config(struct drm_connector *connector,
+                              struct drm_display_mode *mode)
+{
+       struct radeon_connector *radeon_connector;
+       struct radeon_connector_atom_dig *dig_connector;
+
+       if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+               return;
+
+       radeon_connector = to_radeon_connector(connector);
+       if (!radeon_connector->con_priv)
+               return;
+       dig_connector = radeon_connector->con_priv;
+
+       dig_connector->dp_clock =
+               dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock);
+       dig_connector->dp_lane_count =
+               dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock);
+}
+
+int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
+                               struct drm_display_mode *mode)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+
+       return dp_mode_valid(dig_connector->dpcd, mode->clock);
+}
+
+static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
+                                   u8 link_status[DP_LINK_STATUS_SIZE])
+{
+       int ret;
+       ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, 100,
+                                       DP_LINK_STATUS_SIZE, link_status);
+       if (!ret) {
+               DRM_ERROR("displayport link status failed\n");
+               return false;
+       }
+
+       DRM_DEBUG("link status %02x %02x %02x %02x %02x %02x\n",
+                 link_status[0], link_status[1], link_status[2],
+                 link_status[3], link_status[4], link_status[5]);
+       return true;
+}
+
+bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       u8 link_status[DP_LINK_STATUS_SIZE];
+
+       if (!atom_dp_get_link_status(radeon_connector, link_status))
+               return false;
+       if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
+               return false;
+       return true;
+}
+
+static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+
+       if (dig_connector->dpcd[0] >= 0x11) {
+               radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1,
+                                          &power_state);
+       }
+}
+
+static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread)
+{
+       radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1,
+                                  &downspread);
+}
+
+static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector,
+                                u8 link_configuration[DP_LINK_CONFIGURATION_SIZE])
+{
+       radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, 2,
+                                  link_configuration);
+}
+
+static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector,
+                               struct drm_encoder *encoder,
+                               u8 train_set[4])
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       int i;
+
+       for (i = 0; i < dig_connector->dp_lane_count; i++)
+               atombios_dig_transmitter_setup(encoder,
+                                              ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
+                                              i, train_set[i]);
+
+       radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET,
+                                  dig_connector->dp_lane_count, train_set);
+}
+
+static void dp_set_training(struct radeon_connector *radeon_connector,
+                           u8 training)
+{
+       radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET,
+                                  1, &training);
+}
+
+void dp_link_train(struct drm_encoder *encoder,
+                  struct drm_connector *connector)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_atom_dig *dig;
+       struct radeon_connector *radeon_connector;
+       struct radeon_connector_atom_dig *dig_connector;
+       int enc_id = 0;
+       bool clock_recovery, channel_eq;
+       u8 link_status[DP_LINK_STATUS_SIZE];
+       u8 link_configuration[DP_LINK_CONFIGURATION_SIZE];
+       u8 tries, voltage;
+       u8 train_set[4];
+       int i;
+
+       if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+               return;
+
+       if (!radeon_encoder->enc_priv)
+               return;
+       dig = radeon_encoder->enc_priv;
+
+       radeon_connector = to_radeon_connector(connector);
+       if (!radeon_connector->con_priv)
+               return;
+       dig_connector = radeon_connector->con_priv;
+
+       if (ASIC_IS_DCE32(rdev)) {
+               if (dig->dig_block)
+                       enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
+               else
+                       enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
+               if (dig_connector->linkb)
+                       enc_id |= ATOM_DP_CONFIG_LINK_B;
+               else
+                       enc_id |= ATOM_DP_CONFIG_LINK_A;
+       } else {
+               if (dig_connector->linkb)
+                       enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER | ATOM_DP_CONFIG_LINK_B;
+               else
+                       enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER | ATOM_DP_CONFIG_LINK_A;
+       }
+
+       memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
+       if (dig_connector->dp_clock == 270000)
+               link_configuration[0] = DP_LINK_BW_2_7;
+       else
+               link_configuration[0] = DP_LINK_BW_1_62;
+       link_configuration[1] = dig_connector->dp_lane_count;
+       if (dig_connector->dpcd[0] >= 0x11)
+               link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+       /* power up the sink */
+       dp_set_power(radeon_connector, DP_SET_POWER_D0);
+       /* disable the training pattern on the sink */
+       dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
+       /* set link bw and lanes on the sink */
+       dp_set_link_bw_lanes(radeon_connector, link_configuration);
+       /* disable downspread on the sink */
+       dp_set_downspread(radeon_connector, 0);
+       /* start training on the source */
+       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
+                                 dig_connector->dp_clock, enc_id, 0);
+       /* set training pattern 1 on the source */
+       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
+                                 dig_connector->dp_clock, enc_id, 0);
+
+       /* set initial vs/emph */
+       memset(train_set, 0, 4);
+       udelay(400);
+       /* set training pattern 1 on the sink */
+       dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1);
+
+       dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+
+       /* clock recovery loop */
+       clock_recovery = false;
+       tries = 0;
+       voltage = 0xff;
+       for (;;) {
+               udelay(100);
+               if (!atom_dp_get_link_status(radeon_connector, link_status))
+                       break;
+
+               if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) {
+                       clock_recovery = true;
+                       break;
+               }
+
+               for (i = 0; i < dig_connector->dp_lane_count; i++) {
+                       if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+                               break;
+               }
+               if (i == dig_connector->dp_lane_count) {
+                       DRM_ERROR("clock recovery reached max voltage\n");
+                       break;
+               }
+
+               if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+                       ++tries;
+                       if (tries == 5) {
+                               DRM_ERROR("clock recovery tried 5 times\n");
+                               break;
+                       }
+               } else
+                       tries = 0;
+
+               voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+               /* Compute new train_set as requested by sink */
+               dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
+               dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+       }
+       if (!clock_recovery)
+               DRM_ERROR("clock recovery failed\n");
+       else
+               DRM_DEBUG("clock recovery at voltage %d pre-emphasis %d\n",
+                         train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+                         (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+                         DP_TRAIN_PRE_EMPHASIS_SHIFT);
+
+
+       /* set training pattern 2 on the sink */
+       dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
+       /* set training pattern 2 on the source */
+       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
+                                 dig_connector->dp_clock, enc_id, 1);
+
+       /* channel equalization loop */
+       tries = 0;
+       channel_eq = false;
+       for (;;) {
+               udelay(400);
+               if (!atom_dp_get_link_status(radeon_connector, link_status))
+                       break;
+
+               if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) {
+                       channel_eq = true;
+                       break;
+               }
+
+               /* Try 5 times */
+               if (tries > 5) {
+                       DRM_ERROR("channel eq failed: 5 tries\n");
+                       break;
+               }
+
+               /* Compute new train_set as requested by sink */
+               dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
+               dp_update_dpvs_emph(radeon_connector, encoder, train_set);
+
+               tries++;
+       }
+
+       if (!channel_eq)
+               DRM_ERROR("channel eq failed\n");
+       else
+               DRM_DEBUG("channel eq at voltage %d pre-emphasis %d\n",
+                         train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
+                         (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
+                         >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
+
+       /* disable the training pattern on the sink */
+       dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
+
+       radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
+                                 dig_connector->dp_clock, enc_id, 0);
+}
+
+int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+                        uint8_t write_byte, uint8_t *read_byte)
+{
+       struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+       struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
+       int ret = 0;
+       uint16_t address = algo_data->address;
+       uint8_t msg[5];
+       uint8_t reply[2];
+       int msg_len, dp_msg_len;
+       int reply_bytes;
+
+       /* Set up the command byte */
+       if (mode & MODE_I2C_READ)
+               msg[2] = AUX_I2C_READ << 4;
+       else
+               msg[2] = AUX_I2C_WRITE << 4;
+
+       if (!(mode & MODE_I2C_STOP))
+               msg[2] |= AUX_I2C_MOT << 4;
+
+       msg[0] = address;
+       msg[1] = address >> 8;
+
+       reply_bytes = 1;
+
+       msg_len = 4;
+       dp_msg_len = 3;
+       switch (mode) {
+       case MODE_I2C_WRITE:
+               msg[4] = write_byte;
+               msg_len++;
+               dp_msg_len += 2;
+               break;
+       case MODE_I2C_READ:
+               dp_msg_len += 1;
+               break;
+       default:
+               break;
+       }
+
+       msg[3] = (dp_msg_len) << 4;
+       ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0);
+
+       if (ret) {
+               if (read_byte)
+                       *read_byte = reply[0];
+               return reply_bytes;
+       }
+       return -EREMOTEIO;
+}
+
index c9e93ea..824cc64 100644 (file)
@@ -65,6 +65,95 @@ MODULE_FIRMWARE(FIRMWARE_R520);
  * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
  */
 
+/* hpd for digital panel detect/disconnect */
+bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
+{
+       bool connected = false;
+
+       switch (hpd) {
+       case RADEON_HPD_1:
+               if (RREG32(RADEON_FP_GEN_CNTL) & RADEON_FP_DETECT_SENSE)
+                       connected = true;
+               break;
+       case RADEON_HPD_2:
+               if (RREG32(RADEON_FP2_GEN_CNTL) & RADEON_FP2_DETECT_SENSE)
+                       connected = true;
+               break;
+       default:
+               break;
+       }
+       return connected;
+}
+
+void r100_hpd_set_polarity(struct radeon_device *rdev,
+                          enum radeon_hpd_id hpd)
+{
+       u32 tmp;
+       bool connected = r100_hpd_sense(rdev, hpd);
+
+       switch (hpd) {
+       case RADEON_HPD_1:
+               tmp = RREG32(RADEON_FP_GEN_CNTL);
+               if (connected)
+                       tmp &= ~RADEON_FP_DETECT_INT_POL;
+               else
+                       tmp |= RADEON_FP_DETECT_INT_POL;
+               WREG32(RADEON_FP_GEN_CNTL, tmp);
+               break;
+       case RADEON_HPD_2:
+               tmp = RREG32(RADEON_FP2_GEN_CNTL);
+               if (connected)
+                       tmp &= ~RADEON_FP2_DETECT_INT_POL;
+               else
+                       tmp |= RADEON_FP2_DETECT_INT_POL;
+               WREG32(RADEON_FP2_GEN_CNTL, tmp);
+               break;
+       default:
+               break;
+       }
+}
+
+void r100_hpd_init(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               switch (radeon_connector->hpd.hpd) {
+               case RADEON_HPD_1:
+                       rdev->irq.hpd[0] = true;
+                       break;
+               case RADEON_HPD_2:
+                       rdev->irq.hpd[1] = true;
+                       break;
+               default:
+                       break;
+               }
+       }
+       r100_irq_set(rdev);
+}
+
+void r100_hpd_fini(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               switch (radeon_connector->hpd.hpd) {
+               case RADEON_HPD_1:
+                       rdev->irq.hpd[0] = false;
+                       break;
+               case RADEON_HPD_2:
+                       rdev->irq.hpd[1] = false;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
 /*
  * PCI GART
  */
@@ -94,6 +183,15 @@ int r100_pci_gart_init(struct radeon_device *rdev)
        return radeon_gart_table_ram_alloc(rdev);
 }
 
+/* required on r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */
+void r100_enable_bm(struct radeon_device *rdev)
+{
+       uint32_t tmp;
+       /* Enable bus mastering */
+       tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
+       WREG32(RADEON_BUS_CNTL, tmp);
+}
+
 int r100_pci_gart_enable(struct radeon_device *rdev)
 {
        uint32_t tmp;
@@ -105,9 +203,6 @@ int r100_pci_gart_enable(struct radeon_device *rdev)
        WREG32(RADEON_AIC_LO_ADDR, rdev->mc.gtt_location);
        tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
        WREG32(RADEON_AIC_HI_ADDR, tmp);
-       /* Enable bus mastering */
-       tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
-       WREG32(RADEON_BUS_CNTL, tmp);
        /* set PCI GART page-table base address */
        WREG32(RADEON_AIC_PT_BASE, rdev->gart.table_addr);
        tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN;
@@ -157,6 +252,12 @@ int r100_irq_set(struct radeon_device *rdev)
        if (rdev->irq.crtc_vblank_int[1]) {
                tmp |= RADEON_CRTC2_VBLANK_MASK;
        }
+       if (rdev->irq.hpd[0]) {
+               tmp |= RADEON_FP_DETECT_MASK;
+       }
+       if (rdev->irq.hpd[1]) {
+               tmp |= RADEON_FP2_DETECT_MASK;
+       }
        WREG32(RADEON_GEN_INT_CNTL, tmp);
        return 0;
 }
@@ -175,8 +276,9 @@ void r100_irq_disable(struct radeon_device *rdev)
 static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
 {
        uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
-       uint32_t irq_mask = RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT |
-               RADEON_CRTC2_VBLANK_STAT;
+       uint32_t irq_mask = RADEON_SW_INT_TEST |
+               RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
+               RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT;
 
        if (irqs) {
                WREG32(RADEON_GEN_INT_STATUS, irqs);
@@ -187,6 +289,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
 int r100_irq_process(struct radeon_device *rdev)
 {
        uint32_t status, msi_rearm;
+       bool queue_hotplug = false;
 
        status = r100_irq_ack(rdev);
        if (!status) {
@@ -207,8 +310,18 @@ int r100_irq_process(struct radeon_device *rdev)
                if (status & RADEON_CRTC2_VBLANK_STAT) {
                        drm_handle_vblank(rdev->ddev, 1);
                }
+               if (status & RADEON_FP_DETECT_STAT) {
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD1\n");
+               }
+               if (status & RADEON_FP2_DETECT_STAT) {
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD2\n");
+               }
                status = r100_irq_ack(rdev);
        }
+       if (queue_hotplug)
+               queue_work(rdev->wq, &rdev->hotplug_work);
        if (rdev->msi_enabled) {
                switch (rdev->family) {
                case CHIP_RS400:
@@ -255,24 +368,27 @@ int r100_wb_init(struct radeon_device *rdev)
        int r;
 
        if (rdev->wb.wb_obj == NULL) {
-               r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE,
-                                        true,
-                                        RADEON_GEM_DOMAIN_GTT,
-                                        false, &rdev->wb.wb_obj);
+               r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
+                                       RADEON_GEM_DOMAIN_GTT,
+                                       &rdev->wb.wb_obj);
                if (r) {
-                       DRM_ERROR("radeon: failed to create WB buffer (%d).\n", r);
+                       dev_err(rdev->dev, "(%d) create WB buffer failed\n", r);
                        return r;
                }
-               r = radeon_object_pin(rdev->wb.wb_obj,
-                                     RADEON_GEM_DOMAIN_GTT,
-                                     &rdev->wb.gpu_addr);
+               r = radeon_bo_reserve(rdev->wb.wb_obj, false);
+               if (unlikely(r != 0))
+                       return r;
+               r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
+                                       &rdev->wb.gpu_addr);
                if (r) {
-                       DRM_ERROR("radeon: failed to pin WB buffer (%d).\n", r);
+                       dev_err(rdev->dev, "(%d) pin WB buffer failed\n", r);
+                       radeon_bo_unreserve(rdev->wb.wb_obj);
                        return r;
                }
-               r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
+               r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
+               radeon_bo_unreserve(rdev->wb.wb_obj);
                if (r) {
-                       DRM_ERROR("radeon: failed to map WB buffer (%d).\n", r);
+                       dev_err(rdev->dev, "(%d) map WB buffer failed\n", r);
                        return r;
                }
        }
@@ -290,11 +406,19 @@ void r100_wb_disable(struct radeon_device *rdev)
 
 void r100_wb_fini(struct radeon_device *rdev)
 {
+       int r;
+
        r100_wb_disable(rdev);
        if (rdev->wb.wb_obj) {
-               radeon_object_kunmap(rdev->wb.wb_obj);
-               radeon_object_unpin(rdev->wb.wb_obj);
-               radeon_object_unref(&rdev->wb.wb_obj);
+               r = radeon_bo_reserve(rdev->wb.wb_obj, false);
+               if (unlikely(r != 0)) {
+                       dev_err(rdev->dev, "(%d) can't finish WB\n", r);
+                       return;
+               }
+               radeon_bo_kunmap(rdev->wb.wb_obj);
+               radeon_bo_unpin(rdev->wb.wb_obj);
+               radeon_bo_unreserve(rdev->wb.wb_obj);
+               radeon_bo_unref(&rdev->wb.wb_obj);
                rdev->wb.wb = NULL;
                rdev->wb.wb_obj = NULL;
        }
@@ -1288,17 +1412,17 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
 
 int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p,
                                         struct radeon_cs_packet *pkt,
-                                        struct radeon_object *robj)
+                                        struct radeon_bo *robj)
 {
        unsigned idx;
        u32 value;
        idx = pkt->idx + 1;
        value = radeon_get_ib_value(p, idx + 2);
-       if ((value + 1) > radeon_object_size(robj)) {
+       if ((value + 1) > radeon_bo_size(robj)) {
                DRM_ERROR("[drm] Buffer too small for PACKET3 INDX_BUFFER "
                          "(need %u have %lu) !\n",
                          value + 1,
-                         radeon_object_size(robj));
+                         radeon_bo_size(robj));
                return -EINVAL;
        }
        return 0;
@@ -1583,6 +1707,14 @@ void r100_gpu_init(struct radeon_device *rdev)
        r100_hdp_reset(rdev);
 }
 
+void r100_hdp_flush(struct radeon_device *rdev)
+{
+       u32 tmp;
+       tmp = RREG32(RADEON_HOST_PATH_CNTL);
+       tmp |= RADEON_HDP_READ_BUFFER_INVALIDATE;
+       WREG32(RADEON_HOST_PATH_CNTL, tmp);
+}
+
 void r100_hdp_reset(struct radeon_device *rdev)
 {
        uint32_t tmp;
@@ -1650,6 +1782,17 @@ int r100_gpu_reset(struct radeon_device *rdev)
        return 0;
 }
 
+void r100_set_common_regs(struct radeon_device *rdev)
+{
+       /* set these so they don't interfere with anything */
+       WREG32(RADEON_OV0_SCALE_CNTL, 0);
+       WREG32(RADEON_SUBPIC_CNTL, 0);
+       WREG32(RADEON_VIPH_CONTROL, 0);
+       WREG32(RADEON_I2C_CNTL_1, 0);
+       WREG32(RADEON_DVI_I2C_CNTL_1, 0);
+       WREG32(RADEON_CAP0_TRIG_CNTL, 0);
+       WREG32(RADEON_CAP1_TRIG_CNTL, 0);
+}
 
 /*
  * VRAM info
@@ -2594,7 +2737,7 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
                              struct r100_cs_track *track, unsigned idx)
 {
        unsigned face, w, h;
-       struct radeon_object *cube_robj;
+       struct radeon_bo *cube_robj;
        unsigned long size;
 
        for (face = 0; face < 5; face++) {
@@ -2607,9 +2750,9 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
 
                size += track->textures[idx].cube_info[face].offset;
 
-               if (size > radeon_object_size(cube_robj)) {
+               if (size > radeon_bo_size(cube_robj)) {
                        DRM_ERROR("Cube texture offset greater than object size %lu %lu\n",
-                                 size, radeon_object_size(cube_robj));
+                                 size, radeon_bo_size(cube_robj));
                        r100_cs_track_texture_print(&track->textures[idx]);
                        return -1;
                }
@@ -2620,7 +2763,7 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
 static int r100_cs_track_texture_check(struct radeon_device *rdev,
                                       struct r100_cs_track *track)
 {
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        unsigned long size;
        unsigned u, i, w, h;
        int ret;
@@ -2676,9 +2819,9 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev,
                                  "%u\n", track->textures[u].tex_coord_type, u);
                        return -EINVAL;
                }
-               if (size > radeon_object_size(robj)) {
+               if (size > radeon_bo_size(robj)) {
                        DRM_ERROR("Texture of unit %u needs %lu bytes but is "
-                                 "%lu\n", u, size, radeon_object_size(robj));
+                                 "%lu\n", u, size, radeon_bo_size(robj));
                        r100_cs_track_texture_print(&track->textures[u]);
                        return -EINVAL;
                }
@@ -2700,10 +2843,10 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
                }
                size = track->cb[i].pitch * track->cb[i].cpp * track->maxy;
                size += track->cb[i].offset;
-               if (size > radeon_object_size(track->cb[i].robj)) {
+               if (size > radeon_bo_size(track->cb[i].robj)) {
                        DRM_ERROR("[drm] Buffer too small for color buffer %d "
                                  "(need %lu have %lu) !\n", i, size,
-                                 radeon_object_size(track->cb[i].robj));
+                                 radeon_bo_size(track->cb[i].robj));
                        DRM_ERROR("[drm] color buffer %d (%u %u %u %u)\n",
                                  i, track->cb[i].pitch, track->cb[i].cpp,
                                  track->cb[i].offset, track->maxy);
@@ -2717,10 +2860,10 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
                }
                size = track->zb.pitch * track->zb.cpp * track->maxy;
                size += track->zb.offset;
-               if (size > radeon_object_size(track->zb.robj)) {
+               if (size > radeon_bo_size(track->zb.robj)) {
                        DRM_ERROR("[drm] Buffer too small for z buffer "
                                  "(need %lu have %lu) !\n", size,
-                                 radeon_object_size(track->zb.robj));
+                                 radeon_bo_size(track->zb.robj));
                        DRM_ERROR("[drm] zbuffer (%u %u %u %u)\n",
                                  track->zb.pitch, track->zb.cpp,
                                  track->zb.offset, track->maxy);
@@ -2738,11 +2881,12 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
                                          "bound\n", prim_walk, i);
                                return -EINVAL;
                        }
-                       if (size > radeon_object_size(track->arrays[i].robj)) {
-                               DRM_ERROR("(PW %u) Vertex array %u need %lu dwords "
-                                          "have %lu dwords\n", prim_walk, i,
-                                          size >> 2,
-                                          radeon_object_size(track->arrays[i].robj) >> 2);
+                       if (size > radeon_bo_size(track->arrays[i].robj)) {
+                               dev_err(rdev->dev, "(PW %u) Vertex array %u "
+                                       "need %lu dwords have %lu dwords\n",
+                                       prim_walk, i, size >> 2,
+                                       radeon_bo_size(track->arrays[i].robj)
+                                       >> 2);
                                DRM_ERROR("Max indices %u\n", track->max_indx);
                                return -EINVAL;
                        }
@@ -2756,10 +2900,12 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
                                          "bound\n", prim_walk, i);
                                return -EINVAL;
                        }
-                       if (size > radeon_object_size(track->arrays[i].robj)) {
-                               DRM_ERROR("(PW %u) Vertex array %u need %lu dwords "
-                                          "have %lu dwords\n", prim_walk, i, size >> 2,
-                                          radeon_object_size(track->arrays[i].robj) >> 2);
+                       if (size > radeon_bo_size(track->arrays[i].robj)) {
+                               dev_err(rdev->dev, "(PW %u) Vertex array %u "
+                                       "need %lu dwords have %lu dwords\n",
+                                       prim_walk, i, size >> 2,
+                                       radeon_bo_size(track->arrays[i].robj)
+                                       >> 2);
                                return -EINVAL;
                        }
                }
@@ -3101,6 +3247,9 @@ static int r100_startup(struct radeon_device *rdev)
 {
        int r;
 
+       /* set common regs */
+       r100_set_common_regs(rdev);
+       /* program mc */
        r100_mc_program(rdev);
        /* Resume clock */
        r100_clock_startup(rdev);
@@ -3108,13 +3257,13 @@ static int r100_startup(struct radeon_device *rdev)
        r100_gpu_init(rdev);
        /* Initialize GART (initialize after TTM so we can allocate
         * memory through TTM but finalize after TTM) */
+       r100_enable_bm(rdev);
        if (rdev->flags & RADEON_IS_PCI) {
                r = r100_pci_gart_enable(rdev);
                if (r)
                        return r;
        }
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        r100_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -3150,6 +3299,8 @@ int r100_resume(struct radeon_device *rdev)
        radeon_combios_asic_init(rdev->ddev);
        /* Resume clock after posting */
        r100_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return r100_startup(rdev);
 }
 
@@ -3174,7 +3325,7 @@ void r100_fini(struct radeon_device *rdev)
                r100_pci_gart_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -3242,10 +3393,8 @@ int r100_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               radeon_combios_asic_init(rdev->ddev);
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
        /* Set asic errata */
        r100_errata(rdev);
        /* Initialize clocks */
@@ -3264,7 +3413,7 @@ int r100_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        if (rdev->flags & RADEON_IS_PCI) {
index 0daf0d7..ca50903 100644 (file)
  * CS functions
  */
 struct r100_cs_track_cb {
-       struct radeon_object    *robj;
+       struct radeon_bo        *robj;
        unsigned                pitch;
        unsigned                cpp;
        unsigned                offset;
 };
 
 struct r100_cs_track_array {
-       struct radeon_object    *robj;
+       struct radeon_bo        *robj;
        unsigned                esize;
 };
 
 struct r100_cs_cube_info {
-       struct radeon_object    *robj;
-       unsigned                offset;
+       struct radeon_bo        *robj;
+       unsigned                offset;
        unsigned                width;
        unsigned                height;
 };
 
 struct r100_cs_track_texture {
-       struct radeon_object    *robj;
+       struct radeon_bo        *robj;
        struct r100_cs_cube_info cube_info[5]; /* info for 5 non-primary faces */
        unsigned                pitch;
        unsigned                width;
index 2f43ee8..83378c3 100644 (file)
@@ -137,14 +137,19 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
 
 void rv370_pcie_gart_disable(struct radeon_device *rdev)
 {
-       uint32_t tmp;
+       u32 tmp;
+       int r;
 
        tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL);
        tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
        WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp & ~RADEON_PCIE_TX_GART_EN);
        if (rdev->gart.table.vram.robj) {
-               radeon_object_kunmap(rdev->gart.table.vram.robj);
-               radeon_object_unpin(rdev->gart.table.vram.robj);
+               r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->gart.table.vram.robj);
+                       radeon_bo_unpin(rdev->gart.table.vram.robj);
+                       radeon_bo_unreserve(rdev->gart.table.vram.robj);
+               }
        }
 }
 
@@ -1181,6 +1186,9 @@ static int r300_startup(struct radeon_device *rdev)
 {
        int r;
 
+       /* set common regs */
+       r100_set_common_regs(rdev);
+       /* program mc */
        r300_mc_program(rdev);
        /* Resume clock */
        r300_clock_startup(rdev);
@@ -1193,13 +1201,18 @@ static int r300_startup(struct radeon_device *rdev)
                if (r)
                        return r;
        }
+
+       if (rdev->family == CHIP_R300 ||
+           rdev->family == CHIP_R350 ||
+           rdev->family == CHIP_RV350)
+               r100_enable_bm(rdev);
+
        if (rdev->flags & RADEON_IS_PCI) {
                r = r100_pci_gart_enable(rdev);
                if (r)
                        return r;
        }
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        r100_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -1237,6 +1250,8 @@ int r300_resume(struct radeon_device *rdev)
        radeon_combios_asic_init(rdev->ddev);
        /* Resume clock after posting */
        r300_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return r300_startup(rdev);
 }
 
@@ -1265,7 +1280,7 @@ void r300_fini(struct radeon_device *rdev)
                r100_pci_gart_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -1303,10 +1318,8 @@ int r300_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               radeon_combios_asic_init(rdev->ddev);
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
        /* Set asic errata */
        r300_errata(rdev);
        /* Initialize clocks */
@@ -1325,7 +1338,7 @@ int r300_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        if (rdev->flags & RADEON_IS_PCIE) {
index 1cefdbc..c05a727 100644 (file)
@@ -169,6 +169,9 @@ static int r420_startup(struct radeon_device *rdev)
 {
        int r;
 
+       /* set common regs */
+       r100_set_common_regs(rdev);
+       /* program mc */
        r300_mc_program(rdev);
        /* Resume clock */
        r420_clock_resume(rdev);
@@ -186,7 +189,6 @@ static int r420_startup(struct radeon_device *rdev)
        }
        r420_pipes_init(rdev);
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        r100_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -229,7 +231,8 @@ int r420_resume(struct radeon_device *rdev)
        }
        /* Resume clock after posting */
        r420_clock_resume(rdev);
-
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return r420_startup(rdev);
 }
 
@@ -258,7 +261,7 @@ void r420_fini(struct radeon_device *rdev)
        radeon_agp_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        if (rdev->is_atom_bios) {
                radeon_atombios_fini(rdev);
        } else {
@@ -301,14 +304,9 @@ int r420_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               if (rdev->is_atom_bios) {
-                       atom_asic_init(rdev->mode_info.atom_context);
-               } else {
-                       radeon_combios_asic_init(rdev->ddev);
-               }
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
+
        /* Initialize clocks */
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
@@ -331,10 +329,13 @@ int r420_init(struct radeon_device *rdev)
                return r;
        }
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r) {
                return r;
        }
+       if (rdev->family == CHIP_R420)
+               r100_enable_bm(rdev);
+
        if (rdev->flags & RADEON_IS_PCIE) {
                r = rv370_pcie_gart_init(rdev);
                if (r)
index 7baa739..74ad89b 100644 (file)
 
 #define AVIVO_DVOA_BIT_DEPTH_CONTROL                   0x7988
 
+#define AVIVO_DC_GPIO_HPD_A                 0x7e94
+
 #define AVIVO_GPIO_0                        0x7e30
 #define AVIVO_GPIO_1                        0x7e40
 #define AVIVO_GPIO_2                        0x7e50
index f743518..0f3843b 100644 (file)
@@ -185,7 +185,6 @@ static int r520_startup(struct radeon_device *rdev)
                        return r;
        }
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        rs600_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -221,6 +220,8 @@ int r520_resume(struct radeon_device *rdev)
        atom_asic_init(rdev->mode_info.atom_context);
        /* Resume clock after posting */
        rv515_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return r520_startup(rdev);
 }
 
@@ -254,6 +255,9 @@ int r520_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
+
        if (!radeon_card_posted(rdev) && rdev->bios) {
                DRM_INFO("GPU not posted. posting now...\n");
                atom_asic_init(rdev->mode_info.atom_context);
@@ -277,7 +281,7 @@ int r520_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        r = rv370_pcie_gart_init(rdev);
index 278f646..36656bd 100644 (file)
 
 #define PFP_UCODE_SIZE 576
 #define PM4_UCODE_SIZE 1792
+#define RLC_UCODE_SIZE 768
 #define R700_PFP_UCODE_SIZE 848
 #define R700_PM4_UCODE_SIZE 1360
+#define R700_RLC_UCODE_SIZE 1024
 
 /* Firmware Names */
 MODULE_FIRMWARE("radeon/R600_pfp.bin");
@@ -62,6 +64,8 @@ MODULE_FIRMWARE("radeon/RV730_pfp.bin");
 MODULE_FIRMWARE("radeon/RV730_me.bin");
 MODULE_FIRMWARE("radeon/RV710_pfp.bin");
 MODULE_FIRMWARE("radeon/RV710_me.bin");
+MODULE_FIRMWARE("radeon/R600_rlc.bin");
+MODULE_FIRMWARE("radeon/R700_rlc.bin");
 
 int r600_debugfs_mc_info_init(struct radeon_device *rdev);
 
@@ -70,6 +74,281 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev);
 void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 
+/* hpd for digital panel detect/disconnect */
+bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
+{
+       bool connected = false;
+
+       if (ASIC_IS_DCE3(rdev)) {
+               switch (hpd) {
+               case RADEON_HPD_1:
+                       if (RREG32(DC_HPD1_INT_STATUS) & DC_HPDx_SENSE)
+                               connected = true;
+                       break;
+               case RADEON_HPD_2:
+                       if (RREG32(DC_HPD2_INT_STATUS) & DC_HPDx_SENSE)
+                               connected = true;
+                       break;
+               case RADEON_HPD_3:
+                       if (RREG32(DC_HPD3_INT_STATUS) & DC_HPDx_SENSE)
+                               connected = true;
+                       break;
+               case RADEON_HPD_4:
+                       if (RREG32(DC_HPD4_INT_STATUS) & DC_HPDx_SENSE)
+                               connected = true;
+                       break;
+                       /* DCE 3.2 */
+               case RADEON_HPD_5:
+                       if (RREG32(DC_HPD5_INT_STATUS) & DC_HPDx_SENSE)
+                               connected = true;
+                       break;
+               case RADEON_HPD_6:
+                       if (RREG32(DC_HPD6_INT_STATUS) & DC_HPDx_SENSE)
+                               connected = true;
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               switch (hpd) {
+               case RADEON_HPD_1:
+                       if (RREG32(DC_HOT_PLUG_DETECT1_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
+                               connected = true;
+                       break;
+               case RADEON_HPD_2:
+                       if (RREG32(DC_HOT_PLUG_DETECT2_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
+                               connected = true;
+                       break;
+               case RADEON_HPD_3:
+                       if (RREG32(DC_HOT_PLUG_DETECT3_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
+                               connected = true;
+                       break;
+               default:
+                       break;
+               }
+       }
+       return connected;
+}
+
+void r600_hpd_set_polarity(struct radeon_device *rdev,
+                          enum radeon_hpd_id hpd)
+{
+       u32 tmp;
+       bool connected = r600_hpd_sense(rdev, hpd);
+
+       if (ASIC_IS_DCE3(rdev)) {
+               switch (hpd) {
+               case RADEON_HPD_1:
+                       tmp = RREG32(DC_HPD1_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HPDx_INT_POLARITY;
+                       else
+                               tmp |= DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD1_INT_CONTROL, tmp);
+                       break;
+               case RADEON_HPD_2:
+                       tmp = RREG32(DC_HPD2_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HPDx_INT_POLARITY;
+                       else
+                               tmp |= DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD2_INT_CONTROL, tmp);
+                       break;
+               case RADEON_HPD_3:
+                       tmp = RREG32(DC_HPD3_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HPDx_INT_POLARITY;
+                       else
+                               tmp |= DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD3_INT_CONTROL, tmp);
+                       break;
+               case RADEON_HPD_4:
+                       tmp = RREG32(DC_HPD4_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HPDx_INT_POLARITY;
+                       else
+                               tmp |= DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD4_INT_CONTROL, tmp);
+                       break;
+               case RADEON_HPD_5:
+                       tmp = RREG32(DC_HPD5_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HPDx_INT_POLARITY;
+                       else
+                               tmp |= DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD5_INT_CONTROL, tmp);
+                       break;
+                       /* DCE 3.2 */
+               case RADEON_HPD_6:
+                       tmp = RREG32(DC_HPD6_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HPDx_INT_POLARITY;
+                       else
+                               tmp |= DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD6_INT_CONTROL, tmp);
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               switch (hpd) {
+               case RADEON_HPD_1:
+                       tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
+                       else
+                               tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
+                       WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
+                       break;
+               case RADEON_HPD_2:
+                       tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
+                       else
+                               tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
+                       WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
+                       break;
+               case RADEON_HPD_3:
+                       tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL);
+                       if (connected)
+                               tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
+                       else
+                               tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
+                       WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+void r600_hpd_init(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_connector *connector;
+
+       if (ASIC_IS_DCE3(rdev)) {
+               u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa);
+               if (ASIC_IS_DCE32(rdev))
+                       tmp |= DC_HPDx_EN;
+
+               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+                       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+                       switch (radeon_connector->hpd.hpd) {
+                       case RADEON_HPD_1:
+                               WREG32(DC_HPD1_CONTROL, tmp);
+                               rdev->irq.hpd[0] = true;
+                               break;
+                       case RADEON_HPD_2:
+                               WREG32(DC_HPD2_CONTROL, tmp);
+                               rdev->irq.hpd[1] = true;
+                               break;
+                       case RADEON_HPD_3:
+                               WREG32(DC_HPD3_CONTROL, tmp);
+                               rdev->irq.hpd[2] = true;
+                               break;
+                       case RADEON_HPD_4:
+                               WREG32(DC_HPD4_CONTROL, tmp);
+                               rdev->irq.hpd[3] = true;
+                               break;
+                               /* DCE 3.2 */
+                       case RADEON_HPD_5:
+                               WREG32(DC_HPD5_CONTROL, tmp);
+                               rdev->irq.hpd[4] = true;
+                               break;
+                       case RADEON_HPD_6:
+                               WREG32(DC_HPD6_CONTROL, tmp);
+                               rdev->irq.hpd[5] = true;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       } else {
+               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+                       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+                       switch (radeon_connector->hpd.hpd) {
+                       case RADEON_HPD_1:
+                               WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN);
+                               rdev->irq.hpd[0] = true;
+                               break;
+                       case RADEON_HPD_2:
+                               WREG32(DC_HOT_PLUG_DETECT2_CONTROL, DC_HOT_PLUG_DETECTx_EN);
+                               rdev->irq.hpd[1] = true;
+                               break;
+                       case RADEON_HPD_3:
+                               WREG32(DC_HOT_PLUG_DETECT3_CONTROL, DC_HOT_PLUG_DETECTx_EN);
+                               rdev->irq.hpd[2] = true;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+       r600_irq_set(rdev);
+}
+
+void r600_hpd_fini(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_connector *connector;
+
+       if (ASIC_IS_DCE3(rdev)) {
+               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+                       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+                       switch (radeon_connector->hpd.hpd) {
+                       case RADEON_HPD_1:
+                               WREG32(DC_HPD1_CONTROL, 0);
+                               rdev->irq.hpd[0] = false;
+                               break;
+                       case RADEON_HPD_2:
+                               WREG32(DC_HPD2_CONTROL, 0);
+                               rdev->irq.hpd[1] = false;
+                               break;
+                       case RADEON_HPD_3:
+                               WREG32(DC_HPD3_CONTROL, 0);
+                               rdev->irq.hpd[2] = false;
+                               break;
+                       case RADEON_HPD_4:
+                               WREG32(DC_HPD4_CONTROL, 0);
+                               rdev->irq.hpd[3] = false;
+                               break;
+                               /* DCE 3.2 */
+                       case RADEON_HPD_5:
+                               WREG32(DC_HPD5_CONTROL, 0);
+                               rdev->irq.hpd[4] = false;
+                               break;
+                       case RADEON_HPD_6:
+                               WREG32(DC_HPD6_CONTROL, 0);
+                               rdev->irq.hpd[5] = false;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       } else {
+               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+                       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+                       switch (radeon_connector->hpd.hpd) {
+                       case RADEON_HPD_1:
+                               WREG32(DC_HOT_PLUG_DETECT1_CONTROL, 0);
+                               rdev->irq.hpd[0] = false;
+                               break;
+                       case RADEON_HPD_2:
+                               WREG32(DC_HOT_PLUG_DETECT2_CONTROL, 0);
+                               rdev->irq.hpd[1] = false;
+                               break;
+                       case RADEON_HPD_3:
+                               WREG32(DC_HOT_PLUG_DETECT3_CONTROL, 0);
+                               rdev->irq.hpd[2] = false;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+}
+
 /*
  * R600 PCIE GART
  */
@@ -180,7 +459,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
 void r600_pcie_gart_disable(struct radeon_device *rdev)
 {
        u32 tmp;
-       int i;
+       int i, r;
 
        /* Disable all tables */
        for (i = 0; i < 7; i++)
@@ -208,8 +487,12 @@ void r600_pcie_gart_disable(struct radeon_device *rdev)
        WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp);
        WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp);
        if (rdev->gart.table.vram.robj) {
-               radeon_object_kunmap(rdev->gart.table.vram.robj);
-               radeon_object_unpin(rdev->gart.table.vram.robj);
+               r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->gart.table.vram.robj);
+                       radeon_bo_unpin(rdev->gart.table.vram.robj);
+                       radeon_bo_unreserve(rdev->gart.table.vram.robj);
+               }
        }
 }
 
@@ -394,11 +677,11 @@ int r600_mc_init(struct radeon_device *rdev)
                 * AGP so that GPU can catch out of VRAM/AGP access
                 */
                if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
-                       /* Enought place before */
+                       /* Enough place before */
                        rdev->mc.vram_location = rdev->mc.gtt_location -
                                                        rdev->mc.mc_vram_size;
                } else if (tmp > rdev->mc.mc_vram_size) {
-                       /* Enought place after */
+                       /* Enough place after */
                        rdev->mc.vram_location = rdev->mc.gtt_location +
                                                        rdev->mc.gtt_size;
                } else {
@@ -1101,6 +1384,10 @@ void r600_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v)
        (void)RREG32(PCIE_PORT_DATA);
 }
 
+void r600_hdp_flush(struct radeon_device *rdev)
+{
+       WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
+}
 
 /*
  * CP & Ring
@@ -1110,11 +1397,12 @@ void r600_cp_stop(struct radeon_device *rdev)
        WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(1));
 }
 
-int r600_cp_init_microcode(struct radeon_device *rdev)
+int r600_init_microcode(struct radeon_device *rdev)
 {
        struct platform_device *pdev;
        const char *chip_name;
-       size_t pfp_req_size, me_req_size;
+       const char *rlc_chip_name;
+       size_t pfp_req_size, me_req_size, rlc_req_size;
        char fw_name[30];
        int err;
 
@@ -1128,30 +1416,62 @@ int r600_cp_init_microcode(struct radeon_device *rdev)
        }
 
        switch (rdev->family) {
-       case CHIP_R600: chip_name = "R600"; break;
-       case CHIP_RV610: chip_name = "RV610"; break;
-       case CHIP_RV630: chip_name = "RV630"; break;
-       case CHIP_RV620: chip_name = "RV620"; break;
-       case CHIP_RV635: chip_name = "RV635"; break;
-       case CHIP_RV670: chip_name = "RV670"; break;
+       case CHIP_R600:
+               chip_name = "R600";
+               rlc_chip_name = "R600";
+               break;
+       case CHIP_RV610:
+               chip_name = "RV610";
+               rlc_chip_name = "R600";
+               break;
+       case CHIP_RV630:
+               chip_name = "RV630";
+               rlc_chip_name = "R600";
+               break;
+       case CHIP_RV620:
+               chip_name = "RV620";
+               rlc_chip_name = "R600";
+               break;
+       case CHIP_RV635:
+               chip_name = "RV635";
+               rlc_chip_name = "R600";
+               break;
+       case CHIP_RV670:
+               chip_name = "RV670";
+               rlc_chip_name = "R600";
+               break;
        case CHIP_RS780:
-       case CHIP_RS880: chip_name = "RS780"; break;
-       case CHIP_RV770: chip_name = "RV770"; break;
+       case CHIP_RS880:
+               chip_name = "RS780";
+               rlc_chip_name = "R600";
+               break;
+       case CHIP_RV770:
+               chip_name = "RV770";
+               rlc_chip_name = "R700";
+               break;
        case CHIP_RV730:
-       case CHIP_RV740: chip_name = "RV730"; break;
-       case CHIP_RV710: chip_name = "RV710"; break;
+       case CHIP_RV740:
+               chip_name = "RV730";
+               rlc_chip_name = "R700";
+               break;
+       case CHIP_RV710:
+               chip_name = "RV710";
+               rlc_chip_name = "R700";
+               break;
        default: BUG();
        }
 
        if (rdev->family >= CHIP_RV770) {
                pfp_req_size = R700_PFP_UCODE_SIZE * 4;
                me_req_size = R700_PM4_UCODE_SIZE * 4;
+               rlc_req_size = R700_RLC_UCODE_SIZE * 4;
        } else {
                pfp_req_size = PFP_UCODE_SIZE * 4;
                me_req_size = PM4_UCODE_SIZE * 12;
+               rlc_req_size = RLC_UCODE_SIZE * 4;
        }
 
-       DRM_INFO("Loading %s CP Microcode\n", chip_name);
+       DRM_INFO("Loading %s Microcode\n", chip_name);
 
        snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
        err = request_firmware(&rdev->pfp_fw, fw_name, &pdev->dev);
@@ -1175,6 +1495,18 @@ int r600_cp_init_microcode(struct radeon_device *rdev)
                       rdev->me_fw->size, fw_name);
                err = -EINVAL;
        }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", rlc_chip_name);
+       err = request_firmware(&rdev->rlc_fw, fw_name, &pdev->dev);
+       if (err)
+               goto out;
+       if (rdev->rlc_fw->size != rlc_req_size) {
+               printk(KERN_ERR
+                      "r600_rlc: Bogus length %zu in firmware \"%s\"\n",
+                      rdev->rlc_fw->size, fw_name);
+               err = -EINVAL;
+       }
+
 out:
        platform_device_unregister(pdev);
 
@@ -1187,6 +1519,8 @@ out:
                rdev->pfp_fw = NULL;
                release_firmware(rdev->me_fw);
                rdev->me_fw = NULL;
+               release_firmware(rdev->rlc_fw);
+               rdev->rlc_fw = NULL;
        }
        return err;
 }
@@ -1381,10 +1715,16 @@ int r600_ring_test(struct radeon_device *rdev)
 
 void r600_wb_disable(struct radeon_device *rdev)
 {
+       int r;
+
        WREG32(SCRATCH_UMSK, 0);
        if (rdev->wb.wb_obj) {
-               radeon_object_kunmap(rdev->wb.wb_obj);
-               radeon_object_unpin(rdev->wb.wb_obj);
+               r = radeon_bo_reserve(rdev->wb.wb_obj, false);
+               if (unlikely(r != 0))
+                       return;
+               radeon_bo_kunmap(rdev->wb.wb_obj);
+               radeon_bo_unpin(rdev->wb.wb_obj);
+               radeon_bo_unreserve(rdev->wb.wb_obj);
        }
 }
 
@@ -1392,7 +1732,7 @@ void r600_wb_fini(struct radeon_device *rdev)
 {
        r600_wb_disable(rdev);
        if (rdev->wb.wb_obj) {
-               radeon_object_unref(&rdev->wb.wb_obj);
+               radeon_bo_unref(&rdev->wb.wb_obj);
                rdev->wb.wb = NULL;
                rdev->wb.wb_obj = NULL;
        }
@@ -1403,22 +1743,29 @@ int r600_wb_enable(struct radeon_device *rdev)
        int r;
 
        if (rdev->wb.wb_obj == NULL) {
-               r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
-                               RADEON_GEM_DOMAIN_GTT, false, &rdev->wb.wb_obj);
+               r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
+                               RADEON_GEM_DOMAIN_GTT, &rdev->wb.wb_obj);
                if (r) {
-                       dev_warn(rdev->dev, "failed to create WB buffer (%d).\n", r);
+                       dev_warn(rdev->dev, "(%d) create WB bo failed\n", r);
                        return r;
                }
-               r = radeon_object_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
+               r = radeon_bo_reserve(rdev->wb.wb_obj, false);
+               if (unlikely(r != 0)) {
+                       r600_wb_fini(rdev);
+                       return r;
+               }
+               r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
                                &rdev->wb.gpu_addr);
                if (r) {
-                       dev_warn(rdev->dev, "failed to pin WB buffer (%d).\n", r);
+                       radeon_bo_unreserve(rdev->wb.wb_obj);
+                       dev_warn(rdev->dev, "(%d) pin WB bo failed\n", r);
                        r600_wb_fini(rdev);
                        return r;
                }
-               r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
+               r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
+               radeon_bo_unreserve(rdev->wb.wb_obj);
                if (r) {
-                       dev_warn(rdev->dev, "failed to map WB buffer (%d).\n", r);
+                       dev_warn(rdev->dev, "(%d) map WB bo failed\n", r);
                        r600_wb_fini(rdev);
                        return r;
                }
@@ -1433,10 +1780,14 @@ int r600_wb_enable(struct radeon_device *rdev)
 void r600_fence_ring_emit(struct radeon_device *rdev,
                          struct radeon_fence *fence)
 {
+       /* Also consider EVENT_WRITE_EOP.  it handles the interrupts + timestamps + events */
        /* Emit fence sequence & fire IRQ */
        radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
        radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
        radeon_ring_write(rdev, fence->seq);
+       /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
+       radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
+       radeon_ring_write(rdev, RB_INT_STAT);
 }
 
 int r600_copy_dma(struct radeon_device *rdev,
@@ -1459,18 +1810,6 @@ int r600_copy_blit(struct radeon_device *rdev,
        return 0;
 }
 
-int r600_irq_process(struct radeon_device *rdev)
-{
-       /* FIXME: implement */
-       return 0;
-}
-
-int r600_irq_set(struct radeon_device *rdev)
-{
-       /* FIXME: implement */
-       return 0;
-}
-
 int r600_set_surface_reg(struct radeon_device *rdev, int reg,
                         uint32_t tiling_flags, uint32_t pitch,
                         uint32_t offset, uint32_t obj_size)
@@ -1506,6 +1845,14 @@ int r600_startup(struct radeon_device *rdev)
 {
        int r;
 
+       if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
+               r = r600_init_microcode(rdev);
+               if (r) {
+                       DRM_ERROR("Failed to load firmware!\n");
+                       return r;
+               }
+       }
+
        r600_mc_program(rdev);
        if (rdev->flags & RADEON_IS_AGP) {
                r600_agp_enable(rdev);
@@ -1516,13 +1863,26 @@ int r600_startup(struct radeon_device *rdev)
        }
        r600_gpu_init(rdev);
 
-       r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
-                             &rdev->r600_blit.shader_gpu_addr);
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+                       &rdev->r600_blit.shader_gpu_addr);
+       radeon_bo_unreserve(rdev->r600_blit.shader_obj);
        if (r) {
-               DRM_ERROR("failed to pin blit object %d\n", r);
+               dev_err(rdev->dev, "(%d) pin blit object failed\n", r);
                return r;
        }
 
+       /* Enable IRQ */
+       r = r600_irq_init(rdev);
+       if (r) {
+               DRM_ERROR("radeon: IH init failed (%d).\n", r);
+               radeon_irq_kms_fini(rdev);
+               return r;
+       }
+       r600_irq_set(rdev);
+
        r = radeon_ring_init(rdev, rdev->cp.ring_size);
        if (r)
                return r;
@@ -1583,13 +1943,19 @@ int r600_resume(struct radeon_device *rdev)
 
 int r600_suspend(struct radeon_device *rdev)
 {
+       int r;
+
        /* FIXME: we should wait for ring to be empty */
        r600_cp_stop(rdev);
        rdev->cp.ready = false;
        r600_wb_disable(rdev);
        r600_pcie_gart_disable(rdev);
        /* unpin shaders bo */
-       radeon_object_unpin(rdev->r600_blit.shader_obj);
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (unlikely(r != 0))
+               return r;
+       radeon_bo_unpin(rdev->r600_blit.shader_obj);
+       radeon_bo_unreserve(rdev->r600_blit.shader_obj);
        return 0;
 }
 
@@ -1627,7 +1993,11 @@ int r600_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Post card if necessary */
-       if (!r600_card_posted(rdev) && rdev->bios) {
+       if (!r600_card_posted(rdev)) {
+               if (!rdev->bios) {
+                       dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
+                       return -EINVAL;
+               }
                DRM_INFO("GPU not posted. posting now...\n");
                atom_asic_init(rdev->mode_info.atom_context);
        }
@@ -1650,31 +2020,31 @@ int r600_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
+
+       r = radeon_irq_kms_init(rdev);
+       if (r)
+               return r;
+
        rdev->cp.ring_obj = NULL;
        r600_ring_init(rdev, 1024 * 1024);
 
-       if (!rdev->me_fw || !rdev->pfp_fw) {
-               r = r600_cp_init_microcode(rdev);
-               if (r) {
-                       DRM_ERROR("Failed to load firmware!\n");
-                       return r;
-               }
-       }
+       rdev->ih.ring_obj = NULL;
+       r600_ih_ring_init(rdev, 64 * 1024);
 
        r = r600_pcie_gart_init(rdev);
        if (r)
                return r;
 
-       rdev->accel_working = true;
        r = r600_blit_init(rdev);
        if (r) {
-               DRM_ERROR("radeon: failled blitter (%d).\n", r);
+               DRM_ERROR("radeon: failed blitter (%d).\n", r);
                return r;
        }
 
+       rdev->accel_working = true;
        r = r600_startup(rdev);
        if (r) {
                r600_suspend(rdev);
@@ -1686,12 +2056,12 @@ int r600_init(struct radeon_device *rdev)
        if (rdev->accel_working) {
                r = radeon_ib_pool_init(rdev);
                if (r) {
-                       DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
+                       DRM_ERROR("radeon: failed initializing IB pool (%d).\n", r);
                        rdev->accel_working = false;
                }
                r = r600_ib_test(rdev);
                if (r) {
-                       DRM_ERROR("radeon: failled testing IB (%d).\n", r);
+                       DRM_ERROR("radeon: failed testing IB (%d).\n", r);
                        rdev->accel_working = false;
                }
        }
@@ -1704,6 +2074,8 @@ void r600_fini(struct radeon_device *rdev)
        r600_suspend(rdev);
 
        r600_blit_fini(rdev);
+       r600_irq_fini(rdev);
+       radeon_irq_kms_fini(rdev);
        radeon_ring_fini(rdev);
        r600_wb_fini(rdev);
        r600_pcie_gart_fini(rdev);
@@ -1712,7 +2084,7 @@ void r600_fini(struct radeon_device *rdev)
        radeon_clocks_fini(rdev);
        if (rdev->flags & RADEON_IS_AGP)
                radeon_agp_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -1798,8 +2170,657 @@ int r600_ib_test(struct radeon_device *rdev)
        return r;
 }
 
+/*
+ * Interrupts
+ *
+ * Interrupts use a ring buffer on r6xx/r7xx hardware.  It works pretty
+ * the same as the CP ring buffer, but in reverse.  Rather than the CPU
+ * writing to the ring and the GPU consuming, the GPU writes to the ring
+ * and host consumes.  As the host irq handler processes interrupts, it
+ * increments the rptr.  When the rptr catches up with the wptr, all the
+ * current interrupts have been processed.
+ */
+
+void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size)
+{
+       u32 rb_bufsz;
+
+       /* Align ring size */
+       rb_bufsz = drm_order(ring_size / 4);
+       ring_size = (1 << rb_bufsz) * 4;
+       rdev->ih.ring_size = ring_size;
+       rdev->ih.align_mask = 4 - 1;
+}
+
+static int r600_ih_ring_alloc(struct radeon_device *rdev, unsigned ring_size)
+{
+       int r;
+
+       rdev->ih.ring_size = ring_size;
+       /* Allocate ring buffer */
+       if (rdev->ih.ring_obj == NULL) {
+               r = radeon_bo_create(rdev, NULL, rdev->ih.ring_size,
+                                    true,
+                                    RADEON_GEM_DOMAIN_GTT,
+                                    &rdev->ih.ring_obj);
+               if (r) {
+                       DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r);
+                       return r;
+               }
+               r = radeon_bo_reserve(rdev->ih.ring_obj, false);
+               if (unlikely(r != 0))
+                       return r;
+               r = radeon_bo_pin(rdev->ih.ring_obj,
+                                 RADEON_GEM_DOMAIN_GTT,
+                                 &rdev->ih.gpu_addr);
+               if (r) {
+                       radeon_bo_unreserve(rdev->ih.ring_obj);
+                       DRM_ERROR("radeon: failed to pin ih ring buffer (%d).\n", r);
+                       return r;
+               }
+               r = radeon_bo_kmap(rdev->ih.ring_obj,
+                                  (void **)&rdev->ih.ring);
+               radeon_bo_unreserve(rdev->ih.ring_obj);
+               if (r) {
+                       DRM_ERROR("radeon: failed to map ih ring buffer (%d).\n", r);
+                       return r;
+               }
+       }
+       rdev->ih.ptr_mask = (rdev->cp.ring_size / 4) - 1;
+       rdev->ih.rptr = 0;
+
+       return 0;
+}
+
+static void r600_ih_ring_fini(struct radeon_device *rdev)
+{
+       int r;
+       if (rdev->ih.ring_obj) {
+               r = radeon_bo_reserve(rdev->ih.ring_obj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->ih.ring_obj);
+                       radeon_bo_unpin(rdev->ih.ring_obj);
+                       radeon_bo_unreserve(rdev->ih.ring_obj);
+               }
+               radeon_bo_unref(&rdev->ih.ring_obj);
+               rdev->ih.ring = NULL;
+               rdev->ih.ring_obj = NULL;
+       }
+}
+
+static void r600_rlc_stop(struct radeon_device *rdev)
+{
+
+       if (rdev->family >= CHIP_RV770) {
+               /* r7xx asics need to soft reset RLC before halting */
+               WREG32(SRBM_SOFT_RESET, SOFT_RESET_RLC);
+               RREG32(SRBM_SOFT_RESET);
+               udelay(15000);
+               WREG32(SRBM_SOFT_RESET, 0);
+               RREG32(SRBM_SOFT_RESET);
+       }
+
+       WREG32(RLC_CNTL, 0);
+}
+
+static void r600_rlc_start(struct radeon_device *rdev)
+{
+       WREG32(RLC_CNTL, RLC_ENABLE);
+}
+
+static int r600_rlc_init(struct radeon_device *rdev)
+{
+       u32 i;
+       const __be32 *fw_data;
+
+       if (!rdev->rlc_fw)
+               return -EINVAL;
+
+       r600_rlc_stop(rdev);
+
+       WREG32(RLC_HB_BASE, 0);
+       WREG32(RLC_HB_CNTL, 0);
+       WREG32(RLC_HB_RPTR, 0);
+       WREG32(RLC_HB_WPTR, 0);
+       WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
+       WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
+       WREG32(RLC_MC_CNTL, 0);
+       WREG32(RLC_UCODE_CNTL, 0);
+
+       fw_data = (const __be32 *)rdev->rlc_fw->data;
+       if (rdev->family >= CHIP_RV770) {
+               for (i = 0; i < R700_RLC_UCODE_SIZE; i++) {
+                       WREG32(RLC_UCODE_ADDR, i);
+                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+               }
+       } else {
+               for (i = 0; i < RLC_UCODE_SIZE; i++) {
+                       WREG32(RLC_UCODE_ADDR, i);
+                       WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+               }
+       }
+       WREG32(RLC_UCODE_ADDR, 0);
+
+       r600_rlc_start(rdev);
+
+       return 0;
+}
+
+static void r600_enable_interrupts(struct radeon_device *rdev)
+{
+       u32 ih_cntl = RREG32(IH_CNTL);
+       u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
+
+       ih_cntl |= ENABLE_INTR;
+       ih_rb_cntl |= IH_RB_ENABLE;
+       WREG32(IH_CNTL, ih_cntl);
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+       rdev->ih.enabled = true;
+}
+
+static void r600_disable_interrupts(struct radeon_device *rdev)
+{
+       u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
+       u32 ih_cntl = RREG32(IH_CNTL);
+
+       ih_rb_cntl &= ~IH_RB_ENABLE;
+       ih_cntl &= ~ENABLE_INTR;
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+       WREG32(IH_CNTL, ih_cntl);
+       /* set rptr, wptr to 0 */
+       WREG32(IH_RB_RPTR, 0);
+       WREG32(IH_RB_WPTR, 0);
+       rdev->ih.enabled = false;
+       rdev->ih.wptr = 0;
+       rdev->ih.rptr = 0;
+}
+
+static void r600_disable_interrupt_state(struct radeon_device *rdev)
+{
+       u32 tmp;
+
+       WREG32(CP_INT_CNTL, 0);
+       WREG32(GRBM_INT_CNTL, 0);
+       WREG32(DxMODE_INT_MASK, 0);
+       if (ASIC_IS_DCE3(rdev)) {
+               WREG32(DCE3_DACA_AUTODETECT_INT_CONTROL, 0);
+               WREG32(DCE3_DACB_AUTODETECT_INT_CONTROL, 0);
+               tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD1_INT_CONTROL, tmp);
+               tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD2_INT_CONTROL, tmp);
+               tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD3_INT_CONTROL, tmp);
+               tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD4_INT_CONTROL, tmp);
+               if (ASIC_IS_DCE32(rdev)) {
+                       tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD5_INT_CONTROL, 0);
+                       tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+                       WREG32(DC_HPD6_INT_CONTROL, 0);
+               }
+       } else {
+               WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
+               WREG32(DACB_AUTODETECT_INT_CONTROL, 0);
+               tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
+               WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, 0);
+               tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
+               WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, 0);
+               tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
+               WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, 0);
+       }
+}
+
+int r600_irq_init(struct radeon_device *rdev)
+{
+       int ret = 0;
+       int rb_bufsz;
+       u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
+
+       /* allocate ring */
+       ret = r600_ih_ring_alloc(rdev, rdev->ih.ring_size);
+       if (ret)
+               return ret;
+
+       /* disable irqs */
+       r600_disable_interrupts(rdev);
+
+       /* init rlc */
+       ret = r600_rlc_init(rdev);
+       if (ret) {
+               r600_ih_ring_fini(rdev);
+               return ret;
+       }
+
+       /* setup interrupt control */
+       /* set dummy read address to ring address */
+       WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8);
+       interrupt_cntl = RREG32(INTERRUPT_CNTL);
+       /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi
+        * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN
+        */
+       interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
+       /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */
+       interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
+       WREG32(INTERRUPT_CNTL, interrupt_cntl);
+
+       WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8);
+       rb_bufsz = drm_order(rdev->ih.ring_size / 4);
+
+       ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE |
+                     IH_WPTR_OVERFLOW_CLEAR |
+                     (rb_bufsz << 1));
+       /* WPTR writeback, not yet */
+       /*ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE;*/
+       WREG32(IH_RB_WPTR_ADDR_LO, 0);
+       WREG32(IH_RB_WPTR_ADDR_HI, 0);
+
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+
+       /* set rptr, wptr to 0 */
+       WREG32(IH_RB_RPTR, 0);
+       WREG32(IH_RB_WPTR, 0);
+
+       /* Default settings for IH_CNTL (disabled at first) */
+       ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10);
+       /* RPTR_REARM only works if msi's are enabled */
+       if (rdev->msi_enabled)
+               ih_cntl |= RPTR_REARM;
+
+#ifdef __BIG_ENDIAN
+       ih_cntl |= IH_MC_SWAP(IH_MC_SWAP_32BIT);
+#endif
+       WREG32(IH_CNTL, ih_cntl);
+
+       /* force the active interrupt state to all disabled */
+       r600_disable_interrupt_state(rdev);
+
+       /* enable irqs */
+       r600_enable_interrupts(rdev);
+
+       return ret;
+}
+
+void r600_irq_fini(struct radeon_device *rdev)
+{
+       r600_disable_interrupts(rdev);
+       r600_rlc_stop(rdev);
+       r600_ih_ring_fini(rdev);
+}
+
+int r600_irq_set(struct radeon_device *rdev)
+{
+       u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
+       u32 mode_int = 0;
+       u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
+
+       /* don't enable anything if the ih is disabled */
+       if (!rdev->ih.enabled)
+               return 0;
+
+       if (ASIC_IS_DCE3(rdev)) {
+               hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               if (ASIC_IS_DCE32(rdev)) {
+                       hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN;
+                       hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               }
+       } else {
+               hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               hpd2 = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & ~DC_HPDx_INT_EN;
+               hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN;
+       }
+
+       if (rdev->irq.sw_int) {
+               DRM_DEBUG("r600_irq_set: sw int\n");
+               cp_int_cntl |= RB_INT_ENABLE;
+       }
+       if (rdev->irq.crtc_vblank_int[0]) {
+               DRM_DEBUG("r600_irq_set: vblank 0\n");
+               mode_int |= D1MODE_VBLANK_INT_MASK;
+       }
+       if (rdev->irq.crtc_vblank_int[1]) {
+               DRM_DEBUG("r600_irq_set: vblank 1\n");
+               mode_int |= D2MODE_VBLANK_INT_MASK;
+       }
+       if (rdev->irq.hpd[0]) {
+               DRM_DEBUG("r600_irq_set: hpd 1\n");
+               hpd1 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[1]) {
+               DRM_DEBUG("r600_irq_set: hpd 2\n");
+               hpd2 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[2]) {
+               DRM_DEBUG("r600_irq_set: hpd 3\n");
+               hpd3 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[3]) {
+               DRM_DEBUG("r600_irq_set: hpd 4\n");
+               hpd4 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[4]) {
+               DRM_DEBUG("r600_irq_set: hpd 5\n");
+               hpd5 |= DC_HPDx_INT_EN;
+       }
+       if (rdev->irq.hpd[5]) {
+               DRM_DEBUG("r600_irq_set: hpd 6\n");
+               hpd6 |= DC_HPDx_INT_EN;
+       }
+
+       WREG32(CP_INT_CNTL, cp_int_cntl);
+       WREG32(DxMODE_INT_MASK, mode_int);
+       if (ASIC_IS_DCE3(rdev)) {
+               WREG32(DC_HPD1_INT_CONTROL, hpd1);
+               WREG32(DC_HPD2_INT_CONTROL, hpd2);
+               WREG32(DC_HPD3_INT_CONTROL, hpd3);
+               WREG32(DC_HPD4_INT_CONTROL, hpd4);
+               if (ASIC_IS_DCE32(rdev)) {
+                       WREG32(DC_HPD5_INT_CONTROL, hpd5);
+                       WREG32(DC_HPD6_INT_CONTROL, hpd6);
+               }
+       } else {
+               WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
+               WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
+               WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3);
+       }
+
+       return 0;
+}
+
+static inline void r600_irq_ack(struct radeon_device *rdev,
+                               u32 *disp_int,
+                               u32 *disp_int_cont,
+                               u32 *disp_int_cont2)
+{
+       u32 tmp;
+
+       if (ASIC_IS_DCE3(rdev)) {
+               *disp_int = RREG32(DCE3_DISP_INTERRUPT_STATUS);
+               *disp_int_cont = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE);
+               *disp_int_cont2 = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE2);
+       } else {
+               *disp_int = RREG32(DISP_INTERRUPT_STATUS);
+               *disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE);
+               *disp_int_cont2 = 0;
+       }
+
+       if (*disp_int & LB_D1_VBLANK_INTERRUPT)
+               WREG32(D1MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK);
+       if (*disp_int & LB_D1_VLINE_INTERRUPT)
+               WREG32(D1MODE_VLINE_STATUS, DxMODE_VLINE_ACK);
+       if (*disp_int & LB_D2_VBLANK_INTERRUPT)
+               WREG32(D2MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK);
+       if (*disp_int & LB_D2_VLINE_INTERRUPT)
+               WREG32(D2MODE_VLINE_STATUS, DxMODE_VLINE_ACK);
+       if (*disp_int & DC_HPD1_INTERRUPT) {
+               if (ASIC_IS_DCE3(rdev)) {
+                       tmp = RREG32(DC_HPD1_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HPD1_INT_CONTROL, tmp);
+               } else {
+                       tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
+               }
+       }
+       if (*disp_int & DC_HPD2_INTERRUPT) {
+               if (ASIC_IS_DCE3(rdev)) {
+                       tmp = RREG32(DC_HPD2_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HPD2_INT_CONTROL, tmp);
+               } else {
+                       tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
+               }
+       }
+       if (*disp_int_cont & DC_HPD3_INTERRUPT) {
+               if (ASIC_IS_DCE3(rdev)) {
+                       tmp = RREG32(DC_HPD3_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HPD3_INT_CONTROL, tmp);
+               } else {
+                       tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp);
+               }
+       }
+       if (*disp_int_cont & DC_HPD4_INTERRUPT) {
+               tmp = RREG32(DC_HPD4_INT_CONTROL);
+               tmp |= DC_HPDx_INT_ACK;
+               WREG32(DC_HPD4_INT_CONTROL, tmp);
+       }
+       if (ASIC_IS_DCE32(rdev)) {
+               if (*disp_int_cont2 & DC_HPD5_INTERRUPT) {
+                       tmp = RREG32(DC_HPD5_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HPD5_INT_CONTROL, tmp);
+               }
+               if (*disp_int_cont2 & DC_HPD6_INTERRUPT) {
+                       tmp = RREG32(DC_HPD5_INT_CONTROL);
+                       tmp |= DC_HPDx_INT_ACK;
+                       WREG32(DC_HPD6_INT_CONTROL, tmp);
+               }
+       }
+}
+
+void r600_irq_disable(struct radeon_device *rdev)
+{
+       u32 disp_int, disp_int_cont, disp_int_cont2;
+
+       r600_disable_interrupts(rdev);
+       /* Wait and acknowledge irq */
+       mdelay(1);
+       r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2);
+       r600_disable_interrupt_state(rdev);
+}
+
+static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
+{
+       u32 wptr, tmp;
 
+       /* XXX use writeback */
+       wptr = RREG32(IH_RB_WPTR);
 
+       if (wptr & RB_OVERFLOW) {
+               WARN_ON(1);
+               /* XXX deal with overflow */
+               DRM_ERROR("IH RB overflow\n");
+               tmp = RREG32(IH_RB_CNTL);
+               tmp |= IH_WPTR_OVERFLOW_CLEAR;
+               WREG32(IH_RB_CNTL, tmp);
+       }
+       wptr = wptr & WPTR_OFFSET_MASK;
+
+       return wptr;
+}
+
+/*        r600 IV Ring
+ * Each IV ring entry is 128 bits:
+ * [7:0]    - interrupt source id
+ * [31:8]   - reserved
+ * [59:32]  - interrupt source data
+ * [127:60]  - reserved
+ *
+ * The basic interrupt vector entries
+ * are decoded as follows:
+ * src_id  src_data  description
+ *      1         0  D1 Vblank
+ *      1         1  D1 Vline
+ *      5         0  D2 Vblank
+ *      5         1  D2 Vline
+ *     19         0  FP Hot plug detection A
+ *     19         1  FP Hot plug detection B
+ *     19         2  DAC A auto-detection
+ *     19         3  DAC B auto-detection
+ *    176         -  CP_INT RB
+ *    177         -  CP_INT IB1
+ *    178         -  CP_INT IB2
+ *    181         -  EOP Interrupt
+ *    233         -  GUI Idle
+ *
+ * Note, these are based on r600 and may need to be
+ * adjusted or added to on newer asics
+ */
+
+int r600_irq_process(struct radeon_device *rdev)
+{
+       u32 wptr = r600_get_ih_wptr(rdev);
+       u32 rptr = rdev->ih.rptr;
+       u32 src_id, src_data;
+       u32 last_entry = rdev->ih.ring_size - 16;
+       u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
+       unsigned long flags;
+       bool queue_hotplug = false;
+
+       DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
+
+       spin_lock_irqsave(&rdev->ih.lock, flags);
+
+       if (rptr == wptr) {
+               spin_unlock_irqrestore(&rdev->ih.lock, flags);
+               return IRQ_NONE;
+       }
+       if (rdev->shutdown) {
+               spin_unlock_irqrestore(&rdev->ih.lock, flags);
+               return IRQ_NONE;
+       }
+
+restart_ih:
+       /* display interrupts */
+       r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2);
+
+       rdev->ih.wptr = wptr;
+       while (rptr != wptr) {
+               /* wptr/rptr are in bytes! */
+               ring_index = rptr / 4;
+               src_id =  rdev->ih.ring[ring_index] & 0xff;
+               src_data = rdev->ih.ring[ring_index + 1] & 0xfffffff;
+
+               switch (src_id) {
+               case 1: /* D1 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D1 vblank */
+                               if (disp_int & LB_D1_VBLANK_INTERRUPT) {
+                                       drm_handle_vblank(rdev->ddev, 0);
+                                       disp_int &= ~LB_D1_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D1 vblank\n");
+                               }
+                               break;
+                       case 1: /* D1 vline */
+                               if (disp_int & LB_D1_VLINE_INTERRUPT) {
+                                       disp_int &= ~LB_D1_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D1 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 5: /* D2 vblank/vline */
+                       switch (src_data) {
+                       case 0: /* D2 vblank */
+                               if (disp_int & LB_D2_VBLANK_INTERRUPT) {
+                                       drm_handle_vblank(rdev->ddev, 1);
+                                       disp_int &= ~LB_D2_VBLANK_INTERRUPT;
+                                       DRM_DEBUG("IH: D2 vblank\n");
+                               }
+                               break;
+                       case 1: /* D1 vline */
+                               if (disp_int & LB_D2_VLINE_INTERRUPT) {
+                                       disp_int &= ~LB_D2_VLINE_INTERRUPT;
+                                       DRM_DEBUG("IH: D2 vline\n");
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 19: /* HPD/DAC hotplug */
+                       switch (src_data) {
+                       case 0:
+                               if (disp_int & DC_HPD1_INTERRUPT) {
+                                       disp_int &= ~DC_HPD1_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD1\n");
+                               }
+                               break;
+                       case 1:
+                               if (disp_int & DC_HPD2_INTERRUPT) {
+                                       disp_int &= ~DC_HPD2_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD2\n");
+                               }
+                               break;
+                       case 4:
+                               if (disp_int_cont & DC_HPD3_INTERRUPT) {
+                                       disp_int_cont &= ~DC_HPD3_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD3\n");
+                               }
+                               break;
+                       case 5:
+                               if (disp_int_cont & DC_HPD4_INTERRUPT) {
+                                       disp_int_cont &= ~DC_HPD4_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD4\n");
+                               }
+                               break;
+                       case 10:
+                               if (disp_int_cont2 & DC_HPD5_INTERRUPT) {
+                                       disp_int_cont &= ~DC_HPD5_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD5\n");
+                               }
+                               break;
+                       case 12:
+                               if (disp_int_cont2 & DC_HPD6_INTERRUPT) {
+                                       disp_int_cont &= ~DC_HPD6_INTERRUPT;
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD6\n");
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
+                               break;
+                       }
+                       break;
+               case 176: /* CP_INT in ring buffer */
+               case 177: /* CP_INT in IB1 */
+               case 178: /* CP_INT in IB2 */
+                       DRM_DEBUG("IH: CP int: 0x%08x\n", src_data);
+                       radeon_fence_process(rdev);
+                       break;
+               case 181: /* CP EOP event */
+                       DRM_DEBUG("IH: CP EOP\n");
+                       break;
+               default:
+                       DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
+                       break;
+               }
+
+               /* wptr/rptr are in bytes! */
+               if (rptr == last_entry)
+                       rptr = 0;
+               else
+                       rptr += 16;
+       }
+       /* make sure wptr hasn't changed while processing */
+       wptr = r600_get_ih_wptr(rdev);
+       if (wptr != rdev->ih.wptr)
+               goto restart_ih;
+       if (queue_hotplug)
+               queue_work(rdev->wq, &rdev->hotplug_work);
+       rdev->ih.rptr = rptr;
+       WREG32(IH_RB_RPTR, rdev->ih.rptr);
+       spin_unlock_irqrestore(&rdev->ih.lock, flags);
+       return IRQ_HANDLED;
+}
 
 /*
  * Debugfs info
@@ -1811,21 +2832,21 @@ static int r600_debugfs_cp_ring_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        struct radeon_device *rdev = dev->dev_private;
-       uint32_t rdp, wdp;
        unsigned count, i, j;
 
        radeon_ring_free_size(rdev);
-       rdp = RREG32(CP_RB_RPTR);
-       wdp = RREG32(CP_RB_WPTR);
-       count = (rdp + rdev->cp.ring_size - wdp) & rdev->cp.ptr_mask;
+       count = (rdev->cp.ring_size / 4) - rdev->cp.ring_free_dw;
        seq_printf(m, "CP_STAT 0x%08x\n", RREG32(CP_STAT));
-       seq_printf(m, "CP_RB_WPTR 0x%08x\n", wdp);
-       seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp);
+       seq_printf(m, "CP_RB_WPTR 0x%08x\n", RREG32(CP_RB_WPTR));
+       seq_printf(m, "CP_RB_RPTR 0x%08x\n", RREG32(CP_RB_RPTR));
+       seq_printf(m, "driver's copy of the CP_RB_WPTR 0x%08x\n", rdev->cp.wptr);
+       seq_printf(m, "driver's copy of the CP_RB_RPTR 0x%08x\n", rdev->cp.rptr);
        seq_printf(m, "%u free dwords in ring\n", rdev->cp.ring_free_dw);
        seq_printf(m, "%u dwords in ring\n", count);
+       i = rdev->cp.rptr;
        for (j = 0; j <= count; j++) {
-               i = (rdp + j) & rdev->cp.ptr_mask;
                seq_printf(m, "r[%04d]=0x%08x\n", i, rdev->cp.ring[i]);
+               i = (i + 1) & rdev->cp.ptr_mask;
        }
        return 0;
 }
index dbf716e..9aecafb 100644 (file)
@@ -473,9 +473,8 @@ int r600_blit_init(struct radeon_device *rdev)
        obj_size += r6xx_ps_size * 4;
        obj_size = ALIGN(obj_size, 256);
 
-       r = radeon_object_create(rdev, NULL, obj_size,
-                                true, RADEON_GEM_DOMAIN_VRAM,
-                                false, &rdev->r600_blit.shader_obj);
+       r = radeon_bo_create(rdev, NULL, obj_size, true, RADEON_GEM_DOMAIN_VRAM,
+                               &rdev->r600_blit.shader_obj);
        if (r) {
                DRM_ERROR("r600 failed to allocate shader\n");
                return r;
@@ -485,12 +484,14 @@ int r600_blit_init(struct radeon_device *rdev)
                  obj_size,
                  rdev->r600_blit.vs_offset, rdev->r600_blit.ps_offset);
 
-       r = radeon_object_kmap(rdev->r600_blit.shader_obj, &ptr);
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_kmap(rdev->r600_blit.shader_obj, &ptr);
        if (r) {
                DRM_ERROR("failed to map blit object %d\n", r);
                return r;
        }
-
        if (rdev->family >= CHIP_RV770)
                memcpy_toio(ptr + rdev->r600_blit.state_offset,
                            r7xx_default_state, rdev->r600_blit.state_len * 4);
@@ -500,19 +501,26 @@ int r600_blit_init(struct radeon_device *rdev)
        if (num_packet2s)
                memcpy_toio(ptr + rdev->r600_blit.state_offset + (rdev->r600_blit.state_len * 4),
                            packet2s, num_packet2s * 4);
-
-
        memcpy(ptr + rdev->r600_blit.vs_offset, r6xx_vs, r6xx_vs_size * 4);
        memcpy(ptr + rdev->r600_blit.ps_offset, r6xx_ps, r6xx_ps_size * 4);
-
-       radeon_object_kunmap(rdev->r600_blit.shader_obj);
+       radeon_bo_kunmap(rdev->r600_blit.shader_obj);
+       radeon_bo_unreserve(rdev->r600_blit.shader_obj);
        return 0;
 }
 
 void r600_blit_fini(struct radeon_device *rdev)
 {
-       radeon_object_unpin(rdev->r600_blit.shader_obj);
-       radeon_object_unref(&rdev->r600_blit.shader_obj);
+       int r;
+
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (unlikely(r != 0)) {
+               dev_err(rdev->dev, "(%d) can't finish r600 blit\n", r);
+               goto out_unref;
+       }
+       radeon_bo_unpin(rdev->r600_blit.shader_obj);
+       radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+out_unref:
+       radeon_bo_unref(&rdev->r600_blit.shader_obj);
 }
 
 int r600_vb_ib_get(struct radeon_device *rdev)
@@ -569,9 +577,9 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
        ring_size = num_loops * dwords_per_loop;
        /* set default  + shaders */
        ring_size += 40; /* shaders + def state */
-       ring_size += 3; /* fence emit for VB IB */
+       ring_size += 5; /* fence emit for VB IB */
        ring_size += 5; /* done copy */
-       ring_size += 3; /* fence emit for done copy */
+       ring_size += 5; /* fence emit for done copy */
        r = radeon_ring_lock(rdev, ring_size);
        WARN_ON(r);
 
index 27ab428..05894ed 100644 (file)
 #define         WAIT_2D_IDLECLEAN_bit                           (1 << 16)
 #define         WAIT_3D_IDLECLEAN_bit                           (1 << 17)
 
-
+#define IH_RB_CNTL                                        0x3e00
+#       define IH_RB_ENABLE                               (1 << 0)
+#       define IH_IB_SIZE(x)                              ((x) << 1) /* log2 */
+#       define IH_RB_FULL_DRAIN_ENABLE                    (1 << 6)
+#       define IH_WPTR_WRITEBACK_ENABLE                   (1 << 8)
+#       define IH_WPTR_WRITEBACK_TIMER(x)                 ((x) << 9) /* log2 */
+#       define IH_WPTR_OVERFLOW_ENABLE                    (1 << 16)
+#       define IH_WPTR_OVERFLOW_CLEAR                     (1 << 31)
+#define IH_RB_BASE                                        0x3e04
+#define IH_RB_RPTR                                        0x3e08
+#define IH_RB_WPTR                                        0x3e0c
+#       define RB_OVERFLOW                                (1 << 0)
+#       define WPTR_OFFSET_MASK                           0x3fffc
+#define IH_RB_WPTR_ADDR_HI                                0x3e10
+#define IH_RB_WPTR_ADDR_LO                                0x3e14
+#define IH_CNTL                                           0x3e18
+#       define ENABLE_INTR                                (1 << 0)
+#       define IH_MC_SWAP(x)                              ((x) << 2)
+#       define IH_MC_SWAP_NONE                            0
+#       define IH_MC_SWAP_16BIT                           1
+#       define IH_MC_SWAP_32BIT                           2
+#       define IH_MC_SWAP_64BIT                           3
+#       define RPTR_REARM                                 (1 << 4)
+#       define MC_WRREQ_CREDIT(x)                         ((x) << 15)
+#       define MC_WR_CLEAN_CNT(x)                         ((x) << 20)
+
+#define RLC_CNTL                                          0x3f00
+#       define RLC_ENABLE                                 (1 << 0)
+#define RLC_HB_BASE                                       0x3f10
+#define RLC_HB_CNTL                                       0x3f0c
+#define RLC_HB_RPTR                                       0x3f20
+#define RLC_HB_WPTR                                       0x3f1c
+#define RLC_HB_WPTR_LSB_ADDR                              0x3f14
+#define RLC_HB_WPTR_MSB_ADDR                              0x3f18
+#define RLC_MC_CNTL                                       0x3f44
+#define RLC_UCODE_CNTL                                    0x3f48
+#define RLC_UCODE_ADDR                                    0x3f2c
+#define RLC_UCODE_DATA                                    0x3f30
+
+#define SRBM_SOFT_RESET                                   0xe60
+#       define SOFT_RESET_RLC                             (1 << 13)
+
+#define CP_INT_CNTL                                       0xc124
+#       define CNTX_BUSY_INT_ENABLE                       (1 << 19)
+#       define CNTX_EMPTY_INT_ENABLE                      (1 << 20)
+#       define SCRATCH_INT_ENABLE                         (1 << 25)
+#       define TIME_STAMP_INT_ENABLE                      (1 << 26)
+#       define IB2_INT_ENABLE                             (1 << 29)
+#       define IB1_INT_ENABLE                             (1 << 30)
+#       define RB_INT_ENABLE                              (1 << 31)
+#define CP_INT_STATUS                                     0xc128
+#       define SCRATCH_INT_STAT                           (1 << 25)
+#       define TIME_STAMP_INT_STAT                        (1 << 26)
+#       define IB2_INT_STAT                               (1 << 29)
+#       define IB1_INT_STAT                               (1 << 30)
+#       define RB_INT_STAT                                (1 << 31)
+
+#define GRBM_INT_CNTL                                     0x8060
+#       define RDERR_INT_ENABLE                           (1 << 0)
+#       define WAIT_COUNT_TIMEOUT_INT_ENABLE              (1 << 1)
+#       define GUI_IDLE_INT_ENABLE                        (1 << 19)
+
+#define INTERRUPT_CNTL                                    0x5468
+#       define IH_DUMMY_RD_OVERRIDE                       (1 << 0)
+#       define IH_DUMMY_RD_EN                             (1 << 1)
+#       define IH_REQ_NONSNOOP_EN                         (1 << 3)
+#       define GEN_IH_INT_EN                              (1 << 8)
+#define INTERRUPT_CNTL2                                   0x546c
+
+#define D1MODE_VBLANK_STATUS                              0x6534
+#define D2MODE_VBLANK_STATUS                              0x6d34
+#       define DxMODE_VBLANK_OCCURRED                     (1 << 0)
+#       define DxMODE_VBLANK_ACK                          (1 << 4)
+#       define DxMODE_VBLANK_STAT                         (1 << 12)
+#       define DxMODE_VBLANK_INTERRUPT                    (1 << 16)
+#       define DxMODE_VBLANK_INTERRUPT_TYPE               (1 << 17)
+#define D1MODE_VLINE_STATUS                               0x653c
+#define D2MODE_VLINE_STATUS                               0x6d3c
+#       define DxMODE_VLINE_OCCURRED                      (1 << 0)
+#       define DxMODE_VLINE_ACK                           (1 << 4)
+#       define DxMODE_VLINE_STAT                          (1 << 12)
+#       define DxMODE_VLINE_INTERRUPT                     (1 << 16)
+#       define DxMODE_VLINE_INTERRUPT_TYPE                (1 << 17)
+#define DxMODE_INT_MASK                                   0x6540
+#       define D1MODE_VBLANK_INT_MASK                     (1 << 0)
+#       define D1MODE_VLINE_INT_MASK                      (1 << 4)
+#       define D2MODE_VBLANK_INT_MASK                     (1 << 8)
+#       define D2MODE_VLINE_INT_MASK                      (1 << 12)
+#define DCE3_DISP_INTERRUPT_STATUS                        0x7ddc
+#       define DC_HPD1_INTERRUPT                          (1 << 18)
+#       define DC_HPD2_INTERRUPT                          (1 << 19)
+#define DISP_INTERRUPT_STATUS                             0x7edc
+#       define LB_D1_VLINE_INTERRUPT                      (1 << 2)
+#       define LB_D2_VLINE_INTERRUPT                      (1 << 3)
+#       define LB_D1_VBLANK_INTERRUPT                     (1 << 4)
+#       define LB_D2_VBLANK_INTERRUPT                     (1 << 5)
+#       define DACA_AUTODETECT_INTERRUPT                  (1 << 16)
+#       define DACB_AUTODETECT_INTERRUPT                  (1 << 17)
+#       define DC_HOT_PLUG_DETECT1_INTERRUPT              (1 << 18)
+#       define DC_HOT_PLUG_DETECT2_INTERRUPT              (1 << 19)
+#       define DC_I2C_SW_DONE_INTERRUPT                   (1 << 20)
+#       define DC_I2C_HW_DONE_INTERRUPT                   (1 << 21)
+#define DISP_INTERRUPT_STATUS_CONTINUE                    0x7ee8
+#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE               0x7de8
+#       define DC_HPD4_INTERRUPT                          (1 << 14)
+#       define DC_HPD4_RX_INTERRUPT                       (1 << 15)
+#       define DC_HPD3_INTERRUPT                          (1 << 28)
+#       define DC_HPD1_RX_INTERRUPT                       (1 << 29)
+#       define DC_HPD2_RX_INTERRUPT                       (1 << 30)
+#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE2              0x7dec
+#       define DC_HPD3_RX_INTERRUPT                       (1 << 0)
+#       define DIGA_DP_VID_STREAM_DISABLE_INTERRUPT       (1 << 1)
+#       define DIGA_DP_STEER_FIFO_OVERFLOW_INTERRUPT      (1 << 2)
+#       define DIGB_DP_VID_STREAM_DISABLE_INTERRUPT       (1 << 3)
+#       define DIGB_DP_STEER_FIFO_OVERFLOW_INTERRUPT      (1 << 4)
+#       define AUX1_SW_DONE_INTERRUPT                     (1 << 5)
+#       define AUX1_LS_DONE_INTERRUPT                     (1 << 6)
+#       define AUX2_SW_DONE_INTERRUPT                     (1 << 7)
+#       define AUX2_LS_DONE_INTERRUPT                     (1 << 8)
+#       define AUX3_SW_DONE_INTERRUPT                     (1 << 9)
+#       define AUX3_LS_DONE_INTERRUPT                     (1 << 10)
+#       define AUX4_SW_DONE_INTERRUPT                     (1 << 11)
+#       define AUX4_LS_DONE_INTERRUPT                     (1 << 12)
+#       define DIGA_DP_FAST_TRAINING_COMPLETE_INTERRUPT   (1 << 13)
+#       define DIGB_DP_FAST_TRAINING_COMPLETE_INTERRUPT   (1 << 14)
+/* DCE 3.2 */
+#       define AUX5_SW_DONE_INTERRUPT                     (1 << 15)
+#       define AUX5_LS_DONE_INTERRUPT                     (1 << 16)
+#       define AUX6_SW_DONE_INTERRUPT                     (1 << 17)
+#       define AUX6_LS_DONE_INTERRUPT                     (1 << 18)
+#       define DC_HPD5_INTERRUPT                          (1 << 19)
+#       define DC_HPD5_RX_INTERRUPT                       (1 << 20)
+#       define DC_HPD6_INTERRUPT                          (1 << 21)
+#       define DC_HPD6_RX_INTERRUPT                       (1 << 22)
+
+#define DACA_AUTO_DETECT_CONTROL                          0x7828
+#define DACB_AUTO_DETECT_CONTROL                          0x7a28
+#define DCE3_DACA_AUTO_DETECT_CONTROL                     0x7028
+#define DCE3_DACB_AUTO_DETECT_CONTROL                     0x7128
+#       define DACx_AUTODETECT_MODE(x)                    ((x) << 0)
+#       define DACx_AUTODETECT_MODE_NONE                  0
+#       define DACx_AUTODETECT_MODE_CONNECT               1
+#       define DACx_AUTODETECT_MODE_DISCONNECT            2
+#       define DACx_AUTODETECT_FRAME_TIME_COUNTER(x)      ((x) << 8)
+/* bit 18 = R/C, 17 = G/Y, 16 = B/Comp */
+#       define DACx_AUTODETECT_CHECK_MASK(x)              ((x) << 16)
+
+#define DCE3_DACA_AUTODETECT_INT_CONTROL                  0x7038
+#define DCE3_DACB_AUTODETECT_INT_CONTROL                  0x7138
+#define DACA_AUTODETECT_INT_CONTROL                       0x7838
+#define DACB_AUTODETECT_INT_CONTROL                       0x7a38
+#       define DACx_AUTODETECT_ACK                        (1 << 0)
+#       define DACx_AUTODETECT_INT_ENABLE                 (1 << 16)
+
+#define DC_HOT_PLUG_DETECT1_CONTROL                       0x7d00
+#define DC_HOT_PLUG_DETECT2_CONTROL                       0x7d10
+#define DC_HOT_PLUG_DETECT3_CONTROL                       0x7d24
+#       define DC_HOT_PLUG_DETECTx_EN                     (1 << 0)
+
+#define DC_HOT_PLUG_DETECT1_INT_STATUS                    0x7d04
+#define DC_HOT_PLUG_DETECT2_INT_STATUS                    0x7d14
+#define DC_HOT_PLUG_DETECT3_INT_STATUS                    0x7d28
+#       define DC_HOT_PLUG_DETECTx_INT_STATUS             (1 << 0)
+#       define DC_HOT_PLUG_DETECTx_SENSE                  (1 << 1)
+
+/* DCE 3.0 */
+#define DC_HPD1_INT_STATUS                                0x7d00
+#define DC_HPD2_INT_STATUS                                0x7d0c
+#define DC_HPD3_INT_STATUS                                0x7d18
+#define DC_HPD4_INT_STATUS                                0x7d24
+/* DCE 3.2 */
+#define DC_HPD5_INT_STATUS                                0x7dc0
+#define DC_HPD6_INT_STATUS                                0x7df4
+#       define DC_HPDx_INT_STATUS                         (1 << 0)
+#       define DC_HPDx_SENSE                              (1 << 1)
+#       define DC_HPDx_RX_INT_STATUS                      (1 << 8)
+
+#define DC_HOT_PLUG_DETECT1_INT_CONTROL                   0x7d08
+#define DC_HOT_PLUG_DETECT2_INT_CONTROL                   0x7d18
+#define DC_HOT_PLUG_DETECT3_INT_CONTROL                   0x7d2c
+#       define DC_HOT_PLUG_DETECTx_INT_ACK                (1 << 0)
+#       define DC_HOT_PLUG_DETECTx_INT_POLARITY           (1 << 8)
+#       define DC_HOT_PLUG_DETECTx_INT_EN                 (1 << 16)
+/* DCE 3.0 */
+#define DC_HPD1_INT_CONTROL                               0x7d04
+#define DC_HPD2_INT_CONTROL                               0x7d10
+#define DC_HPD3_INT_CONTROL                               0x7d1c
+#define DC_HPD4_INT_CONTROL                               0x7d28
+/* DCE 3.2 */
+#define DC_HPD5_INT_CONTROL                               0x7dc4
+#define DC_HPD6_INT_CONTROL                               0x7df8
+#       define DC_HPDx_INT_ACK                            (1 << 0)
+#       define DC_HPDx_INT_POLARITY                       (1 << 8)
+#       define DC_HPDx_INT_EN                             (1 << 16)
+#       define DC_HPDx_RX_INT_ACK                         (1 << 20)
+#       define DC_HPDx_RX_INT_EN                          (1 << 24)
+
+/* DCE 3.0 */
+#define DC_HPD1_CONTROL                                   0x7d08
+#define DC_HPD2_CONTROL                                   0x7d14
+#define DC_HPD3_CONTROL                                   0x7d20
+#define DC_HPD4_CONTROL                                   0x7d2c
+/* DCE 3.2 */
+#define DC_HPD5_CONTROL                                   0x7dc8
+#define DC_HPD6_CONTROL                                   0x7dfc
+#       define DC_HPDx_CONNECTION_TIMER(x)                ((x) << 0)
+#       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
+/* DCE 3.2 */
+#       define DC_HPDx_EN                                 (1 << 28)
 
 /*
  * PM4
 #define        PACKET3_WAIT_REG_MEM                            0x3C
 #define        PACKET3_MEM_WRITE                               0x3D
 #define        PACKET3_INDIRECT_BUFFER                         0x32
-#define        PACKET3_CP_INTERRUPT                            0x40
 #define        PACKET3_SURFACE_SYNC                            0x43
 #              define PACKET3_CB0_DEST_BASE_ENA    (1 << 6)
 #              define PACKET3_TC_ACTION_ENA        (1 << 23)
 #define                S_000E60_SOFT_RESET_TSC(x)              (((x) & 1) << 16)
 #define                S_000E60_SOFT_RESET_VMC(x)              (((x) & 1) << 17)
 
+#define R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL          0x5480
 #endif
index 224506a..c938bb5 100644 (file)
@@ -28,8 +28,6 @@
 #ifndef __RADEON_H__
 #define __RADEON_H__
 
-#include "radeon_object.h"
-
 /* TODO: Here are things that needs to be done :
  *     - surface allocator & initializer : (bit like scratch reg) should
  *       initialize HDP_ stuff on RS600, R600, R700 hw, well anythings
 #include <linux/list.h>
 #include <linux/kref.h>
 
+#include <ttm/ttm_bo_api.h>
+#include <ttm/ttm_bo_driver.h>
+#include <ttm/ttm_placement.h>
+#include <ttm/ttm_module.h>
+
 #include "radeon_family.h"
 #include "radeon_mode.h"
 #include "radeon_reg.h"
@@ -85,6 +88,7 @@ extern int radeon_benchmarking;
 extern int radeon_testing;
 extern int radeon_connector_table;
 extern int radeon_tv;
+extern int radeon_new_pll;
 
 /*
  * Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -186,76 +190,62 @@ void radeon_fence_unref(struct radeon_fence **fence);
  * Tiling registers
  */
 struct radeon_surface_reg {
-       struct radeon_object *robj;
+       struct radeon_bo *bo;
 };
 
 #define RADEON_GEM_MAX_SURFACES 8
 
 /*
- * Radeon buffer.
+ * TTM.
  */
-struct radeon_object;
+struct radeon_mman {
+       struct ttm_bo_global_ref        bo_global_ref;
+       struct ttm_global_reference     mem_global_ref;
+       bool                            mem_global_referenced;
+       struct ttm_bo_device            bdev;
+};
+
+struct radeon_bo {
+       /* Protected by gem.mutex */
+       struct list_head                list;
+       /* Protected by tbo.reserved */
+       u32                             placements[3];
+       struct ttm_placement            placement;
+       struct ttm_buffer_object        tbo;
+       struct ttm_bo_kmap_obj          kmap;
+       unsigned                        pin_count;
+       void                            *kptr;
+       u32                             tiling_flags;
+       u32                             pitch;
+       int                             surface_reg;
+       /* Constant after initialization */
+       struct radeon_device            *rdev;
+       struct drm_gem_object           *gobj;
+};
 
-struct radeon_object_list {
+struct radeon_bo_list {
        struct list_head        list;
-       struct radeon_object    *robj;
+       struct radeon_bo        *bo;
        uint64_t                gpu_offset;
        unsigned                rdomain;
        unsigned                wdomain;
-       uint32_t                tiling_flags;
+       u32                     tiling_flags;
 };
 
-int radeon_object_init(struct radeon_device *rdev);
-void radeon_object_fini(struct radeon_device *rdev);
-int radeon_object_create(struct radeon_device *rdev,
-                        struct drm_gem_object *gobj,
-                        unsigned long size,
-                        bool kernel,
-                        uint32_t domain,
-                        bool interruptible,
-                        struct radeon_object **robj_ptr);
-int radeon_object_kmap(struct radeon_object *robj, void **ptr);
-void radeon_object_kunmap(struct radeon_object *robj);
-void radeon_object_unref(struct radeon_object **robj);
-int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
-                     uint64_t *gpu_addr);
-void radeon_object_unpin(struct radeon_object *robj);
-int radeon_object_wait(struct radeon_object *robj);
-int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement);
-int radeon_object_evict_vram(struct radeon_device *rdev);
-int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset);
-void radeon_object_force_delete(struct radeon_device *rdev);
-void radeon_object_list_add_object(struct radeon_object_list *lobj,
-                                  struct list_head *head);
-int radeon_object_list_validate(struct list_head *head, void *fence);
-void radeon_object_list_unvalidate(struct list_head *head);
-void radeon_object_list_clean(struct list_head *head);
-int radeon_object_fbdev_mmap(struct radeon_object *robj,
-                            struct vm_area_struct *vma);
-unsigned long radeon_object_size(struct radeon_object *robj);
-void radeon_object_clear_surface_reg(struct radeon_object *robj);
-int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved,
-                              bool force_drop);
-void radeon_object_set_tiling_flags(struct radeon_object *robj,
-                                   uint32_t tiling_flags, uint32_t pitch);
-void radeon_object_get_tiling_flags(struct radeon_object *robj, uint32_t *tiling_flags, uint32_t *pitch);
-void radeon_bo_move_notify(struct ttm_buffer_object *bo,
-                          struct ttm_mem_reg *mem);
-void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
 /*
  * GEM objects.
  */
 struct radeon_gem {
+       struct mutex            mutex;
        struct list_head        objects;
 };
 
 int radeon_gem_init(struct radeon_device *rdev);
 void radeon_gem_fini(struct radeon_device *rdev);
 int radeon_gem_object_create(struct radeon_device *rdev, int size,
-                            int alignment, int initial_domain,
-                            bool discardable, bool kernel,
-                            bool interruptible,
-                            struct drm_gem_object **obj);
+                               int alignment, int initial_domain,
+                               bool discardable, bool kernel,
+                               struct drm_gem_object **obj);
 int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
                          uint64_t *gpu_addr);
 void radeon_gem_object_unpin(struct drm_gem_object *obj);
@@ -271,7 +261,7 @@ struct radeon_gart_table_ram {
 };
 
 struct radeon_gart_table_vram {
-       struct radeon_object            *robj;
+       struct radeon_bo                *robj;
        volatile uint32_t               *ptr;
 };
 
@@ -352,11 +342,16 @@ struct radeon_irq {
        bool            sw_int;
        /* FIXME: use a define max crtc rather than hardcode it */
        bool            crtc_vblank_int[2];
+       /* FIXME: use defines for max hpd/dacs */
+       bool            hpd[6];
+       spinlock_t sw_lock;
+       int sw_refcount;
 };
 
 int radeon_irq_kms_init(struct radeon_device *rdev);
 void radeon_irq_kms_fini(struct radeon_device *rdev);
-
+void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev);
+void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev);
 
 /*
  * CP & ring.
@@ -376,7 +371,7 @@ struct radeon_ib {
  */
 struct radeon_ib_pool {
        struct mutex            mutex;
-       struct radeon_object    *robj;
+       struct radeon_bo        *robj;
        struct list_head        scheduled_ibs;
        struct radeon_ib        ibs[RADEON_IB_POOL_SIZE];
        bool                    ready;
@@ -384,7 +379,7 @@ struct radeon_ib_pool {
 };
 
 struct radeon_cp {
-       struct radeon_object    *ring_obj;
+       struct radeon_bo        *ring_obj;
        volatile uint32_t       *ring;
        unsigned                rptr;
        unsigned                wptr;
@@ -399,8 +394,25 @@ struct radeon_cp {
        bool                    ready;
 };
 
+/*
+ * R6xx+ IH ring
+ */
+struct r600_ih {
+       struct radeon_bo        *ring_obj;
+       volatile uint32_t       *ring;
+       unsigned                rptr;
+       unsigned                wptr;
+       unsigned                wptr_old;
+       unsigned                ring_size;
+       uint64_t                gpu_addr;
+       uint32_t                align_mask;
+       uint32_t                ptr_mask;
+       spinlock_t              lock;
+       bool                    enabled;
+};
+
 struct r600_blit {
-       struct radeon_object    *shader_obj;
+       struct radeon_bo        *shader_obj;
        u64 shader_gpu_addr;
        u32 vs_offset, ps_offset;
        u32 state_offset;
@@ -430,8 +442,8 @@ void radeon_ring_fini(struct radeon_device *rdev);
  */
 struct radeon_cs_reloc {
        struct drm_gem_object           *gobj;
-       struct radeon_object            *robj;
-       struct radeon_object_list       lobj;
+       struct radeon_bo                *robj;
+       struct radeon_bo_list           lobj;
        uint32_t                        handle;
        uint32_t                        flags;
 };
@@ -527,7 +539,7 @@ void radeon_agp_fini(struct radeon_device *rdev);
  * Writeback
  */
 struct radeon_wb {
-       struct radeon_object    *wb_obj;
+       struct radeon_bo        *wb_obj;
        volatile uint32_t       *wb;
        uint64_t                gpu_addr;
 };
@@ -639,6 +651,11 @@ struct radeon_asic {
                               uint32_t offset, uint32_t obj_size);
        int (*clear_surface_reg)(struct radeon_device *rdev, int reg);
        void (*bandwidth_update)(struct radeon_device *rdev);
+       void (*hdp_flush)(struct radeon_device *rdev);
+       void (*hpd_init)(struct radeon_device *rdev);
+       void (*hpd_fini)(struct radeon_device *rdev);
+       bool (*hpd_sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+       void (*hpd_set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
 };
 
 /*
@@ -751,9 +768,9 @@ struct radeon_device {
        uint8_t                         *bios;
        bool                            is_atom_bios;
        uint16_t                        bios_header_start;
-       struct radeon_object            *stollen_vga_memory;
+       struct radeon_bo                *stollen_vga_memory;
        struct fb_info                  *fbdev_info;
-       struct radeon_object            *fbdev_robj;
+       struct radeon_bo                *fbdev_rbo;
        struct radeon_framebuffer       *fbdev_rfb;
        /* Register mmio */
        resource_size_t                 rmmio_base;
@@ -791,8 +808,12 @@ struct radeon_device {
        struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES];
        const struct firmware *me_fw;   /* all family ME firmware */
        const struct firmware *pfp_fw;  /* r6/700 PFP firmware */
+       const struct firmware *rlc_fw;  /* r6/700 RLC firmware */
        struct r600_blit r600_blit;
        int msi_enabled; /* msi enabled */
+       struct r600_ih ih; /* r6/700 interrupt ring */
+       struct workqueue_struct *wq;
+       struct work_struct hotplug_work;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
@@ -829,6 +850,10 @@ static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32
        }
 }
 
+/*
+ * Cast helper
+ */
+#define to_radeon_fence(p) ((struct radeon_fence *)(p))
 
 /*
  * Registers read & write functions.
@@ -965,18 +990,24 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
 #define radeon_get_engine_clock(rdev) (rdev)->asic->get_engine_clock((rdev))
 #define radeon_set_engine_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
 #define radeon_get_memory_clock(rdev) (rdev)->asic->get_memory_clock((rdev))
-#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
+#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_memory_clock((rdev), (e))
 #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l))
 #define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e))
 #define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->set_surface_reg((rdev), (r), (f), (p), (o), (s)))
 #define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->clear_surface_reg((rdev), (r)))
 #define radeon_bandwidth_update(rdev) (rdev)->asic->bandwidth_update((rdev))
+#define radeon_hdp_flush(rdev) (rdev)->asic->hdp_flush((rdev))
+#define radeon_hpd_init(rdev) (rdev)->asic->hpd_init((rdev))
+#define radeon_hpd_fini(rdev) (rdev)->asic->hpd_fini((rdev))
+#define radeon_hpd_sense(rdev, hpd) (rdev)->asic->hpd_sense((rdev), (hpd))
+#define radeon_hpd_set_polarity(rdev, hpd) (rdev)->asic->hpd_set_polarity((rdev), (hpd))
 
 /* Common functions */
 extern int radeon_gart_table_vram_pin(struct radeon_device *rdev);
 extern int radeon_modeset_init(struct radeon_device *rdev);
 extern void radeon_modeset_fini(struct radeon_device *rdev);
 extern bool radeon_card_posted(struct radeon_device *rdev);
+extern bool radeon_boot_test_post_card(struct radeon_device *rdev);
 extern int radeon_clocks_init(struct radeon_device *rdev);
 extern void radeon_clocks_fini(struct radeon_device *rdev);
 extern void radeon_scratch_init(struct radeon_device *rdev);
@@ -984,6 +1015,7 @@ extern void radeon_surface_init(struct radeon_device *rdev);
 extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data);
 extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);
 extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
+extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
 
 /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
 struct r100_mc_save {
@@ -1021,7 +1053,7 @@ extern int r100_cp_reset(struct radeon_device *rdev);
 extern void r100_vga_render_disable(struct radeon_device *rdev);
 extern int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p,
                                                struct radeon_cs_packet *pkt,
-                                               struct radeon_object *robj);
+                                               struct radeon_bo *robj);
 extern int r100_cs_parse_packet0(struct radeon_cs_parser *p,
                                struct radeon_cs_packet *pkt,
                                const unsigned *auth, unsigned n,
@@ -1029,6 +1061,8 @@ extern int r100_cs_parse_packet0(struct radeon_cs_parser *p,
 extern int r100_cs_packet_parse(struct radeon_cs_parser *p,
                                struct radeon_cs_packet *pkt,
                                unsigned idx);
+extern void r100_enable_bm(struct radeon_device *rdev);
+extern void r100_set_common_regs(struct radeon_device *rdev);
 
 /* rv200,rv250,rv280 */
 extern void r200_set_safe_registers(struct radeon_device *rdev);
@@ -1104,7 +1138,14 @@ extern void r600_wb_disable(struct radeon_device *rdev);
 extern void r600_scratch_init(struct radeon_device *rdev);
 extern int r600_blit_init(struct radeon_device *rdev);
 extern void r600_blit_fini(struct radeon_device *rdev);
-extern int r600_cp_init_microcode(struct radeon_device *rdev);
+extern int r600_init_microcode(struct radeon_device *rdev);
 extern int r600_gpu_reset(struct radeon_device *rdev);
+/* r600 irq */
+extern int r600_irq_init(struct radeon_device *rdev);
+extern void r600_irq_fini(struct radeon_device *rdev);
+extern void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size);
+extern int r600_irq_set(struct radeon_device *rdev);
+
+#include "radeon_object.h"
 
 #endif
index c18fbee..636116b 100644 (file)
@@ -76,6 +76,12 @@ int r100_clear_surface_reg(struct radeon_device *rdev, int reg);
 void r100_bandwidth_update(struct radeon_device *rdev);
 void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
 int r100_ring_test(struct radeon_device *rdev);
+void r100_hdp_flush(struct radeon_device *rdev);
+void r100_hpd_init(struct radeon_device *rdev);
+void r100_hpd_fini(struct radeon_device *rdev);
+bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+void r100_hpd_set_polarity(struct radeon_device *rdev,
+                          enum radeon_hpd_id hpd);
 
 static struct radeon_asic r100_asic = {
        .init = &r100_init,
@@ -107,6 +113,11 @@ static struct radeon_asic r100_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &r100_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &r100_hpd_init,
+       .hpd_fini = &r100_hpd_fini,
+       .hpd_sense = &r100_hpd_sense,
+       .hpd_set_polarity = &r100_hpd_set_polarity,
 };
 
 
@@ -162,6 +173,11 @@ static struct radeon_asic r300_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &r100_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &r100_hpd_init,
+       .hpd_fini = &r100_hpd_fini,
+       .hpd_sense = &r100_hpd_sense,
+       .hpd_set_polarity = &r100_hpd_set_polarity,
 };
 
 /*
@@ -201,6 +217,11 @@ static struct radeon_asic r420_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &r100_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &r100_hpd_init,
+       .hpd_fini = &r100_hpd_fini,
+       .hpd_sense = &r100_hpd_sense,
+       .hpd_set_polarity = &r100_hpd_set_polarity,
 };
 
 
@@ -245,6 +266,11 @@ static struct radeon_asic rs400_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &r100_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &r100_hpd_init,
+       .hpd_fini = &r100_hpd_fini,
+       .hpd_sense = &r100_hpd_sense,
+       .hpd_set_polarity = &r100_hpd_set_polarity,
 };
 
 
@@ -263,6 +289,12 @@ int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
 uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg);
 void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
 void rs600_bandwidth_update(struct radeon_device *rdev);
+void rs600_hpd_init(struct radeon_device *rdev);
+void rs600_hpd_fini(struct radeon_device *rdev);
+bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+void rs600_hpd_set_polarity(struct radeon_device *rdev,
+                           enum radeon_hpd_id hpd);
+
 static struct radeon_asic rs600_asic = {
        .init = &rs600_init,
        .fini = &rs600_fini,
@@ -291,6 +323,11 @@ static struct radeon_asic rs600_asic = {
        .set_pcie_lanes = NULL,
        .set_clock_gating = &radeon_atom_set_clock_gating,
        .bandwidth_update = &rs600_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &rs600_hpd_init,
+       .hpd_fini = &rs600_hpd_fini,
+       .hpd_sense = &rs600_hpd_sense,
+       .hpd_set_polarity = &rs600_hpd_set_polarity,
 };
 
 
@@ -334,6 +371,11 @@ static struct radeon_asic rs690_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &rs690_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &rs600_hpd_init,
+       .hpd_fini = &rs600_hpd_fini,
+       .hpd_sense = &rs600_hpd_sense,
+       .hpd_set_polarity = &rs600_hpd_set_polarity,
 };
 
 
@@ -381,6 +423,11 @@ static struct radeon_asic rv515_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &rv515_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &rs600_hpd_init,
+       .hpd_fini = &rs600_hpd_fini,
+       .hpd_sense = &rs600_hpd_sense,
+       .hpd_set_polarity = &rs600_hpd_set_polarity,
 };
 
 
@@ -419,6 +466,11 @@ static struct radeon_asic r520_asic = {
        .set_surface_reg = r100_set_surface_reg,
        .clear_surface_reg = r100_clear_surface_reg,
        .bandwidth_update = &rv515_bandwidth_update,
+       .hdp_flush = &r100_hdp_flush,
+       .hpd_init = &rs600_hpd_init,
+       .hpd_fini = &rs600_hpd_fini,
+       .hpd_sense = &rs600_hpd_sense,
+       .hpd_set_polarity = &rs600_hpd_set_polarity,
 };
 
 /*
@@ -455,6 +507,12 @@ int r600_ring_test(struct radeon_device *rdev);
 int r600_copy_blit(struct radeon_device *rdev,
                   uint64_t src_offset, uint64_t dst_offset,
                   unsigned num_pages, struct radeon_fence *fence);
+void r600_hdp_flush(struct radeon_device *rdev);
+void r600_hpd_init(struct radeon_device *rdev);
+void r600_hpd_fini(struct radeon_device *rdev);
+bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+void r600_hpd_set_polarity(struct radeon_device *rdev,
+                          enum radeon_hpd_id hpd);
 
 static struct radeon_asic r600_asic = {
        .init = &r600_init,
@@ -470,6 +528,7 @@ static struct radeon_asic r600_asic = {
        .ring_ib_execute = &r600_ring_ib_execute,
        .irq_set = &r600_irq_set,
        .irq_process = &r600_irq_process,
+       .get_vblank_counter = &rs600_get_vblank_counter,
        .fence_ring_emit = &r600_fence_ring_emit,
        .cs_parse = &r600_cs_parse,
        .copy_blit = &r600_copy_blit,
@@ -484,6 +543,11 @@ static struct radeon_asic r600_asic = {
        .set_surface_reg = r600_set_surface_reg,
        .clear_surface_reg = r600_clear_surface_reg,
        .bandwidth_update = &rv515_bandwidth_update,
+       .hdp_flush = &r600_hdp_flush,
+       .hpd_init = &r600_hpd_init,
+       .hpd_fini = &r600_hpd_fini,
+       .hpd_sense = &r600_hpd_sense,
+       .hpd_set_polarity = &r600_hpd_set_polarity,
 };
 
 /*
@@ -509,6 +573,7 @@ static struct radeon_asic rv770_asic = {
        .ring_ib_execute = &r600_ring_ib_execute,
        .irq_set = &r600_irq_set,
        .irq_process = &r600_irq_process,
+       .get_vblank_counter = &rs600_get_vblank_counter,
        .fence_ring_emit = &r600_fence_ring_emit,
        .cs_parse = &r600_cs_parse,
        .copy_blit = &r600_copy_blit,
@@ -523,6 +588,11 @@ static struct radeon_asic rv770_asic = {
        .set_surface_reg = r600_set_surface_reg,
        .clear_surface_reg = r600_clear_surface_reg,
        .bandwidth_update = &rv515_bandwidth_update,
+       .hdp_flush = &r600_hdp_flush,
+       .hpd_init = &r600_hpd_init,
+       .hpd_fini = &r600_hpd_fini,
+       .hpd_sense = &r600_hpd_sense,
+       .hpd_set_polarity = &r600_hpd_set_polarity,
 };
 
 #endif
index 2ed88a8..12a0c76 100644 (file)
@@ -47,7 +47,8 @@ radeon_add_atom_connector(struct drm_device *dev,
                          int connector_type,
                          struct radeon_i2c_bus_rec *i2c_bus,
                          bool linkb, uint32_t igp_lane_info,
-                         uint16_t connector_object_id);
+                         uint16_t connector_object_id,
+                         struct radeon_hpd *hpd);
 
 /* from radeon_legacy_encoder.c */
 extern void
@@ -60,16 +61,16 @@ union atom_supported_devices {
        struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1;
 };
 
-static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device
-                                                          *dev, uint8_t id)
+static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev,
+                                                              uint8_t id)
 {
-       struct radeon_device *rdev = dev->dev_private;
        struct atom_context *ctx = rdev->mode_info.atom_context;
-       ATOM_GPIO_I2C_ASSIGMENT gpio;
+       ATOM_GPIO_I2C_ASSIGMENT *gpio;
        struct radeon_i2c_bus_rec i2c;
        int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
        struct _ATOM_GPIO_I2C_INFO *i2c_info;
        uint16_t data_offset;
+       int i;
 
        memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
        i2c.valid = false;
@@ -78,34 +79,121 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device
 
        i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
 
-       gpio = i2c_info->asGPIO_Info[id];
-
-       i2c.mask_clk_reg = le16_to_cpu(gpio.usClkMaskRegisterIndex) * 4;
-       i2c.mask_data_reg = le16_to_cpu(gpio.usDataMaskRegisterIndex) * 4;
-       i2c.put_clk_reg = le16_to_cpu(gpio.usClkEnRegisterIndex) * 4;
-       i2c.put_data_reg = le16_to_cpu(gpio.usDataEnRegisterIndex) * 4;
-       i2c.get_clk_reg = le16_to_cpu(gpio.usClkY_RegisterIndex) * 4;
-       i2c.get_data_reg = le16_to_cpu(gpio.usDataY_RegisterIndex) * 4;
-       i2c.a_clk_reg = le16_to_cpu(gpio.usClkA_RegisterIndex) * 4;
-       i2c.a_data_reg = le16_to_cpu(gpio.usDataA_RegisterIndex) * 4;
-       i2c.mask_clk_mask = (1 << gpio.ucClkMaskShift);
-       i2c.mask_data_mask = (1 << gpio.ucDataMaskShift);
-       i2c.put_clk_mask = (1 << gpio.ucClkEnShift);
-       i2c.put_data_mask = (1 << gpio.ucDataEnShift);
-       i2c.get_clk_mask = (1 << gpio.ucClkY_Shift);
-       i2c.get_data_mask = (1 << gpio.ucDataY_Shift);
-       i2c.a_clk_mask = (1 << gpio.ucClkA_Shift);
-       i2c.a_data_mask = (1 << gpio.ucDataA_Shift);
-       i2c.valid = true;
+
+       for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
+               gpio = &i2c_info->asGPIO_Info[i];
+
+               if (gpio->sucI2cId.ucAccess == id) {
+                       i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
+                       i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
+                       i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
+                       i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
+                       i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
+                       i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
+                       i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
+                       i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
+                       i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
+                       i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
+                       i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
+                       i2c.en_data_mask = (1 << gpio->ucDataEnShift);
+                       i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
+                       i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
+                       i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
+                       i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
+
+                       if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
+                               i2c.hw_capable = true;
+                       else
+                               i2c.hw_capable = false;
+
+                       if (gpio->sucI2cId.ucAccess == 0xa0)
+                               i2c.mm_i2c = true;
+                       else
+                               i2c.mm_i2c = false;
+
+                       i2c.i2c_id = gpio->sucI2cId.ucAccess;
+
+                       i2c.valid = true;
+               }
+       }
 
        return i2c;
 }
 
+static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
+                                                       u8 id)
+{
+       struct atom_context *ctx = rdev->mode_info.atom_context;
+       struct radeon_gpio_rec gpio;
+       int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT);
+       struct _ATOM_GPIO_PIN_LUT *gpio_info;
+       ATOM_GPIO_PIN_ASSIGNMENT *pin;
+       u16 data_offset, size;
+       int i, num_indices;
+
+       memset(&gpio, 0, sizeof(struct radeon_gpio_rec));
+       gpio.valid = false;
+
+       atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset);
+
+       gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset);
+
+       num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_PIN_ASSIGNMENT);
+
+       for (i = 0; i < num_indices; i++) {
+               pin = &gpio_info->asGPIO_Pin[i];
+               if (id == pin->ucGPIO_ID) {
+                       gpio.id = pin->ucGPIO_ID;
+                       gpio.reg = pin->usGpioPin_AIndex * 4;
+                       gpio.mask = (1 << pin->ucGpioPinBitShift);
+                       gpio.valid = true;
+                       break;
+               }
+       }
+
+       return gpio;
+}
+
+static struct radeon_hpd radeon_atom_get_hpd_info_from_gpio(struct radeon_device *rdev,
+                                                           struct radeon_gpio_rec *gpio)
+{
+       struct radeon_hpd hpd;
+       hpd.gpio = *gpio;
+       if (gpio->reg == AVIVO_DC_GPIO_HPD_A) {
+               switch(gpio->mask) {
+               case (1 << 0):
+                       hpd.hpd = RADEON_HPD_1;
+                       break;
+               case (1 << 8):
+                       hpd.hpd = RADEON_HPD_2;
+                       break;
+               case (1 << 16):
+                       hpd.hpd = RADEON_HPD_3;
+                       break;
+               case (1 << 24):
+                       hpd.hpd = RADEON_HPD_4;
+                       break;
+               case (1 << 26):
+                       hpd.hpd = RADEON_HPD_5;
+                       break;
+               case (1 << 28):
+                       hpd.hpd = RADEON_HPD_6;
+                       break;
+               default:
+                       hpd.hpd = RADEON_HPD_NONE;
+                       break;
+               }
+       } else
+               hpd.hpd = RADEON_HPD_NONE;
+       return hpd;
+}
+
 static bool radeon_atom_apply_quirks(struct drm_device *dev,
                                     uint32_t supported_device,
                                     int *connector_type,
                                     struct radeon_i2c_bus_rec *i2c_bus,
-                                    uint16_t *line_mux)
+                                    uint16_t *line_mux,
+                                    struct radeon_hpd *hpd)
 {
 
        /* Asus M2A-VM HDMI board lists the DVI port as HDMI */
@@ -135,6 +223,23 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
                }
        }
 
+       /* HIS X1300 is DVI+VGA, not DVI+DVI */
+       if ((dev->pdev->device == 0x7146) &&
+           (dev->pdev->subsystem_vendor == 0x17af) &&
+           (dev->pdev->subsystem_device == 0x2058)) {
+               if (supported_device == ATOM_DEVICE_DFP1_SUPPORT)
+                       return false;
+       }
+
+       /* Gigabyte X1300 is DVI+VGA, not DVI+DVI */
+       if ((dev->pdev->device == 0x7142) &&
+           (dev->pdev->subsystem_vendor == 0x1458) &&
+           (dev->pdev->subsystem_device == 0x2134)) {
+               if (supported_device == ATOM_DEVICE_DFP1_SUPPORT)
+                       return false;
+       }
+
+
        /* Funky macbooks */
        if ((dev->pdev->device == 0x71C5) &&
            (dev->pdev->subsystem_vendor == 0x106b) &&
@@ -172,6 +277,15 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
                }
        }
 
+       /* Acer laptop reports DVI-D as DVI-I */
+       if ((dev->pdev->device == 0x95c4) &&
+           (dev->pdev->subsystem_vendor == 0x1025) &&
+           (dev->pdev->subsystem_device == 0x013c)) {
+               if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
+                   (supported_device == ATOM_DEVICE_DFP1_SUPPORT))
+                       *connector_type = DRM_MODE_CONNECTOR_DVID;
+       }
+
        return true;
 }
 
@@ -240,16 +354,18 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
        struct radeon_mode_info *mode_info = &rdev->mode_info;
        struct atom_context *ctx = mode_info->atom_context;
        int index = GetIndexIntoMasterTable(DATA, Object_Header);
-       uint16_t size, data_offset;
-       uint8_t frev, crev, line_mux = 0;
+       u16 size, data_offset;
+       u8 frev, crev;
        ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
        ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
        ATOM_OBJECT_HEADER *obj_header;
        int i, j, path_size, device_support;
        int connector_type;
-       uint16_t igp_lane_info, conn_id, connector_object_id;
+       u16 igp_lane_info, conn_id, connector_object_id;
        bool linkb;
        struct radeon_i2c_bus_rec ddc_bus;
+       struct radeon_gpio_rec gpio;
+       struct radeon_hpd hpd;
 
        atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
 
@@ -276,7 +392,6 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
                path = (ATOM_DISPLAY_OBJECT_PATH *) addr;
                path_size += le16_to_cpu(path->usSize);
                linkb = false;
-
                if (device_support & le16_to_cpu(path->usDeviceTag)) {
                        uint8_t con_obj_id, con_obj_num, con_obj_type;
 
@@ -377,10 +492,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
                                }
                        }
 
-                       /* look up gpio for ddc */
+                       /* look up gpio for ddc, hpd */
                        if ((le16_to_cpu(path->usDeviceTag) &
-                            (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
-                           == 0) {
+                            (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) == 0) {
                                for (j = 0; j < con_obj->ucNumberOfObjects; j++) {
                                        if (le16_to_cpu(path->usConnObjectId) ==
                                            le16_to_cpu(con_obj->asObjects[j].
@@ -394,21 +508,34 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
                                                                 asObjects[j].
                                                                 usRecordOffset));
                                                ATOM_I2C_RECORD *i2c_record;
+                                               ATOM_HPD_INT_RECORD *hpd_record;
+                                               ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
+                                               hpd.hpd = RADEON_HPD_NONE;
 
                                                while (record->ucRecordType > 0
                                                       && record->
                                                       ucRecordType <=
                                                       ATOM_MAX_OBJECT_RECORD_NUMBER) {
-                                                       switch (record->
-                                                               ucRecordType) {
+                                                       switch (record->ucRecordType) {
                                                        case ATOM_I2C_RECORD_TYPE:
                                                                i2c_record =
-                                                                   (ATOM_I2C_RECORD
-                                                                    *) record;
-                                                               line_mux =
-                                                                   i2c_record->
-                                                                   sucI2cId.
-                                                                   bfI2C_LineMux;
+                                                                   (ATOM_I2C_RECORD *)
+                                                                       record;
+                                                               i2c_config =
+                                                                       (ATOM_I2C_ID_CONFIG_ACCESS *)
+                                                                       &i2c_record->sucI2cId;
+                                                               ddc_bus = radeon_lookup_i2c_gpio(rdev,
+                                                                                                i2c_config->
+                                                                                                ucAccess);
+                                                               break;
+                                                       case ATOM_HPD_INT_RECORD_TYPE:
+                                                               hpd_record =
+                                                                       (ATOM_HPD_INT_RECORD *)
+                                                                       record;
+                                                               gpio = radeon_lookup_gpio(rdev,
+                                                                                         hpd_record->ucHPDIntGPIOID);
+                                                               hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
+                                                               hpd.plugged_state = hpd_record->ucPlugged_PinState;
                                                                break;
                                                        }
                                                        record =
@@ -421,24 +548,16 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
                                                break;
                                        }
                                }
-                       } else
-                               line_mux = 0;
-
-                       if ((le16_to_cpu(path->usDeviceTag) ==
-                            ATOM_DEVICE_TV1_SUPPORT)
-                           || (le16_to_cpu(path->usDeviceTag) ==
-                               ATOM_DEVICE_TV2_SUPPORT)
-                           || (le16_to_cpu(path->usDeviceTag) ==
-                               ATOM_DEVICE_CV_SUPPORT))
+                       } else {
+                               hpd.hpd = RADEON_HPD_NONE;
                                ddc_bus.valid = false;
-                       else
-                               ddc_bus = radeon_lookup_gpio(dev, line_mux);
+                       }
 
                        conn_id = le16_to_cpu(path->usConnObjectId);
 
                        if (!radeon_atom_apply_quirks
                            (dev, le16_to_cpu(path->usDeviceTag), &connector_type,
-                            &ddc_bus, &conn_id))
+                            &ddc_bus, &conn_id, &hpd))
                                continue;
 
                        radeon_add_atom_connector(dev,
@@ -447,7 +566,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
                                                              usDeviceTag),
                                                  connector_type, &ddc_bus,
                                                  linkb, igp_lane_info,
-                                                 connector_object_id);
+                                                 connector_object_id,
+                                                 &hpd);
 
                }
        }
@@ -502,6 +622,7 @@ struct bios_connector {
        uint16_t devices;
        int connector_type;
        struct radeon_i2c_bus_rec ddc_bus;
+       struct radeon_hpd hpd;
 };
 
 bool radeon_get_atom_connector_info_from_supported_devices_table(struct
@@ -517,7 +638,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
        uint16_t device_support;
        uint8_t dac;
        union atom_supported_devices *supported_devices;
-       int i, j;
+       int i, j, max_device;
        struct bios_connector bios_connectors[ATOM_MAX_SUPPORTED_DEVICE];
 
        atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
@@ -527,7 +648,12 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
 
        device_support = le16_to_cpu(supported_devices->info.usDeviceSupport);
 
-       for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
+       if (frev > 1)
+               max_device = ATOM_MAX_SUPPORTED_DEVICE;
+       else
+               max_device = ATOM_MAX_SUPPORTED_DEVICE_INFO;
+
+       for (i = 0; i < max_device; i++) {
                ATOM_CONNECTOR_INFO_I2C ci =
                    supported_devices->info.asConnInfo[i];
 
@@ -553,22 +679,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
 
                dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC;
 
-               if ((rdev->family == CHIP_RS690) ||
-                   (rdev->family == CHIP_RS740)) {
-                       if ((i == ATOM_DEVICE_DFP2_INDEX)
-                           && (ci.sucI2cId.sbfAccess.bfI2C_LineMux == 2))
-                               bios_connectors[i].line_mux =
-                                   ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1;
-                       else if ((i == ATOM_DEVICE_DFP3_INDEX)
-                                && (ci.sucI2cId.sbfAccess.bfI2C_LineMux == 1))
-                               bios_connectors[i].line_mux =
-                                   ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1;
-                       else
-                               bios_connectors[i].line_mux =
-                                   ci.sucI2cId.sbfAccess.bfI2C_LineMux;
-               } else
-                       bios_connectors[i].line_mux =
-                           ci.sucI2cId.sbfAccess.bfI2C_LineMux;
+               bios_connectors[i].line_mux =
+                       ci.sucI2cId.ucAccess;
 
                /* give tv unique connector ids */
                if (i == ATOM_DEVICE_TV1_INDEX) {
@@ -582,8 +694,30 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
                        bios_connectors[i].line_mux = 52;
                } else
                        bios_connectors[i].ddc_bus =
-                           radeon_lookup_gpio(dev,
-                                              bios_connectors[i].line_mux);
+                           radeon_lookup_i2c_gpio(rdev,
+                                                  bios_connectors[i].line_mux);
+
+               if ((crev > 1) && (frev > 1)) {
+                       u8 isb = supported_devices->info_2d1.asIntSrcInfo[i].ucIntSrcBitmap;
+                       switch (isb) {
+                       case 0x4:
+                               bios_connectors[i].hpd.hpd = RADEON_HPD_1;
+                               break;
+                       case 0xa:
+                               bios_connectors[i].hpd.hpd = RADEON_HPD_2;
+                               break;
+                       default:
+                               bios_connectors[i].hpd.hpd = RADEON_HPD_NONE;
+                               break;
+                       }
+               } else {
+                       if (i == ATOM_DEVICE_DFP1_INDEX)
+                               bios_connectors[i].hpd.hpd = RADEON_HPD_1;
+                       else if (i == ATOM_DEVICE_DFP2_INDEX)
+                               bios_connectors[i].hpd.hpd = RADEON_HPD_2;
+                       else
+                               bios_connectors[i].hpd.hpd = RADEON_HPD_NONE;
+               }
 
                /* Always set the connector type to VGA for CRT1/CRT2. if they are
                 * shared with a DVI port, we'll pick up the DVI connector when we
@@ -595,7 +729,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
 
                if (!radeon_atom_apply_quirks
                    (dev, (1 << i), &bios_connectors[i].connector_type,
-                    &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux))
+                    &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux,
+                    &bios_connectors[i].hpd))
                        continue;
 
                bios_connectors[i].valid = true;
@@ -617,9 +752,9 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
        }
 
        /* combine shared connectors */
-       for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
+       for (i = 0; i < max_device; i++) {
                if (bios_connectors[i].valid) {
-                       for (j = 0; j < ATOM_MAX_SUPPORTED_DEVICE; j++) {
+                       for (j = 0; j < max_device; j++) {
                                if (bios_connectors[j].valid && (i != j)) {
                                        if (bios_connectors[i].line_mux ==
                                            bios_connectors[j].line_mux) {
@@ -643,6 +778,10 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
                                                        bios_connectors[i].
                                                            connector_type =
                                                            DRM_MODE_CONNECTOR_DVII;
+                                                       if (bios_connectors[j].devices &
+                                                           (ATOM_DEVICE_DFP_SUPPORT))
+                                                               bios_connectors[i].hpd =
+                                                                       bios_connectors[j].hpd;
                                                        bios_connectors[j].
                                                            valid = false;
                                                }
@@ -653,7 +792,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
        }
 
        /* add the connectors */
-       for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
+       for (i = 0; i < max_device; i++) {
                if (bios_connectors[i].valid) {
                        uint16_t connector_object_id =
                                atombios_get_connector_object_id(dev,
@@ -666,7 +805,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
                                                  connector_type,
                                                  &bios_connectors[i].ddc_bus,
                                                  false, 0,
-                                                 connector_object_id);
+                                                 connector_object_id,
+                                                 &bios_connectors[i].hpd);
                }
        }
 
@@ -731,7 +871,8 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
                         * pre-DCE 3.0 r6xx hardware.  This might need to be adjusted per
                         * family.
                         */
-                       p1pll->pll_out_min = 64800;
+                       if (!radeon_new_pll)
+                               p1pll->pll_out_min = 64800;
                }
 
                p1pll->pll_in_min =
@@ -861,6 +1002,7 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
        struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info;
        uint8_t frev, crev;
        struct radeon_atom_ss *ss = NULL;
+       int i;
 
        if (id > ATOM_MAX_SS_ENTRY)
                return NULL;
@@ -878,12 +1020,17 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
                if (!ss)
                        return NULL;
 
-               ss->percentage = le16_to_cpu(ss_info->asSS_Info[id].usSpreadSpectrumPercentage);
-               ss->type = ss_info->asSS_Info[id].ucSpreadSpectrumType;
-               ss->step = ss_info->asSS_Info[id].ucSS_Step;
-               ss->delay = ss_info->asSS_Info[id].ucSS_Delay;
-               ss->range = ss_info->asSS_Info[id].ucSS_Range;
-               ss->refdiv = ss_info->asSS_Info[id].ucRecommendedRef_Div;
+               for (i = 0; i < ATOM_MAX_SS_ENTRY; i++) {
+                       if (ss_info->asSS_Info[i].ucSS_Id == id) {
+                               ss->percentage =
+                                       le16_to_cpu(ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
+                               ss->type = ss_info->asSS_Info[i].ucSpreadSpectrumType;
+                               ss->step = ss_info->asSS_Info[i].ucSS_Step;
+                               ss->delay = ss_info->asSS_Info[i].ucSS_Delay;
+                               ss->range = ss_info->asSS_Info[i].ucSS_Range;
+                               ss->refdiv = ss_info->asSS_Info[i].ucRecommendedRef_Div;
+                       }
+               }
        }
        return ss;
 }
@@ -901,7 +1048,7 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_mode_info *mode_info = &rdev->mode_info;
        int index = GetIndexIntoMasterTable(DATA, LVDS_Info);
-       uint16_t data_offset;
+       uint16_t data_offset, misc;
        union lvds_info *lvds_info;
        uint8_t frev, crev;
        struct radeon_encoder_atom_dig *lvds = NULL;
@@ -940,6 +1087,19 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
                lvds->panel_pwr_delay =
                    le16_to_cpu(lvds_info->info.usOffDelayInMs);
                lvds->lvds_misc = lvds_info->info.ucLVDS_Misc;
+
+               misc = le16_to_cpu(lvds_info->info.sLCDTiming.susModeMiscInfo.usAccess);
+               if (misc & ATOM_VSYNC_POLARITY)
+                       lvds->native_mode.flags |= DRM_MODE_FLAG_NVSYNC;
+               if (misc & ATOM_HSYNC_POLARITY)
+                       lvds->native_mode.flags |= DRM_MODE_FLAG_NHSYNC;
+               if (misc & ATOM_COMPOSITESYNC)
+                       lvds->native_mode.flags |= DRM_MODE_FLAG_CSYNC;
+               if (misc & ATOM_INTERLACE)
+                       lvds->native_mode.flags |= DRM_MODE_FLAG_INTERLACE;
+               if (misc & ATOM_DOUBLE_CLOCK_MODE)
+                       lvds->native_mode.flags |= DRM_MODE_FLAG_DBLSCAN;
+
                /* set crtc values */
                drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V);
 
index 10bd50a..4ddfd4b 100644 (file)
@@ -29,8 +29,8 @@
 void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
                           unsigned sdomain, unsigned ddomain)
 {
-       struct radeon_object *dobj = NULL;
-       struct radeon_object *sobj = NULL;
+       struct radeon_bo *dobj = NULL;
+       struct radeon_bo *sobj = NULL;
        struct radeon_fence *fence = NULL;
        uint64_t saddr, daddr;
        unsigned long start_jiffies;
@@ -41,19 +41,27 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
 
        size = bsize;
        n = 1024;
-       r = radeon_object_create(rdev, NULL, size, true, sdomain, false, &sobj);
+       r = radeon_bo_create(rdev, NULL, size, true, sdomain, &sobj);
        if (r) {
                goto out_cleanup;
        }
-       r = radeon_object_pin(sobj, sdomain, &saddr);
+       r = radeon_bo_reserve(sobj, false);
+       if (unlikely(r != 0))
+               goto out_cleanup;
+       r = radeon_bo_pin(sobj, sdomain, &saddr);
+       radeon_bo_unreserve(sobj);
        if (r) {
                goto out_cleanup;
        }
-       r = radeon_object_create(rdev, NULL, size, true, ddomain, false, &dobj);
+       r = radeon_bo_create(rdev, NULL, size, true, ddomain, &dobj);
        if (r) {
                goto out_cleanup;
        }
-       r = radeon_object_pin(dobj, ddomain, &daddr);
+       r = radeon_bo_reserve(dobj, false);
+       if (unlikely(r != 0))
+               goto out_cleanup;
+       r = radeon_bo_pin(dobj, ddomain, &daddr);
+       radeon_bo_unreserve(dobj);
        if (r) {
                goto out_cleanup;
        }
@@ -109,12 +117,20 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
        }
 out_cleanup:
        if (sobj) {
-               radeon_object_unpin(sobj);
-               radeon_object_unref(&sobj);
+               r = radeon_bo_reserve(sobj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_unpin(sobj);
+                       radeon_bo_unreserve(sobj);
+               }
+               radeon_bo_unref(&sobj);
        }
        if (dobj) {
-               radeon_object_unpin(dobj);
-               radeon_object_unref(&dobj);
+               r = radeon_bo_reserve(dobj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_unpin(dobj);
+                       radeon_bo_unreserve(dobj);
+               }
+               radeon_bo_unref(&dobj);
        }
        if (fence) {
                radeon_fence_unref(&fence);
index a813541..b062109 100644 (file)
@@ -44,6 +44,10 @@ uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev)
 
        ref_div =
            RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK;
+
+       if (ref_div == 0)
+               return 0;
+
        sclk = fb_div / ref_div;
 
        post_div = RREG32_PLL(RADEON_SCLK_CNTL) & RADEON_SCLK_SRC_SEL_MASK;
@@ -70,6 +74,10 @@ static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
 
        ref_div =
            RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK;
+
+       if (ref_div == 0)
+               return 0;
+
        mclk = fb_div / ref_div;
 
        post_div = RREG32_PLL(RADEON_MCLK_CNTL) & 0x7;
@@ -98,8 +106,19 @@ void radeon_get_clock_info(struct drm_device *dev)
                ret = radeon_combios_get_clock_info(dev);
 
        if (ret) {
-               if (p1pll->reference_div < 2)
-                       p1pll->reference_div = 12;
+               if (p1pll->reference_div < 2) {
+                       if (!ASIC_IS_AVIVO(rdev)) {
+                               u32 tmp = RREG32_PLL(RADEON_PPLL_REF_DIV);
+                               if (ASIC_IS_R300(rdev))
+                                       p1pll->reference_div =
+                                               (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
+                               else
+                                       p1pll->reference_div = tmp & RADEON_PPLL_REF_DIV_MASK;
+                               if (p1pll->reference_div < 2)
+                                       p1pll->reference_div = 12;
+                       } else
+                               p1pll->reference_div = 12;
+               }
                if (p2pll->reference_div < 2)
                        p2pll->reference_div = 12;
                if (rdev->family < CHIP_RS600) {
index 5253cbf..c5021a3 100644 (file)
@@ -50,7 +50,8 @@ radeon_add_legacy_connector(struct drm_device *dev,
                            uint32_t supported_device,
                            int connector_type,
                            struct radeon_i2c_bus_rec *i2c_bus,
-                           uint16_t connector_object_id);
+                           uint16_t connector_object_id,
+                           struct radeon_hpd *hpd);
 
 /* from radeon_legacy_encoder.c */
 extern void
@@ -442,38 +443,70 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
 
 }
 
-struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line)
+static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
+                                                      int ddc_line)
 {
        struct radeon_i2c_bus_rec i2c;
 
-       i2c.mask_clk_mask = RADEON_GPIO_EN_1;
-       i2c.mask_data_mask = RADEON_GPIO_EN_0;
-       i2c.a_clk_mask = RADEON_GPIO_A_1;
-       i2c.a_data_mask = RADEON_GPIO_A_0;
-       i2c.put_clk_mask = RADEON_GPIO_EN_1;
-       i2c.put_data_mask = RADEON_GPIO_EN_0;
-       i2c.get_clk_mask = RADEON_GPIO_Y_1;
-       i2c.get_data_mask = RADEON_GPIO_Y_0;
-       if ((ddc_line == RADEON_LCD_GPIO_MASK) ||
-           (ddc_line == RADEON_MDGPIO_EN_REG)) {
-               i2c.mask_clk_reg = ddc_line;
-               i2c.mask_data_reg = ddc_line;
-               i2c.a_clk_reg = ddc_line;
-               i2c.a_data_reg = ddc_line;
-               i2c.put_clk_reg = ddc_line;
-               i2c.put_data_reg = ddc_line;
-               i2c.get_clk_reg = ddc_line + 4;
-               i2c.get_data_reg = ddc_line + 4;
+       if (ddc_line == RADEON_GPIOPAD_MASK) {
+               i2c.mask_clk_reg = RADEON_GPIOPAD_MASK;
+               i2c.mask_data_reg = RADEON_GPIOPAD_MASK;
+               i2c.a_clk_reg = RADEON_GPIOPAD_A;
+               i2c.a_data_reg = RADEON_GPIOPAD_A;
+               i2c.en_clk_reg = RADEON_GPIOPAD_EN;
+               i2c.en_data_reg = RADEON_GPIOPAD_EN;
+               i2c.y_clk_reg = RADEON_GPIOPAD_Y;
+               i2c.y_data_reg = RADEON_GPIOPAD_Y;
+       } else if (ddc_line == RADEON_MDGPIO_MASK) {
+               i2c.mask_clk_reg = RADEON_MDGPIO_MASK;
+               i2c.mask_data_reg = RADEON_MDGPIO_MASK;
+               i2c.a_clk_reg = RADEON_MDGPIO_A;
+               i2c.a_data_reg = RADEON_MDGPIO_A;
+               i2c.en_clk_reg = RADEON_MDGPIO_EN;
+               i2c.en_data_reg = RADEON_MDGPIO_EN;
+               i2c.y_clk_reg = RADEON_MDGPIO_Y;
+               i2c.y_data_reg = RADEON_MDGPIO_Y;
        } else {
+               i2c.mask_clk_mask = RADEON_GPIO_EN_1;
+               i2c.mask_data_mask = RADEON_GPIO_EN_0;
+               i2c.a_clk_mask = RADEON_GPIO_A_1;
+               i2c.a_data_mask = RADEON_GPIO_A_0;
+               i2c.en_clk_mask = RADEON_GPIO_EN_1;
+               i2c.en_data_mask = RADEON_GPIO_EN_0;
+               i2c.y_clk_mask = RADEON_GPIO_Y_1;
+               i2c.y_data_mask = RADEON_GPIO_Y_0;
+
                i2c.mask_clk_reg = ddc_line;
                i2c.mask_data_reg = ddc_line;
                i2c.a_clk_reg = ddc_line;
                i2c.a_data_reg = ddc_line;
-               i2c.put_clk_reg = ddc_line;
-               i2c.put_data_reg = ddc_line;
-               i2c.get_clk_reg = ddc_line;
-               i2c.get_data_reg = ddc_line;
+               i2c.en_clk_reg = ddc_line;
+               i2c.en_data_reg = ddc_line;
+               i2c.y_clk_reg = ddc_line;
+               i2c.y_data_reg = ddc_line;
+       }
+
+       if (rdev->family < CHIP_R200)
+               i2c.hw_capable = false;
+       else {
+               switch (ddc_line) {
+               case RADEON_GPIO_VGA_DDC:
+               case RADEON_GPIO_DVI_DDC:
+                       i2c.hw_capable = true;
+                       break;
+               case RADEON_GPIO_MONID:
+                       /* hw i2c on RADEON_GPIO_MONID doesn't seem to work
+                        * reliably on some pre-r4xx hardware; not sure why.
+                        */
+                       i2c.hw_capable = false;
+                       break;
+               default:
+                       i2c.hw_capable = false;
+                       break;
+               }
        }
+       i2c.mm_i2c = false;
+       i2c.i2c_id = 0;
 
        if (ddc_line)
                i2c.valid = true;
@@ -495,7 +528,7 @@ bool radeon_combios_get_clock_info(struct drm_device *dev)
        uint16_t sclk, mclk;
 
        if (rdev->bios == NULL)
-               return NULL;
+               return false;
 
        pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE);
        if (pll_info) {
@@ -993,8 +1026,8 @@ static const struct radeon_tmds_pll default_tmds_pll[CHIP_LAST][4] = {
        {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},        /* CHIP_R420  */
        {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},        /* CHIP_R423  */
        {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},        /* CHIP_RV410 */
-       {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}},      /* CHIP_RS400 */
-       {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}},      /* CHIP_RS480 */
+       { {0, 0}, {0, 0}, {0, 0}, {0, 0} },     /* CHIP_RS400 */
+       { {0, 0}, {0, 0}, {0, 0}, {0, 0} },     /* CHIP_RS480 */
 };
 
 bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
@@ -1028,7 +1061,6 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
        tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE);
 
        if (tmds_info) {
-
                ver = RBIOS8(tmds_info);
                DRM_INFO("DFP table revision: %d\n", ver);
                if (ver == 3) {
@@ -1063,51 +1095,139 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
                                          tmds->tmds_pll[i].value);
                        }
                }
-       } else
+       } else {
                DRM_INFO("No TMDS info found in BIOS\n");
+               return false;
+       }
        return true;
 }
 
-struct radeon_encoder_int_tmds *radeon_combios_get_tmds_info(struct radeon_encoder *encoder)
+bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
+                                               struct radeon_encoder_ext_tmds *tmds)
 {
-       struct radeon_encoder_int_tmds *tmds = NULL;
-       bool ret;
+       struct drm_device *dev = encoder->base.dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_i2c_bus_rec i2c_bus;
 
-       tmds = kzalloc(sizeof(struct radeon_encoder_int_tmds), GFP_KERNEL);
+       /* default for macs */
+       i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+       tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
 
-       if (!tmds)
-               return NULL;
-
-       ret = radeon_legacy_get_tmds_info_from_combios(encoder, tmds);
-       if (ret == false)
-               radeon_legacy_get_tmds_info_from_table(encoder, tmds);
+       /* XXX some macs have duallink chips */
+       switch (rdev->mode_info.connector_table) {
+       case CT_POWERBOOK_EXTERNAL:
+       case CT_MINI_EXTERNAL:
+       default:
+               tmds->dvo_chip = DVO_SIL164;
+               tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
+               break;
+       }
 
-       return tmds;
+       return true;
 }
 
-void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder)
+bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder,
+                                                 struct radeon_encoder_ext_tmds *tmds)
 {
        struct drm_device *dev = encoder->base.dev;
        struct radeon_device *rdev = dev->dev_private;
-       uint16_t ext_tmds_info;
-       uint8_t ver;
+       uint16_t offset;
+       uint8_t ver, id, blocks, clk, data;
+       int i;
+       enum radeon_combios_ddc gpio;
+       struct radeon_i2c_bus_rec i2c_bus;
 
        if (rdev->bios == NULL)
-               return;
+               return false;
 
-       ext_tmds_info =
-           combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
-       if (ext_tmds_info) {
-               ver = RBIOS8(ext_tmds_info);
-               DRM_INFO("External TMDS Table revision: %d\n", ver);
-               // TODO
+       tmds->i2c_bus = NULL;
+       if (rdev->flags & RADEON_IS_IGP) {
+               offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
+               if (offset) {
+                       ver = RBIOS8(offset);
+                       DRM_INFO("GPIO Table revision: %d\n", ver);
+                       blocks = RBIOS8(offset + 2);
+                       for (i = 0; i < blocks; i++) {
+                               id = RBIOS8(offset + 3 + (i * 5) + 0);
+                               if (id == 136) {
+                                       clk = RBIOS8(offset + 3 + (i * 5) + 3);
+                                       data = RBIOS8(offset + 3 + (i * 5) + 4);
+                                       i2c_bus.valid = true;
+                                       i2c_bus.mask_clk_mask = (1 << clk);
+                                       i2c_bus.mask_data_mask = (1 << data);
+                                       i2c_bus.a_clk_mask = (1 << clk);
+                                       i2c_bus.a_data_mask = (1 << data);
+                                       i2c_bus.en_clk_mask = (1 << clk);
+                                       i2c_bus.en_data_mask = (1 << data);
+                                       i2c_bus.y_clk_mask = (1 << clk);
+                                       i2c_bus.y_data_mask = (1 << data);
+                                       i2c_bus.mask_clk_reg = RADEON_GPIOPAD_MASK;
+                                       i2c_bus.mask_data_reg = RADEON_GPIOPAD_MASK;
+                                       i2c_bus.a_clk_reg = RADEON_GPIOPAD_A;
+                                       i2c_bus.a_data_reg = RADEON_GPIOPAD_A;
+                                       i2c_bus.en_clk_reg = RADEON_GPIOPAD_EN;
+                                       i2c_bus.en_data_reg = RADEON_GPIOPAD_EN;
+                                       i2c_bus.y_clk_reg = RADEON_GPIOPAD_Y;
+                                       i2c_bus.y_data_reg = RADEON_GPIOPAD_Y;
+                                       tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+                                       tmds->dvo_chip = DVO_SIL164;
+                                       tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
+               if (offset) {
+                       ver = RBIOS8(offset);
+                       DRM_INFO("External TMDS Table revision: %d\n", ver);
+                       tmds->slave_addr = RBIOS8(offset + 4 + 2);
+                       tmds->slave_addr >>= 1; /* 7 bit addressing */
+                       gpio = RBIOS8(offset + 4 + 3);
+                       switch (gpio) {
+                       case DDC_MONID:
+                               i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+                               tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+                               break;
+                       case DDC_DVI:
+                               i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+                               tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+                               break;
+                       case DDC_VGA:
+                               i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+                               tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+                               break;
+                       case DDC_CRT2:
+                               /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
+                               if (rdev->family >= CHIP_R300)
+                                       i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+                               else
+                                       i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+                               tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+                               break;
+                       case DDC_LCD: /* MM i2c */
+                               DRM_ERROR("MM i2c requires hw i2c engine\n");
+                               break;
+                       default:
+                               DRM_ERROR("Unsupported gpio %d\n", gpio);
+                               break;
+                       }
+               }
        }
+
+       if (!tmds->i2c_bus) {
+               DRM_INFO("No valid Ext TMDS info found in BIOS\n");
+               return false;
+       }
+
+       return true;
 }
 
 bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
        struct radeon_i2c_bus_rec ddc_i2c;
+       struct radeon_hpd hpd;
 
        rdev->mode_info.connector_table = radeon_connector_table;
        if (rdev->mode_info.connector_table == CT_NONE) {
@@ -1168,7 +1288,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                /* these are the most common settings */
                if (rdev->flags & RADEON_SINGLE_CRTC) {
                        /* VGA - primary dac */
-                       ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+                       ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_encoder(dev,
                                                  radeon_get_encoder_id(dev,
                                                                        ATOM_DEVICE_CRT1_SUPPORT,
@@ -1178,10 +1299,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                                    ATOM_DEVICE_CRT1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_VGA,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_VGA);
+                                                   CONNECTOR_OBJECT_ID_VGA,
+                                                   &hpd);
                } else if (rdev->flags & RADEON_IS_MOBILITY) {
                        /* LVDS */
-                       ddc_i2c = combios_setup_i2c_bus(RADEON_LCD_GPIO_MASK);
+                       ddc_i2c = combios_setup_i2c_bus(rdev, 0);
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_encoder(dev,
                                                  radeon_get_encoder_id(dev,
                                                                        ATOM_DEVICE_LCD1_SUPPORT,
@@ -1191,10 +1314,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                                    ATOM_DEVICE_LCD1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_LVDS,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_LVDS);
+                                                   CONNECTOR_OBJECT_ID_LVDS,
+                                                   &hpd);
 
                        /* VGA - primary dac */
-                       ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+                       ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_encoder(dev,
                                                  radeon_get_encoder_id(dev,
                                                                        ATOM_DEVICE_CRT1_SUPPORT,
@@ -1204,10 +1329,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                                    ATOM_DEVICE_CRT1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_VGA,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_VGA);
+                                                   CONNECTOR_OBJECT_ID_VGA,
+                                                   &hpd);
                } else {
                        /* DVI-I - tv dac, int tmds */
-                       ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+                       ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+                       hpd.hpd = RADEON_HPD_1;
                        radeon_add_legacy_encoder(dev,
                                                  radeon_get_encoder_id(dev,
                                                                        ATOM_DEVICE_DFP1_SUPPORT,
@@ -1223,10 +1350,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                                    ATOM_DEVICE_CRT2_SUPPORT,
                                                    DRM_MODE_CONNECTOR_DVII,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
+                                                   CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
+                                                   &hpd);
 
                        /* VGA - primary dac */
-                       ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+                       ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_encoder(dev,
                                                  radeon_get_encoder_id(dev,
                                                                        ATOM_DEVICE_CRT1_SUPPORT,
@@ -1236,11 +1365,14 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                                    ATOM_DEVICE_CRT1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_VGA,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_VGA);
+                                                   CONNECTOR_OBJECT_ID_VGA,
+                                                   &hpd);
                }
 
                if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) {
                        /* TV - tv dac */
+                       ddc_i2c.valid = false;
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_encoder(dev,
                                                  radeon_get_encoder_id(dev,
                                                                        ATOM_DEVICE_TV1_SUPPORT,
@@ -1250,14 +1382,16 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                                    ATOM_DEVICE_TV1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_SVIDEO,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_SVIDEO);
+                                                   CONNECTOR_OBJECT_ID_SVIDEO,
+                                                   &hpd);
                }
                break;
        case CT_IBOOK:
                DRM_INFO("Connector Table: %d (ibook)\n",
                         rdev->mode_info.connector_table);
                /* LVDS */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_LCD1_SUPPORT,
@@ -1265,9 +1399,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_LCD1_SUPPORT);
                radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
                                            DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_LVDS);
+                                           CONNECTOR_OBJECT_ID_LVDS,
+                                           &hpd);
                /* VGA - TV DAC */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_CRT2_SUPPORT,
@@ -1275,8 +1411,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_CRT2_SUPPORT);
                radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
                                            DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_VGA);
+                                           CONNECTOR_OBJECT_ID_VGA,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1285,13 +1424,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_POWERBOOK_EXTERNAL:
                DRM_INFO("Connector Table: %d (powerbook external tmds)\n",
                         rdev->mode_info.connector_table);
                /* LVDS */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_LCD1_SUPPORT,
@@ -1299,9 +1440,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_LCD1_SUPPORT);
                radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
                                            DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_LVDS);
+                                           CONNECTOR_OBJECT_ID_LVDS,
+                                           &hpd);
                /* DVI-I - primary dac, ext tmds */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+               hpd.hpd = RADEON_HPD_2; /* ??? */
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_DFP2_SUPPORT,
@@ -1317,8 +1460,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                            ATOM_DEVICE_DFP2_SUPPORT |
                                            ATOM_DEVICE_CRT1_SUPPORT,
                                            DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I);
+                                           CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1327,13 +1473,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_POWERBOOK_INTERNAL:
                DRM_INFO("Connector Table: %d (powerbook internal tmds)\n",
                         rdev->mode_info.connector_table);
                /* LVDS */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_LCD1_SUPPORT,
@@ -1341,9 +1489,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_LCD1_SUPPORT);
                radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
                                            DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_LVDS);
+                                           CONNECTOR_OBJECT_ID_LVDS,
+                                           &hpd);
                /* DVI-I - primary dac, int tmds */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+               hpd.hpd = RADEON_HPD_1; /* ??? */
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_DFP1_SUPPORT,
@@ -1358,8 +1508,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                            ATOM_DEVICE_DFP1_SUPPORT |
                                            ATOM_DEVICE_CRT1_SUPPORT,
                                            DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
+                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1368,13 +1521,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_POWERBOOK_VGA:
                DRM_INFO("Connector Table: %d (powerbook vga)\n",
                         rdev->mode_info.connector_table);
                /* LVDS */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_LCD1_SUPPORT,
@@ -1382,9 +1537,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_LCD1_SUPPORT);
                radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
                                            DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_LVDS);
+                                           CONNECTOR_OBJECT_ID_LVDS,
+                                           &hpd);
                /* VGA - primary dac */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_CRT1_SUPPORT,
@@ -1392,8 +1549,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_CRT1_SUPPORT);
                radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT,
                                            DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_VGA);
+                                           CONNECTOR_OBJECT_ID_VGA,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1402,13 +1562,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_MINI_EXTERNAL:
                DRM_INFO("Connector Table: %d (mini external tmds)\n",
                         rdev->mode_info.connector_table);
                /* DVI-I - tv dac, ext tmds */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+               hpd.hpd = RADEON_HPD_2; /* ??? */
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_DFP2_SUPPORT,
@@ -1424,8 +1586,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                            ATOM_DEVICE_DFP2_SUPPORT |
                                            ATOM_DEVICE_CRT2_SUPPORT,
                                            DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
+                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1434,13 +1599,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_MINI_INTERNAL:
                DRM_INFO("Connector Table: %d (mini internal tmds)\n",
                         rdev->mode_info.connector_table);
                /* DVI-I - tv dac, int tmds */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+               hpd.hpd = RADEON_HPD_1; /* ??? */
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_DFP1_SUPPORT,
@@ -1455,8 +1622,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                            ATOM_DEVICE_DFP1_SUPPORT |
                                            ATOM_DEVICE_CRT2_SUPPORT,
                                            DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
+                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1465,13 +1635,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_IMAC_G5_ISIGHT:
                DRM_INFO("Connector Table: %d (imac g5 isight)\n",
                         rdev->mode_info.connector_table);
                /* DVI-D - int tmds */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+               hpd.hpd = RADEON_HPD_1; /* ??? */
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_DFP1_SUPPORT,
@@ -1479,9 +1651,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_DFP1_SUPPORT);
                radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT,
                                            DRM_MODE_CONNECTOR_DVID, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D);
+                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
+                                           &hpd);
                /* VGA - tv dac */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_CRT2_SUPPORT,
@@ -1489,8 +1663,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_CRT2_SUPPORT);
                radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
                                            DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_VGA);
+                                           CONNECTOR_OBJECT_ID_VGA,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1499,13 +1676,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        case CT_EMAC:
                DRM_INFO("Connector Table: %d (emac)\n",
                         rdev->mode_info.connector_table);
                /* VGA - primary dac */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_CRT1_SUPPORT,
@@ -1513,9 +1692,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_CRT1_SUPPORT);
                radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT,
                                            DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_VGA);
+                                           CONNECTOR_OBJECT_ID_VGA,
+                                           &hpd);
                /* VGA - tv dac */
-               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
+               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_CRT2_SUPPORT,
@@ -1523,8 +1704,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                                          ATOM_DEVICE_CRT2_SUPPORT);
                radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
                                            DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_VGA);
+                                           CONNECTOR_OBJECT_ID_VGA,
+                                           &hpd);
                /* TV - TV DAC */
+               ddc_i2c.valid = false;
+               hpd.hpd = RADEON_HPD_NONE;
                radeon_add_legacy_encoder(dev,
                                          radeon_get_encoder_id(dev,
                                                                ATOM_DEVICE_TV1_SUPPORT,
@@ -1533,7 +1717,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
                radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
                                            DRM_MODE_CONNECTOR_SVIDEO,
                                            &ddc_i2c,
-                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                           &hpd);
                break;
        default:
                DRM_INFO("Connector table: %d (invalid)\n",
@@ -1550,7 +1735,8 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
                                       int bios_index,
                                       enum radeon_combios_connector
                                       *legacy_connector,
-                                      struct radeon_i2c_bus_rec *ddc_i2c)
+                                      struct radeon_i2c_bus_rec *ddc_i2c,
+                                      struct radeon_hpd *hpd)
 {
        struct radeon_device *rdev = dev->dev_private;
 
@@ -1558,29 +1744,26 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
        if ((rdev->family == CHIP_RS400 ||
             rdev->family == CHIP_RS480) &&
            ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
-               *ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID);
+               *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
        else if ((rdev->family == CHIP_RS400 ||
                  rdev->family == CHIP_RS480) &&
                 ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) {
-               ddc_i2c->valid = true;
+               *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIOPAD_MASK);
                ddc_i2c->mask_clk_mask = (0x20 << 8);
                ddc_i2c->mask_data_mask = 0x80;
                ddc_i2c->a_clk_mask = (0x20 << 8);
                ddc_i2c->a_data_mask = 0x80;
-               ddc_i2c->put_clk_mask = (0x20 << 8);
-               ddc_i2c->put_data_mask = 0x80;
-               ddc_i2c->get_clk_mask = (0x20 << 8);
-               ddc_i2c->get_data_mask = 0x80;
-               ddc_i2c->mask_clk_reg = RADEON_GPIOPAD_MASK;
-               ddc_i2c->mask_data_reg = RADEON_GPIOPAD_MASK;
-               ddc_i2c->a_clk_reg = RADEON_GPIOPAD_A;
-               ddc_i2c->a_data_reg = RADEON_GPIOPAD_A;
-               ddc_i2c->put_clk_reg = RADEON_GPIOPAD_EN;
-               ddc_i2c->put_data_reg = RADEON_GPIOPAD_EN;
-               ddc_i2c->get_clk_reg = RADEON_LCD_GPIO_Y_REG;
-               ddc_i2c->get_data_reg = RADEON_LCD_GPIO_Y_REG;
+               ddc_i2c->en_clk_mask = (0x20 << 8);
+               ddc_i2c->en_data_mask = 0x80;
+               ddc_i2c->y_clk_mask = (0x20 << 8);
+               ddc_i2c->y_data_mask = 0x80;
        }
 
+       /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
+       if ((rdev->family >= CHIP_R300) &&
+           ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
+               *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+
        /* Certain IBM chipset RN50s have a BIOS reporting two VGAs,
           one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */
        if (dev->pdev->device == 0x515e &&
@@ -1624,6 +1807,12 @@ static bool radeon_apply_legacy_tv_quirks(struct drm_device *dev)
            dev->pdev->subsystem_device == 0x280a)
                return false;
 
+       /* MSI S270 has non-existent TV port */
+       if (dev->pdev->device == 0x5955 &&
+           dev->pdev->subsystem_vendor == 0x1462 &&
+           dev->pdev->subsystem_device == 0x0131)
+               return false;
+
        return true;
 }
 
@@ -1671,6 +1860,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
        enum radeon_combios_connector connector;
        int i = 0;
        struct radeon_i2c_bus_rec ddc_i2c;
+       struct radeon_hpd hpd;
 
        if (rdev->bios == NULL)
                return false;
@@ -1691,26 +1881,40 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                        switch (ddc_type) {
                        case DDC_MONID:
                                ddc_i2c =
-                                   combios_setup_i2c_bus(RADEON_GPIO_MONID);
+                                       combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
                                break;
                        case DDC_DVI:
                                ddc_i2c =
-                                   combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+                                       combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
                                break;
                        case DDC_VGA:
                                ddc_i2c =
-                                   combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+                                       combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
                                break;
                        case DDC_CRT2:
                                ddc_i2c =
-                                   combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
+                                       combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
                                break;
                        default:
                                break;
                        }
 
+                       switch (connector) {
+                       case CONNECTOR_PROPRIETARY_LEGACY:
+                       case CONNECTOR_DVI_I_LEGACY:
+                       case CONNECTOR_DVI_D_LEGACY:
+                               if ((tmp >> 4) & 0x1)
+                                       hpd.hpd = RADEON_HPD_2;
+                               else
+                                       hpd.hpd = RADEON_HPD_1;
+                               break;
+                       default:
+                               hpd.hpd = RADEON_HPD_NONE;
+                               break;
+                       }
+
                        if (!radeon_apply_legacy_quirks(dev, i, &connector,
-                                                      &ddc_i2c))
+                                                       &ddc_i2c, &hpd))
                                continue;
 
                        switch (connector) {
@@ -1727,7 +1931,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                            legacy_connector_convert
                                                            [connector],
                                                            &ddc_i2c,
-                                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D);
+                                                           CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
+                                                           &hpd);
                                break;
                        case CONNECTOR_CRT_LEGACY:
                                if (tmp & 0x1) {
@@ -1753,7 +1958,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                            legacy_connector_convert
                                                            [connector],
                                                            &ddc_i2c,
-                                                           CONNECTOR_OBJECT_ID_VGA);
+                                                           CONNECTOR_OBJECT_ID_VGA,
+                                                           &hpd);
                                break;
                        case CONNECTOR_DVI_I_LEGACY:
                                devices = 0;
@@ -1799,7 +2005,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                            legacy_connector_convert
                                                            [connector],
                                                            &ddc_i2c,
-                                                           connector_object_id);
+                                                           connector_object_id,
+                                                           &hpd);
                                break;
                        case CONNECTOR_DVI_D_LEGACY:
                                if ((tmp >> 4) & 0x1) {
@@ -1817,7 +2024,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                            legacy_connector_convert
                                                            [connector],
                                                            &ddc_i2c,
-                                                           connector_object_id);
+                                                           connector_object_id,
+                                                           &hpd);
                                break;
                        case CONNECTOR_CTV_LEGACY:
                        case CONNECTOR_STV_LEGACY:
@@ -1832,7 +2040,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                            legacy_connector_convert
                                                            [connector],
                                                            &ddc_i2c,
-                                                           CONNECTOR_OBJECT_ID_SVIDEO);
+                                                           CONNECTOR_OBJECT_ID_SVIDEO,
+                                                           &hpd);
                                break;
                        default:
                                DRM_ERROR("Unknown connector type: %d\n",
@@ -1858,14 +2067,16 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                                        0),
                                                  ATOM_DEVICE_DFP1_SUPPORT);
 
-                       ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
+                       ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_connector(dev,
                                                    0,
                                                    ATOM_DEVICE_CRT1_SUPPORT |
                                                    ATOM_DEVICE_DFP1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_DVII,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
+                                                   CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
+                                                   &hpd);
                } else {
                        uint16_t crt_info =
                                combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
@@ -1876,13 +2087,15 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                                                ATOM_DEVICE_CRT1_SUPPORT,
                                                                                1),
                                                          ATOM_DEVICE_CRT1_SUPPORT);
-                               ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
+                               ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+                               hpd.hpd = RADEON_HPD_NONE;
                                radeon_add_legacy_connector(dev,
                                                            0,
                                                            ATOM_DEVICE_CRT1_SUPPORT,
                                                            DRM_MODE_CONNECTOR_VGA,
                                                            &ddc_i2c,
-                                                           CONNECTOR_OBJECT_ID_VGA);
+                                                           CONNECTOR_OBJECT_ID_VGA,
+                                                           &hpd);
                        } else {
                                DRM_DEBUG("No connector info found\n");
                                return false;
@@ -1910,27 +2123,27 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                case DDC_MONID:
                                        ddc_i2c =
                                            combios_setup_i2c_bus
-                                           (RADEON_GPIO_MONID);
+                                               (rdev, RADEON_GPIO_MONID);
                                        break;
                                case DDC_DVI:
                                        ddc_i2c =
                                            combios_setup_i2c_bus
-                                           (RADEON_GPIO_DVI_DDC);
+                                               (rdev, RADEON_GPIO_DVI_DDC);
                                        break;
                                case DDC_VGA:
                                        ddc_i2c =
                                            combios_setup_i2c_bus
-                                           (RADEON_GPIO_VGA_DDC);
+                                               (rdev, RADEON_GPIO_VGA_DDC);
                                        break;
                                case DDC_CRT2:
                                        ddc_i2c =
                                            combios_setup_i2c_bus
-                                           (RADEON_GPIO_CRT2_DDC);
+                                               (rdev, RADEON_GPIO_CRT2_DDC);
                                        break;
                                case DDC_LCD:
                                        ddc_i2c =
                                            combios_setup_i2c_bus
-                                           (RADEON_LCD_GPIO_MASK);
+                                               (rdev, RADEON_GPIOPAD_MASK);
                                        ddc_i2c.mask_clk_mask =
                                            RBIOS32(lcd_ddc_info + 3);
                                        ddc_i2c.mask_data_mask =
@@ -1939,19 +2152,19 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                            RBIOS32(lcd_ddc_info + 3);
                                        ddc_i2c.a_data_mask =
                                            RBIOS32(lcd_ddc_info + 7);
-                                       ddc_i2c.put_clk_mask =
+                                       ddc_i2c.en_clk_mask =
                                            RBIOS32(lcd_ddc_info + 3);
-                                       ddc_i2c.put_data_mask =
+                                       ddc_i2c.en_data_mask =
                                            RBIOS32(lcd_ddc_info + 7);
-                                       ddc_i2c.get_clk_mask =
+                                       ddc_i2c.y_clk_mask =
                                            RBIOS32(lcd_ddc_info + 3);
-                                       ddc_i2c.get_data_mask =
+                                       ddc_i2c.y_data_mask =
                                            RBIOS32(lcd_ddc_info + 7);
                                        break;
                                case DDC_GPIO:
                                        ddc_i2c =
                                            combios_setup_i2c_bus
-                                           (RADEON_MDGPIO_EN_REG);
+                                               (rdev, RADEON_MDGPIO_MASK);
                                        ddc_i2c.mask_clk_mask =
                                            RBIOS32(lcd_ddc_info + 3);
                                        ddc_i2c.mask_data_mask =
@@ -1960,13 +2173,13 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                            RBIOS32(lcd_ddc_info + 3);
                                        ddc_i2c.a_data_mask =
                                            RBIOS32(lcd_ddc_info + 7);
-                                       ddc_i2c.put_clk_mask =
+                                       ddc_i2c.en_clk_mask =
                                            RBIOS32(lcd_ddc_info + 3);
-                                       ddc_i2c.put_data_mask =
+                                       ddc_i2c.en_data_mask =
                                            RBIOS32(lcd_ddc_info + 7);
-                                       ddc_i2c.get_clk_mask =
+                                       ddc_i2c.y_clk_mask =
                                            RBIOS32(lcd_ddc_info + 3);
-                                       ddc_i2c.get_data_mask =
+                                       ddc_i2c.y_data_mask =
                                            RBIOS32(lcd_ddc_info + 7);
                                        break;
                                default:
@@ -1977,12 +2190,14 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                        } else
                                ddc_i2c.valid = false;
 
+                       hpd.hpd = RADEON_HPD_NONE;
                        radeon_add_legacy_connector(dev,
                                                    5,
                                                    ATOM_DEVICE_LCD1_SUPPORT,
                                                    DRM_MODE_CONNECTOR_LVDS,
                                                    &ddc_i2c,
-                                                   CONNECTOR_OBJECT_ID_LVDS);
+                                                   CONNECTOR_OBJECT_ID_LVDS,
+                                                   &hpd);
                }
        }
 
@@ -1993,6 +2208,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                if (tv_info) {
                        if (RBIOS8(tv_info + 6) == 'T') {
                                if (radeon_apply_legacy_tv_quirks(dev)) {
+                                       hpd.hpd = RADEON_HPD_NONE;
                                        radeon_add_legacy_encoder(dev,
                                                                  radeon_get_encoder_id
                                                                  (dev,
@@ -2003,7 +2219,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
                                                                    ATOM_DEVICE_TV1_SUPPORT,
                                                                    DRM_MODE_CONNECTOR_SVIDEO,
                                                                    &ddc_i2c,
-                                                                   CONNECTOR_OBJECT_ID_SVIDEO);
+                                                                   CONNECTOR_OBJECT_ID_SVIDEO,
+                                                                   &hpd);
                                }
                        }
                }
@@ -2014,6 +2231,193 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
        return true;
 }
 
+void radeon_external_tmds_setup(struct drm_encoder *encoder)
+{
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
+
+       if (!tmds)
+               return;
+
+       switch (tmds->dvo_chip) {
+       case DVO_SIL164:
+               /* sil 164 */
+               radeon_i2c_do_lock(tmds->i2c_bus, 1);
+               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                      tmds->slave_addr,
+                                      0x08, 0x30);
+               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                      tmds->slave_addr,
+                                      0x09, 0x00);
+               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                      tmds->slave_addr,
+                                      0x0a, 0x90);
+               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                      tmds->slave_addr,
+                                      0x0c, 0x89);
+               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                      tmds->slave_addr,
+                                      0x08, 0x3b);
+               radeon_i2c_do_lock(tmds->i2c_bus, 0);
+               break;
+       case DVO_SIL1178:
+               /* sil 1178 - untested */
+               /*
+                * 0x0f, 0x44
+                * 0x0f, 0x4c
+                * 0x0e, 0x01
+                * 0x0a, 0x80
+                * 0x09, 0x30
+                * 0x0c, 0xc9
+                * 0x0d, 0x70
+                * 0x08, 0x32
+                * 0x08, 0x33
+                */
+               break;
+       default:
+               break;
+       }
+
+}
+
+bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       uint16_t offset;
+       uint8_t blocks, slave_addr, rev;
+       uint32_t index, id;
+       uint32_t reg, val, and_mask, or_mask;
+       struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
+
+       if (rdev->bios == NULL)
+               return false;
+
+       if (!tmds)
+               return false;
+
+       if (rdev->flags & RADEON_IS_IGP) {
+               offset = combios_get_table_offset(dev, COMBIOS_TMDS_POWER_ON_TABLE);
+               rev = RBIOS8(offset);
+               if (offset) {
+                       rev = RBIOS8(offset);
+                       if (rev > 1) {
+                               blocks = RBIOS8(offset + 3);
+                               index = offset + 4;
+                               while (blocks > 0) {
+                                       id = RBIOS16(index);
+                                       index += 2;
+                                       switch (id >> 13) {
+                                       case 0:
+                                               reg = (id & 0x1fff) * 4;
+                                               val = RBIOS32(index);
+                                               index += 4;
+                                               WREG32(reg, val);
+                                               break;
+                                       case 2:
+                                               reg = (id & 0x1fff) * 4;
+                                               and_mask = RBIOS32(index);
+                                               index += 4;
+                                               or_mask = RBIOS32(index);
+                                               index += 4;
+                                               val = RREG32(reg);
+                                               val = (val & and_mask) | or_mask;
+                                               WREG32(reg, val);
+                                               break;
+                                       case 3:
+                                               val = RBIOS16(index);
+                                               index += 2;
+                                               udelay(val);
+                                               break;
+                                       case 4:
+                                               val = RBIOS16(index);
+                                               index += 2;
+                                               udelay(val * 1000);
+                                               break;
+                                       case 6:
+                                               slave_addr = id & 0xff;
+                                               slave_addr >>= 1; /* 7 bit addressing */
+                                               index++;
+                                               reg = RBIOS8(index);
+                                               index++;
+                                               val = RBIOS8(index);
+                                               index++;
+                                               radeon_i2c_do_lock(tmds->i2c_bus, 1);
+                                               radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                                                      slave_addr,
+                                                                      reg, val);
+                                               radeon_i2c_do_lock(tmds->i2c_bus, 0);
+                                               break;
+                                       default:
+                                               DRM_ERROR("Unknown id %d\n", id >> 13);
+                                               break;
+                                       }
+                                       blocks--;
+                               }
+                               return true;
+                       }
+               }
+       } else {
+               offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
+               if (offset) {
+                       index = offset + 10;
+                       id = RBIOS16(index);
+                       while (id != 0xffff) {
+                               index += 2;
+                               switch (id >> 13) {
+                               case 0:
+                                       reg = (id & 0x1fff) * 4;
+                                       val = RBIOS32(index);
+                                       WREG32(reg, val);
+                                       break;
+                               case 2:
+                                       reg = (id & 0x1fff) * 4;
+                                       and_mask = RBIOS32(index);
+                                       index += 4;
+                                       or_mask = RBIOS32(index);
+                                       index += 4;
+                                       val = RREG32(reg);
+                                       val = (val & and_mask) | or_mask;
+                                       WREG32(reg, val);
+                                       break;
+                               case 4:
+                                       val = RBIOS16(index);
+                                       index += 2;
+                                       udelay(val);
+                                       break;
+                               case 5:
+                                       reg = id & 0x1fff;
+                                       and_mask = RBIOS32(index);
+                                       index += 4;
+                                       or_mask = RBIOS32(index);
+                                       index += 4;
+                                       val = RREG32_PLL(reg);
+                                       val = (val & and_mask) | or_mask;
+                                       WREG32_PLL(reg, val);
+                                       break;
+                               case 6:
+                                       reg = id & 0x1fff;
+                                       val = RBIOS8(index);
+                                       index += 1;
+                                       radeon_i2c_do_lock(tmds->i2c_bus, 1);
+                                       radeon_i2c_sw_put_byte(tmds->i2c_bus,
+                                                              tmds->slave_addr,
+                                                              reg, val);
+                                       radeon_i2c_do_lock(tmds->i2c_bus, 0);
+                                       break;
+                               default:
+                                       DRM_ERROR("Unknown id %d\n", id >> 13);
+                                       break;
+                               }
+                               id = RBIOS16(index);
+                       }
+                       return true;
+               }
+       }
+       return false;
+}
+
 static void combios_parse_mmio_table(struct drm_device *dev, uint16_t offset)
 {
        struct radeon_device *rdev = dev->dev_private;
index 29763ce..5eece18 100644 (file)
@@ -40,6 +40,26 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
                                       struct drm_encoder *encoder,
                                       bool connected);
 
+void radeon_connector_hotplug(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+       if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
+               radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
+
+       if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+               if (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
+                       if (radeon_dp_needs_link_train(radeon_connector)) {
+                               if (connector->encoder)
+                                       dp_link_train(connector->encoder, connector);
+                       }
+               }
+       }
+
+}
+
 static void radeon_property_change_mode(struct drm_encoder *encoder)
 {
        struct drm_crtc *crtc = encoder->crtc;
@@ -445,10 +465,10 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec
                ret = connector_status_connected;
        else {
                if (radeon_connector->ddc_bus) {
-                       radeon_i2c_do_lock(radeon_connector, 1);
+                       radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                        radeon_connector->edid = drm_get_edid(&radeon_connector->base,
                                                              &radeon_connector->ddc_bus->adapter);
-                       radeon_i2c_do_lock(radeon_connector, 0);
+                       radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
                        if (radeon_connector->edid)
                                ret = connector_status_connected;
                }
@@ -553,17 +573,17 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
        if (!encoder)
                ret = connector_status_disconnected;
 
-       radeon_i2c_do_lock(radeon_connector, 1);
+       radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
        dret = radeon_ddc_probe(radeon_connector);
-       radeon_i2c_do_lock(radeon_connector, 0);
+       radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        if (dret) {
                if (radeon_connector->edid) {
                        kfree(radeon_connector->edid);
                        radeon_connector->edid = NULL;
                }
-               radeon_i2c_do_lock(radeon_connector, 1);
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector, 0);
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
 
                if (!radeon_connector->edid) {
                        DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
@@ -708,17 +728,17 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
        enum drm_connector_status ret = connector_status_disconnected;
        bool dret;
 
-       radeon_i2c_do_lock(radeon_connector, 1);
+       radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
        dret = radeon_ddc_probe(radeon_connector);
-       radeon_i2c_do_lock(radeon_connector, 0);
+       radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        if (dret) {
                if (radeon_connector->edid) {
                        kfree(radeon_connector->edid);
                        radeon_connector->edid = NULL;
                }
-               radeon_i2c_do_lock(radeon_connector, 1);
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector, 0);
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
 
                if (!radeon_connector->edid) {
                        DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
@@ -735,6 +755,39 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
                                ret = connector_status_disconnected;
                        } else
                                ret = connector_status_connected;
+
+                       /* multiple connectors on the same encoder with the same ddc line
+                        * This tends to be HDMI and DVI on the same encoder with the
+                        * same ddc line.  If the edid says HDMI, consider the HDMI port
+                        * connected and the DVI port disconnected.  If the edid doesn't
+                        * say HDMI, vice versa.
+                        */
+                       if (radeon_connector->shared_ddc && connector_status_connected) {
+                               struct drm_device *dev = connector->dev;
+                               struct drm_connector *list_connector;
+                               struct radeon_connector *list_radeon_connector;
+                               list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
+                                       if (connector == list_connector)
+                                               continue;
+                                       list_radeon_connector = to_radeon_connector(list_connector);
+                                       if (radeon_connector->devices == list_radeon_connector->devices) {
+                                               if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
+                                                       if (connector->connector_type == DRM_MODE_CONNECTOR_DVID) {
+                                                               kfree(radeon_connector->edid);
+                                                               radeon_connector->edid = NULL;
+                                                               ret = connector_status_disconnected;
+                                                       }
+                                               } else {
+                                                       if ((connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) ||
+                                                           (connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)) {
+                                                               kfree(radeon_connector->edid);
+                                                               radeon_connector->edid = NULL;
+                                                               ret = connector_status_disconnected;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
                }
        }
 
@@ -863,6 +916,91 @@ struct drm_connector_funcs radeon_dvi_connector_funcs = {
        .force = radeon_dvi_force,
 };
 
+static void radeon_dp_connector_destroy(struct drm_connector *connector)
+{
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+       struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
+
+       if (radeon_connector->ddc_bus)
+               radeon_i2c_destroy(radeon_connector->ddc_bus);
+       if (radeon_connector->edid)
+               kfree(radeon_connector->edid);
+       if (radeon_dig_connector->dp_i2c_bus)
+               radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus);
+       kfree(radeon_connector->con_priv);
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
+static int radeon_dp_get_modes(struct drm_connector *connector)
+{
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+       int ret;
+
+       ret = radeon_ddc_get_modes(radeon_connector);
+       return ret;
+}
+
+static enum drm_connector_status radeon_dp_detect(struct drm_connector *connector)
+{
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+       enum drm_connector_status ret = connector_status_disconnected;
+       struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
+       u8 sink_type;
+
+       if (radeon_connector->edid) {
+               kfree(radeon_connector->edid);
+               radeon_connector->edid = NULL;
+       }
+
+       sink_type = radeon_dp_getsinktype(radeon_connector);
+       if (sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
+               if (radeon_dp_getdpcd(radeon_connector)) {
+                       radeon_dig_connector->dp_sink_type = sink_type;
+                       ret = connector_status_connected;
+               }
+       } else {
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+               if (radeon_ddc_probe(radeon_connector)) {
+                       radeon_dig_connector->dp_sink_type = sink_type;
+                       ret = connector_status_connected;
+               }
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
+       }
+
+       return ret;
+}
+
+static int radeon_dp_mode_valid(struct drm_connector *connector,
+                                 struct drm_display_mode *mode)
+{
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+       struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
+
+       /* XXX check mode bandwidth */
+
+       if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT)
+               return radeon_dp_mode_valid_helper(radeon_connector, mode);
+       else
+               return MODE_OK;
+}
+
+struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = {
+       .get_modes = radeon_dp_get_modes,
+       .mode_valid = radeon_dp_mode_valid,
+       .best_encoder = radeon_dvi_encoder,
+};
+
+struct drm_connector_funcs radeon_dp_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = radeon_dp_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = radeon_connector_set_property,
+       .destroy = radeon_dp_connector_destroy,
+       .force = radeon_dvi_force,
+};
+
 void
 radeon_add_atom_connector(struct drm_device *dev,
                          uint32_t connector_id,
@@ -871,7 +1009,8 @@ radeon_add_atom_connector(struct drm_device *dev,
                          struct radeon_i2c_bus_rec *i2c_bus,
                          bool linkb,
                          uint32_t igp_lane_info,
-                         uint16_t connector_object_id)
+                         uint16_t connector_object_id,
+                         struct radeon_hpd *hpd)
 {
        struct radeon_device *rdev = dev->dev_private;
        struct drm_connector *connector;
@@ -911,6 +1050,7 @@ radeon_add_atom_connector(struct drm_device *dev,
        radeon_connector->devices = supported_device;
        radeon_connector->shared_ddc = shared_ddc;
        radeon_connector->connector_object_id = connector_object_id;
+       radeon_connector->hpd = *hpd;
        switch (connector_type) {
        case DRM_MODE_CONNECTOR_VGA:
                drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
@@ -963,10 +1103,12 @@ radeon_add_atom_connector(struct drm_device *dev,
                drm_connector_attach_property(&radeon_connector->base,
                                              rdev->mode_info.coherent_mode_property,
                                              1);
-               radeon_connector->dac_load_detect = true;
-               drm_connector_attach_property(&radeon_connector->base,
-                                             rdev->mode_info.load_detect_property,
-                                             1);
+               if (connector_type == DRM_MODE_CONNECTOR_DVII) {
+                       radeon_connector->dac_load_detect = true;
+                       drm_connector_attach_property(&radeon_connector->base,
+                                                     rdev->mode_info.load_detect_property,
+                                                     1);
+               }
                break;
        case DRM_MODE_CONNECTOR_HDMIA:
        case DRM_MODE_CONNECTOR_HDMIB:
@@ -997,16 +1139,23 @@ radeon_add_atom_connector(struct drm_device *dev,
                radeon_dig_connector->linkb = linkb;
                radeon_dig_connector->igp_lane_info = igp_lane_info;
                radeon_connector->con_priv = radeon_dig_connector;
-               drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
-               ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
+               drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
+               ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
                if (ret)
                        goto failed;
                if (i2c_bus->valid) {
+                       /* add DP i2c bus */
+                       radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
+                       if (!radeon_dig_connector->dp_i2c_bus)
+                               goto failed;
                        radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
                        if (!radeon_connector->ddc_bus)
                                goto failed;
                }
                subpixel_order = SubPixelHorizontalRGB;
+               drm_connector_attach_property(&radeon_connector->base,
+                                             rdev->mode_info.coherent_mode_property,
+                                             1);
                break;
        case DRM_MODE_CONNECTOR_SVIDEO:
        case DRM_MODE_CONNECTOR_Composite:
@@ -1020,6 +1169,9 @@ radeon_add_atom_connector(struct drm_device *dev,
                        drm_connector_attach_property(&radeon_connector->base,
                                                      rdev->mode_info.load_detect_property,
                                                      1);
+                       drm_connector_attach_property(&radeon_connector->base,
+                                                     rdev->mode_info.tv_std_property,
+                                                     1);
                }
                break;
        case DRM_MODE_CONNECTOR_LVDS:
@@ -1038,7 +1190,6 @@ radeon_add_atom_connector(struct drm_device *dev,
                        if (!radeon_connector->ddc_bus)
                                goto failed;
                }
-               drm_mode_create_scaling_mode_property(dev);
                drm_connector_attach_property(&radeon_connector->base,
                                              dev->mode_config.scaling_mode_property,
                                              DRM_MODE_SCALE_FULLSCREEN);
@@ -1063,7 +1214,8 @@ radeon_add_legacy_connector(struct drm_device *dev,
                            uint32_t supported_device,
                            int connector_type,
                            struct radeon_i2c_bus_rec *i2c_bus,
-                           uint16_t connector_object_id)
+                           uint16_t connector_object_id,
+                           struct radeon_hpd *hpd)
 {
        struct radeon_device *rdev = dev->dev_private;
        struct drm_connector *connector;
@@ -1093,6 +1245,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
        radeon_connector->connector_id = connector_id;
        radeon_connector->devices = supported_device;
        radeon_connector->connector_object_id = connector_object_id;
+       radeon_connector->hpd = *hpd;
        switch (connector_type) {
        case DRM_MODE_CONNECTOR_VGA:
                drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
@@ -1160,6 +1313,9 @@ radeon_add_legacy_connector(struct drm_device *dev,
                        drm_connector_attach_property(&radeon_connector->base,
                                                      rdev->mode_info.load_detect_property,
                                                      1);
+                       drm_connector_attach_property(&radeon_connector->base,
+                                                     rdev->mode_info.tv_std_property,
+                                                     1);
                }
                break;
        case DRM_MODE_CONNECTOR_LVDS:
index 4f7afc7..0b2f9c2 100644 (file)
@@ -1941,8 +1941,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
        for (t = 0; t < dev_priv->usec_timeout; t++) {
                u32 done_age = GET_SCRATCH(dev_priv, 1);
                DRM_DEBUG("done_age = %d\n", done_age);
-               for (i = start; i < dma->buf_count; i++) {
-                       buf = dma->buflist[i];
+               for (i = 0; i < dma->buf_count; i++) {
+                       buf = dma->buflist[start];
                        buf_priv = buf->dev_private;
                        if (buf->file_priv == NULL || (buf->pending &&
                                                       buf_priv->age <=
@@ -1951,7 +1951,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
                                buf->pending = 0;
                                return buf;
                        }
-                       start = 0;
+                       if (++start >= dma->buf_count)
+                               start = 0;
                }
 
                if (t) {
@@ -1960,47 +1961,9 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
                }
        }
 
-       DRM_DEBUG("returning NULL!\n");
        return NULL;
 }
 
-#if 0
-struct drm_buf *radeon_freelist_get(struct drm_device * dev)
-{
-       struct drm_device_dma *dma = dev->dma;
-       drm_radeon_private_t *dev_priv = dev->dev_private;
-       drm_radeon_buf_priv_t *buf_priv;
-       struct drm_buf *buf;
-       int i, t;
-       int start;
-       u32 done_age;
-
-       done_age = radeon_read_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1));
-       if (++dev_priv->last_buf >= dma->buf_count)
-               dev_priv->last_buf = 0;
-
-       start = dev_priv->last_buf;
-       dev_priv->stats.freelist_loops++;
-
-       for (t = 0; t < 2; t++) {
-               for (i = start; i < dma->buf_count; i++) {
-                       buf = dma->buflist[i];
-                       buf_priv = buf->dev_private;
-                       if (buf->file_priv == 0 || (buf->pending &&
-                                                   buf_priv->age <=
-                                                   done_age)) {
-                               dev_priv->stats.requested_bufs++;
-                               buf->pending = 0;
-                               return buf;
-                       }
-               }
-               start = 0;
-       }
-
-       return NULL;
-}
-#endif
-
 void radeon_freelist_reset(struct drm_device * dev)
 {
        struct drm_device_dma *dma = dev->dma;
index 5ab2cf9..65590a0 100644 (file)
@@ -76,17 +76,17 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
                        }
                        p->relocs_ptr[i] = &p->relocs[i];
                        p->relocs[i].robj = p->relocs[i].gobj->driver_private;
-                       p->relocs[i].lobj.robj = p->relocs[i].robj;
+                       p->relocs[i].lobj.bo = p->relocs[i].robj;
                        p->relocs[i].lobj.rdomain = r->read_domains;
                        p->relocs[i].lobj.wdomain = r->write_domain;
                        p->relocs[i].handle = r->handle;
                        p->relocs[i].flags = r->flags;
                        INIT_LIST_HEAD(&p->relocs[i].lobj.list);
-                       radeon_object_list_add_object(&p->relocs[i].lobj,
-                                                     &p->validated);
+                       radeon_bo_list_add_object(&p->relocs[i].lobj,
+                                               &p->validated);
                }
        }
-       return radeon_object_list_validate(&p->validated, p->ib->fence);
+       return radeon_bo_list_validate(&p->validated, p->ib->fence);
 }
 
 int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
@@ -190,9 +190,10 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
        unsigned i;
 
        if (error) {
-               radeon_object_list_unvalidate(&parser->validated);
+               radeon_bo_list_unvalidate(&parser->validated,
+                                               parser->ib->fence);
        } else {
-               radeon_object_list_clean(&parser->validated);
+               radeon_bo_list_unreserve(&parser->validated);
        }
        for (i = 0; i < parser->nrelocs; i++) {
                if (parser->relocs[i].gobj) {
index 41bb76f..02bcdb1 100644 (file)
@@ -44,10 +44,11 @@ void radeon_surface_init(struct radeon_device *rdev)
        if (rdev->family < CHIP_R600) {
                int i;
 
-               for (i = 0; i < 8; i++) {
-                       WREG32(RADEON_SURFACE0_INFO +
-                              i * (RADEON_SURFACE1_INFO - RADEON_SURFACE0_INFO),
-                              0);
+               for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) {
+                       if (rdev->surface_regs[i].bo)
+                               radeon_bo_get_surface_reg(rdev->surface_regs[i].bo);
+                       else
+                               radeon_clear_surface_reg(rdev, i);
                }
                /* enable surfaces */
                WREG32(RADEON_SURFACE_CNTL, 0);
@@ -208,6 +209,24 @@ bool radeon_card_posted(struct radeon_device *rdev)
 
 }
 
+bool radeon_boot_test_post_card(struct radeon_device *rdev)
+{
+       if (radeon_card_posted(rdev))
+               return true;
+
+       if (rdev->bios) {
+               DRM_INFO("GPU not posted. posting now...\n");
+               if (rdev->is_atom_bios)
+                       atom_asic_init(rdev->mode_info.atom_context);
+               else
+                       radeon_combios_asic_init(rdev->ddev);
+               return true;
+       } else {
+               dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
+               return false;
+       }
+}
+
 int radeon_dummy_page_init(struct radeon_device *rdev)
 {
        rdev->dummy_page.page = alloc_page(GFP_DMA32 | GFP_KERNEL | __GFP_ZERO);
@@ -463,12 +482,16 @@ int radeon_atombios_init(struct radeon_device *rdev)
 
        rdev->mode_info.atom_context = atom_parse(atom_card_info, rdev->bios);
        radeon_atom_initialize_bios_scratch_regs(rdev->ddev);
+       atom_allocate_fb_scratch(rdev->mode_info.atom_context);
        return 0;
 }
 
 void radeon_atombios_fini(struct radeon_device *rdev)
 {
-       kfree(rdev->mode_info.atom_context);
+       if (rdev->mode_info.atom_context) {
+               kfree(rdev->mode_info.atom_context->scratch);
+               kfree(rdev->mode_info.atom_context);
+       }
        kfree(rdev->mode_info.atom_card_info);
 }
 
@@ -544,16 +567,24 @@ int radeon_device_init(struct radeon_device *rdev,
        mutex_init(&rdev->cs_mutex);
        mutex_init(&rdev->ib_pool.mutex);
        mutex_init(&rdev->cp.mutex);
+       if (rdev->family >= CHIP_R600)
+               spin_lock_init(&rdev->ih.lock);
+       mutex_init(&rdev->gem.mutex);
        rwlock_init(&rdev->fence_drv.lock);
        INIT_LIST_HEAD(&rdev->gem.objects);
 
+       /* setup workqueue */
+       rdev->wq = create_workqueue("radeon");
+       if (rdev->wq == NULL)
+               return -ENOMEM;
+
        /* Set asic functions */
        r = radeon_asic_init(rdev);
        if (r) {
                return r;
        }
 
-       if (radeon_agpmode == -1) {
+       if (rdev->flags & RADEON_IS_AGP && radeon_agpmode == -1) {
                radeon_agp_disable(rdev);
        }
 
@@ -620,6 +651,7 @@ void radeon_device_fini(struct radeon_device *rdev)
        DRM_INFO("radeon: finishing device.\n");
        rdev->shutdown = true;
        radeon_fini(rdev);
+       destroy_workqueue(rdev->wq);
        vga_client_register(rdev->pdev, NULL, NULL, NULL);
        iounmap(rdev->rmmio);
        rdev->rmmio = NULL;
@@ -633,6 +665,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
 {
        struct radeon_device *rdev = dev->dev_private;
        struct drm_crtc *crtc;
+       int r;
 
        if (dev == NULL || rdev == NULL) {
                return -ENODEV;
@@ -643,26 +676,31 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
        /* unpin the front buffers */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb);
-               struct radeon_object *robj;
+               struct radeon_bo *robj;
 
                if (rfb == NULL || rfb->obj == NULL) {
                        continue;
                }
                robj = rfb->obj->driver_private;
-               if (robj != rdev->fbdev_robj) {
-                       radeon_object_unpin(robj);
+               if (robj != rdev->fbdev_rbo) {
+                       r = radeon_bo_reserve(robj, false);
+                       if (unlikely(r == 0)) {
+                               radeon_bo_unpin(robj);
+                               radeon_bo_unreserve(robj);
+                       }
                }
        }
        /* evict vram memory */
-       radeon_object_evict_vram(rdev);
+       radeon_bo_evict_vram(rdev);
        /* wait for gpu to finish processing current batch */
        radeon_fence_wait_last(rdev);
 
        radeon_save_bios_scratch_regs(rdev);
 
        radeon_suspend(rdev);
+       radeon_hpd_fini(rdev);
        /* evict remaining vram memory */
-       radeon_object_evict_vram(rdev);
+       radeon_bo_evict_vram(rdev);
 
        pci_save_state(dev->pdev);
        if (state.event == PM_EVENT_SUSPEND) {
@@ -695,6 +733,8 @@ int radeon_resume_kms(struct drm_device *dev)
        fb_set_suspend(rdev->fbdev_info, 0);
        release_console_sem();
 
+       /* reset hpd state */
+       radeon_hpd_init(rdev);
        /* blat the mode back in */
        drm_helper_resume_force_mode(dev);
        return 0;
index c85df4a..a133b83 100644 (file)
@@ -250,6 +250,16 @@ static const char *connector_names[13] = {
        "HDMI-B",
 };
 
+static const char *hpd_names[7] = {
+       "NONE",
+       "HPD1",
+       "HPD2",
+       "HPD3",
+       "HPD4",
+       "HPD5",
+       "HPD6",
+};
+
 static void radeon_print_display_setup(struct drm_device *dev)
 {
        struct drm_connector *connector;
@@ -264,16 +274,18 @@ static void radeon_print_display_setup(struct drm_device *dev)
                radeon_connector = to_radeon_connector(connector);
                DRM_INFO("Connector %d:\n", i);
                DRM_INFO("  %s\n", connector_names[connector->connector_type]);
+               if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
+                       DRM_INFO("  %s\n", hpd_names[radeon_connector->hpd.hpd]);
                if (radeon_connector->ddc_bus)
                        DRM_INFO("  DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
                                 radeon_connector->ddc_bus->rec.mask_clk_reg,
                                 radeon_connector->ddc_bus->rec.mask_data_reg,
                                 radeon_connector->ddc_bus->rec.a_clk_reg,
                                 radeon_connector->ddc_bus->rec.a_data_reg,
-                                radeon_connector->ddc_bus->rec.put_clk_reg,
-                                radeon_connector->ddc_bus->rec.put_data_reg,
-                                radeon_connector->ddc_bus->rec.get_clk_reg,
-                                radeon_connector->ddc_bus->rec.get_data_reg);
+                                radeon_connector->ddc_bus->rec.en_clk_reg,
+                                radeon_connector->ddc_bus->rec.en_data_reg,
+                                radeon_connector->ddc_bus->rec.y_clk_reg,
+                                radeon_connector->ddc_bus->rec.y_data_reg);
                DRM_INFO("  Encoders:\n");
                list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                        radeon_encoder = to_radeon_encoder(encoder);
@@ -324,6 +336,7 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
                        ret = radeon_get_legacy_connector_info_from_table(dev);
        }
        if (ret) {
+               radeon_setup_encoder_clones(dev);
                radeon_print_display_setup(dev);
                list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head)
                        radeon_ddc_dump(drm_connector);
@@ -336,12 +349,17 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
 {
        int ret = 0;
 
+       if (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+               struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
+               if (dig->dp_i2c_bus)
+                       radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter);
+       }
        if (!radeon_connector->ddc_bus)
                return -1;
        if (!radeon_connector->edid) {
-               radeon_i2c_do_lock(radeon_connector, 1);
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
                radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
-               radeon_i2c_do_lock(radeon_connector, 0);
+               radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        }
 
        if (radeon_connector->edid) {
@@ -361,9 +379,9 @@ static int radeon_ddc_dump(struct drm_connector *connector)
 
        if (!radeon_connector->ddc_bus)
                return -1;
-       radeon_i2c_do_lock(radeon_connector, 1);
+       radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
        edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
-       radeon_i2c_do_lock(radeon_connector, 0);
+       radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
        if (edid) {
                kfree(edid);
        }
@@ -542,6 +560,98 @@ void radeon_compute_pll(struct radeon_pll *pll,
        *post_div_p = best_post_div;
 }
 
+void radeon_compute_pll_avivo(struct radeon_pll *pll,
+                             uint64_t freq,
+                             uint32_t *dot_clock_p,
+                             uint32_t *fb_div_p,
+                             uint32_t *frac_fb_div_p,
+                             uint32_t *ref_div_p,
+                             uint32_t *post_div_p,
+                             int flags)
+{
+       fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq;
+       fixed20_12 pll_out_max, pll_out_min;
+       fixed20_12 pll_in_max, pll_in_min;
+       fixed20_12 reference_freq;
+       fixed20_12 error, ffreq, a, b;
+
+       pll_out_max.full = rfixed_const(pll->pll_out_max);
+       pll_out_min.full = rfixed_const(pll->pll_out_min);
+       pll_in_max.full = rfixed_const(pll->pll_in_max);
+       pll_in_min.full = rfixed_const(pll->pll_in_min);
+       reference_freq.full = rfixed_const(pll->reference_freq);
+       do_div(freq, 10);
+       ffreq.full = rfixed_const(freq);
+       error.full = rfixed_const(100 * 100);
+
+       /* max p */
+       p.full = rfixed_div(pll_out_max, ffreq);
+       p.full = rfixed_floor(p);
+
+       /* min m */
+       m.full = rfixed_div(reference_freq, pll_in_max);
+       m.full = rfixed_ceil(m);
+
+       while (1) {
+               n.full = rfixed_div(ffreq, reference_freq);
+               n.full = rfixed_mul(n, m);
+               n.full = rfixed_mul(n, p);
+
+               f_vco.full = rfixed_div(n, m);
+               f_vco.full = rfixed_mul(f_vco, reference_freq);
+
+               f_pclk.full = rfixed_div(f_vco, p);
+
+               if (f_pclk.full > ffreq.full)
+                       error.full = f_pclk.full - ffreq.full;
+               else
+                       error.full = ffreq.full - f_pclk.full;
+               error.full = rfixed_div(error, f_pclk);
+               a.full = rfixed_const(100 * 100);
+               error.full = rfixed_mul(error, a);
+
+               a.full = rfixed_mul(m, p);
+               a.full = rfixed_div(n, a);
+               best_freq.full = rfixed_mul(reference_freq, a);
+
+               if (rfixed_trunc(error) < 25)
+                       break;
+
+               a.full = rfixed_const(1);
+               m.full = m.full + a.full;
+               a.full = rfixed_div(reference_freq, m);
+               if (a.full >= pll_in_min.full)
+                       continue;
+
+               m.full = rfixed_div(reference_freq, pll_in_max);
+               m.full = rfixed_ceil(m);
+               a.full= rfixed_const(1);
+               p.full = p.full - a.full;
+               a.full = rfixed_mul(p, ffreq);
+               if (a.full >= pll_out_min.full)
+                       continue;
+               else {
+                       DRM_ERROR("Unable to find pll dividers\n");
+                       break;
+               }
+       }
+
+       a.full = rfixed_const(10);
+       b.full = rfixed_mul(n, a);
+
+       frac_n.full = rfixed_floor(n);
+       frac_n.full = rfixed_mul(frac_n, a);
+       frac_n.full = b.full - frac_n.full;
+
+       *dot_clock_p = rfixed_trunc(best_freq);
+       *fb_div_p = rfixed_trunc(n);
+       *frac_fb_div_p = rfixed_trunc(frac_n);
+       *ref_div_p = rfixed_trunc(m);
+       *post_div_p = rfixed_trunc(p);
+
+       DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
+}
+
 static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
        struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
@@ -642,7 +752,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
                        return -ENOMEM;
 
                rdev->mode_info.coherent_mode_property->values[0] = 0;
-               rdev->mode_info.coherent_mode_property->values[0] = 1;
+               rdev->mode_info.coherent_mode_property->values[1] = 1;
        }
 
        if (!ASIC_IS_AVIVO(rdev)) {
@@ -666,7 +776,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
        if (!rdev->mode_info.load_detect_property)
                return -ENOMEM;
        rdev->mode_info.load_detect_property->values[0] = 0;
-       rdev->mode_info.load_detect_property->values[0] = 1;
+       rdev->mode_info.load_detect_property->values[1] = 1;
 
        drm_mode_create_scaling_mode_property(rdev->ddev);
 
@@ -723,6 +833,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
        if (!ret) {
                return ret;
        }
+       /* initialize hpd */
+       radeon_hpd_init(rdev);
        drm_helper_initial_config(rdev->ddev);
        return 0;
 }
@@ -730,6 +842,7 @@ int radeon_modeset_init(struct radeon_device *rdev)
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
        if (rdev->mode_info.mode_config_initialized) {
+               radeon_hpd_fini(rdev);
                drm_mode_config_cleanup(rdev->ddev);
                rdev->mode_info.mode_config_initialized = false;
        }
@@ -750,9 +863,17 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
                if (first) {
-                       radeon_crtc->rmx_type = radeon_encoder->rmx_type;
+                       /* set scaling */
+                       if (radeon_encoder->rmx_type == RMX_OFF)
+                               radeon_crtc->rmx_type = RMX_OFF;
+                       else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay ||
+                                mode->vdisplay < radeon_encoder->native_mode.vdisplay)
+                               radeon_crtc->rmx_type = radeon_encoder->rmx_type;
+                       else
+                               radeon_crtc->rmx_type = RMX_OFF;
+                       /* copy native mode */
                        memcpy(&radeon_crtc->native_mode,
-                               &radeon_encoder->native_mode,
+                              &radeon_encoder->native_mode,
                                sizeof(struct drm_display_mode));
                        first = false;
                } else {
index 7f50fb8..2807724 100644 (file)
@@ -86,6 +86,7 @@ int radeon_benchmarking = 0;
 int radeon_testing = 0;
 int radeon_connector_table = 0;
 int radeon_tv = 1;
+int radeon_new_pll = 1;
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
 module_param_named(no_wb, radeon_no_wb, int, 0444);
@@ -120,6 +121,9 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
 MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
 module_param_named(tv, radeon_tv, int, 0444);
 
+MODULE_PARM_DESC(r4xx_atom, "Select new PLL code for AVIVO chips");
+module_param_named(new_pll, radeon_new_pll, int, 0444);
+
 static int radeon_suspend(struct drm_device *dev, pm_message_t state)
 {
        drm_radeon_private_t *dev_priv = dev->dev_private;
index 350962e..e137852 100644 (file)
@@ -1104,7 +1104,6 @@ extern u32 radeon_get_scratch(drm_radeon_private_t *dev_priv, int index);
 #       define R600_IT_WAIT_REG_MEM             0x00003C00
 #       define R600_IT_MEM_WRITE                0x00003D00
 #       define R600_IT_INDIRECT_BUFFER          0x00003200
-#       define R600_IT_CP_INTERRUPT             0x00004000
 #       define R600_IT_SURFACE_SYNC             0x00004300
 #              define R600_CB0_DEST_BASE_ENA    (1 << 6)
 #              define R600_TC_ACTION_ENA        (1 << 23)
index d42bc51..b4f23ec 100644 (file)
@@ -35,6 +35,51 @@ extern int atom_debug;
 bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
                                struct drm_display_mode *mode);
 
+static uint32_t radeon_encoder_clones(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct drm_encoder *clone_encoder;
+       uint32_t index_mask = 0;
+       int count;
+
+       /* DIG routing gets problematic */
+       if (rdev->family >= CHIP_R600)
+               return index_mask;
+       /* LVDS/TV are too wacky */
+       if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
+               return index_mask;
+       /* DVO requires 2x ppll clocks depending on tmds chip */
+       if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT)
+               return index_mask;
+       
+       count = -1;
+       list_for_each_entry(clone_encoder, &dev->mode_config.encoder_list, head) {
+               struct radeon_encoder *radeon_clone = to_radeon_encoder(clone_encoder);
+               count++;
+
+               if (clone_encoder == encoder)
+                       continue;
+               if (radeon_clone->devices & (ATOM_DEVICE_LCD_SUPPORT))
+                       continue;
+               if (radeon_clone->devices & ATOM_DEVICE_DFP2_SUPPORT)
+                       continue;
+               else
+                       index_mask |= (1 << count);
+       }
+       return index_mask;
+}
+
+void radeon_setup_encoder_clones(struct drm_device *dev)
+{
+       struct drm_encoder *encoder;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               encoder->possible_clones = radeon_encoder_clones(encoder);
+       }
+}
+
 uint32_t
 radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, uint8_t dac)
 {
@@ -163,29 +208,6 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
        return NULL;
 }
 
-/* used for both atom and legacy */
-void radeon_rmx_mode_fixup(struct drm_encoder *encoder,
-                          struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode)
-{
-       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
-       struct drm_device *dev = encoder->dev;
-       struct radeon_device *rdev = dev->dev_private;
-       struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
-
-       if (mode->hdisplay < native_mode->hdisplay ||
-           mode->vdisplay < native_mode->vdisplay) {
-               int mode_id = adjusted_mode->base.id;
-               *adjusted_mode = *native_mode;
-               if (!ASIC_IS_AVIVO(rdev)) {
-                       adjusted_mode->hdisplay = mode->hdisplay;
-                       adjusted_mode->vdisplay = mode->vdisplay;
-               }
-               adjusted_mode->base.id = mode_id;
-       }
-}
-
-
 static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
                                   struct drm_display_mode *mode,
                                   struct drm_display_mode *adjusted_mode)
@@ -198,14 +220,24 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
        radeon_encoder_set_active_device(encoder);
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 
-       if (radeon_encoder->rmx_type != RMX_OFF)
-               radeon_rmx_mode_fixup(encoder, mode, adjusted_mode);
-
        /* hw bug */
        if ((mode->flags & DRM_MODE_FLAG_INTERLACE)
            && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
                adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
 
+       /* get the native mode for LVDS */
+       if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
+               struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+               int mode_id = adjusted_mode->base.id;
+               *adjusted_mode = *native_mode;
+               if (!ASIC_IS_AVIVO(rdev)) {
+                       adjusted_mode->hdisplay = mode->hdisplay;
+                       adjusted_mode->vdisplay = mode->vdisplay;
+               }
+               adjusted_mode->base.id = mode_id;
+       }
+
+       /* get the native mode for TV */
        if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
                struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
                if (tv_dac) {
@@ -218,6 +250,12 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
                }
        }
 
+       if (ASIC_IS_DCE3(rdev) &&
+           (radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT))) {
+               struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+               radeon_dp_set_link_config(connector, mode);
+       }
+
        return true;
 }
 
@@ -392,7 +430,7 @@ union lvds_encoder_control {
        LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2;
 };
 
-static void
+void
 atombios_digital_setup(struct drm_encoder *encoder, int action)
 {
        struct drm_device *dev = encoder->dev;
@@ -522,6 +560,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
 {
        struct drm_connector *connector;
        struct radeon_connector *radeon_connector;
+       struct radeon_connector_atom_dig *radeon_dig_connector;
 
        connector = radeon_get_connector_for_encoder(encoder);
        if (!connector)
@@ -551,10 +590,10 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
                return ATOM_ENCODER_MODE_LVDS;
                break;
        case DRM_MODE_CONNECTOR_DisplayPort:
-               /*if (radeon_output->MonType == MT_DP)
-                 return ATOM_ENCODER_MODE_DP;
-                 else*/
-               if (drm_detect_hdmi_monitor(radeon_connector->edid))
+               radeon_dig_connector = radeon_connector->con_priv;
+               if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT)
+                       return ATOM_ENCODER_MODE_DP;
+               else if (drm_detect_hdmi_monitor(radeon_connector->edid))
                        return ATOM_ENCODER_MODE_HDMI;
                else
                        return ATOM_ENCODER_MODE_DVI;
@@ -573,6 +612,30 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
        }
 }
 
+/*
+ * DIG Encoder/Transmitter Setup
+ *
+ * DCE 3.0/3.1
+ * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
+ * Supports up to 3 digital outputs
+ * - 2 DIG encoder blocks.
+ * DIG1 can drive UNIPHY link A or link B
+ * DIG2 can drive UNIPHY link B or LVTMA
+ *
+ * DCE 3.2
+ * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
+ * Supports up to 5 digital outputs
+ * - 2 DIG encoder blocks.
+ * DIG1/2 can drive UNIPHY0/1/2 link A or link B
+ *
+ * Routing
+ * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
+ * Examples:
+ * crtc0 -> dig2 -> LVTMA   links A+B -> TMDS/HDMI
+ * crtc1 -> dig1 -> UNIPHY0 link  B   -> DP
+ * crtc0 -> dig1 -> UNIPHY2 link  A   -> LVDS
+ * crtc1 -> dig2 -> UNIPHY1 link  B+A -> TMDS/HDMI
+ */
 static void
 atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
 {
@@ -614,10 +677,17 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
        } else {
                switch (radeon_encoder->encoder_id) {
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
-                       index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+                       /* XXX doesn't really matter which dig encoder we pick as long as it's
+                        * not already in use
+                        */
+                       if (dig_connector->linkb)
+                               index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
+                       else
+                               index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
                        num = 1;
                        break;
                case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+                       /* Only dig2 encoder can drive LVTMA */
                        index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
                        num = 2;
                        break;
@@ -652,18 +722,21 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
                }
        }
 
-       if (radeon_encoder->pixel_clock > 165000) {
-               args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B;
+       args.ucEncoderMode = atombios_get_encoder_mode(encoder);
+
+       if (args.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
+               if (dig_connector->dp_clock == 270000)
+                       args.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
+               args.ucLaneNum = dig_connector->dp_lane_count;
+       } else if (radeon_encoder->pixel_clock > 165000)
                args.ucLaneNum = 8;
-       } else {
-               if (dig_connector->linkb)
-                       args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
-               else
-                       args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
+       else
                args.ucLaneNum = 4;
-       }
 
-       args.ucEncoderMode = atombios_get_encoder_mode(encoder);
+       if (dig_connector->linkb)
+               args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
+       else
+               args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
 
        atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
@@ -674,8 +747,8 @@ union dig_transmitter_control {
        DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
 };
 
-static void
-atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
+void
+atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set)
 {
        struct drm_device *dev = encoder->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -687,6 +760,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
        struct drm_connector *connector;
        struct radeon_connector *radeon_connector;
        struct radeon_connector_atom_dig *dig_connector;
+       bool is_dp = false;
 
        connector = radeon_get_connector_for_encoder(encoder);
        if (!connector)
@@ -704,6 +778,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
 
        dig_connector = radeon_connector->con_priv;
 
+       if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP)
+               is_dp = true;
+
        memset(&args, 0, sizeof(args));
 
        if (ASIC_IS_DCE32(rdev))
@@ -724,17 +801,23 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
        args.v1.ucAction = action;
        if (action == ATOM_TRANSMITTER_ACTION_INIT) {
                args.v1.usInitInfo = radeon_connector->connector_object_id;
+       } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
+               args.v1.asMode.ucLaneSel = lane_num;
+               args.v1.asMode.ucLaneSet = lane_set;
        } else {
-               if (radeon_encoder->pixel_clock > 165000)
+               if (is_dp)
+                       args.v1.usPixelClock =
+                               cpu_to_le16(dig_connector->dp_clock / 10);
+               else if (radeon_encoder->pixel_clock > 165000)
                        args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
                else
                        args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
        }
        if (ASIC_IS_DCE32(rdev)) {
-               if (radeon_encoder->pixel_clock > 165000)
-                       args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
                if (dig->dig_block)
                        args.v2.acConfig.ucEncoderSel = 1;
+               if (dig_connector->linkb)
+                       args.v2.acConfig.ucLinkSel = 1;
 
                switch (radeon_encoder->encoder_id) {
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
@@ -751,7 +834,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
                        break;
                }
 
-               if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+               if (is_dp)
+                       args.v2.acConfig.fCoherentMode = 1;
+               else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
                        if (dig->coherent_mode)
                                args.v2.acConfig.fCoherentMode = 1;
                }
@@ -760,17 +845,20 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
 
                switch (radeon_encoder->encoder_id) {
                case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
-                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
+                       /* XXX doesn't really matter which dig encoder we pick as long as it's
+                        * not already in use
+                        */
+                       if (dig_connector->linkb)
+                               args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
+                       else
+                               args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
                        if (rdev->flags & RADEON_IS_IGP) {
                                if (radeon_encoder->pixel_clock > 165000) {
-                                       args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
-                                                            ATOM_TRANSMITTER_CONFIG_LINKA_B);
                                        if (dig_connector->igp_lane_info & 0x3)
                                                args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
                                        else if (dig_connector->igp_lane_info & 0xc)
                                                args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
                                } else {
-                                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
                                        if (dig_connector->igp_lane_info & 0x1)
                                                args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
                                        else if (dig_connector->igp_lane_info & 0x2)
@@ -780,35 +868,25 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
                                        else if (dig_connector->igp_lane_info & 0x8)
                                                args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
                                }
-                       } else {
-                               if (radeon_encoder->pixel_clock > 165000)
-                                       args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
-                                                            ATOM_TRANSMITTER_CONFIG_LINKA_B |
-                                                            ATOM_TRANSMITTER_CONFIG_LANE_0_7);
-                               else {
-                                       if (dig_connector->linkb)
-                                               args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
-                                       else
-                                               args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
-                               }
                        }
                        break;
                case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+                       /* Only dig2 encoder can drive LVTMA */
                        args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
-                       if (radeon_encoder->pixel_clock > 165000)
-                               args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
-                                                    ATOM_TRANSMITTER_CONFIG_LINKA_B |
-                                                    ATOM_TRANSMITTER_CONFIG_LANE_0_7);
-                       else {
-                               if (dig_connector->linkb)
-                                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
-                               else
-                                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
-                       }
                        break;
                }
 
-               if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+               if (radeon_encoder->pixel_clock > 165000)
+                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
+
+               if (dig_connector->linkb)
+                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
+               else
+                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
+
+               if (is_dp)
+                       args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
+               else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
                        if (dig->coherent_mode)
                                args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
                }
@@ -918,12 +996,16 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
        if (is_dig) {
                switch (mode) {
                case DRM_MODE_DPMS_ON:
-                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
+                       {
+                               struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+                               dp_link_train(encoder, connector);
+                       }
                        break;
                case DRM_MODE_DPMS_STANDBY:
                case DRM_MODE_DPMS_SUSPEND:
                case DRM_MODE_DPMS_OFF:
-                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
+                       atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
                        break;
                }
        } else {
@@ -1025,13 +1107,33 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
                                                args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
                                        else
                                                args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
-                               } else
-                                       args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
+                               } else {
+                                       struct drm_connector *connector;
+                                       struct radeon_connector *radeon_connector;
+                                       struct radeon_connector_atom_dig *dig_connector;
+
+                                       connector = radeon_get_connector_for_encoder(encoder);
+                                       if (!connector)
+                                               return;
+                                       radeon_connector = to_radeon_connector(connector);
+                                       if (!radeon_connector->con_priv)
+                                               return;
+                                       dig_connector = radeon_connector->con_priv;
+
+                                       /* XXX doesn't really matter which dig encoder we pick as long as it's
+                                        * not already in use
+                                        */
+                                       if (dig_connector->linkb)
+                                               args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
+                                       else
+                                               args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
+                               }
                                break;
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
                                args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
                                break;
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+                               /* Only dig2 encoder can drive LVTMA */
                                args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
                                break;
                        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
@@ -1104,11 +1206,14 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
 
-       if (radeon_encoder->enc_priv) {
-               struct radeon_encoder_atom_dig *dig;
+       if (radeon_encoder->active_device &
+           (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) {
+               if (radeon_encoder->enc_priv) {
+                       struct radeon_encoder_atom_dig *dig;
 
-               dig = radeon_encoder->enc_priv;
-               dig->dig_block = radeon_crtc->crtc_id;
+                       dig = radeon_encoder->enc_priv;
+                       dig->dig_block = radeon_crtc->crtc_id;
+               }
        }
        radeon_encoder->pixel_clock = adjusted_mode->clock;
 
@@ -1134,14 +1239,14 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
        case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
        case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
                /* disable the encoder and transmitter */
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
+               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
                atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
 
                /* setup and enable the encoder and transmitter */
                atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT);
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP);
-               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
+               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
+               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
+               atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
                break;
        case ENCODER_OBJECT_ID_INTERNAL_DDI:
                atombios_ddia_setup(encoder, ATOM_ENABLE);
@@ -1354,7 +1459,6 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
                encoder->possible_crtcs = 0x1;
        else
                encoder->possible_crtcs = 0x3;
-       encoder->possible_clones = 0;
 
        radeon_encoder->enc_priv = NULL;
 
index b38c4c8..3ba213d 100644 (file)
@@ -59,7 +59,7 @@ static struct fb_ops radeonfb_ops = {
 };
 
 /**
- * Curretly it is assumed that the old framebuffer is reused.
+ * Currently it is assumed that the old framebuffer is reused.
  *
  * LOCKING
  * caller should hold the mode config lock.
@@ -140,7 +140,7 @@ int radeonfb_create(struct drm_device *dev,
        struct radeon_framebuffer *rfb;
        struct drm_mode_fb_cmd mode_cmd;
        struct drm_gem_object *gobj = NULL;
-       struct radeon_object *robj = NULL;
+       struct radeon_bo *rbo = NULL;
        struct device *device = &rdev->pdev->dev;
        int size, aligned_size, ret;
        u64 fb_gpuaddr;
@@ -168,14 +168,14 @@ int radeonfb_create(struct drm_device *dev,
        ret = radeon_gem_object_create(rdev, aligned_size, 0,
                        RADEON_GEM_DOMAIN_VRAM,
                        false, ttm_bo_type_kernel,
-                       false, &gobj);
+                       &gobj);
        if (ret) {
                printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
                       surface_width, surface_height);
                ret = -ENOMEM;
                goto out;
        }
-       robj = gobj->driver_private;
+       rbo = gobj->driver_private;
 
        if (fb_tiled)
                tiling_flags = RADEON_TILING_MACRO;
@@ -192,8 +192,13 @@ int radeonfb_create(struct drm_device *dev,
        }
 #endif
 
-       if (tiling_flags)
-               radeon_object_set_tiling_flags(robj, tiling_flags | RADEON_TILING_SURFACE, mode_cmd.pitch);
+       if (tiling_flags) {
+               ret = radeon_bo_set_tiling_flags(rbo,
+                                       tiling_flags | RADEON_TILING_SURFACE,
+                                       mode_cmd.pitch);
+               if (ret)
+                       dev_err(rdev->dev, "FB failed to set tiling flags\n");
+       }
        mutex_lock(&rdev->ddev->struct_mutex);
        fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
        if (fb == NULL) {
@@ -201,10 +206,19 @@ int radeonfb_create(struct drm_device *dev,
                ret = -ENOMEM;
                goto out_unref;
        }
-       ret = radeon_object_pin(robj, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
+       ret = radeon_bo_reserve(rbo, false);
+       if (unlikely(ret != 0))
+               goto out_unref;
+       ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
+       if (ret) {
+               radeon_bo_unreserve(rbo);
+               goto out_unref;
+       }
+       if (fb_tiled)
+               radeon_bo_check_tiling(rbo, 0, 0);
+       ret = radeon_bo_kmap(rbo, &fbptr);
+       radeon_bo_unreserve(rbo);
        if (ret) {
-               printk(KERN_ERR "failed to pin framebuffer\n");
-               ret = -ENOMEM;
                goto out_unref;
        }
 
@@ -213,7 +227,7 @@ int radeonfb_create(struct drm_device *dev,
        *fb_p = fb;
        rfb = to_radeon_framebuffer(fb);
        rdev->fbdev_rfb = rfb;
-       rdev->fbdev_robj = robj;
+       rdev->fbdev_rbo = rbo;
 
        info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
        if (info == NULL) {
@@ -234,15 +248,7 @@ int radeonfb_create(struct drm_device *dev,
        if (ret)
                goto out_unref;
 
-       if (fb_tiled)
-               radeon_object_check_tiling(robj, 0, 0);
-
-       ret = radeon_object_kmap(robj, &fbptr);
-       if (ret) {
-               goto out_unref;
-       }
-
-       memset_io(fbptr, 0, aligned_size);
+       memset_io(fbptr, 0xff, aligned_size);
 
        strcpy(info->fix.id, "radeondrmfb");
 
@@ -288,8 +294,12 @@ int radeonfb_create(struct drm_device *dev,
        return 0;
 
 out_unref:
-       if (robj) {
-               radeon_object_kunmap(robj);
+       if (rbo) {
+               ret = radeon_bo_reserve(rbo, false);
+               if (likely(ret == 0)) {
+                       radeon_bo_kunmap(rbo);
+                       radeon_bo_unreserve(rbo);
+               }
        }
        if (fb && ret) {
                list_del(&fb->filp_head);
@@ -321,14 +331,22 @@ int radeon_parse_options(char *options)
 
 int radeonfb_probe(struct drm_device *dev)
 {
-       return drm_fb_helper_single_fb_probe(dev, 32, &radeonfb_create);
+       struct radeon_device *rdev = dev->dev_private;
+       int bpp_sel = 32;
+
+       /* select 8 bpp console on RN50 or 16MB cards */
+       if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
+               bpp_sel = 8;
+
+       return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create);
 }
 
 int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
 {
        struct fb_info *info;
        struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
-       struct radeon_object *robj;
+       struct radeon_bo *rbo;
+       int r;
 
        if (!fb) {
                return -EINVAL;
@@ -336,10 +354,14 @@ int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
        info = fb->fbdev;
        if (info) {
                struct radeon_fb_device *rfbdev = info->par;
-               robj = rfb->obj->driver_private;
+               rbo = rfb->obj->driver_private;
                unregister_framebuffer(info);
-               radeon_object_kunmap(robj);
-               radeon_object_unpin(robj);
+               r = radeon_bo_reserve(rbo, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rbo);
+                       radeon_bo_unpin(rbo);
+                       radeon_bo_unreserve(rbo);
+               }
                drm_fb_helper_free(&rfbdev->helper);
                framebuffer_release(info);
        }
index 3beb26d..cb4cd97 100644 (file)
@@ -168,37 +168,6 @@ bool radeon_fence_signaled(struct radeon_fence *fence)
        return signaled;
 }
 
-int r600_fence_wait(struct radeon_fence *fence,  bool intr, bool lazy)
-{
-       struct radeon_device *rdev;
-       int ret = 0;
-
-       rdev = fence->rdev;
-
-       __set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
-
-       while (1) {
-               if (radeon_fence_signaled(fence))
-                       break;
-
-               if (time_after_eq(jiffies, fence->timeout)) {
-                       ret = -EBUSY;
-                       break;
-               }
-
-               if (lazy)
-                       schedule_timeout(1);
-
-               if (intr && signal_pending(current)) {
-                       ret = -ERESTARTSYS;
-                       break;
-               }
-       }
-       __set_current_state(TASK_RUNNING);
-       return ret;
-}
-
-
 int radeon_fence_wait(struct radeon_fence *fence, bool intr)
 {
        struct radeon_device *rdev;
@@ -216,13 +185,6 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
                return 0;
        }
 
-       if (rdev->family >= CHIP_R600) {
-               r = r600_fence_wait(fence, intr, 0);
-               if (r == -ERESTARTSYS)
-                       return -EBUSY;
-               return r;
-       }
-
 retry:
        cur_jiffies = jiffies;
        timeout = HZ / 100;
@@ -231,14 +193,17 @@ retry:
        }
 
        if (intr) {
+               radeon_irq_kms_sw_irq_get(rdev);
                r = wait_event_interruptible_timeout(rdev->fence_drv.queue,
                                radeon_fence_signaled(fence), timeout);
-               if (unlikely(r == -ERESTARTSYS)) {
-                       return -EBUSY;
-               }
+               radeon_irq_kms_sw_irq_put(rdev);
+               if (unlikely(r < 0))
+                       return r;
        } else {
+               radeon_irq_kms_sw_irq_get(rdev);
                r = wait_event_timeout(rdev->fence_drv.queue,
                         radeon_fence_signaled(fence), timeout);
+               radeon_irq_kms_sw_irq_put(rdev);
        }
        if (unlikely(!radeon_fence_signaled(fence))) {
                if (unlikely(r == 0)) {
index 90187d1..3d4d84e 100644 (file)
@@ -38,6 +38,23 @@ typedef union rfixed {
 #define fixed_init_half(A) { .full = rfixed_const_half((A)) }
 #define rfixed_trunc(A) ((A).full >> 12)
 
+static inline u32 rfixed_floor(fixed20_12 A)
+{
+       u32 non_frac = rfixed_trunc(A);
+
+       return rfixed_const(non_frac);
+}
+
+static inline u32 rfixed_ceil(fixed20_12 A)
+{
+       u32 non_frac = rfixed_trunc(A);
+
+       if (A.full > rfixed_const(non_frac))
+               return rfixed_const(non_frac + 1);
+       else
+               return rfixed_const(non_frac);
+}
+
 static inline u32 rfixed_div(fixed20_12 A, fixed20_12 B)
 {
        u64 tmp = ((u64)A.full << 13);
index a68d756..e73d56e 100644 (file)
@@ -78,11 +78,9 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev)
        int r;
 
        if (rdev->gart.table.vram.robj == NULL) {
-               r = radeon_object_create(rdev, NULL,
-                                        rdev->gart.table_size,
-                                        true,
-                                        RADEON_GEM_DOMAIN_VRAM,
-                                        false, &rdev->gart.table.vram.robj);
+               r = radeon_bo_create(rdev, NULL, rdev->gart.table_size,
+                                       true, RADEON_GEM_DOMAIN_VRAM,
+                                       &rdev->gart.table.vram.robj);
                if (r) {
                        return r;
                }
@@ -95,32 +93,38 @@ int radeon_gart_table_vram_pin(struct radeon_device *rdev)
        uint64_t gpu_addr;
        int r;
 
-       r = radeon_object_pin(rdev->gart.table.vram.robj,
-                             RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
-       if (r) {
-               radeon_object_unref(&rdev->gart.table.vram.robj);
+       r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+       if (unlikely(r != 0))
                return r;
-       }
-       r = radeon_object_kmap(rdev->gart.table.vram.robj,
-                              (void **)&rdev->gart.table.vram.ptr);
+       r = radeon_bo_pin(rdev->gart.table.vram.robj,
+                               RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
        if (r) {
-               radeon_object_unpin(rdev->gart.table.vram.robj);
-               radeon_object_unref(&rdev->gart.table.vram.robj);
-               DRM_ERROR("radeon: failed to map gart vram table.\n");
+               radeon_bo_unreserve(rdev->gart.table.vram.robj);
                return r;
        }
+       r = radeon_bo_kmap(rdev->gart.table.vram.robj,
+                               (void **)&rdev->gart.table.vram.ptr);
+       if (r)
+               radeon_bo_unpin(rdev->gart.table.vram.robj);
+       radeon_bo_unreserve(rdev->gart.table.vram.robj);
        rdev->gart.table_addr = gpu_addr;
-       return 0;
+       return r;
 }
 
 void radeon_gart_table_vram_free(struct radeon_device *rdev)
 {
+       int r;
+
        if (rdev->gart.table.vram.robj == NULL) {
                return;
        }
-       radeon_object_kunmap(rdev->gart.table.vram.robj);
-       radeon_object_unpin(rdev->gart.table.vram.robj);
-       radeon_object_unref(&rdev->gart.table.vram.robj);
+       r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+       if (likely(r == 0)) {
+               radeon_bo_kunmap(rdev->gart.table.vram.robj);
+               radeon_bo_unpin(rdev->gart.table.vram.robj);
+               radeon_bo_unreserve(rdev->gart.table.vram.robj);
+       }
+       radeon_bo_unref(&rdev->gart.table.vram.robj);
 }
 
 
index d880edf..2944486 100644 (file)
@@ -38,22 +38,21 @@ int radeon_gem_object_init(struct drm_gem_object *obj)
 
 void radeon_gem_object_free(struct drm_gem_object *gobj)
 {
-       struct radeon_object *robj = gobj->driver_private;
+       struct radeon_bo *robj = gobj->driver_private;
 
        gobj->driver_private = NULL;
        if (robj) {
-               radeon_object_unref(&robj);
+               radeon_bo_unref(&robj);
        }
 }
 
 int radeon_gem_object_create(struct radeon_device *rdev, int size,
-                            int alignment, int initial_domain,
-                            bool discardable, bool kernel,
-                            bool interruptible,
-                            struct drm_gem_object **obj)
+                               int alignment, int initial_domain,
+                               bool discardable, bool kernel,
+                               struct drm_gem_object **obj)
 {
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        int r;
 
        *obj = NULL;
@@ -65,8 +64,7 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
        if (alignment < PAGE_SIZE) {
                alignment = PAGE_SIZE;
        }
-       r = radeon_object_create(rdev, gobj, size, kernel, initial_domain,
-                                interruptible, &robj);
+       r = radeon_bo_create(rdev, gobj, size, kernel, initial_domain, &robj);
        if (r) {
                DRM_ERROR("Failed to allocate GEM object (%d, %d, %u)\n",
                          size, initial_domain, alignment);
@@ -83,33 +81,33 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
 int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
                          uint64_t *gpu_addr)
 {
-       struct radeon_object *robj = obj->driver_private;
-       uint32_t flags;
+       struct radeon_bo *robj = obj->driver_private;
+       int r;
 
-       switch (pin_domain) {
-       case RADEON_GEM_DOMAIN_VRAM:
-               flags = TTM_PL_FLAG_VRAM;
-               break;
-       case RADEON_GEM_DOMAIN_GTT:
-               flags = TTM_PL_FLAG_TT;
-               break;
-       default:
-               flags = TTM_PL_FLAG_SYSTEM;
-               break;
-       }
-       return radeon_object_pin(robj, flags, gpu_addr);
+       r = radeon_bo_reserve(robj, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(robj, pin_domain, gpu_addr);
+       radeon_bo_unreserve(robj);
+       return r;
 }
 
 void radeon_gem_object_unpin(struct drm_gem_object *obj)
 {
-       struct radeon_object *robj = obj->driver_private;
-       radeon_object_unpin(robj);
+       struct radeon_bo *robj = obj->driver_private;
+       int r;
+
+       r = radeon_bo_reserve(robj, false);
+       if (likely(r == 0)) {
+               radeon_bo_unpin(robj);
+               radeon_bo_unreserve(robj);
+       }
 }
 
 int radeon_gem_set_domain(struct drm_gem_object *gobj,
                          uint32_t rdomain, uint32_t wdomain)
 {
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        uint32_t domain;
        int r;
 
@@ -127,11 +125,12 @@ int radeon_gem_set_domain(struct drm_gem_object *gobj,
        }
        if (domain == RADEON_GEM_DOMAIN_CPU) {
                /* Asking for cpu access wait for object idle */
-               r = radeon_object_wait(robj);
+               r = radeon_bo_wait(robj, NULL, false);
                if (r) {
                        printk(KERN_ERR "Failed to wait for object !\n");
                        return r;
                }
+               radeon_hdp_flush(robj->rdev);
        }
        return 0;
 }
@@ -144,7 +143,7 @@ int radeon_gem_init(struct radeon_device *rdev)
 
 void radeon_gem_fini(struct radeon_device *rdev)
 {
-       radeon_object_force_delete(rdev);
+       radeon_bo_force_delete(rdev);
 }
 
 
@@ -158,9 +157,13 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
        struct drm_radeon_gem_info *args = data;
 
        args->vram_size = rdev->mc.real_vram_size;
-       /* FIXME: report somethings that makes sense */
-       args->vram_visible = rdev->mc.real_vram_size - (4 * 1024 * 1024);
-       args->gart_size = rdev->mc.gtt_size;
+       args->vram_visible = rdev->mc.real_vram_size;
+       if (rdev->stollen_vga_memory)
+               args->vram_visible -= radeon_bo_size(rdev->stollen_vga_memory);
+       if (rdev->fbdev_rbo)
+               args->vram_visible -= radeon_bo_size(rdev->fbdev_rbo);
+       args->gart_size = rdev->mc.gtt_size - rdev->cp.ring_size - 4096 -
+               RADEON_IB_POOL_SIZE*64*1024;
        return 0;
 }
 
@@ -192,8 +195,8 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
        /* create a gem object to contain this object in */
        args->size = roundup(args->size, PAGE_SIZE);
        r = radeon_gem_object_create(rdev, args->size, args->alignment,
-                                    args->initial_domain, false,
-                                    false, true, &gobj);
+                                       args->initial_domain, false,
+                                       false, &gobj);
        if (r) {
                return r;
        }
@@ -218,7 +221,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
         * just validate the BO into a certain domain */
        struct drm_radeon_gem_set_domain *args = data;
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        int r;
 
        /* for now if someone requests domain CPU -
@@ -244,19 +247,18 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_radeon_gem_mmap *args = data;
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
-       int r;
+       struct radeon_bo *robj;
 
        gobj = drm_gem_object_lookup(dev, filp, args->handle);
        if (gobj == NULL) {
                return -EINVAL;
        }
        robj = gobj->driver_private;
-       r = radeon_object_mmap(robj, &args->addr_ptr);
+       args->addr_ptr = radeon_bo_mmap_offset(robj);
        mutex_lock(&dev->struct_mutex);
        drm_gem_object_unreference(gobj);
        mutex_unlock(&dev->struct_mutex);
-       return r;
+       return 0;
 }
 
 int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
@@ -264,16 +266,16 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_radeon_gem_busy *args = data;
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        int r;
-       uint32_t cur_placement;
+       uint32_t cur_placement = 0;
 
        gobj = drm_gem_object_lookup(dev, filp, args->handle);
        if (gobj == NULL) {
                return -EINVAL;
        }
        robj = gobj->driver_private;
-       r = radeon_object_busy_domain(robj, &cur_placement);
+       r = radeon_bo_wait(robj, &cur_placement, true);
        switch (cur_placement) {
        case TTM_PL_VRAM:
                args->domain = RADEON_GEM_DOMAIN_VRAM;
@@ -297,7 +299,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_radeon_gem_wait_idle *args = data;
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        int r;
 
        gobj = drm_gem_object_lookup(dev, filp, args->handle);
@@ -305,10 +307,11 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
                return -EINVAL;
        }
        robj = gobj->driver_private;
-       r = radeon_object_wait(robj);
+       r = radeon_bo_wait(robj, NULL, false);
        mutex_lock(&dev->struct_mutex);
        drm_gem_object_unreference(gobj);
        mutex_unlock(&dev->struct_mutex);
+       radeon_hdp_flush(robj->rdev);
        return r;
 }
 
@@ -317,7 +320,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_radeon_gem_set_tiling *args = data;
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
+       struct radeon_bo *robj;
        int r = 0;
 
        DRM_DEBUG("%d \n", args->handle);
@@ -325,7 +328,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
        if (gobj == NULL)
                return -EINVAL;
        robj = gobj->driver_private;
-       radeon_object_set_tiling_flags(robj, args->tiling_flags, args->pitch);
+       r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch);
        mutex_lock(&dev->struct_mutex);
        drm_gem_object_unreference(gobj);
        mutex_unlock(&dev->struct_mutex);
@@ -337,16 +340,19 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_radeon_gem_get_tiling *args = data;
        struct drm_gem_object *gobj;
-       struct radeon_object *robj;
+       struct radeon_bo *rbo;
        int r = 0;
 
        DRM_DEBUG("\n");
        gobj = drm_gem_object_lookup(dev, filp, args->handle);
        if (gobj == NULL)
                return -EINVAL;
-       robj = gobj->driver_private;
-       radeon_object_get_tiling_flags(robj, &args->tiling_flags,
-                                      &args->pitch);
+       rbo = gobj->driver_private;
+       r = radeon_bo_reserve(rbo, false);
+       if (unlikely(r != 0))
+               return r;
+       radeon_bo_get_tiling_flags(rbo, &args->tiling_flags, &args->pitch);
+       radeon_bo_unreserve(rbo);
        mutex_lock(&dev->struct_mutex);
        drm_gem_object_unreference(gobj);
        mutex_unlock(&dev->struct_mutex);
index dd438d3..da3da1e 100644 (file)
@@ -59,35 +59,43 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
 }
 
 
-void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state)
+void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
 {
-       struct radeon_device *rdev = radeon_connector->base.dev->dev_private;
+       struct radeon_device *rdev = i2c->dev->dev_private;
+       struct radeon_i2c_bus_rec *rec = &i2c->rec;
        uint32_t temp;
-       struct radeon_i2c_bus_rec *rec = &radeon_connector->ddc_bus->rec;
 
        /* RV410 appears to have a bug where the hw i2c in reset
         * holds the i2c port in a bad state - switch hw i2c away before
         * doing DDC - do this for all r200s/r300s/r400s for safety sake
         */
-       if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
-               if (rec->a_clk_reg == RADEON_GPIO_MONID) {
-                       WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
-                                               R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
-               } else {
-                       WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
-                                               R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
+       if (rec->hw_capable) {
+               if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
+                       if (rec->a_clk_reg == RADEON_GPIO_MONID) {
+                               WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
+                                                              R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
+                       } else {
+                               WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
+                                                              R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
+                       }
                }
        }
-       if (lock_state) {
-               temp = RREG32(rec->a_clk_reg);
-               temp &= ~(rec->a_clk_mask);
-               WREG32(rec->a_clk_reg, temp);
-
-               temp = RREG32(rec->a_data_reg);
-               temp &= ~(rec->a_data_mask);
-               WREG32(rec->a_data_reg, temp);
-       }
 
+       /* clear the output pin values */
+       temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask;
+       WREG32(rec->a_clk_reg, temp);
+
+       temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask;
+       WREG32(rec->a_data_reg, temp);
+
+       /* set the pins to input */
+       temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
+       WREG32(rec->en_clk_reg, temp);
+
+       temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
+       WREG32(rec->en_data_reg, temp);
+
+       /* mask the gpio pins for software use */
        temp = RREG32(rec->mask_clk_reg);
        if (lock_state)
                temp |= rec->mask_clk_mask;
@@ -112,8 +120,9 @@ static int get_clock(void *i2c_priv)
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
        uint32_t val;
 
-       val = RREG32(rec->get_clk_reg);
-       val &= rec->get_clk_mask;
+       /* read the value off the pin */
+       val = RREG32(rec->y_clk_reg);
+       val &= rec->y_clk_mask;
 
        return (val != 0);
 }
@@ -126,8 +135,10 @@ static int get_data(void *i2c_priv)
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
        uint32_t val;
 
-       val = RREG32(rec->get_data_reg);
-       val &= rec->get_data_mask;
+       /* read the value off the pin */
+       val = RREG32(rec->y_data_reg);
+       val &= rec->y_data_mask;
+
        return (val != 0);
 }
 
@@ -138,9 +149,10 @@ static void set_clock(void *i2c_priv, int clock)
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
        uint32_t val;
 
-       val = RREG32(rec->put_clk_reg) & (uint32_t)~(rec->put_clk_mask);
-       val |= clock ? 0 : rec->put_clk_mask;
-       WREG32(rec->put_clk_reg, val);
+       /* set pin direction */
+       val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
+       val |= clock ? 0 : rec->en_clk_mask;
+       WREG32(rec->en_clk_reg, val);
 }
 
 static void set_data(void *i2c_priv, int data)
@@ -150,14 +162,15 @@ static void set_data(void *i2c_priv, int data)
        struct radeon_i2c_bus_rec *rec = &i2c->rec;
        uint32_t val;
 
-       val = RREG32(rec->put_data_reg) & (uint32_t)~(rec->put_data_mask);
-       val |= data ? 0 : rec->put_data_mask;
-       WREG32(rec->put_data_reg, val);
+       /* set pin direction */
+       val = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
+       val |= data ? 0 : rec->en_data_mask;
+       WREG32(rec->en_data_reg, val);
 }
 
 struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
-               struct radeon_i2c_bus_rec *rec,
-               const char *name)
+                                         struct radeon_i2c_bus_rec *rec,
+                                         const char *name)
 {
        struct radeon_i2c_chan *i2c;
        int ret;
@@ -167,20 +180,19 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
                return NULL;
 
        i2c->adapter.owner = THIS_MODULE;
-       i2c->adapter.algo_data = &i2c->algo;
        i2c->dev = dev;
-       i2c->algo.setsda = set_data;
-       i2c->algo.setscl = set_clock;
-       i2c->algo.getsda = get_data;
-       i2c->algo.getscl = get_clock;
-       i2c->algo.udelay = 20;
+       i2c_set_adapdata(&i2c->adapter, i2c);
+       i2c->adapter.algo_data = &i2c->algo.bit;
+       i2c->algo.bit.setsda = set_data;
+       i2c->algo.bit.setscl = set_clock;
+       i2c->algo.bit.getsda = get_data;
+       i2c->algo.bit.getscl = get_clock;
+       i2c->algo.bit.udelay = 20;
        /* vesa says 2.2 ms is enough, 1 jiffy doesn't seem to always
         * make this, 2 jiffies is a lot more reliable */
-       i2c->algo.timeout = 2;
-       i2c->algo.data = i2c;
+       i2c->algo.bit.timeout = 2;
+       i2c->algo.bit.data = i2c;
        i2c->rec = *rec;
-       i2c_set_adapdata(&i2c->adapter, i2c);
-
        ret = i2c_bit_add_bus(&i2c->adapter);
        if (ret) {
                DRM_INFO("Failed to register i2c %s\n", name);
@@ -194,6 +206,38 @@ out_free:
 
 }
 
+struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
+                                            struct radeon_i2c_bus_rec *rec,
+                                            const char *name)
+{
+       struct radeon_i2c_chan *i2c;
+       int ret;
+
+       i2c = kzalloc(sizeof(struct radeon_i2c_chan), GFP_KERNEL);
+       if (i2c == NULL)
+               return NULL;
+
+       i2c->rec = *rec;
+       i2c->adapter.owner = THIS_MODULE;
+       i2c->dev = dev;
+       i2c_set_adapdata(&i2c->adapter, i2c);
+       i2c->adapter.algo_data = &i2c->algo.dp;
+       i2c->algo.dp.aux_ch = radeon_dp_i2c_aux_ch;
+       i2c->algo.dp.address = 0;
+       ret = i2c_dp_aux_add_bus(&i2c->adapter);
+       if (ret) {
+               DRM_INFO("Failed to register i2c %s\n", name);
+               goto out_free;
+       }
+
+       return i2c;
+out_free:
+       kfree(i2c);
+       return NULL;
+
+}
+
+
 void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
 {
        if (!i2c)
@@ -207,3 +251,59 @@ struct drm_encoder *radeon_best_encoder(struct drm_connector *connector)
 {
        return NULL;
 }
+
+void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
+                           u8 slave_addr,
+                           u8 addr,
+                           u8 *val)
+{
+       u8 out_buf[2];
+       u8 in_buf[2];
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = in_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(&i2c_bus->adapter, msgs, 2) == 2) {
+               *val = in_buf[0];
+               DRM_DEBUG("val = 0x%02x\n", *val);
+       } else {
+               DRM_ERROR("i2c 0x%02x 0x%02x read failed\n",
+                         addr, *val);
+       }
+}
+
+void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c_bus,
+                           u8 slave_addr,
+                           u8 addr,
+                           u8 val)
+{
+       uint8_t out_buf[2];
+       struct i2c_msg msg = {
+               .addr = slave_addr,
+               .flags = 0,
+               .len = 2,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = val;
+
+       if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1)
+               DRM_ERROR("i2c 0x%02x 0x%02x write failed\n",
+                         addr, val);
+}
+
index a0fe623..9223296 100644 (file)
@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
        return radeon_irq_process(rdev);
 }
 
+/*
+ * Handle hotplug events outside the interrupt handler proper.
+ */
+static void radeon_hotplug_work_func(struct work_struct *work)
+{
+       struct radeon_device *rdev = container_of(work, struct radeon_device,
+                                                 hotplug_work);
+       struct drm_device *dev = rdev->ddev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+
+       if (mode_config->num_connector) {
+               list_for_each_entry(connector, &mode_config->connector_list, head)
+                       radeon_connector_hotplug(connector);
+       }
+       /* Just fire off a uevent and let userspace tell us what to do */
+       drm_sysfs_hotplug_event(dev);
+}
+
 void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
        unsigned i;
 
+       INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
+
        /* Disable *all* interrupts */
        rdev->irq.sw_int = false;
        for (i = 0; i < 2; i++) {
@@ -87,17 +108,25 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
 
        if (rdev->flags & RADEON_SINGLE_CRTC)
                num_crtc = 1;
-
+       spin_lock_init(&rdev->irq.sw_lock);
        r = drm_vblank_init(rdev->ddev, num_crtc);
        if (r) {
                return r;
        }
        /* enable msi */
        rdev->msi_enabled = 0;
-       if (rdev->family >= CHIP_RV380) {
+       /* MSIs don't seem to work on my rs780;
+        * not sure about rs880 or other rs780s.
+        * Needs more investigation.
+        */
+       if ((rdev->family >= CHIP_RV380) &&
+           (rdev->family != CHIP_RS780) &&
+           (rdev->family != CHIP_RS880)) {
                int ret = pci_enable_msi(rdev->pdev);
-               if (!ret)
+               if (!ret) {
                        rdev->msi_enabled = 1;
+                       DRM_INFO("radeon: using MSI.\n");
+               }
        }
        drm_irq_install(rdev->ddev);
        rdev->irq.installed = true;
@@ -114,3 +143,29 @@ void radeon_irq_kms_fini(struct radeon_device *rdev)
                        pci_disable_msi(rdev->pdev);
        }
 }
+
+void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&rdev->irq.sw_lock, irqflags);
+       if (rdev->ddev->irq_enabled && (++rdev->irq.sw_refcount == 1)) {
+               rdev->irq.sw_int = true;
+               radeon_irq_set(rdev);
+       }
+       spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
+}
+
+void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev)
+{
+       unsigned long irqflags;
+
+       spin_lock_irqsave(&rdev->irq.sw_lock, irqflags);
+       BUG_ON(rdev->ddev->irq_enabled && rdev->irq.sw_refcount <= 0);
+       if (rdev->ddev->irq_enabled && (--rdev->irq.sw_refcount == 0)) {
+               rdev->irq.sw_int = false;
+               radeon_irq_set(rdev);
+       }
+       spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
+}
+
index ba12862..f23b056 100644 (file)
 #include "radeon.h"
 #include "radeon_drm.h"
 
+int radeon_driver_unload_kms(struct drm_device *dev)
+{
+       struct radeon_device *rdev = dev->dev_private;
+
+       if (rdev == NULL)
+               return 0;
+       radeon_modeset_fini(rdev);
+       radeon_device_fini(rdev);
+       kfree(rdev);
+       dev->dev_private = NULL;
+       return 0;
+}
 
-/*
- * Driver load/unload
- */
 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
 {
        struct radeon_device *rdev;
@@ -62,31 +71,20 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
         */
        r = radeon_device_init(rdev, dev, dev->pdev, flags);
        if (r) {
-               DRM_ERROR("Fatal error while trying to initialize radeon.\n");
-               return r;
+               dev_err(&dev->pdev->dev, "Fatal error during GPU init\n");
+               goto out;
        }
        /* Again modeset_init should fail only on fatal error
         * otherwise it should provide enough functionalities
         * for shadowfb to run
         */
        r = radeon_modeset_init(rdev);
-       if (r) {
-               return r;
-       }
-       return 0;
-}
-
-int radeon_driver_unload_kms(struct drm_device *dev)
-{
-       struct radeon_device *rdev = dev->dev_private;
-
-       if (rdev == NULL)
-               return 0;
-       radeon_modeset_fini(rdev);
-       radeon_device_fini(rdev);
-       kfree(rdev);
-       dev->dev_private = NULL;
-       return 0;
+       if (r)
+               dev_err(&dev->pdev->dev, "Fatal error during modeset init\n");
+out:
+       if (r)
+               radeon_driver_unload_kms(dev);
+       return r;
 }
 
 
index 8d0b7aa..b82ede9 100644 (file)
 #include "radeon.h"
 #include "atom.h"
 
+static void radeon_overscan_setup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+
+       WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0);
+       WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0);
+       WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0);
+}
+
 static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc,
                                       struct drm_display_mode *mode,
                                       struct drm_display_mode *adjusted_mode)
@@ -292,8 +304,7 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
        uint32_t mask;
 
        if (radeon_crtc->crtc_id)
-               mask = (RADEON_CRTC2_EN |
-                       RADEON_CRTC2_DISP_DIS |
+               mask = (RADEON_CRTC2_DISP_DIS |
                        RADEON_CRTC2_VSYNC_DIS |
                        RADEON_CRTC2_HSYNC_DIS |
                        RADEON_CRTC2_DISP_REQ_EN_B);
@@ -305,7 +316,7 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
        switch (mode) {
        case DRM_MODE_DPMS_ON:
                if (radeon_crtc->crtc_id)
-                       WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~mask);
+                       WREG32_P(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_EN, ~(RADEON_CRTC2_EN | mask));
                else {
                        WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_EN, ~(RADEON_CRTC_EN |
                                                                         RADEON_CRTC_DISP_REQ_EN_B));
@@ -319,7 +330,7 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_OFF:
                drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
                if (radeon_crtc->crtc_id)
-                       WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~mask);
+                       WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~(RADEON_CRTC2_EN | mask));
                else {
                        WREG32_P(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_DISP_REQ_EN_B, ~(RADEON_CRTC_EN |
                                                                                    RADEON_CRTC_DISP_REQ_EN_B));
@@ -400,14 +411,21 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
        struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
        struct radeon_framebuffer *radeon_fb;
        struct drm_gem_object *obj;
+       struct radeon_bo *rbo;
        uint64_t base;
        uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0;
        uint32_t crtc_pitch, pitch_pixels;
        uint32_t tiling_flags;
        int format;
        uint32_t gen_cntl_reg, gen_cntl_val;
+       int r;
 
        DRM_DEBUG("\n");
+       /* no fb bound */
+       if (!crtc->fb) {
+               DRM_DEBUG("No FB bound\n");
+               return 0;
+       }
 
        radeon_fb = to_radeon_framebuffer(crtc->fb);
 
@@ -431,10 +449,22 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
                return false;
        }
 
+       /* Pin framebuffer & get tilling informations */
        obj = radeon_fb->obj;
-       if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &base)) {
+       rbo = obj->driver_private;
+       r = radeon_bo_reserve(rbo, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+       if (unlikely(r != 0)) {
+               radeon_bo_unreserve(rbo);
                return -EINVAL;
        }
+       radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
+       radeon_bo_unreserve(rbo);
+       if (tiling_flags & RADEON_TILING_MICRO)
+               DRM_ERROR("trying to scanout microtiled buffer\n");
+
        /* if scanout was in GTT this really wouldn't work */
        /* crtc offset is from display base addr not FB location */
        radeon_crtc->legacy_display_base_addr = rdev->mc.vram_location;
@@ -449,10 +479,6 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
                       (crtc->fb->bits_per_pixel * 8));
        crtc_pitch |= crtc_pitch << 16;
 
-       radeon_object_get_tiling_flags(obj->driver_private,
-                                      &tiling_flags, NULL);
-       if (tiling_flags & RADEON_TILING_MICRO)
-               DRM_ERROR("trying to scanout microtiled buffer\n");
 
        if (tiling_flags & RADEON_TILING_MACRO) {
                if (ASIC_IS_R300(rdev))
@@ -530,7 +556,12 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
 
        if (old_fb && old_fb != crtc->fb) {
                radeon_fb = to_radeon_framebuffer(old_fb);
-               radeon_gem_object_unpin(radeon_fb->obj);
+               rbo = radeon_fb->obj->driver_private;
+               r = radeon_bo_reserve(rbo, false);
+               if (unlikely(r != 0))
+                       return r;
+               radeon_bo_unpin(rbo);
+               radeon_bo_unreserve(rbo);
        }
 
        /* Bytes per pixel may have changed */
@@ -642,12 +673,8 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
                uint32_t crtc2_gen_cntl;
                uint32_t disp2_merge_cntl;
 
-               /* check to see if TV DAC is enabled for another crtc and keep it enabled */
-               if (RREG32(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_CRT2_ON)
-                       crtc2_gen_cntl = RADEON_CRTC2_CRT2_ON;
-               else
-                       crtc2_gen_cntl = 0;
-
+               /* if TV DAC is enabled for another crtc and keep it enabled */
+               crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080;
                crtc2_gen_cntl |= ((format << 8)
                                   | RADEON_CRTC2_VSYNC_DIS
                                   | RADEON_CRTC2_HSYNC_DIS
@@ -676,7 +703,8 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
                uint32_t crtc_ext_cntl;
                uint32_t disp_merge_cntl;
 
-               crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN
+               crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000;
+               crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN
                                 | (format << 8)
                                 | RADEON_CRTC_DISP_REQ_EN_B
                                 | ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
@@ -779,15 +807,17 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
                        if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
                                pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
                        if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
-                               struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
-                               struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
-                               if (lvds) {
-                                       if (lvds->use_bios_dividers) {
-                                               pll_ref_div = lvds->panel_ref_divider;
-                                               pll_fb_post_div   = (lvds->panel_fb_divider |
-                                                                    (lvds->panel_post_divider << 16));
-                                               htotal_cntl  = 0;
-                                               use_bios_divs = true;
+                               if (!rdev->is_atom_bios) {
+                                       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+                                       struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
+                                       if (lvds) {
+                                               if (lvds->use_bios_dividers) {
+                                                       pll_ref_div = lvds->panel_ref_divider;
+                                                       pll_fb_post_div   = (lvds->panel_fb_divider |
+                                                                            (lvds->panel_post_divider << 16));
+                                                       htotal_cntl  = 0;
+                                                       use_bios_divs = true;
+                                               }
                                        }
                                }
                                pll_flags |= RADEON_PLL_USE_REF_DIV;
@@ -1027,6 +1057,7 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
        radeon_crtc_set_base(crtc, x, y, old_fb);
        radeon_set_crtc_timing(crtc, adjusted_mode);
        radeon_set_pll(crtc, adjusted_mode);
+       radeon_overscan_setup(crtc, adjusted_mode);
        if (radeon_crtc->crtc_id == 0) {
                radeon_legacy_rmx_mode_set(crtc, mode, adjusted_mode);
        } else {
@@ -1042,12 +1073,29 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
 
 static void radeon_crtc_prepare(struct drm_crtc *crtc)
 {
-       radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       struct drm_device *dev = crtc->dev;
+       struct drm_crtc *crtci;
+
+       /*
+       * The hardware wedges sometimes if you reconfigure one CRTC
+       * whilst another is running (see fdo bug #24611).
+       */
+       list_for_each_entry(crtci, &dev->mode_config.crtc_list, head)
+               radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF);
 }
 
 static void radeon_crtc_commit(struct drm_crtc *crtc)
 {
-       radeon_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+       struct drm_device *dev = crtc->dev;
+       struct drm_crtc *crtci;
+
+       /*
+       * Reenable the CRTCs that should be running.
+       */
+       list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) {
+               if (crtci->enabled)
+                       radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
+       }
 }
 
 static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
index 0038212..df00515 100644 (file)
@@ -136,7 +136,14 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
        lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN;
 
        lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL);
-       if ((!rdev->is_atom_bios)) {
+       if (rdev->is_atom_bios) {
+               /* LVDS_GEN_CNTL parameters are computed in LVDSEncoderControl
+                * need to call that on resume to set up the reg properly.
+                */
+               radeon_encoder->pixel_clock = adjusted_mode->clock;
+               atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE);
+               lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
+       } else {
                struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
                if (lvds) {
                        DRM_DEBUG("bios LVDS_GEN_CNTL: 0x%x\n", lvds->lvds_gen_cntl);
@@ -147,8 +154,7 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
                                             (lvds->panel_blon_delay << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT));
                } else
                        lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
-       } else
-               lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
+       }
        lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
        lvds_gen_cntl &= ~(RADEON_LVDS_ON |
                           RADEON_LVDS_BLON |
@@ -184,9 +190,9 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
                radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
 }
 
-static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder,
-                                         struct drm_display_mode *mode,
-                                         struct drm_display_mode *adjusted_mode)
+static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder,
+                                    struct drm_display_mode *mode,
+                                    struct drm_display_mode *adjusted_mode)
 {
        struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
 
@@ -194,15 +200,22 @@ static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder,
        radeon_encoder_set_active_device(encoder);
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 
-       if (radeon_encoder->rmx_type != RMX_OFF)
-               radeon_rmx_mode_fixup(encoder, mode, adjusted_mode);
+       /* get the native mode for LVDS */
+       if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
+               struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+               int mode_id = adjusted_mode->base.id;
+               *adjusted_mode = *native_mode;
+               adjusted_mode->hdisplay = mode->hdisplay;
+               adjusted_mode->vdisplay = mode->vdisplay;
+               adjusted_mode->base.id = mode_id;
+       }
 
        return true;
 }
 
 static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = {
        .dpms = radeon_legacy_lvds_dpms,
-       .mode_fixup = radeon_legacy_lvds_mode_fixup,
+       .mode_fixup = radeon_legacy_mode_fixup,
        .prepare = radeon_legacy_lvds_prepare,
        .mode_set = radeon_legacy_lvds_mode_set,
        .commit = radeon_legacy_lvds_commit,
@@ -214,17 +227,6 @@ static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = {
        .destroy = radeon_enc_destroy,
 };
 
-static bool radeon_legacy_primary_dac_mode_fixup(struct drm_encoder *encoder,
-                                                struct drm_display_mode *mode,
-                                                struct drm_display_mode *adjusted_mode)
-{
-       /* set the active encoder to connector routing */
-       radeon_encoder_set_active_device(encoder);
-       drm_mode_set_crtcinfo(adjusted_mode, 0);
-
-       return true;
-}
-
 static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode)
 {
        struct drm_device *dev = encoder->dev;
@@ -410,7 +412,7 @@ static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_enc
 
 static const struct drm_encoder_helper_funcs radeon_legacy_primary_dac_helper_funcs = {
        .dpms = radeon_legacy_primary_dac_dpms,
-       .mode_fixup = radeon_legacy_primary_dac_mode_fixup,
+       .mode_fixup = radeon_legacy_mode_fixup,
        .prepare = radeon_legacy_primary_dac_prepare,
        .mode_set = radeon_legacy_primary_dac_mode_set,
        .commit = radeon_legacy_primary_dac_commit,
@@ -423,16 +425,6 @@ static const struct drm_encoder_funcs radeon_legacy_primary_dac_enc_funcs = {
        .destroy = radeon_enc_destroy,
 };
 
-static bool radeon_legacy_tmds_int_mode_fixup(struct drm_encoder *encoder,
-                                             struct drm_display_mode *mode,
-                                             struct drm_display_mode *adjusted_mode)
-{
-
-       drm_mode_set_crtcinfo(adjusted_mode, 0);
-
-       return true;
-}
-
 static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode)
 {
        struct drm_device *dev = encoder->dev;
@@ -584,7 +576,7 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder,
 
 static const struct drm_encoder_helper_funcs radeon_legacy_tmds_int_helper_funcs = {
        .dpms = radeon_legacy_tmds_int_dpms,
-       .mode_fixup = radeon_legacy_tmds_int_mode_fixup,
+       .mode_fixup = radeon_legacy_mode_fixup,
        .prepare = radeon_legacy_tmds_int_prepare,
        .mode_set = radeon_legacy_tmds_int_mode_set,
        .commit = radeon_legacy_tmds_int_commit,
@@ -596,17 +588,6 @@ static const struct drm_encoder_funcs radeon_legacy_tmds_int_enc_funcs = {
        .destroy = radeon_enc_destroy,
 };
 
-static bool radeon_legacy_tmds_ext_mode_fixup(struct drm_encoder *encoder,
-                                             struct drm_display_mode *mode,
-                                             struct drm_display_mode *adjusted_mode)
-{
-       /* set the active encoder to connector routing */
-       radeon_encoder_set_active_device(encoder);
-       drm_mode_set_crtcinfo(adjusted_mode, 0);
-
-       return true;
-}
-
 static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode)
 {
        struct drm_device *dev = encoder->dev;
@@ -697,6 +678,8 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder,
                        /*if (mode->clock > 165000)
                          fp2_gen_cntl |= R300_FP2_DVO_DUAL_CHANNEL_EN;*/
                }
+               if (!radeon_combios_external_tmds_setup(encoder))
+                       radeon_external_tmds_setup(encoder);
        }
 
        if (radeon_crtc->crtc_id == 0) {
@@ -724,9 +707,22 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder,
                radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
 }
 
+static void radeon_ext_tmds_enc_destroy(struct drm_encoder *encoder)
+{
+       struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+       struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
+       if (tmds) {
+               if (tmds->i2c_bus)
+                       radeon_i2c_destroy(tmds->i2c_bus);
+       }
+       kfree(radeon_encoder->enc_priv);
+       drm_encoder_cleanup(encoder);
+       kfree(radeon_encoder);
+}
+
 static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs = {
        .dpms = radeon_legacy_tmds_ext_dpms,
-       .mode_fixup = radeon_legacy_tmds_ext_mode_fixup,
+       .mode_fixup = radeon_legacy_mode_fixup,
        .prepare = radeon_legacy_tmds_ext_prepare,
        .mode_set = radeon_legacy_tmds_ext_mode_set,
        .commit = radeon_legacy_tmds_ext_commit,
@@ -735,20 +731,9 @@ static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs
 
 
 static const struct drm_encoder_funcs radeon_legacy_tmds_ext_enc_funcs = {
-       .destroy = radeon_enc_destroy,
+       .destroy = radeon_ext_tmds_enc_destroy,
 };
 
-static bool radeon_legacy_tv_dac_mode_fixup(struct drm_encoder *encoder,
-                                           struct drm_display_mode *mode,
-                                           struct drm_display_mode *adjusted_mode)
-{
-       /* set the active encoder to connector routing */
-       radeon_encoder_set_active_device(encoder);
-       drm_mode_set_crtcinfo(adjusted_mode, 0);
-
-       return true;
-}
-
 static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode)
 {
        struct drm_device *dev = encoder->dev;
@@ -1265,7 +1250,7 @@ static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder
 
 static const struct drm_encoder_helper_funcs radeon_legacy_tv_dac_helper_funcs = {
        .dpms = radeon_legacy_tv_dac_dpms,
-       .mode_fixup = radeon_legacy_tv_dac_mode_fixup,
+       .mode_fixup = radeon_legacy_mode_fixup,
        .prepare = radeon_legacy_tv_dac_prepare,
        .mode_set = radeon_legacy_tv_dac_mode_set,
        .commit = radeon_legacy_tv_dac_commit,
@@ -1302,6 +1287,29 @@ static struct radeon_encoder_int_tmds *radeon_legacy_get_tmds_info(struct radeon
        return tmds;
 }
 
+static struct radeon_encoder_ext_tmds *radeon_legacy_get_ext_tmds_info(struct radeon_encoder *encoder)
+{
+       struct drm_device *dev = encoder->base.dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_encoder_ext_tmds *tmds = NULL;
+       bool ret;
+
+       if (rdev->is_atom_bios)
+               return NULL;
+
+       tmds = kzalloc(sizeof(struct radeon_encoder_ext_tmds), GFP_KERNEL);
+
+       if (!tmds)
+               return NULL;
+
+       ret = radeon_legacy_get_ext_tmds_info_from_combios(encoder, tmds);
+
+       if (ret == false)
+               radeon_legacy_get_ext_tmds_info_from_table(encoder, tmds);
+
+       return tmds;
+}
+
 void
 radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t supported_device)
 {
@@ -1329,7 +1337,6 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
                encoder->possible_crtcs = 0x1;
        else
                encoder->possible_crtcs = 0x3;
-       encoder->possible_clones = 0;
 
        radeon_encoder->enc_priv = NULL;
 
@@ -1373,7 +1380,7 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
                drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, DRM_MODE_ENCODER_TMDS);
                drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs);
                if (!rdev->is_atom_bios)
-                       radeon_combios_get_ext_tmds_info(radeon_encoder);
+                       radeon_encoder->enc_priv = radeon_legacy_get_ext_tmds_info(radeon_encoder);
                break;
        }
 }
index ace726a..44d4b65 100644 (file)
@@ -33,6 +33,7 @@
 #include <drm_crtc.h>
 #include <drm_mode.h>
 #include <drm_edid.h>
+#include <drm_dp_helper.h>
 #include <linux/i2c.h>
 #include <linux/i2c-id.h>
 #include <linux/i2c-algo-bit.h>
@@ -89,24 +90,45 @@ enum radeon_tv_std {
        TV_STD_PAL_CN,
 };
 
+/* radeon gpio-based i2c
+ * 1. "mask" reg and bits
+ *    grabs the gpio pins for software use
+ *    0=not held  1=held
+ * 2. "a" reg and bits
+ *    output pin value
+ *    0=low 1=high
+ * 3. "en" reg and bits
+ *    sets the pin direction
+ *    0=input 1=output
+ * 4. "y" reg and bits
+ *    input pin value
+ *    0=low 1=high
+ */
 struct radeon_i2c_bus_rec {
        bool valid;
+       /* id used by atom */
+       uint8_t i2c_id;
+       /* can be used with hw i2c engine */
+       bool hw_capable;
+       /* uses multi-media i2c engine */
+       bool mm_i2c;
+       /* regs and bits */
        uint32_t mask_clk_reg;
        uint32_t mask_data_reg;
        uint32_t a_clk_reg;
        uint32_t a_data_reg;
-       uint32_t put_clk_reg;
-       uint32_t put_data_reg;
-       uint32_t get_clk_reg;
-       uint32_t get_data_reg;
+       uint32_t en_clk_reg;
+       uint32_t en_data_reg;
+       uint32_t y_clk_reg;
+       uint32_t y_data_reg;
        uint32_t mask_clk_mask;
        uint32_t mask_data_mask;
-       uint32_t put_clk_mask;
-       uint32_t put_data_mask;
-       uint32_t get_clk_mask;
-       uint32_t get_data_mask;
        uint32_t a_clk_mask;
        uint32_t a_data_mask;
+       uint32_t en_clk_mask;
+       uint32_t en_data_mask;
+       uint32_t y_clk_mask;
+       uint32_t y_data_mask;
 };
 
 struct radeon_tmds_pll {
@@ -150,9 +172,12 @@ struct radeon_pll {
 };
 
 struct radeon_i2c_chan {
-       struct drm_device *dev;
        struct i2c_adapter adapter;
-       struct i2c_algo_bit_data algo;
+       struct drm_device *dev;
+       union {
+               struct i2c_algo_dp_aux_data dp;
+               struct i2c_algo_bit_data bit;
+       } algo;
        struct radeon_i2c_bus_rec rec;
 };
 
@@ -170,6 +195,11 @@ enum radeon_connector_table {
        CT_EMAC,
 };
 
+enum radeon_dvo_chip {
+       DVO_SIL164,
+       DVO_SIL1178,
+};
+
 struct radeon_mode_info {
        struct atom_context *atom_context;
        struct card_info *atom_card_info;
@@ -261,6 +291,13 @@ struct radeon_encoder_int_tmds {
        struct radeon_tmds_pll tmds_pll[4];
 };
 
+struct radeon_encoder_ext_tmds {
+       /* tmds over dvo */
+       struct radeon_i2c_chan *i2c_bus;
+       uint8_t slave_addr;
+       enum radeon_dvo_chip dvo_chip;
+};
+
 /* spread spectrum */
 struct radeon_atom_ss {
        uint16_t percentage;
@@ -302,6 +339,35 @@ struct radeon_encoder {
 struct radeon_connector_atom_dig {
        uint32_t igp_lane_info;
        bool linkb;
+       /* displayport */
+       struct radeon_i2c_chan *dp_i2c_bus;
+       u8 dpcd[8];
+       u8 dp_sink_type;
+       int dp_clock;
+       int dp_lane_count;
+};
+
+struct radeon_gpio_rec {
+       bool valid;
+       u8 id;
+       u32 reg;
+       u32 mask;
+};
+
+enum radeon_hpd_id {
+       RADEON_HPD_NONE = 0,
+       RADEON_HPD_1,
+       RADEON_HPD_2,
+       RADEON_HPD_3,
+       RADEON_HPD_4,
+       RADEON_HPD_5,
+       RADEON_HPD_6,
+};
+
+struct radeon_hpd {
+       enum radeon_hpd_id hpd;
+       u8 plugged_state;
+       struct radeon_gpio_rec gpio;
 };
 
 struct radeon_connector {
@@ -318,6 +384,7 @@ struct radeon_connector {
        void *con_priv;
        bool dac_load_detect;
        uint16_t connector_object_id;
+       struct radeon_hpd hpd;
 };
 
 struct radeon_framebuffer {
@@ -325,10 +392,37 @@ struct radeon_framebuffer {
        struct drm_gem_object *obj;
 };
 
+extern void radeon_connector_hotplug(struct drm_connector *connector);
+extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
+extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
+                                      struct drm_display_mode *mode);
+extern void radeon_dp_set_link_config(struct drm_connector *connector,
+                                     struct drm_display_mode *mode);
+extern void dp_link_train(struct drm_encoder *encoder,
+                         struct drm_connector *connector);
+extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
+extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
+extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
+                                          int action, uint8_t lane_num,
+                                          uint8_t lane_set);
+extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+                               uint8_t write_byte, uint8_t *read_byte);
+
+extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
+                                                   struct radeon_i2c_bus_rec *rec,
+                                                   const char *name);
 extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
                                                 struct radeon_i2c_bus_rec *rec,
                                                 const char *name);
 extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c);
+extern void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
+                                  u8 slave_addr,
+                                  u8 addr,
+                                  u8 *val);
+extern void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c,
+                                  u8 slave_addr,
+                                  u8 addr,
+                                  u8 val);
 extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
 extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
 
@@ -343,12 +437,24 @@ extern void radeon_compute_pll(struct radeon_pll *pll,
                               uint32_t *post_div_p,
                               int flags);
 
+extern void radeon_compute_pll_avivo(struct radeon_pll *pll,
+                                    uint64_t freq,
+                                    uint32_t *dot_clock_p,
+                                    uint32_t *fb_div_p,
+                                    uint32_t *frac_fb_div_p,
+                                    uint32_t *ref_div_p,
+                                    uint32_t *post_div_p,
+                                    int flags);
+
+extern void radeon_setup_encoder_clones(struct drm_device *dev);
+
 struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index);
 struct drm_encoder *radeon_encoder_legacy_primary_dac_add(struct drm_device *dev, int bios_index, int with_tv);
 struct drm_encoder *radeon_encoder_legacy_tv_dac_add(struct drm_device *dev, int bios_index, int with_tv);
 struct drm_encoder *radeon_encoder_legacy_tmds_int_add(struct drm_device *dev, int bios_index);
 struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index);
 extern void atombios_external_tmds_setup(struct drm_encoder *encoder, int action);
+extern void atombios_digital_setup(struct drm_encoder *encoder, int action);
 extern int atombios_get_encoder_mode(struct drm_encoder *encoder);
 extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
 
@@ -378,12 +484,16 @@ extern bool radeon_atom_get_clock_info(struct drm_device *dev);
 extern bool radeon_combios_get_clock_info(struct drm_device *dev);
 extern struct radeon_encoder_atom_dig *
 radeon_atombios_get_lvds_info(struct radeon_encoder *encoder);
-bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
-                                  struct radeon_encoder_int_tmds *tmds);
-bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
-                                          struct radeon_encoder_int_tmds *tmds);
-bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
-                                           struct radeon_encoder_int_tmds *tmds);
+extern bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
+                                         struct radeon_encoder_int_tmds *tmds);
+extern bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
+                                                    struct radeon_encoder_int_tmds *tmds);
+extern bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
+                                                  struct radeon_encoder_int_tmds *tmds);
+extern bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder,
+                                                        struct radeon_encoder_ext_tmds *tmds);
+extern bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
+                                                      struct radeon_encoder_ext_tmds *tmds);
 extern struct radeon_encoder_primary_dac *
 radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder);
 extern struct radeon_encoder_tv_dac *
@@ -395,6 +505,8 @@ extern struct radeon_encoder_tv_dac *
 radeon_combios_get_tv_dac_info(struct radeon_encoder *encoder);
 extern struct radeon_encoder_primary_dac *
 radeon_combios_get_primary_dac_info(struct radeon_encoder *encoder);
+extern bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder);
+extern void radeon_external_tmds_setup(struct drm_encoder *encoder);
 extern void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock);
 extern void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev);
 extern void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock);
@@ -426,16 +538,13 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
                               struct radeon_crtc *radeon_crtc);
 void radeon_legacy_init_crtc(struct drm_device *dev,
                             struct radeon_crtc *radeon_crtc);
-void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state);
+extern void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state);
 
 void radeon_get_clock_info(struct drm_device *dev);
 
 extern bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev);
 extern bool radeon_get_atom_connector_info_from_supported_devices_table(struct drm_device *dev);
 
-void radeon_rmx_mode_fixup(struct drm_encoder *encoder,
-                          struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode);
 void radeon_enc_destroy(struct drm_encoder *encoder);
 void radeon_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj);
 void radeon_combios_asic_init(struct drm_device *dev);
index 1f056da..2040937 100644 (file)
 #include "radeon_drm.h"
 #include "radeon.h"
 
-struct radeon_object {
-       struct ttm_buffer_object        tobj;
-       struct list_head                list;
-       struct radeon_device            *rdev;
-       struct drm_gem_object           *gobj;
-       struct ttm_bo_kmap_obj          kmap;
-       unsigned                        pin_count;
-       uint64_t                        gpu_addr;
-       void                            *kptr;
-       bool                            is_iomem;
-       uint32_t                        tiling_flags;
-       uint32_t                        pitch;
-       int                             surface_reg;
-};
 
 int radeon_ttm_init(struct radeon_device *rdev);
 void radeon_ttm_fini(struct radeon_device *rdev);
+static void radeon_bo_clear_surface_reg(struct radeon_bo *bo);
 
 /*
  * To exclude mutual BO access we rely on bo_reserve exclusion, as all
  * function are calling it.
  */
 
-static int radeon_object_reserve(struct radeon_object *robj, bool interruptible)
+static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)
 {
-       return ttm_bo_reserve(&robj->tobj, interruptible, false, false, 0);
-}
-
-static void radeon_object_unreserve(struct radeon_object *robj)
-{
-       ttm_bo_unreserve(&robj->tobj);
-}
-
-static void radeon_ttm_object_object_destroy(struct ttm_buffer_object *tobj)
-{
-       struct radeon_object *robj;
+       struct radeon_bo *bo;
 
-       robj = container_of(tobj, struct radeon_object, tobj);
-       list_del_init(&robj->list);
-       radeon_object_clear_surface_reg(robj);
-       kfree(robj);
+       bo = container_of(tbo, struct radeon_bo, tbo);
+       mutex_lock(&bo->rdev->gem.mutex);
+       list_del_init(&bo->list);
+       mutex_unlock(&bo->rdev->gem.mutex);
+       radeon_bo_clear_surface_reg(bo);
+       kfree(bo);
 }
 
-static inline void radeon_object_gpu_addr(struct radeon_object *robj)
+static inline u32 radeon_ttm_flags_from_domain(u32 domain)
 {
-       /* Default gpu address */
-       robj->gpu_addr = 0xFFFFFFFFFFFFFFFFULL;
-       if (robj->tobj.mem.mm_node == NULL) {
-               return;
-       }
-       robj->gpu_addr = ((u64)robj->tobj.mem.mm_node->start) << PAGE_SHIFT;
-       switch (robj->tobj.mem.mem_type) {
-       case TTM_PL_VRAM:
-               robj->gpu_addr += (u64)robj->rdev->mc.vram_location;
-               break;
-       case TTM_PL_TT:
-               robj->gpu_addr += (u64)robj->rdev->mc.gtt_location;
-               break;
-       default:
-               DRM_ERROR("Unknown placement %d\n", robj->tobj.mem.mem_type);
-               robj->gpu_addr = 0xFFFFFFFFFFFFFFFFULL;
-               return;
-       }
-}
+       u32 flags = 0;
 
-static inline uint32_t radeon_object_flags_from_domain(uint32_t domain)
-{
-       uint32_t flags = 0;
        if (domain & RADEON_GEM_DOMAIN_VRAM) {
                flags |= TTM_PL_FLAG_VRAM | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
        }
@@ -117,17 +75,32 @@ static inline uint32_t radeon_object_flags_from_domain(uint32_t domain)
        return flags;
 }
 
-int radeon_object_create(struct radeon_device *rdev,
-                        struct drm_gem_object *gobj,
-                        unsigned long size,
-                        bool kernel,
-                        uint32_t domain,
-                        bool interruptible,
-                        struct radeon_object **robj_ptr)
+void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
+{
+       u32 c = 0;
+
+       rbo->placement.fpfn = 0;
+       rbo->placement.lpfn = 0;
+       rbo->placement.placement = rbo->placements;
+       rbo->placement.busy_placement = rbo->placements;
+       if (domain & RADEON_GEM_DOMAIN_VRAM)
+               rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
+                                       TTM_PL_FLAG_VRAM;
+       if (domain & RADEON_GEM_DOMAIN_GTT)
+               rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+       if (domain & RADEON_GEM_DOMAIN_CPU)
+               rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+       rbo->placement.num_placement = c;
+       rbo->placement.num_busy_placement = c;
+}
+
+int radeon_bo_create(struct radeon_device *rdev, struct drm_gem_object *gobj,
+                       unsigned long size, bool kernel, u32 domain,
+                       struct radeon_bo **bo_ptr)
 {
-       struct radeon_object *robj;
+       struct radeon_bo *bo;
        enum ttm_bo_type type;
-       uint32_t flags;
+       u32 flags;
        int r;
 
        if (unlikely(rdev->mman.bdev.dev_mapping == NULL)) {
@@ -138,206 +111,125 @@ int radeon_object_create(struct radeon_device *rdev,
        } else {
                type = ttm_bo_type_device;
        }
-       *robj_ptr = NULL;
-       robj = kzalloc(sizeof(struct radeon_object), GFP_KERNEL);
-       if (robj == NULL) {
+       *bo_ptr = NULL;
+       bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
+       if (bo == NULL)
                return -ENOMEM;
-       }
-       robj->rdev = rdev;
-       robj->gobj = gobj;
-       robj->surface_reg = -1;
-       INIT_LIST_HEAD(&robj->list);
-
-       flags = radeon_object_flags_from_domain(domain);
-       r = ttm_buffer_object_init(&rdev->mman.bdev, &robj->tobj, size, type, flags,
-                                  0, 0, false, NULL, size,
-                                  &radeon_ttm_object_object_destroy);
+       bo->rdev = rdev;
+       bo->gobj = gobj;
+       bo->surface_reg = -1;
+       INIT_LIST_HEAD(&bo->list);
+
+       flags = radeon_ttm_flags_from_domain(domain);
+       /* Kernel allocation are uninterruptible */
+       r = ttm_buffer_object_init(&rdev->mman.bdev, &bo->tbo, size, type,
+                                       flags, 0, 0, !kernel, NULL, size,
+                                       &radeon_ttm_bo_destroy);
        if (unlikely(r != 0)) {
-               /* ttm call radeon_ttm_object_object_destroy if error happen */
-               DRM_ERROR("Failed to allocate TTM object (%ld, 0x%08X, %u)\n",
-                         size, flags, 0);
+               if (r != -ERESTARTSYS)
+                       dev_err(rdev->dev,
+                               "object_init failed for (%ld, 0x%08X)\n",
+                               size, flags);
                return r;
        }
-       *robj_ptr = robj;
+       *bo_ptr = bo;
        if (gobj) {
-               list_add_tail(&robj->list, &rdev->gem.objects);
+               mutex_lock(&bo->rdev->gem.mutex);
+               list_add_tail(&bo->list, &rdev->gem.objects);
+               mutex_unlock(&bo->rdev->gem.mutex);
        }
        return 0;
 }
 
-int radeon_object_kmap(struct radeon_object *robj, void **ptr)
+int radeon_bo_kmap(struct radeon_bo *bo, void **ptr)
 {
+       bool is_iomem;
        int r;
 
-       spin_lock(&robj->tobj.lock);
-       if (robj->kptr) {
+       if (bo->kptr) {
                if (ptr) {
-                       *ptr = robj->kptr;
+                       *ptr = bo->kptr;
                }
-               spin_unlock(&robj->tobj.lock);
                return 0;
        }
-       spin_unlock(&robj->tobj.lock);
-       r = ttm_bo_kmap(&robj->tobj, 0, robj->tobj.num_pages, &robj->kmap);
+       r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap);
        if (r) {
                return r;
        }
-       spin_lock(&robj->tobj.lock);
-       robj->kptr = ttm_kmap_obj_virtual(&robj->kmap, &robj->is_iomem);
-       spin_unlock(&robj->tobj.lock);
+       bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem);
        if (ptr) {
-               *ptr = robj->kptr;
+               *ptr = bo->kptr;
        }
-       radeon_object_check_tiling(robj, 0, 0);
+       radeon_bo_check_tiling(bo, 0, 0);
        return 0;
 }
 
-void radeon_object_kunmap(struct radeon_object *robj)
+void radeon_bo_kunmap(struct radeon_bo *bo)
 {
-       spin_lock(&robj->tobj.lock);
-       if (robj->kptr == NULL) {
-               spin_unlock(&robj->tobj.lock);
+       if (bo->kptr == NULL)
                return;
-       }
-       robj->kptr = NULL;
-       spin_unlock(&robj->tobj.lock);
-       radeon_object_check_tiling(robj, 0, 0);
-       ttm_bo_kunmap(&robj->kmap);
+       bo->kptr = NULL;
+       radeon_bo_check_tiling(bo, 0, 0);
+       ttm_bo_kunmap(&bo->kmap);
 }
 
-void radeon_object_unref(struct radeon_object **robj)
+void radeon_bo_unref(struct radeon_bo **bo)
 {
-       struct ttm_buffer_object *tobj;
+       struct ttm_buffer_object *tbo;
 
-       if ((*robj) == NULL) {
+       if ((*bo) == NULL)
                return;
-       }
-       tobj = &((*robj)->tobj);
-       ttm_bo_unref(&tobj);
-       if (tobj == NULL) {
-               *robj = NULL;
-       }
-}
-
-int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset)
-{
-       *offset = robj->tobj.addr_space_offset;
-       return 0;
+       tbo = &((*bo)->tbo);
+       ttm_bo_unref(&tbo);
+       if (tbo == NULL)
+               *bo = NULL;
 }
 
-int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
-                     uint64_t *gpu_addr)
+int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
 {
-       uint32_t flags;
-       uint32_t tmp;
-       int r;
+       int r, i;
 
-       flags = radeon_object_flags_from_domain(domain);
-       spin_lock(&robj->tobj.lock);
-       if (robj->pin_count) {
-               robj->pin_count++;
-               if (gpu_addr != NULL) {
-                       *gpu_addr = robj->gpu_addr;
-               }
-               spin_unlock(&robj->tobj.lock);
+       radeon_ttm_placement_from_domain(bo, domain);
+       if (bo->pin_count) {
+               bo->pin_count++;
+               if (gpu_addr)
+                       *gpu_addr = radeon_bo_gpu_offset(bo);
                return 0;
        }
-       spin_unlock(&robj->tobj.lock);
-       r = radeon_object_reserve(robj, false);
-       if (unlikely(r != 0)) {
-               DRM_ERROR("radeon: failed to reserve object for pinning it.\n");
-               return r;
-       }
-       tmp = robj->tobj.mem.placement;
-       ttm_flag_masked(&tmp, flags, TTM_PL_MASK_MEM);
-       robj->tobj.proposed_placement = tmp | TTM_PL_FLAG_NO_EVICT | TTM_PL_MASK_CACHING;
-       r = ttm_buffer_object_validate(&robj->tobj,
-                                      robj->tobj.proposed_placement,
-                                      false, false);
-       radeon_object_gpu_addr(robj);
-       if (gpu_addr != NULL) {
-               *gpu_addr = robj->gpu_addr;
-       }
-       robj->pin_count = 1;
-       if (unlikely(r != 0)) {
-               DRM_ERROR("radeon: failed to pin object.\n");
-       }
-       radeon_object_unreserve(robj);
+       radeon_ttm_placement_from_domain(bo, domain);
+       for (i = 0; i < bo->placement.num_placement; i++)
+               bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+       r = ttm_buffer_object_validate(&bo->tbo, &bo->placement, false, false);
+       if (likely(r == 0)) {
+               bo->pin_count = 1;
+               if (gpu_addr != NULL)
+                       *gpu_addr = radeon_bo_gpu_offset(bo);
+       }
+       if (unlikely(r != 0))
+               dev_err(bo->rdev->dev, "%p pin failed\n", bo);
        return r;
 }
 
-void radeon_object_unpin(struct radeon_object *robj)
+int radeon_bo_unpin(struct radeon_bo *bo)
 {
-       uint32_t flags;
-       int r;
+       int r, i;
 
-       spin_lock(&robj->tobj.lock);
-       if (!robj->pin_count) {
-               spin_unlock(&robj->tobj.lock);
-               printk(KERN_WARNING "Unpin not necessary for %p !\n", robj);
-               return;
-       }
-       robj->pin_count--;
-       if (robj->pin_count) {
-               spin_unlock(&robj->tobj.lock);
-               return;
-       }
-       spin_unlock(&robj->tobj.lock);
-       r = radeon_object_reserve(robj, false);
-       if (unlikely(r != 0)) {
-               DRM_ERROR("radeon: failed to reserve object for unpinning it.\n");
-               return;
-       }
-       flags = robj->tobj.mem.placement;
-       robj->tobj.proposed_placement = flags & ~TTM_PL_FLAG_NO_EVICT;
-       r = ttm_buffer_object_validate(&robj->tobj,
-                                      robj->tobj.proposed_placement,
-                                      false, false);
-       if (unlikely(r != 0)) {
-               DRM_ERROR("radeon: failed to unpin buffer.\n");
-       }
-       radeon_object_unreserve(robj);
-}
-
-int radeon_object_wait(struct radeon_object *robj)
-{
-       int r = 0;
-
-       /* FIXME: should use block reservation instead */
-       r = radeon_object_reserve(robj, true);
-       if (unlikely(r != 0)) {
-               DRM_ERROR("radeon: failed to reserve object for waiting.\n");
-               return r;
-       }
-       spin_lock(&robj->tobj.lock);
-       if (robj->tobj.sync_obj) {
-               r = ttm_bo_wait(&robj->tobj, true, true, false);
-       }
-       spin_unlock(&robj->tobj.lock);
-       radeon_object_unreserve(robj);
-       return r;
-}
-
-int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement)
-{
-       int r = 0;
-
-       r = radeon_object_reserve(robj, true);
-       if (unlikely(r != 0)) {
-               DRM_ERROR("radeon: failed to reserve object for waiting.\n");
-               return r;
-       }
-       spin_lock(&robj->tobj.lock);
-       *cur_placement = robj->tobj.mem.mem_type;
-       if (robj->tobj.sync_obj) {
-               r = ttm_bo_wait(&robj->tobj, true, true, true);
+       if (!bo->pin_count) {
+               dev_warn(bo->rdev->dev, "%p unpin not necessary\n", bo);
+               return 0;
        }
-       spin_unlock(&robj->tobj.lock);
-       radeon_object_unreserve(robj);
+       bo->pin_count--;
+       if (bo->pin_count)
+               return 0;
+       for (i = 0; i < bo->placement.num_placement; i++)
+               bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+       r = ttm_buffer_object_validate(&bo->tbo, &bo->placement, false, false);
+       if (unlikely(r != 0))
+               dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo);
        return r;
 }
 
-int radeon_object_evict_vram(struct radeon_device *rdev)
+int radeon_bo_evict_vram(struct radeon_device *rdev)
 {
        if (rdev->flags & RADEON_IS_IGP) {
                /* Useless to evict on IGP chips */
@@ -346,30 +238,32 @@ int radeon_object_evict_vram(struct radeon_device *rdev)
        return ttm_bo_evict_mm(&rdev->mman.bdev, TTM_PL_VRAM);
 }
 
-void radeon_object_force_delete(struct radeon_device *rdev)
+void radeon_bo_force_delete(struct radeon_device *rdev)
 {
-       struct radeon_object *robj, *n;
+       struct radeon_bo *bo, *n;
        struct drm_gem_object *gobj;
 
        if (list_empty(&rdev->gem.objects)) {
                return;
        }
-       DRM_ERROR("Userspace still has active objects !\n");
-       list_for_each_entry_safe(robj, n, &rdev->gem.objects, list) {
+       dev_err(rdev->dev, "Userspace still has active objects !\n");
+       list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
                mutex_lock(&rdev->ddev->struct_mutex);
-               gobj = robj->gobj;
-               DRM_ERROR("Force free for (%p,%p,%lu,%lu)\n",
-                         gobj, robj, (unsigned long)gobj->size,
-                         *((unsigned long *)&gobj->refcount));
-               list_del_init(&robj->list);
-               radeon_object_unref(&robj);
+               gobj = bo->gobj;
+               dev_err(rdev->dev, "%p %p %lu %lu force free\n",
+                       gobj, bo, (unsigned long)gobj->size,
+                       *((unsigned long *)&gobj->refcount));
+               mutex_lock(&bo->rdev->gem.mutex);
+               list_del_init(&bo->list);
+               mutex_unlock(&bo->rdev->gem.mutex);
+               radeon_bo_unref(&bo);
                gobj->driver_private = NULL;
                drm_gem_object_unreference(gobj);
                mutex_unlock(&rdev->ddev->struct_mutex);
        }
 }
 
-int radeon_object_init(struct radeon_device *rdev)
+int radeon_bo_init(struct radeon_device *rdev)
 {
        /* Add an MTRR for the VRAM */
        rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size,
@@ -382,13 +276,13 @@ int radeon_object_init(struct radeon_device *rdev)
        return radeon_ttm_init(rdev);
 }
 
-void radeon_object_fini(struct radeon_device *rdev)
+void radeon_bo_fini(struct radeon_device *rdev)
 {
        radeon_ttm_fini(rdev);
 }
 
-void radeon_object_list_add_object(struct radeon_object_list *lobj,
-                                  struct list_head *head)
+void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
+                               struct list_head *head)
 {
        if (lobj->wdomain) {
                list_add(&lobj->list, head);
@@ -397,72 +291,63 @@ void radeon_object_list_add_object(struct radeon_object_list *lobj,
        }
 }
 
-int radeon_object_list_reserve(struct list_head *head)
+int radeon_bo_list_reserve(struct list_head *head)
 {
-       struct radeon_object_list *lobj;
+       struct radeon_bo_list *lobj;
        int r;
 
        list_for_each_entry(lobj, head, list){
-               if (!lobj->robj->pin_count) {
-                       r = radeon_object_reserve(lobj->robj, true);
-                       if (unlikely(r != 0)) {
-                               DRM_ERROR("radeon: failed to reserve object.\n");
-                               return r;
-                       }
-               } else {
-               }
+               r = radeon_bo_reserve(lobj->bo, false);
+               if (unlikely(r != 0))
+                       return r;
        }
        return 0;
 }
 
-void radeon_object_list_unreserve(struct list_head *head)
+void radeon_bo_list_unreserve(struct list_head *head)
 {
-       struct radeon_object_list *lobj;
+       struct radeon_bo_list *lobj;
 
        list_for_each_entry(lobj, head, list) {
-               if (!lobj->robj->pin_count) {
-                       radeon_object_unreserve(lobj->robj);
-               }
+               /* only unreserve object we successfully reserved */
+               if (radeon_bo_is_reserved(lobj->bo))
+                       radeon_bo_unreserve(lobj->bo);
        }
 }
 
-int radeon_object_list_validate(struct list_head *head, void *fence)
+int radeon_bo_list_validate(struct list_head *head, void *fence)
 {
-       struct radeon_object_list *lobj;
-       struct radeon_object *robj;
+       struct radeon_bo_list *lobj;
+       struct radeon_bo *bo;
        struct radeon_fence *old_fence = NULL;
        int r;
 
-       r = radeon_object_list_reserve(head);
+       r = radeon_bo_list_reserve(head);
        if (unlikely(r != 0)) {
-               radeon_object_list_unreserve(head);
                return r;
        }
        list_for_each_entry(lobj, head, list) {
-               robj = lobj->robj;
-               if (!robj->pin_count) {
+               bo = lobj->bo;
+               if (!bo->pin_count) {
                        if (lobj->wdomain) {
-                               robj->tobj.proposed_placement =
-                                       radeon_object_flags_from_domain(lobj->wdomain);
+                               radeon_ttm_placement_from_domain(bo,
+                                                               lobj->wdomain);
                        } else {
-                               robj->tobj.proposed_placement =
-                                       radeon_object_flags_from_domain(lobj->rdomain);
+                               radeon_ttm_placement_from_domain(bo,
+                                                               lobj->rdomain);
                        }
-                       r = ttm_buffer_object_validate(&robj->tobj,
-                                                      robj->tobj.proposed_placement,
-                                                      true, false);
-                       if (unlikely(r)) {
-                               DRM_ERROR("radeon: failed to validate.\n");
+                       r = ttm_buffer_object_validate(&bo->tbo,
+                                               &bo->placement,
+                                               true, false);
+                       if (unlikely(r))
                                return r;
-                       }
-                       radeon_object_gpu_addr(robj);
                }
-               lobj->gpu_offset = robj->gpu_addr;
-               lobj->tiling_flags = robj->tiling_flags;
+               lobj->gpu_offset = radeon_bo_gpu_offset(bo);
+               lobj->tiling_flags = bo->tiling_flags;
                if (fence) {
-                       old_fence = (struct radeon_fence *)robj->tobj.sync_obj;
-                       robj->tobj.sync_obj = radeon_fence_ref(fence);
-                       robj->tobj.sync_obj_arg = NULL;
+                       old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
+                       bo->tbo.sync_obj = radeon_fence_ref(fence);
+                       bo->tbo.sync_obj_arg = NULL;
                }
                if (old_fence) {
                        radeon_fence_unref(&old_fence);
@@ -471,51 +356,44 @@ int radeon_object_list_validate(struct list_head *head, void *fence)
        return 0;
 }
 
-void radeon_object_list_unvalidate(struct list_head *head)
+void radeon_bo_list_unvalidate(struct list_head *head, void *fence)
 {
-       struct radeon_object_list *lobj;
-       struct radeon_fence *old_fence = NULL;
+       struct radeon_bo_list *lobj;
+       struct radeon_fence *old_fence;
 
-       list_for_each_entry(lobj, head, list) {
-               old_fence = (struct radeon_fence *)lobj->robj->tobj.sync_obj;
-               lobj->robj->tobj.sync_obj = NULL;
-               if (old_fence) {
-                       radeon_fence_unref(&old_fence);
+       if (fence)
+               list_for_each_entry(lobj, head, list) {
+                       old_fence = to_radeon_fence(lobj->bo->tbo.sync_obj);
+                       if (old_fence == fence) {
+                               lobj->bo->tbo.sync_obj = NULL;
+                               radeon_fence_unref(&old_fence);
+                       }
                }
-       }
-       radeon_object_list_unreserve(head);
-}
-
-void radeon_object_list_clean(struct list_head *head)
-{
-       radeon_object_list_unreserve(head);
+       radeon_bo_list_unreserve(head);
 }
 
-int radeon_object_fbdev_mmap(struct radeon_object *robj,
+int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
                             struct vm_area_struct *vma)
 {
-       return ttm_fbdev_mmap(vma, &robj->tobj);
+       return ttm_fbdev_mmap(vma, &bo->tbo);
 }
 
-unsigned long radeon_object_size(struct radeon_object *robj)
+int radeon_bo_get_surface_reg(struct radeon_bo *bo)
 {
-       return robj->tobj.num_pages << PAGE_SHIFT;
-}
-
-int radeon_object_get_surface_reg(struct radeon_object *robj)
-{
-       struct radeon_device *rdev = robj->rdev;
+       struct radeon_device *rdev = bo->rdev;
        struct radeon_surface_reg *reg;
-       struct radeon_object *old_object;
+       struct radeon_bo *old_object;
        int steal;
        int i;
 
-       if (!robj->tiling_flags)
+       BUG_ON(!atomic_read(&bo->tbo.reserved));
+
+       if (!bo->tiling_flags)
                return 0;
 
-       if (robj->surface_reg >= 0) {
-               reg = &rdev->surface_regs[robj->surface_reg];
-               i = robj->surface_reg;
+       if (bo->surface_reg >= 0) {
+               reg = &rdev->surface_regs[bo->surface_reg];
+               i = bo->surface_reg;
                goto out;
        }
 
@@ -523,10 +401,10 @@ int radeon_object_get_surface_reg(struct radeon_object *robj)
        for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) {
 
                reg = &rdev->surface_regs[i];
-               if (!reg->robj)
+               if (!reg->bo)
                        break;
 
-               old_object = reg->robj;
+               old_object = reg->bo;
                if (old_object->pin_count == 0)
                        steal = i;
        }
@@ -537,91 +415,101 @@ int radeon_object_get_surface_reg(struct radeon_object *robj)
                        return -ENOMEM;
                /* find someone with a surface reg and nuke their BO */
                reg = &rdev->surface_regs[steal];
-               old_object = reg->robj;
+               old_object = reg->bo;
                /* blow away the mapping */
                DRM_DEBUG("stealing surface reg %d from %p\n", steal, old_object);
-               ttm_bo_unmap_virtual(&old_object->tobj);
+               ttm_bo_unmap_virtual(&old_object->tbo);
                old_object->surface_reg = -1;
                i = steal;
        }
 
-       robj->surface_reg = i;
-       reg->robj = robj;
+       bo->surface_reg = i;
+       reg->bo = bo;
 
 out:
-       radeon_set_surface_reg(rdev, i, robj->tiling_flags, robj->pitch,
-                              robj->tobj.mem.mm_node->start << PAGE_SHIFT,
-                              robj->tobj.num_pages << PAGE_SHIFT);
+       radeon_set_surface_reg(rdev, i, bo->tiling_flags, bo->pitch,
+                              bo->tbo.mem.mm_node->start << PAGE_SHIFT,
+                              bo->tbo.num_pages << PAGE_SHIFT);
        return 0;
 }
 
-void radeon_object_clear_surface_reg(struct radeon_object *robj)
+static void radeon_bo_clear_surface_reg(struct radeon_bo *bo)
 {
-       struct radeon_device *rdev = robj->rdev;
+       struct radeon_device *rdev = bo->rdev;
        struct radeon_surface_reg *reg;
 
-       if (robj->surface_reg == -1)
+       if (bo->surface_reg == -1)
                return;
 
-       reg = &rdev->surface_regs[robj->surface_reg];
-       radeon_clear_surface_reg(rdev, robj->surface_reg);
+       reg = &rdev->surface_regs[bo->surface_reg];
+       radeon_clear_surface_reg(rdev, bo->surface_reg);
 
-       reg->robj = NULL;
-       robj->surface_reg = -1;
+       reg->bo = NULL;
+       bo->surface_reg = -1;
 }
 
-void radeon_object_set_tiling_flags(struct radeon_object *robj,
-                                   uint32_t tiling_flags, uint32_t pitch)
+int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
+                               uint32_t tiling_flags, uint32_t pitch)
 {
-       robj->tiling_flags = tiling_flags;
-       robj->pitch = pitch;
+       int r;
+
+       r = radeon_bo_reserve(bo, false);
+       if (unlikely(r != 0))
+               return r;
+       bo->tiling_flags = tiling_flags;
+       bo->pitch = pitch;
+       radeon_bo_unreserve(bo);
+       return 0;
 }
 
-void radeon_object_get_tiling_flags(struct radeon_object *robj,
-                                   uint32_t *tiling_flags,
-                                   uint32_t *pitch)
+void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
+                               uint32_t *tiling_flags,
+                               uint32_t *pitch)
 {
+       BUG_ON(!atomic_read(&bo->tbo.reserved));
        if (tiling_flags)
-               *tiling_flags = robj->tiling_flags;
+               *tiling_flags = bo->tiling_flags;
        if (pitch)
-               *pitch = robj->pitch;
+               *pitch = bo->pitch;
 }
 
-int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved,
-                              bool force_drop)
+int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
+                               bool force_drop)
 {
-       if (!(robj->tiling_flags & RADEON_TILING_SURFACE))
+       BUG_ON(!atomic_read(&bo->tbo.reserved));
+
+       if (!(bo->tiling_flags & RADEON_TILING_SURFACE))
                return 0;
 
        if (force_drop) {
-               radeon_object_clear_surface_reg(robj);
+               radeon_bo_clear_surface_reg(bo);
                return 0;
        }
 
-       if (robj->tobj.mem.mem_type != TTM_PL_VRAM) {
+       if (bo->tbo.mem.mem_type != TTM_PL_VRAM) {
                if (!has_moved)
                        return 0;
 
-               if (robj->surface_reg >= 0)
-                       radeon_object_clear_surface_reg(robj);
+               if (bo->surface_reg >= 0)
+                       radeon_bo_clear_surface_reg(bo);
                return 0;
        }
 
-       if ((robj->surface_reg >= 0) && !has_moved)
+       if ((bo->surface_reg >= 0) && !has_moved)
                return 0;
 
-       return radeon_object_get_surface_reg(robj);
+       return radeon_bo_get_surface_reg(bo);
 }
 
 void radeon_bo_move_notify(struct ttm_buffer_object *bo,
-                         struct ttm_mem_reg *mem)
+                               struct ttm_mem_reg *mem)
 {
-       struct radeon_object *robj = container_of(bo, struct radeon_object, tobj);
-       radeon_object_check_tiling(robj, 0, 1);
+       struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo);
+       radeon_bo_check_tiling(rbo, 0, 1);
 }
 
 void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
 {
-       struct radeon_object *robj = container_of(bo, struct radeon_object, tobj);
-       radeon_object_check_tiling(robj, 0, 0);
+       struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo);
+       radeon_bo_check_tiling(rbo, 0, 0);
 }
index 10e8af6..f6b69c2 100644 (file)
 #ifndef __RADEON_OBJECT_H__
 #define __RADEON_OBJECT_H__
 
-#include <ttm/ttm_bo_api.h>
-#include <ttm/ttm_bo_driver.h>
-#include <ttm/ttm_placement.h>
-#include <ttm/ttm_module.h>
+#include <drm/radeon_drm.h>
+#include "radeon.h"
 
-/*
- * TTM.
+/**
+ * radeon_mem_type_to_domain - return domain corresponding to mem_type
+ * @mem_type:  ttm memory type
+ *
+ * Returns corresponding domain of the ttm mem_type
+ */
+static inline unsigned radeon_mem_type_to_domain(u32 mem_type)
+{
+       switch (mem_type) {
+       case TTM_PL_VRAM:
+               return RADEON_GEM_DOMAIN_VRAM;
+       case TTM_PL_TT:
+               return RADEON_GEM_DOMAIN_GTT;
+       case TTM_PL_SYSTEM:
+               return RADEON_GEM_DOMAIN_CPU;
+       default:
+               break;
+       }
+       return 0;
+}
+
+/**
+ * radeon_bo_reserve - reserve bo
+ * @bo:                bo structure
+ * @no_wait:           don't sleep while trying to reserve (return -EBUSY)
+ *
+ * Returns:
+ * -EBUSY: buffer is busy and @no_wait is true
+ * -ERESTART: A wait for the buffer to become unreserved was interrupted by
+ * a signal. Release all buffer reservations and return to user-space.
  */
-struct radeon_mman {
-       struct ttm_bo_global_ref        bo_global_ref;
-       struct ttm_global_reference     mem_global_ref;
-       bool                            mem_global_referenced;
-       struct ttm_bo_device            bdev;
-};
+static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait)
+{
+       int r;
+
+retry:
+       r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+       if (unlikely(r != 0)) {
+               if (r == -ERESTART)
+                       goto retry;
+               dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
+               return r;
+       }
+       return 0;
+}
+
+static inline void radeon_bo_unreserve(struct radeon_bo *bo)
+{
+       ttm_bo_unreserve(&bo->tbo);
+}
+
+/**
+ * radeon_bo_gpu_offset - return GPU offset of bo
+ * @bo:        radeon object for which we query the offset
+ *
+ * Returns current GPU offset of the object.
+ *
+ * Note: object should either be pinned or reserved when calling this
+ * function, it might be usefull to add check for this for debugging.
+ */
+static inline u64 radeon_bo_gpu_offset(struct radeon_bo *bo)
+{
+       return bo->tbo.offset;
+}
+
+static inline unsigned long radeon_bo_size(struct radeon_bo *bo)
+{
+       return bo->tbo.num_pages << PAGE_SHIFT;
+}
+
+static inline bool radeon_bo_is_reserved(struct radeon_bo *bo)
+{
+       return !!atomic_read(&bo->tbo.reserved);
+}
+
+/**
+ * radeon_bo_mmap_offset - return mmap offset of bo
+ * @bo:        radeon object for which we query the offset
+ *
+ * Returns mmap offset of the object.
+ *
+ * Note: addr_space_offset is constant after ttm bo init thus isn't protected
+ * by any lock.
+ */
+static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
+{
+       return bo->tbo.addr_space_offset;
+}
+
+static inline int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
+                                       bool no_wait)
+{
+       int r;
+
+retry:
+       r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
+       if (unlikely(r != 0)) {
+               if (r == -ERESTART)
+                       goto retry;
+               dev_err(bo->rdev->dev, "%p reserve failed for wait\n", bo);
+               return r;
+       }
+       spin_lock(&bo->tbo.lock);
+       if (mem_type)
+               *mem_type = bo->tbo.mem.mem_type;
+       if (bo->tbo.sync_obj)
+               r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
+       spin_unlock(&bo->tbo.lock);
+       ttm_bo_unreserve(&bo->tbo);
+       if (unlikely(r == -ERESTART))
+               goto retry;
+       return r;
+}
 
+extern int radeon_bo_create(struct radeon_device *rdev,
+                               struct drm_gem_object *gobj, unsigned long size,
+                               bool kernel, u32 domain,
+                               struct radeon_bo **bo_ptr);
+extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr);
+extern void radeon_bo_kunmap(struct radeon_bo *bo);
+extern void radeon_bo_unref(struct radeon_bo **bo);
+extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr);
+extern int radeon_bo_unpin(struct radeon_bo *bo);
+extern int radeon_bo_evict_vram(struct radeon_device *rdev);
+extern void radeon_bo_force_delete(struct radeon_device *rdev);
+extern int radeon_bo_init(struct radeon_device *rdev);
+extern void radeon_bo_fini(struct radeon_device *rdev);
+extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
+                               struct list_head *head);
+extern int radeon_bo_list_reserve(struct list_head *head);
+extern void radeon_bo_list_unreserve(struct list_head *head);
+extern int radeon_bo_list_validate(struct list_head *head, void *fence);
+extern void radeon_bo_list_unvalidate(struct list_head *head, void *fence);
+extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
+                               struct vm_area_struct *vma);
+extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
+                               u32 tiling_flags, u32 pitch);
+extern void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
+                               u32 *tiling_flags, u32 *pitch);
+extern int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
+                               bool force_drop);
+extern void radeon_bo_move_notify(struct ttm_buffer_object *bo,
+                                       struct ttm_mem_reg *mem);
+extern void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
+extern int radeon_bo_get_surface_reg(struct radeon_bo *bo);
 #endif
index 46146c6..34b08d3 100644 (file)
@@ -27,7 +27,7 @@ int radeon_debugfs_pm_init(struct radeon_device *rdev);
 int radeon_pm_init(struct radeon_device *rdev)
 {
        if (radeon_debugfs_pm_init(rdev)) {
-               DRM_ERROR("Failed to register debugfs file for CP !\n");
+               DRM_ERROR("Failed to register debugfs file for PM!\n");
        }
 
        return 0;
@@ -44,8 +44,8 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
        struct drm_device *dev = node->minor->dev;
        struct radeon_device *rdev = dev->dev_private;
 
-       seq_printf(m, "engine clock: %u0 Hz\n", radeon_get_engine_clock(rdev));
-       seq_printf(m, "memory clock: %u0 Hz\n", radeon_get_memory_clock(rdev));
+       seq_printf(m, "engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
+       seq_printf(m, "memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
 
        return 0;
 }
index 29ab759..6d0a009 100644 (file)
 #       define RADEON_FP_PANEL_FORMAT          (1 <<  3)
 #       define RADEON_FP_EN_TMDS               (1 <<  7)
 #       define RADEON_FP_DETECT_SENSE          (1 <<  8)
+#       define RADEON_FP_DETECT_INT_POL        (1 <<  9)
 #       define R200_FP_SOURCE_SEL_MASK         (3 <<  10)
 #       define R200_FP_SOURCE_SEL_CRTC1        (0 <<  10)
 #       define R200_FP_SOURCE_SEL_CRTC2        (1 <<  10)
 #       define R200_FP_SOURCE_SEL_TRANS        (3 <<  10)
 #       define RADEON_FP_SEL_CRTC1             (0 << 13)
 #       define RADEON_FP_SEL_CRTC2             (1 << 13)
+#       define R300_HPD_SEL(x)                 ((x) << 13)
 #       define RADEON_FP_CRTC_DONT_SHADOW_HPAR (1 << 15)
 #       define RADEON_FP_CRTC_DONT_SHADOW_VPAR (1 << 16)
 #       define RADEON_FP_CRTC_DONT_SHADOW_HEND (1 << 17)
 #       define RADEON_FP2_ON                   (1 <<  2)
 #       define RADEON_FP2_PANEL_FORMAT         (1 <<  3)
 #       define RADEON_FP2_DETECT_SENSE         (1 <<  8)
+#       define RADEON_FP2_DETECT_INT_POL       (1 <<  9)
 #       define R200_FP2_SOURCE_SEL_MASK        (3 << 10)
 #       define R200_FP2_SOURCE_SEL_CRTC1       (0 << 10)
 #       define R200_FP2_SOURCE_SEL_CRTC2       (1 << 10)
 
 #define RADEON_GEN_INT_CNTL                 0x0040
 #      define RADEON_CRTC_VBLANK_MASK          (1 << 0)
+#      define RADEON_FP_DETECT_MASK            (1 << 4)
 #      define RADEON_CRTC2_VBLANK_MASK         (1 << 9)
+#      define RADEON_FP2_DETECT_MASK           (1 << 10)
 #      define RADEON_SW_INT_ENABLE             (1 << 25)
 #define RADEON_GEN_INT_STATUS               0x0044
 #      define AVIVO_DISPLAY_INT_STATUS         (1 << 0)
 #      define RADEON_CRTC_VBLANK_STAT          (1 << 0)
 #      define RADEON_CRTC_VBLANK_STAT_ACK      (1 << 0)
+#      define RADEON_FP_DETECT_STAT            (1 << 4)
+#      define RADEON_FP_DETECT_STAT_ACK        (1 << 4)
 #      define RADEON_CRTC2_VBLANK_STAT         (1 << 9)
 #      define RADEON_CRTC2_VBLANK_STAT_ACK     (1 << 9)
+#      define RADEON_FP2_DETECT_STAT           (1 << 10)
+#      define RADEON_FP2_DETECT_STAT_ACK       (1 << 10)
 #      define RADEON_SW_INT_FIRE               (1 << 26)
 #      define RADEON_SW_INT_TEST               (1 << 25)
 #      define RADEON_SW_INT_TEST_ACK           (1 << 25)
 
        /* Multimedia I2C bus */
 #define RADEON_I2C_CNTL_0                  0x0090
-#define RADEON_I2C_DONE (1<<0)
-#define RADEON_I2C_NACK (1<<1)
-#define RADEON_I2C_HALT (1<<2)
-#define RADEON_I2C_SOFT_RST (1<<5)
-#define RADEON_I2C_DRIVE_EN (1<<6)
-#define RADEON_I2C_DRIVE_SEL (1<<7)
-#define RADEON_I2C_START (1<<8)
-#define RADEON_I2C_STOP (1<<9)
-#define RADEON_I2C_RECEIVE (1<<10)
-#define RADEON_I2C_ABORT (1<<11)
-#define RADEON_I2C_GO (1<<12)
+#define RADEON_I2C_DONE                     (1 << 0)
+#define RADEON_I2C_NACK                     (1 << 1)
+#define RADEON_I2C_HALT                     (1 << 2)
+#define RADEON_I2C_SOFT_RST                 (1 << 5)
+#define RADEON_I2C_DRIVE_EN                 (1 << 6)
+#define RADEON_I2C_DRIVE_SEL                (1 << 7)
+#define RADEON_I2C_START                    (1 << 8)
+#define RADEON_I2C_STOP                     (1 << 9)
+#define RADEON_I2C_RECEIVE                  (1 << 10)
+#define RADEON_I2C_ABORT                    (1 << 11)
+#define RADEON_I2C_GO                       (1 << 12)
+#define RADEON_I2C_PRESCALE_SHIFT           16
 #define RADEON_I2C_CNTL_1                   0x0094
-#define RADEON_I2C_SEL         (1<<16)
-#define RADEON_I2C_EN          (1<<17)
+#define RADEON_I2C_DATA_COUNT_SHIFT         0
+#define RADEON_I2C_ADDR_COUNT_SHIFT         4
+#define RADEON_I2C_INTRA_BYTE_DELAY_SHIFT   8
+#define RADEON_I2C_SEL                      (1 << 16)
+#define RADEON_I2C_EN                       (1 << 17)
+#define RADEON_I2C_TIME_LIMIT_SHIFT         24
 #define RADEON_I2C_DATA                            0x0098
 
 #define RADEON_DVI_I2C_CNTL_0              0x02e0
 #       define R200_SEL_DDC1                0 /* 0x60 - VGA_DDC */
 #       define R200_SEL_DDC2                1 /* 0x64 - DVI_DDC */
 #       define R200_SEL_DDC3                2 /* 0x68 - MONID_DDC */
-#define RADEON_DVI_I2C_CNTL_1               0x02e4 /* ? */
+#define RADEON_DVI_I2C_CNTL_1               0x02e4
 #define RADEON_DVI_I2C_DATA                0x02e8
 
 #define RADEON_INTERRUPT_LINE               0x0f3c /* PCI */
 #       define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13)
 #       define RADEON_MC_MCLK_DYN_ENABLE    (1 << 14)
 #       define RADEON_IO_MCLK_DYN_ENABLE    (1 << 15)
-#define RADEON_LCD_GPIO_MASK                0x01a0
-#define RADEON_GPIOPAD_EN                   0x01a0
-#define RADEON_LCD_GPIO_Y_REG               0x01a4
-#define RADEON_MDGPIO_A_REG                 0x01ac
-#define RADEON_MDGPIO_EN_REG                0x01b0
-#define RADEON_MDGPIO_MASK                  0x0198
+
 #define RADEON_GPIOPAD_MASK                 0x0198
 #define RADEON_GPIOPAD_A                   0x019c
-#define RADEON_MDGPIO_Y_REG                 0x01b4
+#define RADEON_GPIOPAD_EN                   0x01a0
+#define RADEON_GPIOPAD_Y                    0x01a4
+#define RADEON_MDGPIO_MASK                  0x01a8
+#define RADEON_MDGPIO_A                     0x01ac
+#define RADEON_MDGPIO_EN                    0x01b0
+#define RADEON_MDGPIO_Y                     0x01b4
+
 #define RADEON_MEM_ADDR_CONFIG              0x0148
 #define RADEON_MEM_BASE                     0x0f10 /* PCI */
 #define RADEON_MEM_CNTL                     0x0140
 #define RADEON_OVR_CLR                      0x0230
 #define RADEON_OVR_WID_LEFT_RIGHT           0x0234
 #define RADEON_OVR_WID_TOP_BOTTOM           0x0238
+#define RADEON_OVR2_CLR                     0x0330
+#define RADEON_OVR2_WID_LEFT_RIGHT          0x0334
+#define RADEON_OVR2_WID_TOP_BOTTOM          0x0338
 
 /* first capture unit */
 
index 747b4bf..4d12b2d 100644 (file)
@@ -165,19 +165,24 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
                return 0;
        /* Allocate 1M object buffer */
        INIT_LIST_HEAD(&rdev->ib_pool.scheduled_ibs);
-       r = radeon_object_create(rdev, NULL,  RADEON_IB_POOL_SIZE*64*1024,
-                                true, RADEON_GEM_DOMAIN_GTT,
-                                false, &rdev->ib_pool.robj);
+       r = radeon_bo_create(rdev, NULL,  RADEON_IB_POOL_SIZE*64*1024,
+                               true, RADEON_GEM_DOMAIN_GTT,
+                               &rdev->ib_pool.robj);
        if (r) {
                DRM_ERROR("radeon: failed to ib pool (%d).\n", r);
                return r;
        }
-       r = radeon_object_pin(rdev->ib_pool.robj, RADEON_GEM_DOMAIN_GTT, &gpu_addr);
+       r = radeon_bo_reserve(rdev->ib_pool.robj, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rdev->ib_pool.robj, RADEON_GEM_DOMAIN_GTT, &gpu_addr);
        if (r) {
+               radeon_bo_unreserve(rdev->ib_pool.robj);
                DRM_ERROR("radeon: failed to pin ib pool (%d).\n", r);
                return r;
        }
-       r = radeon_object_kmap(rdev->ib_pool.robj, &ptr);
+       r = radeon_bo_kmap(rdev->ib_pool.robj, &ptr);
+       radeon_bo_unreserve(rdev->ib_pool.robj);
        if (r) {
                DRM_ERROR("radeon: failed to map ib poll (%d).\n", r);
                return r;
@@ -203,14 +208,21 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
 
 void radeon_ib_pool_fini(struct radeon_device *rdev)
 {
+       int r;
+
        if (!rdev->ib_pool.ready) {
                return;
        }
        mutex_lock(&rdev->ib_pool.mutex);
        bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
        if (rdev->ib_pool.robj) {
-               radeon_object_kunmap(rdev->ib_pool.robj);
-               radeon_object_unref(&rdev->ib_pool.robj);
+               r = radeon_bo_reserve(rdev->ib_pool.robj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->ib_pool.robj);
+                       radeon_bo_unpin(rdev->ib_pool.robj);
+                       radeon_bo_unreserve(rdev->ib_pool.robj);
+               }
+               radeon_bo_unref(&rdev->ib_pool.robj);
                rdev->ib_pool.robj = NULL;
        }
        mutex_unlock(&rdev->ib_pool.mutex);
@@ -288,29 +300,28 @@ int radeon_ring_init(struct radeon_device *rdev, unsigned ring_size)
        rdev->cp.ring_size = ring_size;
        /* Allocate ring buffer */
        if (rdev->cp.ring_obj == NULL) {
-               r = radeon_object_create(rdev, NULL, rdev->cp.ring_size,
-                                        true,
-                                        RADEON_GEM_DOMAIN_GTT,
-                                        false,
-                                        &rdev->cp.ring_obj);
+               r = radeon_bo_create(rdev, NULL, rdev->cp.ring_size, true,
+                                       RADEON_GEM_DOMAIN_GTT,
+                                       &rdev->cp.ring_obj);
                if (r) {
-                       DRM_ERROR("radeon: failed to create ring buffer (%d).\n", r);
-                       mutex_unlock(&rdev->cp.mutex);
+                       dev_err(rdev->dev, "(%d) ring create failed\n", r);
                        return r;
                }
-               r = radeon_object_pin(rdev->cp.ring_obj,
-                                     RADEON_GEM_DOMAIN_GTT,
-                                     &rdev->cp.gpu_addr);
+               r = radeon_bo_reserve(rdev->cp.ring_obj, false);
+               if (unlikely(r != 0))
+                       return r;
+               r = radeon_bo_pin(rdev->cp.ring_obj, RADEON_GEM_DOMAIN_GTT,
+                                       &rdev->cp.gpu_addr);
                if (r) {
-                       DRM_ERROR("radeon: failed to pin ring buffer (%d).\n", r);
-                       mutex_unlock(&rdev->cp.mutex);
+                       radeon_bo_unreserve(rdev->cp.ring_obj);
+                       dev_err(rdev->dev, "(%d) ring pin failed\n", r);
                        return r;
                }
-               r = radeon_object_kmap(rdev->cp.ring_obj,
+               r = radeon_bo_kmap(rdev->cp.ring_obj,
                                       (void **)&rdev->cp.ring);
+               radeon_bo_unreserve(rdev->cp.ring_obj);
                if (r) {
-                       DRM_ERROR("radeon: failed to map ring buffer (%d).\n", r);
-                       mutex_unlock(&rdev->cp.mutex);
+                       dev_err(rdev->dev, "(%d) ring map failed\n", r);
                        return r;
                }
        }
@@ -321,11 +332,17 @@ int radeon_ring_init(struct radeon_device *rdev, unsigned ring_size)
 
 void radeon_ring_fini(struct radeon_device *rdev)
 {
+       int r;
+
        mutex_lock(&rdev->cp.mutex);
        if (rdev->cp.ring_obj) {
-               radeon_object_kunmap(rdev->cp.ring_obj);
-               radeon_object_unpin(rdev->cp.ring_obj);
-               radeon_object_unref(&rdev->cp.ring_obj);
+               r = radeon_bo_reserve(rdev->cp.ring_obj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->cp.ring_obj);
+                       radeon_bo_unpin(rdev->cp.ring_obj);
+                       radeon_bo_unreserve(rdev->cp.ring_obj);
+               }
+               radeon_bo_unref(&rdev->cp.ring_obj);
                rdev->cp.ring = NULL;
                rdev->cp.ring_obj = NULL;
        }
index 38537d9..067167c 100644 (file)
@@ -1950,7 +1950,7 @@ static void radeon_apply_surface_regs(int surf_index,
  * Note that refcount can be at most 2, since during a free refcount=3
  * might mean we have to allocate a new surface which might not always
  * be available.
- * For example : we allocate three contigous surfaces ABC. If B is
+ * For example : we allocate three contiguous surfaces ABC. If B is
  * freed, we suddenly need two surfaces to store A and C, which might
  * not always be available.
  */
index f8a465d..391c973 100644 (file)
@@ -30,8 +30,8 @@
 /* Test BO GTT->VRAM and VRAM->GTT GPU copies across the whole GTT aperture */
 void radeon_test_moves(struct radeon_device *rdev)
 {
-       struct radeon_object *vram_obj = NULL;
-       struct radeon_object **gtt_obj = NULL;
+       struct radeon_bo *vram_obj = NULL;
+       struct radeon_bo **gtt_obj = NULL;
        struct radeon_fence *fence = NULL;
        uint64_t gtt_addr, vram_addr;
        unsigned i, n, size;
@@ -52,38 +52,42 @@ void radeon_test_moves(struct radeon_device *rdev)
                goto out_cleanup;
        }
 
-       r = radeon_object_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM,
-                                false, &vram_obj);
+       r = radeon_bo_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM,
+                               &vram_obj);
        if (r) {
                DRM_ERROR("Failed to create VRAM object\n");
                goto out_cleanup;
        }
-
-       r = radeon_object_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
+       r = radeon_bo_reserve(vram_obj, false);
+       if (unlikely(r != 0))
+               goto out_cleanup;
+       r = radeon_bo_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
        if (r) {
                DRM_ERROR("Failed to pin VRAM object\n");
                goto out_cleanup;
        }
-
        for (i = 0; i < n; i++) {
                void *gtt_map, *vram_map;
                void **gtt_start, **gtt_end;
                void **vram_start, **vram_end;
 
-               r = radeon_object_create(rdev, NULL, size, true,
-                                        RADEON_GEM_DOMAIN_GTT, false, gtt_obj + i);
+               r = radeon_bo_create(rdev, NULL, size, true,
+                                        RADEON_GEM_DOMAIN_GTT, gtt_obj + i);
                if (r) {
                        DRM_ERROR("Failed to create GTT object %d\n", i);
                        goto out_cleanup;
                }
 
-               r = radeon_object_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, &gtt_addr);
+               r = radeon_bo_reserve(gtt_obj[i], false);
+               if (unlikely(r != 0))
+                       goto out_cleanup;
+               r = radeon_bo_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, &gtt_addr);
                if (r) {
                        DRM_ERROR("Failed to pin GTT object %d\n", i);
                        goto out_cleanup;
                }
 
-               r = radeon_object_kmap(gtt_obj[i], &gtt_map);
+               r = radeon_bo_kmap(gtt_obj[i], &gtt_map);
                if (r) {
                        DRM_ERROR("Failed to map GTT object %d\n", i);
                        goto out_cleanup;
@@ -94,7 +98,7 @@ void radeon_test_moves(struct radeon_device *rdev)
                     gtt_start++)
                        *gtt_start = gtt_start;
 
-               radeon_object_kunmap(gtt_obj[i]);
+               radeon_bo_kunmap(gtt_obj[i]);
 
                r = radeon_fence_create(rdev, &fence);
                if (r) {
@@ -116,7 +120,7 @@ void radeon_test_moves(struct radeon_device *rdev)
 
                radeon_fence_unref(&fence);
 
-               r = radeon_object_kmap(vram_obj, &vram_map);
+               r = radeon_bo_kmap(vram_obj, &vram_map);
                if (r) {
                        DRM_ERROR("Failed to map VRAM object after copy %d\n", i);
                        goto out_cleanup;
@@ -131,13 +135,13 @@ void radeon_test_moves(struct radeon_device *rdev)
                                          "expected 0x%p (GTT map 0x%p-0x%p)\n",
                                          i, *vram_start, gtt_start, gtt_map,
                                          gtt_end);
-                               radeon_object_kunmap(vram_obj);
+                               radeon_bo_kunmap(vram_obj);
                                goto out_cleanup;
                        }
                        *vram_start = vram_start;
                }
 
-               radeon_object_kunmap(vram_obj);
+               radeon_bo_kunmap(vram_obj);
 
                r = radeon_fence_create(rdev, &fence);
                if (r) {
@@ -159,7 +163,7 @@ void radeon_test_moves(struct radeon_device *rdev)
 
                radeon_fence_unref(&fence);
 
-               r = radeon_object_kmap(gtt_obj[i], &gtt_map);
+               r = radeon_bo_kmap(gtt_obj[i], &gtt_map);
                if (r) {
                        DRM_ERROR("Failed to map GTT object after copy %d\n", i);
                        goto out_cleanup;
@@ -174,12 +178,12 @@ void radeon_test_moves(struct radeon_device *rdev)
                                          "expected 0x%p (VRAM map 0x%p-0x%p)\n",
                                          i, *gtt_start, vram_start, vram_map,
                                          vram_end);
-                               radeon_object_kunmap(gtt_obj[i]);
+                               radeon_bo_kunmap(gtt_obj[i]);
                                goto out_cleanup;
                        }
                }
 
-               radeon_object_kunmap(gtt_obj[i]);
+               radeon_bo_kunmap(gtt_obj[i]);
 
                DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n",
                         gtt_addr - rdev->mc.gtt_location);
@@ -187,14 +191,20 @@ void radeon_test_moves(struct radeon_device *rdev)
 
 out_cleanup:
        if (vram_obj) {
-               radeon_object_unpin(vram_obj);
-               radeon_object_unref(&vram_obj);
+               if (radeon_bo_is_reserved(vram_obj)) {
+                       radeon_bo_unpin(vram_obj);
+                       radeon_bo_unreserve(vram_obj);
+               }
+               radeon_bo_unref(&vram_obj);
        }
        if (gtt_obj) {
                for (i = 0; i < n; i++) {
                        if (gtt_obj[i]) {
-                               radeon_object_unpin(gtt_obj[i]);
-                               radeon_object_unref(&gtt_obj[i]);
+                               if (radeon_bo_is_reserved(gtt_obj[i])) {
+                                       radeon_bo_unpin(gtt_obj[i]);
+                                       radeon_bo_unreserve(gtt_obj[i]);
+                               }
+                               radeon_bo_unref(&gtt_obj[i]);
                        }
                }
                kfree(gtt_obj);
@@ -206,4 +216,3 @@ out_cleanup:
                printk(KERN_WARNING "Error while testing BO move.\n");
        }
 }
-
index 1381e06..5a19d52 100644 (file)
@@ -150,7 +150,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                man->default_caching = TTM_PL_FLAG_CACHED;
                break;
        case TTM_PL_TT:
-               man->gpu_offset = 0;
+               man->gpu_offset = rdev->mc.gtt_location;
                man->available_caching = TTM_PL_MASK_CACHING;
                man->default_caching = TTM_PL_FLAG_CACHED;
                man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA;
@@ -180,7 +180,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                break;
        case TTM_PL_VRAM:
                /* "On-card" video ram */
-               man->gpu_offset = 0;
+               man->gpu_offset = rdev->mc.vram_location;
                man->flags = TTM_MEMTYPE_FLAG_FIXED |
                             TTM_MEMTYPE_FLAG_NEEDS_IOREMAP |
                             TTM_MEMTYPE_FLAG_MAPPABLE;
@@ -197,16 +197,19 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
        return 0;
 }
 
-static uint32_t radeon_evict_flags(struct ttm_buffer_object *bo)
+static void radeon_evict_flags(struct ttm_buffer_object *bo,
+                               struct ttm_placement *placement)
 {
-       uint32_t cur_placement = bo->mem.placement & ~TTM_PL_MASK_MEMTYPE;
-
+       struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo);
        switch (bo->mem.mem_type) {
+       case TTM_PL_VRAM:
+               radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT);
+               break;
+       case TTM_PL_TT:
        default:
-               return (cur_placement & ~TTM_PL_MASK_CACHING) |
-                       TTM_PL_FLAG_SYSTEM |
-                       TTM_PL_FLAG_CACHED;
+               radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
        }
+       *placement = rbo->placement;
 }
 
 static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp)
@@ -283,14 +286,21 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
        struct radeon_device *rdev;
        struct ttm_mem_reg *old_mem = &bo->mem;
        struct ttm_mem_reg tmp_mem;
-       uint32_t proposed_placement;
+       u32 placements;
+       struct ttm_placement placement;
        int r;
 
        rdev = radeon_get_rdev(bo->bdev);
        tmp_mem = *new_mem;
        tmp_mem.mm_node = NULL;
-       proposed_placement = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
-       r = ttm_bo_mem_space(bo, proposed_placement, &tmp_mem,
+       placement.fpfn = 0;
+       placement.lpfn = 0;
+       placement.num_placement = 1;
+       placement.placement = &placements;
+       placement.num_busy_placement = 1;
+       placement.busy_placement = &placements;
+       placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+       r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
                             interruptible, no_wait);
        if (unlikely(r)) {
                return r;
@@ -329,15 +339,21 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
        struct radeon_device *rdev;
        struct ttm_mem_reg *old_mem = &bo->mem;
        struct ttm_mem_reg tmp_mem;
-       uint32_t proposed_flags;
+       struct ttm_placement placement;
+       u32 placements;
        int r;
 
        rdev = radeon_get_rdev(bo->bdev);
        tmp_mem = *new_mem;
        tmp_mem.mm_node = NULL;
-       proposed_flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
-       r = ttm_bo_mem_space(bo, proposed_flags, &tmp_mem,
-                            interruptible, no_wait);
+       placement.fpfn = 0;
+       placement.lpfn = 0;
+       placement.num_placement = 1;
+       placement.placement = &placements;
+       placement.num_busy_placement = 1;
+       placement.busy_placement = &placements;
+       placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
+       r = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, no_wait);
        if (unlikely(r)) {
                return r;
        }
@@ -378,7 +394,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
             new_mem->mem_type == TTM_PL_SYSTEM) ||
            (old_mem->mem_type == TTM_PL_SYSTEM &&
             new_mem->mem_type == TTM_PL_TT)) {
-               /* bind is enought */
+               /* bind is enough */
                radeon_move_null(bo, new_mem);
                return 0;
        }
@@ -407,18 +423,6 @@ memcpy:
        return r;
 }
 
-const uint32_t radeon_mem_prios[] = {
-       TTM_PL_VRAM,
-       TTM_PL_TT,
-       TTM_PL_SYSTEM,
-};
-
-const uint32_t radeon_busy_prios[] = {
-       TTM_PL_TT,
-       TTM_PL_VRAM,
-       TTM_PL_SYSTEM,
-};
-
 static int radeon_sync_obj_wait(void *sync_obj, void *sync_arg,
                                bool lazy, bool interruptible)
 {
@@ -446,10 +450,6 @@ static bool radeon_sync_obj_signaled(void *sync_obj, void *sync_arg)
 }
 
 static struct ttm_bo_driver radeon_bo_driver = {
-       .mem_type_prio = radeon_mem_prios,
-       .mem_busy_prio = radeon_busy_prios,
-       .num_mem_type_prio = ARRAY_SIZE(radeon_mem_prios),
-       .num_mem_busy_prio = ARRAY_SIZE(radeon_busy_prios),
        .create_ttm_backend_entry = &radeon_create_ttm_backend_entry,
        .invalidate_caches = &radeon_invalidate_caches,
        .init_mem_type = &radeon_init_mem_type,
@@ -482,27 +482,31 @@ int radeon_ttm_init(struct radeon_device *rdev)
                DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
                return r;
        }
-       r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM, 0,
-                          ((rdev->mc.real_vram_size) >> PAGE_SHIFT));
+       r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM,
+                               rdev->mc.real_vram_size >> PAGE_SHIFT);
        if (r) {
                DRM_ERROR("Failed initializing VRAM heap.\n");
                return r;
        }
-       r = radeon_object_create(rdev, NULL, 256 * 1024, true,
-                                RADEON_GEM_DOMAIN_VRAM, false,
-                                &rdev->stollen_vga_memory);
+       r = radeon_bo_create(rdev, NULL, 256 * 1024, true,
+                               RADEON_GEM_DOMAIN_VRAM,
+                               &rdev->stollen_vga_memory);
        if (r) {
                return r;
        }
-       r = radeon_object_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
+       r = radeon_bo_reserve(rdev->stollen_vga_memory, false);
+       if (r)
+               return r;
+       r = radeon_bo_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
+       radeon_bo_unreserve(rdev->stollen_vga_memory);
        if (r) {
-               radeon_object_unref(&rdev->stollen_vga_memory);
+               radeon_bo_unref(&rdev->stollen_vga_memory);
                return r;
        }
        DRM_INFO("radeon: %uM of VRAM memory ready\n",
                 (unsigned)rdev->mc.real_vram_size / (1024 * 1024));
-       r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT, 0,
-                          ((rdev->mc.gtt_size) >> PAGE_SHIFT));
+       r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT,
+                               rdev->mc.gtt_size >> PAGE_SHIFT);
        if (r) {
                DRM_ERROR("Failed initializing GTT heap.\n");
                return r;
@@ -523,9 +527,15 @@ int radeon_ttm_init(struct radeon_device *rdev)
 
 void radeon_ttm_fini(struct radeon_device *rdev)
 {
+       int r;
+
        if (rdev->stollen_vga_memory) {
-               radeon_object_unpin(rdev->stollen_vga_memory);
-               radeon_object_unref(&rdev->stollen_vga_memory);
+               r = radeon_bo_reserve(rdev->stollen_vga_memory, false);
+               if (r == 0) {
+                       radeon_bo_unpin(rdev->stollen_vga_memory);
+                       radeon_bo_unreserve(rdev->stollen_vga_memory);
+               }
+               radeon_bo_unref(&rdev->stollen_vga_memory);
        }
        ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_VRAM);
        ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_TT);
index ca03716..c1fcddd 100644 (file)
@@ -352,7 +352,7 @@ static int rs400_mc_init(struct radeon_device *rdev)
        u32 tmp;
 
        /* Setup GPU memory space */
-       tmp = G_00015C_MC_FB_START(RREG32(R_00015C_NB_TOM));
+       tmp = RREG32(R_00015C_NB_TOM);
        rdev->mc.vram_location = G_00015C_MC_FB_START(tmp) << 16;
        rdev->mc.gtt_location = 0xFFFFFFFFUL;
        r = radeon_mc_setup(rdev);
@@ -387,13 +387,13 @@ static int rs400_startup(struct radeon_device *rdev)
        r300_clock_startup(rdev);
        /* Initialize GPU configuration (# pipes, ...) */
        rs400_gpu_init(rdev);
+       r100_enable_bm(rdev);
        /* Initialize GART (initialize after TTM so we can allocate
         * memory through TTM but finalize after TTM) */
        r = rs400_gart_enable(rdev);
        if (r)
                return r;
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        r100_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -430,6 +430,8 @@ int rs400_resume(struct radeon_device *rdev)
        radeon_combios_asic_init(rdev->ddev);
        /* Resume clock after posting */
        r300_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return rs400_startup(rdev);
 }
 
@@ -452,7 +454,7 @@ void rs400_fini(struct radeon_device *rdev)
        rs400_gart_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -490,10 +492,9 @@ int rs400_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               radeon_combios_asic_init(rdev->ddev);
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
+
        /* Initialize clocks */
        radeon_get_clock_info(rdev->ddev);
        /* Get vram informations */
@@ -510,7 +511,7 @@ int rs400_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        r = rs400_gart_init(rdev);
index 5f117cd..4f8ea42 100644 (file)
 void rs600_gpu_init(struct radeon_device *rdev);
 int rs600_mc_wait_for_idle(struct radeon_device *rdev);
 
+int rs600_mc_init(struct radeon_device *rdev)
+{
+       /* read back the MC value from the hw */
+       int r;
+       u32 tmp;
+
+       /* Setup GPU memory space */
+       tmp = RREG32_MC(R_000004_MC_FB_LOCATION);
+       rdev->mc.vram_location = G_000004_MC_FB_START(tmp) << 16;
+       rdev->mc.gtt_location = 0xffffffffUL;
+       r = radeon_mc_setup(rdev);
+       if (r)
+               return r;
+       return 0;
+}
+
+/* hpd for digital panel detect/disconnect */
+bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
+{
+       u32 tmp;
+       bool connected = false;
+
+       switch (hpd) {
+       case RADEON_HPD_1:
+               tmp = RREG32(R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS);
+               if (G_007D04_DC_HOT_PLUG_DETECT1_SENSE(tmp))
+                       connected = true;
+               break;
+       case RADEON_HPD_2:
+               tmp = RREG32(R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS);
+               if (G_007D14_DC_HOT_PLUG_DETECT2_SENSE(tmp))
+                       connected = true;
+               break;
+       default:
+               break;
+       }
+       return connected;
+}
+
+void rs600_hpd_set_polarity(struct radeon_device *rdev,
+                           enum radeon_hpd_id hpd)
+{
+       u32 tmp;
+       bool connected = rs600_hpd_sense(rdev, hpd);
+
+       switch (hpd) {
+       case RADEON_HPD_1:
+               tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL);
+               if (connected)
+                       tmp &= ~S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1);
+               else
+                       tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1);
+               WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
+               break;
+       case RADEON_HPD_2:
+               tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL);
+               if (connected)
+                       tmp &= ~S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1);
+               else
+                       tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1);
+               WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
+               break;
+       default:
+               break;
+       }
+}
+
+void rs600_hpd_init(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               switch (radeon_connector->hpd.hpd) {
+               case RADEON_HPD_1:
+                       WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL,
+                              S_007D00_DC_HOT_PLUG_DETECT1_EN(1));
+                       rdev->irq.hpd[0] = true;
+                       break;
+               case RADEON_HPD_2:
+                       WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL,
+                              S_007D10_DC_HOT_PLUG_DETECT2_EN(1));
+                       rdev->irq.hpd[1] = true;
+                       break;
+               default:
+                       break;
+               }
+       }
+       rs600_irq_set(rdev);
+}
+
+void rs600_hpd_fini(struct radeon_device *rdev)
+{
+       struct drm_device *dev = rdev->ddev;
+       struct drm_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+               switch (radeon_connector->hpd.hpd) {
+               case RADEON_HPD_1:
+                       WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL,
+                              S_007D00_DC_HOT_PLUG_DETECT1_EN(0));
+                       rdev->irq.hpd[0] = false;
+                       break;
+               case RADEON_HPD_2:
+                       WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL,
+                              S_007D10_DC_HOT_PLUG_DETECT2_EN(0));
+                       rdev->irq.hpd[1] = false;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
 /*
  * GART.
  */
@@ -100,40 +216,40 @@ int rs600_gart_enable(struct radeon_device *rdev)
        WREG32(R_00004C_BUS_CNTL, tmp);
        /* FIXME: setup default page */
        WREG32_MC(R_000100_MC_PT0_CNTL,
-                (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
-                 S_000100_EFFECTIVE_L2_QUEUE_SIZE(6)));
+                 (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
+                  S_000100_EFFECTIVE_L2_QUEUE_SIZE(6)));
+
        for (i = 0; i < 19; i++) {
                WREG32_MC(R_00016C_MC_PT0_CLIENT0_CNTL + i,
-                       S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) |
-                       S_00016C_SYSTEM_ACCESS_MODE_MASK(
-                               V_00016C_SYSTEM_ACCESS_MODE_IN_SYS) |
-                       S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(
-                               V_00016C_SYSTEM_APERTURE_UNMAPPED_DEFAULT_PAGE) |
-                       S_00016C_EFFECTIVE_L1_CACHE_SIZE(1) |
-                       S_00016C_ENABLE_FRAGMENT_PROCESSING(1) |
-                       S_00016C_EFFECTIVE_L1_QUEUE_SIZE(1));
+                         S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) |
+                         S_00016C_SYSTEM_ACCESS_MODE_MASK(
+                                 V_00016C_SYSTEM_ACCESS_MODE_NOT_IN_SYS) |
+                         S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(
+                                 V_00016C_SYSTEM_APERTURE_UNMAPPED_PASSTHROUGH) |
+                         S_00016C_EFFECTIVE_L1_CACHE_SIZE(3) |
+                         S_00016C_ENABLE_FRAGMENT_PROCESSING(1) |
+                         S_00016C_EFFECTIVE_L1_QUEUE_SIZE(3));
        }
-
-       /* System context map to GART space */
-       WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.gtt_start);
-       WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.gtt_end);
-
        /* enable first context */
-       WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start);
-       WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end);
        WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL,
-                       S_000102_ENABLE_PAGE_TABLE(1) |
-                       S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT));
+                 S_000102_ENABLE_PAGE_TABLE(1) |
+                 S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT));
+
        /* disable all other contexts */
-       for (i = 1; i < 8; i++) {
+       for (i = 1; i < 8; i++)
                WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL + i, 0);
-       }
 
        /* setup the page table */
        WREG32_MC(R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR,
-                       rdev->gart.table_addr);
+                 rdev->gart.table_addr);
+       WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start);
+       WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end);
        WREG32_MC(R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0);
 
+       /* System context maps to VRAM space */
+       WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start);
+       WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end);
+
        /* enable page tables */
        tmp = RREG32_MC(R_000100_MC_PT0_CNTL);
        WREG32_MC(R_000100_MC_PT0_CNTL, (tmp | S_000100_ENABLE_PT(1)));
@@ -146,15 +262,20 @@ int rs600_gart_enable(struct radeon_device *rdev)
 
 void rs600_gart_disable(struct radeon_device *rdev)
 {
-       uint32_t tmp;
+       u32 tmp;
+       int r;
 
        /* FIXME: disable out of gart access */
        WREG32_MC(R_000100_MC_PT0_CNTL, 0);
        tmp = RREG32_MC(R_000009_MC_CNTL1);
        WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES);
        if (rdev->gart.table.vram.robj) {
-               radeon_object_kunmap(rdev->gart.table.vram.robj);
-               radeon_object_unpin(rdev->gart.table.vram.robj);
+               r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+               if (r == 0) {
+                       radeon_bo_kunmap(rdev->gart.table.vram.robj);
+                       radeon_bo_unpin(rdev->gart.table.vram.robj);
+                       radeon_bo_unreserve(rdev->gart.table.vram.robj);
+               }
        }
 }
 
@@ -189,6 +310,10 @@ int rs600_irq_set(struct radeon_device *rdev)
 {
        uint32_t tmp = 0;
        uint32_t mode_int = 0;
+       u32 hpd1 = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL) &
+               ~S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1);
+       u32 hpd2 = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL) &
+               ~S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
 
        if (rdev->irq.sw_int) {
                tmp |= S_000040_SW_INT_EN(1);
@@ -199,8 +324,16 @@ int rs600_irq_set(struct radeon_device *rdev)
        if (rdev->irq.crtc_vblank_int[1]) {
                mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1);
        }
+       if (rdev->irq.hpd[0]) {
+               hpd1 |= S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1);
+       }
+       if (rdev->irq.hpd[1]) {
+               hpd2 |= S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
+       }
        WREG32(R_000040_GEN_INT_CNTL, tmp);
        WREG32(R_006540_DxMODE_INT_MASK, mode_int);
+       WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
+       WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
        return 0;
 }
 
@@ -208,6 +341,7 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
 {
        uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS);
        uint32_t irq_mask = ~C_000044_SW_INT;
+       u32 tmp;
 
        if (G_000044_DISPLAY_INT_STAT(irqs)) {
                *r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS);
@@ -219,6 +353,16 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
                        WREG32(R_006D34_D2MODE_VBLANK_STATUS,
                                S_006D34_D2MODE_VBLANK_ACK(1));
                }
+               if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(*r500_disp_int)) {
+                       tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL);
+                       tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(1);
+                       WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
+               }
+               if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(*r500_disp_int)) {
+                       tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL);
+                       tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(1);
+                       WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
+               }
        } else {
                *r500_disp_int = 0;
        }
@@ -244,6 +388,7 @@ int rs600_irq_process(struct radeon_device *rdev)
 {
        uint32_t status, msi_rearm;
        uint32_t r500_disp_int;
+       bool queue_hotplug = false;
 
        status = rs600_irq_ack(rdev, &r500_disp_int);
        if (!status && !r500_disp_int) {
@@ -258,8 +403,18 @@ int rs600_irq_process(struct radeon_device *rdev)
                        drm_handle_vblank(rdev->ddev, 0);
                if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
                        drm_handle_vblank(rdev->ddev, 1);
+               if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) {
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD1\n");
+               }
+               if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(r500_disp_int)) {
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD2\n");
+               }
                status = rs600_irq_ack(rdev, &r500_disp_int);
        }
+       if (queue_hotplug)
+               queue_work(rdev->wq, &rdev->hotplug_work);
        if (rdev->msi_enabled) {
                switch (rdev->family) {
                case CHIP_RS600:
@@ -301,9 +456,7 @@ int rs600_mc_wait_for_idle(struct radeon_device *rdev)
 
 void rs600_gpu_init(struct radeon_device *rdev)
 {
-       /* FIXME: HDP same place on rs600 ? */
        r100_hdp_reset(rdev);
-       /* FIXME: is this correct ? */
        r420_pipes_init(rdev);
        /* Wait for mc idle */
        if (rs600_mc_wait_for_idle(rdev))
@@ -312,9 +465,20 @@ void rs600_gpu_init(struct radeon_device *rdev)
 
 void rs600_vram_info(struct radeon_device *rdev)
 {
-       /* FIXME: to do or is these values sane ? */
        rdev->mc.vram_is_ddr = true;
        rdev->mc.vram_width = 128;
+
+       rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE);
+       rdev->mc.mc_vram_size = rdev->mc.real_vram_size;
+
+       rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
+       rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
+
+       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
+               rdev->mc.mc_vram_size = rdev->mc.aper_size;
+
+       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
+               rdev->mc.real_vram_size = rdev->mc.aper_size;
 }
 
 void rs600_bandwidth_update(struct radeon_device *rdev)
@@ -388,7 +552,6 @@ static int rs600_startup(struct radeon_device *rdev)
        if (r)
                return r;
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        rs600_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -423,6 +586,8 @@ int rs600_resume(struct radeon_device *rdev)
        atom_asic_init(rdev->mode_info.atom_context);
        /* Resume clock after posting */
        rv515_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return rs600_startup(rdev);
 }
 
@@ -445,7 +610,7 @@ void rs600_fini(struct radeon_device *rdev)
        rs600_gart_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -482,10 +647,9 @@ int rs600_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               atom_asic_init(rdev->mode_info.atom_context);
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
+
        /* Initialize clocks */
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
@@ -493,7 +657,7 @@ int rs600_init(struct radeon_device *rdev)
        /* Get vram informations */
        rs600_vram_info(rdev);
        /* Initialize memory controller (also test AGP) */
-       r = r420_mc_init(rdev);
+       r = rs600_mc_init(rdev);
        if (r)
                return r;
        rs600_debugfs(rdev);
@@ -505,7 +669,7 @@ int rs600_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        r = rs600_gart_init(rdev);
index 8130892..c1c8f58 100644 (file)
 
 /* Registers */
 #define R_000040_GEN_INT_CNTL                        0x000040
-#define   S_000040_DISPLAY_INT_STATUS(x)               (((x) & 0x1) << 0)
-#define   G_000040_DISPLAY_INT_STATUS(x)               (((x) >> 0) & 0x1)
-#define   C_000040_DISPLAY_INT_STATUS                  0xFFFFFFFE
-#define   S_000040_DMA_VIPH0_INT_EN(x)                 (((x) & 0x1) << 12)
-#define   G_000040_DMA_VIPH0_INT_EN(x)                 (((x) >> 12) & 0x1)
-#define   C_000040_DMA_VIPH0_INT_EN                    0xFFFFEFFF
-#define   S_000040_CRTC2_VSYNC(x)                      (((x) & 0x1) << 6)
-#define   G_000040_CRTC2_VSYNC(x)                      (((x) >> 6) & 0x1)
-#define   C_000040_CRTC2_VSYNC                         0xFFFFFFBF
-#define   S_000040_SNAPSHOT2(x)                        (((x) & 0x1) << 7)
-#define   G_000040_SNAPSHOT2(x)                        (((x) >> 7) & 0x1)
-#define   C_000040_SNAPSHOT2                           0xFFFFFF7F
-#define   S_000040_CRTC2_VBLANK(x)                     (((x) & 0x1) << 9)
-#define   G_000040_CRTC2_VBLANK(x)                     (((x) >> 9) & 0x1)
-#define   C_000040_CRTC2_VBLANK                        0xFFFFFDFF
-#define   S_000040_FP2_DETECT(x)                       (((x) & 0x1) << 10)
-#define   G_000040_FP2_DETECT(x)                       (((x) >> 10) & 0x1)
-#define   C_000040_FP2_DETECT                          0xFFFFFBFF
-#define   S_000040_VSYNC_DIFF_OVER_LIMIT(x)            (((x) & 0x1) << 11)
-#define   G_000040_VSYNC_DIFF_OVER_LIMIT(x)            (((x) >> 11) & 0x1)
-#define   C_000040_VSYNC_DIFF_OVER_LIMIT               0xFFFFF7FF
+#define   S_000040_SCRATCH_INT_MASK(x)                 (((x) & 0x1) << 18)
+#define   G_000040_SCRATCH_INT_MASK(x)                 (((x) >> 18) & 0x1)
+#define   C_000040_SCRATCH_INT_MASK                    0xFFFBFFFF
+#define   S_000040_GUI_IDLE_MASK(x)                    (((x) & 0x1) << 19)
+#define   G_000040_GUI_IDLE_MASK(x)                    (((x) >> 19) & 0x1)
+#define   C_000040_GUI_IDLE_MASK                       0xFFF7FFFF
 #define   S_000040_DMA_VIPH1_INT_EN(x)                 (((x) & 0x1) << 13)
 #define   G_000040_DMA_VIPH1_INT_EN(x)                 (((x) >> 13) & 0x1)
 #define   C_000040_DMA_VIPH1_INT_EN                    0xFFFFDFFF
 #define   S_007EDC_LB_D2_VBLANK_INTERRUPT(x)           (((x) & 0x1) << 5)
 #define   G_007EDC_LB_D2_VBLANK_INTERRUPT(x)           (((x) >> 5) & 0x1)
 #define   C_007EDC_LB_D2_VBLANK_INTERRUPT              0xFFFFFFDF
-
+#define   S_007EDC_DACA_AUTODETECT_INTERRUPT(x)        (((x) & 0x1) << 16)
+#define   G_007EDC_DACA_AUTODETECT_INTERRUPT(x)        (((x) >> 16) & 0x1)
+#define   C_007EDC_DACA_AUTODETECT_INTERRUPT           0xFFFEFFFF
+#define   S_007EDC_DACB_AUTODETECT_INTERRUPT(x)        (((x) & 0x1) << 17)
+#define   G_007EDC_DACB_AUTODETECT_INTERRUPT(x)        (((x) >> 17) & 0x1)
+#define   C_007EDC_DACB_AUTODETECT_INTERRUPT           0xFFFDFFFF
+#define   S_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x)    (((x) & 0x1) << 18)
+#define   G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x)    (((x) >> 18) & 0x1)
+#define   C_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT       0xFFFBFFFF
+#define   S_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x)    (((x) & 0x1) << 19)
+#define   G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x)    (((x) >> 19) & 0x1)
+#define   C_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT       0xFFF7FFFF
+#define R_007828_DACA_AUTODETECT_CONTROL               0x007828
+#define   S_007828_DACA_AUTODETECT_MODE(x)             (((x) & 0x3) << 0)
+#define   G_007828_DACA_AUTODETECT_MODE(x)             (((x) >> 0) & 0x3)
+#define   C_007828_DACA_AUTODETECT_MODE                0xFFFFFFFC
+#define   S_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8)
+#define   G_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff)
+#define   C_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER  0xFFFF00FF
+#define   S_007828_DACA_AUTODETECT_CHECK_MASK(x)       (((x) & 0x3) << 16)
+#define   G_007828_DACA_AUTODETECT_CHECK_MASK(x)       (((x) >> 16) & 0x3)
+#define   C_007828_DACA_AUTODETECT_CHECK_MASK          0xFFFCFFFF
+#define R_007838_DACA_AUTODETECT_INT_CONTROL           0x007838
+#define   S_007838_DACA_AUTODETECT_ACK(x)              (((x) & 0x1) << 0)
+#define   C_007838_DACA_DACA_AUTODETECT_ACK            0xFFFFFFFE
+#define   S_007838_DACA_AUTODETECT_INT_ENABLE(x)       (((x) & 0x1) << 16)
+#define   G_007838_DACA_AUTODETECT_INT_ENABLE(x)       (((x) >> 16) & 0x1)
+#define   C_007838_DACA_AUTODETECT_INT_ENABLE          0xFFFCFFFF
+#define R_007A28_DACB_AUTODETECT_CONTROL               0x007A28
+#define   S_007A28_DACB_AUTODETECT_MODE(x)             (((x) & 0x3) << 0)
+#define   G_007A28_DACB_AUTODETECT_MODE(x)             (((x) >> 0) & 0x3)
+#define   C_007A28_DACB_AUTODETECT_MODE                0xFFFFFFFC
+#define   S_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8)
+#define   G_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff)
+#define   C_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER  0xFFFF00FF
+#define   S_007A28_DACB_AUTODETECT_CHECK_MASK(x)       (((x) & 0x3) << 16)
+#define   G_007A28_DACB_AUTODETECT_CHECK_MASK(x)       (((x) >> 16) & 0x3)
+#define   C_007A28_DACB_AUTODETECT_CHECK_MASK          0xFFFCFFFF
+#define R_007A38_DACB_AUTODETECT_INT_CONTROL           0x007A38
+#define   S_007A38_DACB_AUTODETECT_ACK(x)              (((x) & 0x1) << 0)
+#define   C_007A38_DACB_DACA_AUTODETECT_ACK            0xFFFFFFFE
+#define   S_007A38_DACB_AUTODETECT_INT_ENABLE(x)       (((x) & 0x1) << 16)
+#define   G_007A38_DACB_AUTODETECT_INT_ENABLE(x)       (((x) >> 16) & 0x1)
+#define   C_007A38_DACB_AUTODETECT_INT_ENABLE          0xFFFCFFFF
+#define R_007D00_DC_HOT_PLUG_DETECT1_CONTROL           0x007D00
+#define   S_007D00_DC_HOT_PLUG_DETECT1_EN(x)           (((x) & 0x1) << 0)
+#define   G_007D00_DC_HOT_PLUG_DETECT1_EN(x)           (((x) >> 0) & 0x1)
+#define   C_007D00_DC_HOT_PLUG_DETECT1_EN              0xFFFFFFFE
+#define R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS        0x007D04
+#define   S_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x)   (((x) & 0x1) << 0)
+#define   G_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x)   (((x) >> 0) & 0x1)
+#define   C_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS      0xFFFFFFFE
+#define   S_007D04_DC_HOT_PLUG_DETECT1_SENSE(x)        (((x) & 0x1) << 1)
+#define   G_007D04_DC_HOT_PLUG_DETECT1_SENSE(x)        (((x) >> 1) & 0x1)
+#define   C_007D04_DC_HOT_PLUG_DETECT1_SENSE           0xFFFFFFFD
+#define R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL       0x007D08
+#define   S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(x)      (((x) & 0x1) << 0)
+#define   C_007D08_DC_HOT_PLUG_DETECT1_INT_ACK         0xFFFFFFFE
+#define   S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) & 0x1) << 8)
+#define   G_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) >> 8) & 0x1)
+#define   C_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY    0xFFFFFEFF
+#define   S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x)       (((x) & 0x1) << 16)
+#define   G_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x)       (((x) >> 16) & 0x1)
+#define   C_007D08_DC_HOT_PLUG_DETECT1_INT_EN          0xFFFEFFFF
+#define R_007D10_DC_HOT_PLUG_DETECT2_CONTROL           0x007D10
+#define   S_007D10_DC_HOT_PLUG_DETECT2_EN(x)           (((x) & 0x1) << 0)
+#define   G_007D10_DC_HOT_PLUG_DETECT2_EN(x)           (((x) >> 0) & 0x1)
+#define   C_007D10_DC_HOT_PLUG_DETECT2_EN              0xFFFFFFFE
+#define R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS        0x007D14
+#define   S_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x)   (((x) & 0x1) << 0)
+#define   G_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x)   (((x) >> 0) & 0x1)
+#define   C_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS      0xFFFFFFFE
+#define   S_007D14_DC_HOT_PLUG_DETECT2_SENSE(x)        (((x) & 0x1) << 1)
+#define   G_007D14_DC_HOT_PLUG_DETECT2_SENSE(x)        (((x) >> 1) & 0x1)
+#define   C_007D14_DC_HOT_PLUG_DETECT2_SENSE           0xFFFFFFFD
+#define R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL       0x007D18
+#define   S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(x)      (((x) & 0x1) << 0)
+#define   C_007D18_DC_HOT_PLUG_DETECT2_INT_ACK         0xFFFFFFFE
+#define   S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) & 0x1) << 8)
+#define   G_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) >> 8) & 0x1)
+#define   C_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY    0xFFFFFEFF
+#define   S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x)       (((x) & 0x1) << 16)
+#define   G_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x)       (((x) >> 16) & 0x1)
+#define   C_007D18_DC_HOT_PLUG_DETECT2_INT_EN          0xFFFEFFFF
 
 /* MC registers */
 #define R_000000_MC_STATUS                           0x000000
index 2754717..1e22f52 100644 (file)
@@ -131,24 +131,25 @@ void rs690_pm_info(struct radeon_device *rdev)
 
 void rs690_vram_info(struct radeon_device *rdev)
 {
-       uint32_t tmp;
        fixed20_12 a;
 
        rs400_gart_adjust_size(rdev);
-       /* DDR for all card after R300 & IGP */
+
        rdev->mc.vram_is_ddr = true;
-       /* FIXME: is this correct for RS690/RS740 ? */
-       tmp = RREG32(RADEON_MEM_CNTL);
-       if (tmp & R300_MEM_NUM_CHANNELS_MASK) {
-               rdev->mc.vram_width = 128;
-       } else {
-               rdev->mc.vram_width = 64;
-       }
+       rdev->mc.vram_width = 128;
+
        rdev->mc.real_vram_size = RREG32(RADEON_CONFIG_MEMSIZE);
        rdev->mc.mc_vram_size = rdev->mc.real_vram_size;
 
        rdev->mc.aper_base = drm_get_resource_start(rdev->ddev, 0);
        rdev->mc.aper_size = drm_get_resource_len(rdev->ddev, 0);
+
+       if (rdev->mc.mc_vram_size > rdev->mc.aper_size)
+               rdev->mc.mc_vram_size = rdev->mc.aper_size;
+
+       if (rdev->mc.real_vram_size > rdev->mc.aper_size)
+               rdev->mc.real_vram_size = rdev->mc.aper_size;
+
        rs690_pm_info(rdev);
        /* FIXME: we should enforce default clock in case GPU is not in
         * default setup
@@ -161,6 +162,21 @@ void rs690_vram_info(struct radeon_device *rdev)
        rdev->pm.core_bandwidth.full = rfixed_div(rdev->pm.sclk, a);
 }
 
+static int rs690_mc_init(struct radeon_device *rdev)
+{
+       int r;
+       u32 tmp;
+
+       /* Setup GPU memory space */
+       tmp = RREG32_MC(R_000100_MCCFG_FB_LOCATION);
+       rdev->mc.vram_location = G_000100_MC_FB_START(tmp) << 16;
+       rdev->mc.gtt_location = 0xFFFFFFFFUL;
+       r = radeon_mc_setup(rdev);
+       if (r)
+               return r;
+       return 0;
+}
+
 void rs690_line_buffer_adjust(struct radeon_device *rdev,
                              struct drm_display_mode *mode1,
                              struct drm_display_mode *mode2)
@@ -244,8 +260,9 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
 
        b.full = rfixed_const(mode->crtc_hdisplay);
        c.full = rfixed_const(256);
-       a.full = rfixed_mul(wm->num_line_pair, b);
-       request_fifo_depth.full = rfixed_div(a, c);
+       a.full = rfixed_div(b, c);
+       request_fifo_depth.full = rfixed_mul(a, wm->num_line_pair);
+       request_fifo_depth.full = rfixed_ceil(request_fifo_depth);
        if (a.full < rfixed_const(4)) {
                wm->lb_request_fifo_depth = 4;
        } else {
@@ -374,6 +391,7 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
        a.full = rfixed_const(16);
        wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay);
        wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a);
+       wm->priority_mark_max.full = rfixed_ceil(wm->priority_mark_max);
 
        /* Determine estimated width */
        estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full;
@@ -383,6 +401,7 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
        } else {
                a.full = rfixed_const(16);
                wm->priority_mark.full = rfixed_div(estimated_width, a);
+               wm->priority_mark.full = rfixed_ceil(wm->priority_mark);
                wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full;
        }
 }
@@ -605,7 +624,6 @@ static int rs690_startup(struct radeon_device *rdev)
        if (r)
                return r;
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        rs600_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -640,6 +658,8 @@ int rs690_resume(struct radeon_device *rdev)
        atom_asic_init(rdev->mode_info.atom_context);
        /* Resume clock after posting */
        rv515_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return rs690_startup(rdev);
 }
 
@@ -662,7 +682,7 @@ void rs690_fini(struct radeon_device *rdev)
        rs400_gart_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -700,10 +720,9 @@ int rs690_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               atom_asic_init(rdev->mode_info.atom_context);
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
+
        /* Initialize clocks */
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
@@ -711,7 +730,7 @@ int rs690_init(struct radeon_device *rdev)
        /* Get vram informations */
        rs690_vram_info(rdev);
        /* Initialize memory controller (also test AGP) */
-       r = r420_mc_init(rdev);
+       r = rs690_mc_init(rdev);
        if (r)
                return r;
        rv515_debugfs(rdev);
@@ -723,7 +742,7 @@ int rs690_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        r = rs400_gart_init(rdev);
index ba68c9f..59632a5 100644 (file)
@@ -478,7 +478,6 @@ static int rv515_startup(struct radeon_device *rdev)
                        return r;
        }
        /* Enable IRQ */
-       rdev->irq.sw_int = true;
        rs600_irq_set(rdev);
        /* 1M ring buffer */
        r = r100_cp_init(rdev, 1024 * 1024);
@@ -514,6 +513,8 @@ int rv515_resume(struct radeon_device *rdev)
        atom_asic_init(rdev->mode_info.atom_context);
        /* Resume clock after posting */
        rv515_clock_startup(rdev);
+       /* Initialize surface registers */
+       radeon_surface_init(rdev);
        return rv515_startup(rdev);
 }
 
@@ -540,11 +541,11 @@ void rv515_fini(struct radeon_device *rdev)
        r100_wb_fini(rdev);
        r100_ib_fini(rdev);
        radeon_gem_fini(rdev);
-    rv370_pcie_gart_fini(rdev);
+       rv370_pcie_gart_fini(rdev);
        radeon_agp_fini(rdev);
        radeon_irq_kms_fini(rdev);
        radeon_fence_driver_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
@@ -580,10 +581,8 @@ int rv515_init(struct radeon_device *rdev)
                        RREG32(R_0007C0_CP_STAT));
        }
        /* check if cards are posted or not */
-       if (!radeon_card_posted(rdev) && rdev->bios) {
-               DRM_INFO("GPU not posted. posting now...\n");
-               atom_asic_init(rdev->mode_info.atom_context);
-       }
+       if (radeon_boot_test_post_card(rdev) == false)
+               return -EINVAL;
        /* Initialize clocks */
        radeon_get_clock_info(rdev->ddev);
        /* Initialize power management */
@@ -603,7 +602,7 @@ int rv515_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
        if (r)
                return r;
        r = rv370_pcie_gart_init(rdev);
@@ -892,8 +891,9 @@ void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
 
        b.full = rfixed_const(mode->crtc_hdisplay);
        c.full = rfixed_const(256);
-       a.full = rfixed_mul(wm->num_line_pair, b);
-       request_fifo_depth.full = rfixed_div(a, c);
+       a.full = rfixed_div(b, c);
+       request_fifo_depth.full = rfixed_mul(a, wm->num_line_pair);
+       request_fifo_depth.full = rfixed_ceil(request_fifo_depth);
        if (a.full < rfixed_const(4)) {
                wm->lb_request_fifo_depth = 4;
        } else {
@@ -995,15 +995,17 @@ void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
        a.full = rfixed_const(16);
        wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay);
        wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a);
+       wm->priority_mark_max.full = rfixed_ceil(wm->priority_mark_max);
 
        /* Determine estimated width */
        estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full;
        estimated_width.full = rfixed_div(estimated_width, consumption_time);
        if (rfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) {
-               wm->priority_mark.full = rfixed_const(10);
+               wm->priority_mark.full = wm->priority_mark_max.full;
        } else {
                a.full = rfixed_const(16);
                wm->priority_mark.full = rfixed_div(estimated_width, a);
+               wm->priority_mark.full = rfixed_ceil(wm->priority_mark);
                wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full;
        }
 }
index b0efd0d..fbb0357 100644 (file)
@@ -92,7 +92,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
 void rv770_pcie_gart_disable(struct radeon_device *rdev)
 {
        u32 tmp;
-       int i;
+       int i, r;
 
        /* Disable all tables */
        for (i = 0; i < 7; i++)
@@ -113,8 +113,12 @@ void rv770_pcie_gart_disable(struct radeon_device *rdev)
        WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
        WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
        if (rdev->gart.table.vram.robj) {
-               radeon_object_kunmap(rdev->gart.table.vram.robj);
-               radeon_object_unpin(rdev->gart.table.vram.robj);
+               r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
+               if (likely(r == 0)) {
+                       radeon_bo_kunmap(rdev->gart.table.vram.robj);
+                       radeon_bo_unpin(rdev->gart.table.vram.robj);
+                       radeon_bo_unreserve(rdev->gart.table.vram.robj);
+               }
        }
 }
 
@@ -829,11 +833,11 @@ int rv770_mc_init(struct radeon_device *rdev)
                 * AGP so that GPU can catch out of VRAM/AGP access
                 */
                if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
-                       /* Enought place before */
+                       /* Enough place before */
                        rdev->mc.vram_location = rdev->mc.gtt_location -
                                                        rdev->mc.mc_vram_size;
                } else if (tmp > rdev->mc.mc_vram_size) {
-                       /* Enought place after */
+                       /* Enough place after */
                        rdev->mc.vram_location = rdev->mc.gtt_location +
                                                        rdev->mc.gtt_size;
                } else {
@@ -870,6 +874,14 @@ static int rv770_startup(struct radeon_device *rdev)
 {
        int r;
 
+       if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
+               r = r600_init_microcode(rdev);
+               if (r) {
+                       DRM_ERROR("Failed to load firmware!\n");
+                       return r;
+               }
+       }
+
        rv770_mc_program(rdev);
        if (rdev->flags & RADEON_IS_AGP) {
                rv770_agp_enable(rdev);
@@ -880,13 +892,26 @@ static int rv770_startup(struct radeon_device *rdev)
        }
        rv770_gpu_init(rdev);
 
-       r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
-                             &rdev->r600_blit.shader_gpu_addr);
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+                       &rdev->r600_blit.shader_gpu_addr);
+       radeon_bo_unreserve(rdev->r600_blit.shader_obj);
        if (r) {
                DRM_ERROR("failed to pin blit object %d\n", r);
                return r;
        }
 
+       /* Enable IRQ */
+       r = r600_irq_init(rdev);
+       if (r) {
+               DRM_ERROR("radeon: IH init failed (%d).\n", r);
+               radeon_irq_kms_fini(rdev);
+               return r;
+       }
+       r600_irq_set(rdev);
+
        r = radeon_ring_init(rdev, rdev->cp.ring_size);
        if (r)
                return r;
@@ -934,13 +959,19 @@ int rv770_resume(struct radeon_device *rdev)
 
 int rv770_suspend(struct radeon_device *rdev)
 {
+       int r;
+
        /* FIXME: we should wait for ring to be empty */
        r700_cp_stop(rdev);
        rdev->cp.ready = false;
        r600_wb_disable(rdev);
        rv770_pcie_gart_disable(rdev);
        /* unpin shaders bo */
-        radeon_object_unpin(rdev->r600_blit.shader_obj);
+       r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
+       if (likely(r == 0)) {
+               radeon_bo_unpin(rdev->r600_blit.shader_obj);
+               radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+       }
        return 0;
 }
 
@@ -975,7 +1006,11 @@ int rv770_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Post card if necessary */
-       if (!r600_card_posted(rdev) && rdev->bios) {
+       if (!r600_card_posted(rdev)) {
+               if (!rdev->bios) {
+                       dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
+                       return -EINVAL;
+               }
                DRM_INFO("GPU not posted. posting now...\n");
                atom_asic_init(rdev->mode_info.atom_context);
        }
@@ -998,31 +1033,31 @@ int rv770_init(struct radeon_device *rdev)
        if (r)
                return r;
        /* Memory manager */
-       r = radeon_object_init(rdev);
+       r = radeon_bo_init(rdev);
+       if (r)
+               return r;
+
+       r = radeon_irq_kms_init(rdev);
        if (r)
                return r;
+
        rdev->cp.ring_obj = NULL;
        r600_ring_init(rdev, 1024 * 1024);
 
-       if (!rdev->me_fw || !rdev->pfp_fw) {
-               r = r600_cp_init_microcode(rdev);
-               if (r) {
-                       DRM_ERROR("Failed to load firmware!\n");
-                       return r;
-               }
-       }
+       rdev->ih.ring_obj = NULL;
+       r600_ih_ring_init(rdev, 64 * 1024);
 
        r = r600_pcie_gart_init(rdev);
        if (r)
                return r;
 
-       rdev->accel_working = true;
        r = r600_blit_init(rdev);
        if (r) {
-               DRM_ERROR("radeon: failled blitter (%d).\n", r);
-               rdev->accel_working = false;
+               DRM_ERROR("radeon: failed blitter (%d).\n", r);
+               return r;
        }
 
+       rdev->accel_working = true;
        r = rv770_startup(rdev);
        if (r) {
                rv770_suspend(rdev);
@@ -1034,12 +1069,12 @@ int rv770_init(struct radeon_device *rdev)
        if (rdev->accel_working) {
                r = radeon_ib_pool_init(rdev);
                if (r) {
-                       DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
+                       DRM_ERROR("radeon: failed initializing IB pool (%d).\n", r);
                        rdev->accel_working = false;
                }
                r = r600_ib_test(rdev);
                if (r) {
-                       DRM_ERROR("radeon: failled testing IB (%d).\n", r);
+                       DRM_ERROR("radeon: failed testing IB (%d).\n", r);
                        rdev->accel_working = false;
                }
        }
@@ -1051,6 +1086,8 @@ void rv770_fini(struct radeon_device *rdev)
        rv770_suspend(rdev);
 
        r600_blit_fini(rdev);
+       r600_irq_fini(rdev);
+       radeon_irq_kms_fini(rdev);
        radeon_ring_fini(rdev);
        r600_wb_fini(rdev);
        rv770_pcie_gart_fini(rdev);
@@ -1059,7 +1096,7 @@ void rv770_fini(struct radeon_device *rdev)
        radeon_clocks_fini(rdev);
        if (rdev->flags & RADEON_IS_AGP)
                radeon_agp_fini(rdev);
-       radeon_object_fini(rdev);
+       radeon_bo_fini(rdev);
        radeon_atombios_fini(rdev);
        kfree(rdev->bios);
        rdev->bios = NULL;
index b0a9de7..1e138f5 100644 (file)
@@ -3,6 +3,7 @@
 
 ccflags-y := -Iinclude/drm
 ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
-       ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o
+       ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o \
+       ttm_object.o ttm_lock.o ttm_execbuf_util.o
 
 obj-$(CONFIG_DRM_TTM) += ttm.o
index 87c0625..a835b6f 100644 (file)
 /*
  * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
  */
+/* Notes:
+ *
+ * We store bo pointer in drm_mm_node struct so we know which bo own a
+ * specific node. There is no protection on the pointer, thus to make
+ * sure things don't go berserk you have to access this pointer while
+ * holding the global lru lock and make sure anytime you free a node you
+ * reset the pointer to NULL.
+ */
 
 #include "ttm/ttm_module.h"
 #include "ttm/ttm_bo_driver.h"
@@ -51,6 +59,60 @@ static struct attribute ttm_bo_count = {
        .mode = S_IRUGO
 };
 
+static inline int ttm_mem_type_from_flags(uint32_t flags, uint32_t *mem_type)
+{
+       int i;
+
+       for (i = 0; i <= TTM_PL_PRIV5; i++)
+               if (flags & (1 << i)) {
+                       *mem_type = i;
+                       return 0;
+               }
+       return -EINVAL;
+}
+
+static void ttm_mem_type_manager_debug(struct ttm_bo_global *glob,
+                                       struct ttm_mem_type_manager *man)
+{
+       printk(KERN_ERR TTM_PFX "    has_type: %d\n", man->has_type);
+       printk(KERN_ERR TTM_PFX "    use_type: %d\n", man->use_type);
+       printk(KERN_ERR TTM_PFX "    flags: 0x%08X\n", man->flags);
+       printk(KERN_ERR TTM_PFX "    gpu_offset: 0x%08lX\n", man->gpu_offset);
+       printk(KERN_ERR TTM_PFX "    io_offset: 0x%08lX\n", man->io_offset);
+       printk(KERN_ERR TTM_PFX "    io_size: %ld\n", man->io_size);
+       printk(KERN_ERR TTM_PFX "    size: %ld\n", (unsigned long)man->size);
+       printk(KERN_ERR TTM_PFX "    available_caching: 0x%08X\n",
+               man->available_caching);
+       printk(KERN_ERR TTM_PFX "    default_caching: 0x%08X\n",
+               man->default_caching);
+       spin_lock(&glob->lru_lock);
+       drm_mm_debug_table(&man->manager, TTM_PFX);
+       spin_unlock(&glob->lru_lock);
+}
+
+static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
+                                       struct ttm_placement *placement)
+{
+       struct ttm_bo_device *bdev = bo->bdev;
+       struct ttm_bo_global *glob = bo->glob;
+       struct ttm_mem_type_manager *man;
+       int i, ret, mem_type;
+
+       printk(KERN_ERR TTM_PFX "No space for %p (%ld pages, %ldK, %ldM)\n",
+               bo, bo->mem.num_pages, bo->mem.size >> 10,
+               bo->mem.size >> 20);
+       for (i = 0; i < placement->num_placement; i++) {
+               ret = ttm_mem_type_from_flags(placement->placement[i],
+                                               &mem_type);
+               if (ret)
+                       return;
+               man = &bdev->man[mem_type];
+               printk(KERN_ERR TTM_PFX "  placement[%d]=0x%08X (%d)\n",
+                       i, placement->placement[i], mem_type);
+               ttm_mem_type_manager_debug(glob, man);
+       }
+}
+
 static ssize_t ttm_bo_global_show(struct kobject *kobj,
                                  struct attribute *attr,
                                  char *buffer)
@@ -117,7 +179,7 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible)
                ret = wait_event_interruptible(bo->event_queue,
                                               atomic_read(&bo->reserved) == 0);
                if (unlikely(ret != 0))
-                       return -ERESTART;
+                       return ret;
        } else {
                wait_event(bo->event_queue, atomic_read(&bo->reserved) == 0);
        }
@@ -247,7 +309,6 @@ EXPORT_SYMBOL(ttm_bo_unreserve);
 /*
  * Call bo->mutex locked.
  */
-
 static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
 {
        struct ttm_bo_device *bdev = bo->bdev;
@@ -275,9 +336,10 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
                bo->ttm = ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT,
                                        page_flags | TTM_PAGE_FLAG_USER,
                                        glob->dummy_read_page);
-               if (unlikely(bo->ttm == NULL))
+               if (unlikely(bo->ttm == NULL)) {
                        ret = -ENOMEM;
-               break;
+                       break;
+               }
 
                ret = ttm_tt_set_user(bo->ttm, current,
                                      bo->buffer_start, bo->num_pages);
@@ -328,14 +390,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
                }
 
                if (bo->mem.mem_type == TTM_PL_SYSTEM) {
-
-                       struct ttm_mem_reg *old_mem = &bo->mem;
-                       uint32_t save_flags = old_mem->placement;
-
-                       *old_mem = *mem;
+                       bo->mem = *mem;
                        mem->mm_node = NULL;
-                       ttm_flag_masked(&save_flags, mem->placement,
-                                       TTM_PL_MASK_MEMTYPE);
                        goto moved;
                }
 
@@ -418,6 +474,7 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
                        kref_put(&bo->list_kref, ttm_bo_ref_bug);
                }
                if (bo->mem.mm_node) {
+                       bo->mem.mm_node->private = NULL;
                        drm_mm_put_block(bo->mem.mm_node);
                        bo->mem.mm_node = NULL;
                }
@@ -554,24 +611,21 @@ void ttm_bo_unref(struct ttm_buffer_object **p_bo)
 }
 EXPORT_SYMBOL(ttm_bo_unref);
 
-static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type,
-                       bool interruptible, bool no_wait)
+static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
+                       bool no_wait)
 {
-       int ret = 0;
        struct ttm_bo_device *bdev = bo->bdev;
        struct ttm_bo_global *glob = bo->glob;
        struct ttm_mem_reg evict_mem;
-       uint32_t proposed_placement;
-
-       if (bo->mem.mem_type != mem_type)
-               goto out;
+       struct ttm_placement placement;
+       int ret = 0;
 
        spin_lock(&bo->lock);
        ret = ttm_bo_wait(bo, false, interruptible, no_wait);
        spin_unlock(&bo->lock);
 
        if (unlikely(ret != 0)) {
-               if (ret != -ERESTART) {
+               if (ret != -ERESTARTSYS) {
                        printk(KERN_ERR TTM_PFX
                               "Failed to expire sync object before "
                               "buffer eviction.\n");
@@ -584,116 +638,139 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type,
        evict_mem = bo->mem;
        evict_mem.mm_node = NULL;
 
-       proposed_placement = bdev->driver->evict_flags(bo);
-
-       ret = ttm_bo_mem_space(bo, proposed_placement,
-                              &evict_mem, interruptible, no_wait);
-       if (unlikely(ret != 0 && ret != -ERESTART))
-               ret = ttm_bo_mem_space(bo, TTM_PL_FLAG_SYSTEM,
-                                      &evict_mem, interruptible, no_wait);
-
+       placement.fpfn = 0;
+       placement.lpfn = 0;
+       placement.num_placement = 0;
+       placement.num_busy_placement = 0;
+       bdev->driver->evict_flags(bo, &placement);
+       ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible,
+                               no_wait);
        if (ret) {
-               if (ret != -ERESTART)
+               if (ret != -ERESTARTSYS) {
                        printk(KERN_ERR TTM_PFX
                               "Failed to find memory space for "
                               "buffer 0x%p eviction.\n", bo);
+                       ttm_bo_mem_space_debug(bo, &placement);
+               }
                goto out;
        }
 
        ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, interruptible,
                                     no_wait);
        if (ret) {
-               if (ret != -ERESTART)
+               if (ret != -ERESTARTSYS)
                        printk(KERN_ERR TTM_PFX "Buffer eviction failed\n");
+               spin_lock(&glob->lru_lock);
+               if (evict_mem.mm_node) {
+                       evict_mem.mm_node->private = NULL;
+                       drm_mm_put_block(evict_mem.mm_node);
+                       evict_mem.mm_node = NULL;
+               }
+               spin_unlock(&glob->lru_lock);
                goto out;
        }
+       bo->evicted = true;
+out:
+       return ret;
+}
+
+static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
+                               uint32_t mem_type,
+                               bool interruptible, bool no_wait)
+{
+       struct ttm_bo_global *glob = bdev->glob;
+       struct ttm_mem_type_manager *man = &bdev->man[mem_type];
+       struct ttm_buffer_object *bo;
+       int ret, put_count = 0;
 
        spin_lock(&glob->lru_lock);
-       if (evict_mem.mm_node) {
-               drm_mm_put_block(evict_mem.mm_node);
-               evict_mem.mm_node = NULL;
-       }
+       bo = list_first_entry(&man->lru, struct ttm_buffer_object, lru);
+       kref_get(&bo->list_kref);
+       ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, false, 0);
+       if (likely(ret == 0))
+               put_count = ttm_bo_del_from_lru(bo);
        spin_unlock(&glob->lru_lock);
-       bo->evicted = true;
-out:
+       if (unlikely(ret != 0))
+               return ret;
+       while (put_count--)
+               kref_put(&bo->list_kref, ttm_bo_ref_bug);
+       ret = ttm_bo_evict(bo, interruptible, no_wait);
+       ttm_bo_unreserve(bo);
+       kref_put(&bo->list_kref, ttm_bo_release_list);
        return ret;
 }
 
+static int ttm_bo_man_get_node(struct ttm_buffer_object *bo,
+                               struct ttm_mem_type_manager *man,
+                               struct ttm_placement *placement,
+                               struct ttm_mem_reg *mem,
+                               struct drm_mm_node **node)
+{
+       struct ttm_bo_global *glob = bo->glob;
+       unsigned long lpfn;
+       int ret;
+
+       lpfn = placement->lpfn;
+       if (!lpfn)
+               lpfn = man->size;
+       *node = NULL;
+       do {
+               ret = drm_mm_pre_get(&man->manager);
+               if (unlikely(ret))
+                       return ret;
+
+               spin_lock(&glob->lru_lock);
+               *node = drm_mm_search_free_in_range(&man->manager,
+                                       mem->num_pages, mem->page_alignment,
+                                       placement->fpfn, lpfn, 1);
+               if (unlikely(*node == NULL)) {
+                       spin_unlock(&glob->lru_lock);
+                       return 0;
+               }
+               *node = drm_mm_get_block_atomic_range(*node, mem->num_pages,
+                                                       mem->page_alignment,
+                                                       placement->fpfn,
+                                                       lpfn);
+               spin_unlock(&glob->lru_lock);
+       } while (*node == NULL);
+       return 0;
+}
+
 /**
  * Repeatedly evict memory from the LRU for @mem_type until we create enough
  * space, or we've evicted everything and there isn't enough space.
  */
-static int ttm_bo_mem_force_space(struct ttm_bo_device *bdev,
-                                 struct ttm_mem_reg *mem,
-                                 uint32_t mem_type,
-                                 bool interruptible, bool no_wait)
+static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
+                                       uint32_t mem_type,
+                                       struct ttm_placement *placement,
+                                       struct ttm_mem_reg *mem,
+                                       bool interruptible, bool no_wait)
 {
+       struct ttm_bo_device *bdev = bo->bdev;
        struct ttm_bo_global *glob = bdev->glob;
-       struct drm_mm_node *node;
-       struct ttm_buffer_object *entry;
        struct ttm_mem_type_manager *man = &bdev->man[mem_type];
-       struct list_head *lru;
-       unsigned long num_pages = mem->num_pages;
-       int put_count = 0;
+       struct drm_mm_node *node;
        int ret;
 
-retry_pre_get:
-       ret = drm_mm_pre_get(&man->manager);
-       if (unlikely(ret != 0))
-               return ret;
-
-       spin_lock(&glob->lru_lock);
        do {
-               node = drm_mm_search_free(&man->manager, num_pages,
-                                         mem->page_alignment, 1);
+               ret = ttm_bo_man_get_node(bo, man, placement, mem, &node);
+               if (unlikely(ret != 0))
+                       return ret;
                if (node)
                        break;
-
-               lru = &man->lru;
-               if (list_empty(lru))
+               spin_lock(&glob->lru_lock);
+               if (list_empty(&man->lru)) {
+                       spin_unlock(&glob->lru_lock);
                        break;
-
-               entry = list_first_entry(lru, struct ttm_buffer_object, lru);
-               kref_get(&entry->list_kref);
-
-               ret =
-                   ttm_bo_reserve_locked(entry, interruptible, no_wait,
-                                         false, 0);
-
-               if (likely(ret == 0))
-                       put_count = ttm_bo_del_from_lru(entry);
-
+               }
                spin_unlock(&glob->lru_lock);
-
+               ret = ttm_mem_evict_first(bdev, mem_type, interruptible,
+                                               no_wait);
                if (unlikely(ret != 0))
                        return ret;
-
-               while (put_count--)
-                       kref_put(&entry->list_kref, ttm_bo_ref_bug);
-
-               ret = ttm_bo_evict(entry, mem_type, interruptible, no_wait);
-
-               ttm_bo_unreserve(entry);
-
-               kref_put(&entry->list_kref, ttm_bo_release_list);
-               if (ret)
-                       return ret;
-
-               spin_lock(&glob->lru_lock);
        } while (1);
-
-       if (!node) {
-               spin_unlock(&glob->lru_lock);
+       if (node == NULL)
                return -ENOMEM;
-       }
-
-       node = drm_mm_get_block_atomic(node, num_pages, mem->page_alignment);
-       if (unlikely(!node)) {
-               spin_unlock(&glob->lru_lock);
-               goto retry_pre_get;
-       }
-
-       spin_unlock(&glob->lru_lock);
        mem->mm_node = node;
        mem->mem_type = mem_type;
        return 0;
@@ -724,7 +801,6 @@ static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man,
        return result;
 }
 
-
 static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
                                 bool disallow_fixed,
                                 uint32_t mem_type,
@@ -757,66 +833,55 @@ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
  * space.
  */
 int ttm_bo_mem_space(struct ttm_buffer_object *bo,
-                    uint32_t proposed_placement,
-                    struct ttm_mem_reg *mem,
-                    bool interruptible, bool no_wait)
+                       struct ttm_placement *placement,
+                       struct ttm_mem_reg *mem,
+                       bool interruptible, bool no_wait)
 {
        struct ttm_bo_device *bdev = bo->bdev;
-       struct ttm_bo_global *glob = bo->glob;
        struct ttm_mem_type_manager *man;
-
-       uint32_t num_prios = bdev->driver->num_mem_type_prio;
-       const uint32_t *prios = bdev->driver->mem_type_prio;
-       uint32_t i;
        uint32_t mem_type = TTM_PL_SYSTEM;
        uint32_t cur_flags = 0;
        bool type_found = false;
        bool type_ok = false;
-       bool has_eagain = false;
+       bool has_erestartsys = false;
        struct drm_mm_node *node = NULL;
-       int ret;
+       int i, ret;
 
        mem->mm_node = NULL;
-       for (i = 0; i < num_prios; ++i) {
-               mem_type = prios[i];
+       for (i = 0; i <= placement->num_placement; ++i) {
+               ret = ttm_mem_type_from_flags(placement->placement[i],
+                                               &mem_type);
+               if (ret)
+                       return ret;
                man = &bdev->man[mem_type];
 
                type_ok = ttm_bo_mt_compatible(man,
-                                              bo->type == ttm_bo_type_user,
-                                              mem_type, proposed_placement,
-                                              &cur_flags);
+                                               bo->type == ttm_bo_type_user,
+                                               mem_type,
+                                               placement->placement[i],
+                                               &cur_flags);
 
                if (!type_ok)
                        continue;
 
                cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
                                                  cur_flags);
+               /*
+                * Use the access and other non-mapping-related flag bits from
+                * the memory placement flags to the current flags
+                */
+               ttm_flag_masked(&cur_flags, placement->placement[i],
+                               ~TTM_PL_MASK_MEMTYPE);
 
                if (mem_type == TTM_PL_SYSTEM)
                        break;
 
                if (man->has_type && man->use_type) {
                        type_found = true;
-                       do {
-                               ret = drm_mm_pre_get(&man->manager);
-                               if (unlikely(ret))
-                                       return ret;
-
-                               spin_lock(&glob->lru_lock);
-                               node = drm_mm_search_free(&man->manager,
-                                                         mem->num_pages,
-                                                         mem->page_alignment,
-                                                         1);
-                               if (unlikely(!node)) {
-                                       spin_unlock(&glob->lru_lock);
-                                       break;
-                               }
-                               node = drm_mm_get_block_atomic(node,
-                                                              mem->num_pages,
-                                                              mem->
-                                                              page_alignment);
-                               spin_unlock(&glob->lru_lock);
-                       } while (!node);
+                       ret = ttm_bo_man_get_node(bo, man, placement, mem,
+                                                       &node);
+                       if (unlikely(ret))
+                               return ret;
                }
                if (node)
                        break;
@@ -826,67 +891,65 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
                mem->mm_node = node;
                mem->mem_type = mem_type;
                mem->placement = cur_flags;
+               if (node)
+                       node->private = bo;
                return 0;
        }
 
        if (!type_found)
                return -EINVAL;
 
-       num_prios = bdev->driver->num_mem_busy_prio;
-       prios = bdev->driver->mem_busy_prio;
-
-       for (i = 0; i < num_prios; ++i) {
-               mem_type = prios[i];
+       for (i = 0; i <= placement->num_busy_placement; ++i) {
+               ret = ttm_mem_type_from_flags(placement->placement[i],
+                                               &mem_type);
+               if (ret)
+                       return ret;
                man = &bdev->man[mem_type];
-
                if (!man->has_type)
                        continue;
-
                if (!ttm_bo_mt_compatible(man,
-                                         bo->type == ttm_bo_type_user,
-                                         mem_type,
-                                         proposed_placement, &cur_flags))
+                                               bo->type == ttm_bo_type_user,
+                                               mem_type,
+                                               placement->placement[i],
+                                               &cur_flags))
                        continue;
 
                cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
                                                  cur_flags);
+               /*
+                * Use the access and other non-mapping-related flag bits from
+                * the memory placement flags to the current flags
+                */
+               ttm_flag_masked(&cur_flags, placement->placement[i],
+                               ~TTM_PL_MASK_MEMTYPE);
 
-               ret = ttm_bo_mem_force_space(bdev, mem, mem_type,
-                                            interruptible, no_wait);
-
+               ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem,
+                                               interruptible, no_wait);
                if (ret == 0 && mem->mm_node) {
                        mem->placement = cur_flags;
+                       mem->mm_node->private = bo;
                        return 0;
                }
-
-               if (ret == -ERESTART)
-                       has_eagain = true;
+               if (ret == -ERESTARTSYS)
+                       has_erestartsys = true;
        }
-
-       ret = (has_eagain) ? -ERESTART : -ENOMEM;
+       ret = (has_erestartsys) ? -ERESTARTSYS : -ENOMEM;
        return ret;
 }
 EXPORT_SYMBOL(ttm_bo_mem_space);
 
 int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait)
 {
-       int ret = 0;
-
        if ((atomic_read(&bo->cpu_writers) > 0) && no_wait)
                return -EBUSY;
 
-       ret = wait_event_interruptible(bo->event_queue,
-                                      atomic_read(&bo->cpu_writers) == 0);
-
-       if (ret == -ERESTARTSYS)
-               ret = -ERESTART;
-
-       return ret;
+       return wait_event_interruptible(bo->event_queue,
+                                       atomic_read(&bo->cpu_writers) == 0);
 }
 
 int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
-                      uint32_t proposed_placement,
-                      bool interruptible, bool no_wait)
+                       struct ttm_placement *placement,
+                       bool interruptible, bool no_wait)
 {
        struct ttm_bo_global *glob = bo->glob;
        int ret = 0;
@@ -899,101 +962,82 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
         * Have the driver move function wait for idle when necessary,
         * instead of doing it here.
         */
-
        spin_lock(&bo->lock);
        ret = ttm_bo_wait(bo, false, interruptible, no_wait);
        spin_unlock(&bo->lock);
-
        if (ret)
                return ret;
-
        mem.num_pages = bo->num_pages;
        mem.size = mem.num_pages << PAGE_SHIFT;
        mem.page_alignment = bo->mem.page_alignment;
-
        /*
         * Determine where to move the buffer.
         */
-
-       ret = ttm_bo_mem_space(bo, proposed_placement, &mem,
-                              interruptible, no_wait);
+       ret = ttm_bo_mem_space(bo, placement, &mem, interruptible, no_wait);
        if (ret)
                goto out_unlock;
-
        ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait);
-
 out_unlock:
        if (ret && mem.mm_node) {
                spin_lock(&glob->lru_lock);
+               mem.mm_node->private = NULL;
                drm_mm_put_block(mem.mm_node);
                spin_unlock(&glob->lru_lock);
        }
        return ret;
 }
 
-static int ttm_bo_mem_compat(uint32_t proposed_placement,
+static int ttm_bo_mem_compat(struct ttm_placement *placement,
                             struct ttm_mem_reg *mem)
 {
-       if ((proposed_placement & mem->placement & TTM_PL_MASK_MEM) == 0)
-               return 0;
-       if ((proposed_placement & mem->placement & TTM_PL_MASK_CACHING) == 0)
-               return 0;
-
-       return 1;
+       int i;
+
+       for (i = 0; i < placement->num_placement; i++) {
+               if ((placement->placement[i] & mem->placement &
+                       TTM_PL_MASK_CACHING) &&
+                       (placement->placement[i] & mem->placement &
+                       TTM_PL_MASK_MEM))
+                       return i;
+       }
+       return -1;
 }
 
 int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
-                              uint32_t proposed_placement,
-                              bool interruptible, bool no_wait)
+                               struct ttm_placement *placement,
+                               bool interruptible, bool no_wait)
 {
        int ret;
 
        BUG_ON(!atomic_read(&bo->reserved));
-       bo->proposed_placement = proposed_placement;
-
-       TTM_DEBUG("Proposed placement 0x%08lx, Old flags 0x%08lx\n",
-                 (unsigned long)proposed_placement,
-                 (unsigned long)bo->mem.placement);
-
+       /* Check that range is valid */
+       if (placement->lpfn || placement->fpfn)
+               if (placement->fpfn > placement->lpfn ||
+                       (placement->lpfn - placement->fpfn) < bo->num_pages)
+                       return -EINVAL;
        /*
         * Check whether we need to move buffer.
         */
-
-       if (!ttm_bo_mem_compat(bo->proposed_placement, &bo->mem)) {
-               ret = ttm_bo_move_buffer(bo, bo->proposed_placement,
-                                        interruptible, no_wait);
-               if (ret) {
-                       if (ret != -ERESTART)
-                               printk(KERN_ERR TTM_PFX
-                                      "Failed moving buffer. "
-                                      "Proposed placement 0x%08x\n",
-                                      bo->proposed_placement);
-                       if (ret == -ENOMEM)
-                               printk(KERN_ERR TTM_PFX
-                                      "Out of aperture space or "
-                                      "DRM memory quota.\n");
+       ret = ttm_bo_mem_compat(placement, &bo->mem);
+       if (ret < 0) {
+               ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait);
+               if (ret)
                        return ret;
-               }
+       } else {
+               /*
+                * Use the access and other non-mapping-related flag bits from
+                * the compatible memory placement flags to the active flags
+                */
+               ttm_flag_masked(&bo->mem.placement, placement->placement[ret],
+                               ~TTM_PL_MASK_MEMTYPE);
        }
-
        /*
         * We might need to add a TTM.
         */
-
        if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
                ret = ttm_bo_add_ttm(bo, true);
                if (ret)
                        return ret;
        }
-       /*
-        * Validation has succeeded, move the access and other
-        * non-mapping-related flag bits from the proposed flags to
-        * the active flags
-        */
-
-       ttm_flag_masked(&bo->mem.placement, bo->proposed_placement,
-                       ~TTM_PL_MASK_MEMTYPE);
-
        return 0;
 }
 EXPORT_SYMBOL(ttm_buffer_object_validate);
@@ -1041,8 +1085,10 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev,
                           size_t acc_size,
                           void (*destroy) (struct ttm_buffer_object *))
 {
-       int ret = 0;
+       int i, c, ret = 0;
        unsigned long num_pages;
+       uint32_t placements[8];
+       struct ttm_placement placement;
 
        size += buffer_start & ~PAGE_MASK;
        num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -1099,7 +1145,16 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev,
                        goto out_err;
        }
 
-       ret = ttm_buffer_object_validate(bo, flags, interruptible, false);
+       placement.fpfn = 0;
+       placement.lpfn = 0;
+       for (i = 0, c = 0; i <= TTM_PL_PRIV5; i++)
+               if (flags & (1 << i))
+                       placements[c++] = (flags & ~TTM_PL_MASK_MEM) | (1 << i);
+       placement.placement = placements;
+       placement.num_placement = c;
+       placement.busy_placement = placements;
+       placement.num_busy_placement = c;
+       ret = ttm_buffer_object_validate(bo, &placement, interruptible, false);
        if (ret)
                goto out_err;
 
@@ -1134,8 +1189,8 @@ int ttm_buffer_object_create(struct ttm_bo_device *bdev,
                             struct ttm_buffer_object **p_bo)
 {
        struct ttm_buffer_object *bo;
-       int ret;
        struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
+       int ret;
 
        size_t acc_size =
            ttm_bo_size(bdev->glob, (size + PAGE_SIZE - 1) >> PAGE_SHIFT);
@@ -1160,66 +1215,32 @@ int ttm_buffer_object_create(struct ttm_bo_device *bdev,
        return ret;
 }
 
-static int ttm_bo_leave_list(struct ttm_buffer_object *bo,
-                            uint32_t mem_type, bool allow_errors)
-{
-       int ret;
-
-       spin_lock(&bo->lock);
-       ret = ttm_bo_wait(bo, false, false, false);
-       spin_unlock(&bo->lock);
-
-       if (ret && allow_errors)
-               goto out;
-
-       if (bo->mem.mem_type == mem_type)
-               ret = ttm_bo_evict(bo, mem_type, false, false);
-
-       if (ret) {
-               if (allow_errors) {
-                       goto out;
-               } else {
-                       ret = 0;
-                       printk(KERN_ERR TTM_PFX "Cleanup eviction failed\n");
-               }
-       }
-
-out:
-       return ret;
-}
-
 static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
-                                  struct list_head *head,
-                                  unsigned mem_type, bool allow_errors)
+                                       unsigned mem_type, bool allow_errors)
 {
+       struct ttm_mem_type_manager *man = &bdev->man[mem_type];
        struct ttm_bo_global *glob = bdev->glob;
-       struct ttm_buffer_object *entry;
        int ret;
-       int put_count;
 
        /*
         * Can't use standard list traversal since we're unlocking.
         */
 
        spin_lock(&glob->lru_lock);
-
-       while (!list_empty(head)) {
-               entry = list_first_entry(head, struct ttm_buffer_object, lru);
-               kref_get(&entry->list_kref);
-               ret = ttm_bo_reserve_locked(entry, false, false, false, 0);
-               put_count = ttm_bo_del_from_lru(entry);
+       while (!list_empty(&man->lru)) {
                spin_unlock(&glob->lru_lock);
-               while (put_count--)
-                       kref_put(&entry->list_kref, ttm_bo_ref_bug);
-               BUG_ON(ret);
-               ret = ttm_bo_leave_list(entry, mem_type, allow_errors);
-               ttm_bo_unreserve(entry);
-               kref_put(&entry->list_kref, ttm_bo_release_list);
+               ret = ttm_mem_evict_first(bdev, mem_type, false, false);
+               if (ret) {
+                       if (allow_errors) {
+                               return ret;
+                       } else {
+                               printk(KERN_ERR TTM_PFX
+                                       "Cleanup eviction failed\n");
+                       }
+               }
                spin_lock(&glob->lru_lock);
        }
-
        spin_unlock(&glob->lru_lock);
-
        return 0;
 }
 
@@ -1246,7 +1267,7 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
 
        ret = 0;
        if (mem_type > 0) {
-               ttm_bo_force_list_clean(bdev, &man->lru, mem_type, false);
+               ttm_bo_force_list_clean(bdev, mem_type, false);
 
                spin_lock(&glob->lru_lock);
                if (drm_mm_clean(&man->manager))
@@ -1279,12 +1300,12 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type)
                return 0;
        }
 
-       return ttm_bo_force_list_clean(bdev, &man->lru, mem_type, true);
+       return ttm_bo_force_list_clean(bdev, mem_type, true);
 }
 EXPORT_SYMBOL(ttm_bo_evict_mm);
 
 int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
-                  unsigned long p_offset, unsigned long p_size)
+                       unsigned long p_size)
 {
        int ret = -EINVAL;
        struct ttm_mem_type_manager *man;
@@ -1314,7 +1335,7 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
                               type);
                        return ret;
                }
-               ret = drm_mm_init(&man->manager, p_offset, p_size);
+               ret = drm_mm_init(&man->manager, 0, p_size);
                if (ret)
                        return ret;
        }
@@ -1463,7 +1484,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
         * Initialize the system memory buffer type.
         * Other types need to be driver / IOCTL initialized.
         */
-       ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0, 0);
+       ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0);
        if (unlikely(ret != 0))
                goto out_no_sys;
 
@@ -1693,7 +1714,7 @@ int ttm_bo_block_reservation(struct ttm_buffer_object *bo, bool interruptible,
                        ret = wait_event_interruptible
                            (bo->event_queue, atomic_read(&bo->reserved) == 0);
                        if (unlikely(ret != 0))
-                               return -ERESTART;
+                               return ret;
                } else {
                        wait_event(bo->event_queue,
                                   atomic_read(&bo->reserved) == 0);
index c70927e..2ecf7d0 100644 (file)
@@ -369,6 +369,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
 #endif
        return tmp;
 }
+EXPORT_SYMBOL(ttm_io_prot);
 
 static int ttm_bo_ioremap(struct ttm_buffer_object *bo,
                          unsigned long bus_base,
@@ -427,7 +428,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
 
                /*
                 * We need to use vmap to get the desired page protection
-                * or to make the buffer object look contigous.
+                * or to make the buffer object look contiguous.
                 */
                prot = (mem->placement & TTM_PL_FLAG_CACHED) ?
                        PAGE_KERNEL :
index 1c040d0..609a85a 100644 (file)
@@ -114,7 +114,7 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
                ret = ttm_bo_wait(bo, false, true, false);
                spin_unlock(&bo->lock);
                if (unlikely(ret != 0)) {
-                       retval = (ret != -ERESTART) ?
+                       retval = (ret != -ERESTARTSYS) ?
                            VM_FAULT_SIGBUS : VM_FAULT_NOPAGE;
                        goto out_unlock;
                }
@@ -349,9 +349,6 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
        switch (ret) {
        case 0:
                break;
-       case -ERESTART:
-               ret = -EINTR;
-               goto out_unref;
        case -EBUSY:
                ret = -EAGAIN;
                goto out_unref;
@@ -421,8 +418,6 @@ ssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf,
        switch (ret) {
        case 0:
                break;
-       case -ERESTART:
-               return -EINTR;
        case -EBUSY:
                return -EAGAIN;
        default:
diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
new file mode 100644 (file)
index 0000000..c285c29
--- /dev/null
@@ -0,0 +1,117 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., 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.
+ *
+ **************************************************************************/
+
+#include "ttm/ttm_execbuf_util.h"
+#include "ttm/ttm_bo_driver.h"
+#include "ttm/ttm_placement.h"
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+void ttm_eu_backoff_reservation(struct list_head *list)
+{
+       struct ttm_validate_buffer *entry;
+
+       list_for_each_entry(entry, list, head) {
+               struct ttm_buffer_object *bo = entry->bo;
+               if (!entry->reserved)
+                       continue;
+
+               entry->reserved = false;
+               ttm_bo_unreserve(bo);
+       }
+}
+EXPORT_SYMBOL(ttm_eu_backoff_reservation);
+
+/*
+ * Reserve buffers for validation.
+ *
+ * If a buffer in the list is marked for CPU access, we back off and
+ * wait for that buffer to become free for GPU access.
+ *
+ * If a buffer is reserved for another validation, the validator with
+ * the highest validation sequence backs off and waits for that buffer
+ * to become unreserved. This prevents deadlocks when validating multiple
+ * buffers in different orders.
+ */
+
+int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq)
+{
+       struct ttm_validate_buffer *entry;
+       int ret;
+
+retry:
+       list_for_each_entry(entry, list, head) {
+               struct ttm_buffer_object *bo = entry->bo;
+
+               entry->reserved = false;
+               ret = ttm_bo_reserve(bo, true, false, true, val_seq);
+               if (ret != 0) {
+                       ttm_eu_backoff_reservation(list);
+                       if (ret == -EAGAIN) {
+                               ret = ttm_bo_wait_unreserved(bo, true);
+                               if (unlikely(ret != 0))
+                                       return ret;
+                               goto retry;
+                       } else
+                               return ret;
+               }
+
+               entry->reserved = true;
+               if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
+                       ttm_eu_backoff_reservation(list);
+                       ret = ttm_bo_wait_cpu(bo, false);
+                       if (ret)
+                               return ret;
+                       goto retry;
+               }
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ttm_eu_reserve_buffers);
+
+void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj)
+{
+       struct ttm_validate_buffer *entry;
+
+       list_for_each_entry(entry, list, head) {
+               struct ttm_buffer_object *bo = entry->bo;
+               struct ttm_bo_driver *driver = bo->bdev->driver;
+               void *old_sync_obj;
+
+               spin_lock(&bo->lock);
+               old_sync_obj = bo->sync_obj;
+               bo->sync_obj = driver->sync_obj_ref(sync_obj);
+               bo->sync_obj_arg = entry->new_sync_obj_arg;
+               spin_unlock(&bo->lock);
+               ttm_bo_unreserve(bo);
+               entry->reserved = false;
+               if (old_sync_obj)
+                       driver->sync_obj_unref(&old_sync_obj);
+       }
+}
+EXPORT_SYMBOL(ttm_eu_fence_buffer_objects);
diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
new file mode 100644 (file)
index 0000000..f619ebc
--- /dev/null
@@ -0,0 +1,311 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., 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 <thellstrom-at-vmware-dot-com>
+ */
+
+#include "ttm/ttm_lock.h"
+#include "ttm/ttm_module.h"
+#include <asm/atomic.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+#define TTM_WRITE_LOCK_PENDING    (1 << 0)
+#define TTM_VT_LOCK_PENDING       (1 << 1)
+#define TTM_SUSPEND_LOCK_PENDING  (1 << 2)
+#define TTM_VT_LOCK               (1 << 3)
+#define TTM_SUSPEND_LOCK          (1 << 4)
+
+void ttm_lock_init(struct ttm_lock *lock)
+{
+       spin_lock_init(&lock->lock);
+       init_waitqueue_head(&lock->queue);
+       lock->rw = 0;
+       lock->flags = 0;
+       lock->kill_takers = false;
+       lock->signal = SIGKILL;
+}
+EXPORT_SYMBOL(ttm_lock_init);
+
+void ttm_read_unlock(struct ttm_lock *lock)
+{
+       spin_lock(&lock->lock);
+       if (--lock->rw == 0)
+               wake_up_all(&lock->queue);
+       spin_unlock(&lock->lock);
+}
+EXPORT_SYMBOL(ttm_read_unlock);
+
+static bool __ttm_read_lock(struct ttm_lock *lock)
+{
+       bool locked = false;
+
+       spin_lock(&lock->lock);
+       if (unlikely(lock->kill_takers)) {
+               send_sig(lock->signal, current, 0);
+               spin_unlock(&lock->lock);
+               return false;
+       }
+       if (lock->rw >= 0 && lock->flags == 0) {
+               ++lock->rw;
+               locked = true;
+       }
+       spin_unlock(&lock->lock);
+       return locked;
+}
+
+int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
+{
+       int ret = 0;
+
+       if (interruptible)
+               ret = wait_event_interruptible(lock->queue,
+                                              __ttm_read_lock(lock));
+       else
+               wait_event(lock->queue, __ttm_read_lock(lock));
+       return ret;
+}
+EXPORT_SYMBOL(ttm_read_lock);
+
+static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
+{
+       bool block = true;
+
+       *locked = false;
+
+       spin_lock(&lock->lock);
+       if (unlikely(lock->kill_takers)) {
+               send_sig(lock->signal, current, 0);
+               spin_unlock(&lock->lock);
+               return false;
+       }
+       if (lock->rw >= 0 && lock->flags == 0) {
+               ++lock->rw;
+               block = false;
+               *locked = true;
+       } else if (lock->flags == 0) {
+               block = false;
+       }
+       spin_unlock(&lock->lock);
+
+       return !block;
+}
+
+int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
+{
+       int ret = 0;
+       bool locked;
+
+       if (interruptible)
+               ret = wait_event_interruptible
+                       (lock->queue, __ttm_read_trylock(lock, &locked));
+       else
+               wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
+
+       if (unlikely(ret != 0)) {
+               BUG_ON(locked);
+               return ret;
+       }
+
+       return (locked) ? 0 : -EBUSY;
+}
+
+void ttm_write_unlock(struct ttm_lock *lock)
+{
+       spin_lock(&lock->lock);
+       lock->rw = 0;
+       wake_up_all(&lock->queue);
+       spin_unlock(&lock->lock);
+}
+EXPORT_SYMBOL(ttm_write_unlock);
+
+static bool __ttm_write_lock(struct ttm_lock *lock)
+{
+       bool locked = false;
+
+       spin_lock(&lock->lock);
+       if (unlikely(lock->kill_takers)) {
+               send_sig(lock->signal, current, 0);
+               spin_unlock(&lock->lock);
+               return false;
+       }
+       if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
+               lock->rw = -1;
+               lock->flags &= ~TTM_WRITE_LOCK_PENDING;
+               locked = true;
+       } else {
+               lock->flags |= TTM_WRITE_LOCK_PENDING;
+       }
+       spin_unlock(&lock->lock);
+       return locked;
+}
+
+int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
+{
+       int ret = 0;
+
+       if (interruptible) {
+               ret = wait_event_interruptible(lock->queue,
+                                              __ttm_write_lock(lock));
+               if (unlikely(ret != 0)) {
+                       spin_lock(&lock->lock);
+                       lock->flags &= ~TTM_WRITE_LOCK_PENDING;
+                       wake_up_all(&lock->queue);
+                       spin_unlock(&lock->lock);
+               }
+       } else
+               wait_event(lock->queue, __ttm_read_lock(lock));
+
+       return ret;
+}
+EXPORT_SYMBOL(ttm_write_lock);
+
+void ttm_write_lock_downgrade(struct ttm_lock *lock)
+{
+       spin_lock(&lock->lock);
+       lock->rw = 1;
+       wake_up_all(&lock->queue);
+       spin_unlock(&lock->lock);
+}
+
+static int __ttm_vt_unlock(struct ttm_lock *lock)
+{
+       int ret = 0;
+
+       spin_lock(&lock->lock);
+       if (unlikely(!(lock->flags & TTM_VT_LOCK)))
+               ret = -EINVAL;
+       lock->flags &= ~TTM_VT_LOCK;
+       wake_up_all(&lock->queue);
+       spin_unlock(&lock->lock);
+       printk(KERN_INFO TTM_PFX "vt unlock.\n");
+
+       return ret;
+}
+
+static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
+{
+       struct ttm_base_object *base = *p_base;
+       struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
+       int ret;
+
+       *p_base = NULL;
+       ret = __ttm_vt_unlock(lock);
+       BUG_ON(ret != 0);
+}
+
+static bool __ttm_vt_lock(struct ttm_lock *lock)
+{
+       bool locked = false;
+
+       spin_lock(&lock->lock);
+       if (lock->rw == 0) {
+               lock->flags &= ~TTM_VT_LOCK_PENDING;
+               lock->flags |= TTM_VT_LOCK;
+               locked = true;
+       } else {
+               lock->flags |= TTM_VT_LOCK_PENDING;
+       }
+       spin_unlock(&lock->lock);
+       return locked;
+}
+
+int ttm_vt_lock(struct ttm_lock *lock,
+               bool interruptible,
+               struct ttm_object_file *tfile)
+{
+       int ret = 0;
+
+       if (interruptible) {
+               ret = wait_event_interruptible(lock->queue,
+                                              __ttm_vt_lock(lock));
+               if (unlikely(ret != 0)) {
+                       spin_lock(&lock->lock);
+                       lock->flags &= ~TTM_VT_LOCK_PENDING;
+                       wake_up_all(&lock->queue);
+                       spin_unlock(&lock->lock);
+                       return ret;
+               }
+       } else
+               wait_event(lock->queue, __ttm_vt_lock(lock));
+
+       /*
+        * Add a base-object, the destructor of which will
+        * make sure the lock is released if the client dies
+        * while holding it.
+        */
+
+       ret = ttm_base_object_init(tfile, &lock->base, false,
+                                  ttm_lock_type, &ttm_vt_lock_remove, NULL);
+       if (ret)
+               (void)__ttm_vt_unlock(lock);
+       else {
+               lock->vt_holder = tfile;
+               printk(KERN_INFO TTM_PFX "vt lock.\n");
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(ttm_vt_lock);
+
+int ttm_vt_unlock(struct ttm_lock *lock)
+{
+       return ttm_ref_object_base_unref(lock->vt_holder,
+                                        lock->base.hash.key, TTM_REF_USAGE);
+}
+EXPORT_SYMBOL(ttm_vt_unlock);
+
+void ttm_suspend_unlock(struct ttm_lock *lock)
+{
+       spin_lock(&lock->lock);
+       lock->flags &= ~TTM_SUSPEND_LOCK;
+       wake_up_all(&lock->queue);
+       spin_unlock(&lock->lock);
+}
+
+static bool __ttm_suspend_lock(struct ttm_lock *lock)
+{
+       bool locked = false;
+
+       spin_lock(&lock->lock);
+       if (lock->rw == 0) {
+               lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
+               lock->flags |= TTM_SUSPEND_LOCK;
+               locked = true;
+       } else {
+               lock->flags |= TTM_SUSPEND_LOCK_PENDING;
+       }
+       spin_unlock(&lock->lock);
+       return locked;
+}
+
+void ttm_suspend_lock(struct ttm_lock *lock)
+{
+       wait_event(lock->queue, __ttm_suspend_lock(lock));
+}
index 072c281..f5245c0 100644 (file)
@@ -274,16 +274,17 @@ static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob,
 static int ttm_mem_init_highmem_zone(struct ttm_mem_global *glob,
                                     const struct sysinfo *si)
 {
-       struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL);
+       struct ttm_mem_zone *zone;
        uint64_t mem;
        int ret;
 
-       if (unlikely(!zone))
-               return -ENOMEM;
-
        if (si->totalhigh == 0)
                return 0;
 
+       zone = kzalloc(sizeof(*zone), GFP_KERNEL);
+       if (unlikely(!zone))
+               return -ENOMEM;
+
        mem = si->totalram;
        mem *= si->mem_unit;
 
@@ -322,8 +323,10 @@ static int ttm_mem_init_dma32_zone(struct ttm_mem_global *glob,
         * No special dma32 zone needed.
         */
 
-       if (mem <= ((uint64_t) 1ULL << 32))
+       if (mem <= ((uint64_t) 1ULL << 32)) {
+               kfree(zone);
                return 0;
+       }
 
        /*
         * Limit max dma32 memory to 4GB for now
@@ -460,6 +463,7 @@ void ttm_mem_global_free(struct ttm_mem_global *glob,
 {
        return ttm_mem_global_free_zone(glob, NULL, amount);
 }
+EXPORT_SYMBOL(ttm_mem_global_free);
 
 static int ttm_mem_global_reserve(struct ttm_mem_global *glob,
                                  struct ttm_mem_zone *single_zone,
@@ -533,6 +537,7 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory,
        return ttm_mem_global_alloc_zone(glob, NULL, memory, no_wait,
                                         interruptible);
 }
+EXPORT_SYMBOL(ttm_mem_global_alloc);
 
 int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
                              struct page *page,
@@ -588,3 +593,4 @@ size_t ttm_round_pot(size_t size)
        }
        return 0;
 }
+EXPORT_SYMBOL(ttm_round_pot);
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
new file mode 100644 (file)
index 0000000..1099aba
--- /dev/null
@@ -0,0 +1,452 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., 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 <thellstrom-at-vmware-dot-com>
+ */
+/** @file ttm_ref_object.c
+ *
+ * Base- and reference object implementation for the various
+ * ttm objects. Implements reference counting, minimal security checks
+ * and release on file close.
+ */
+
+/**
+ * struct ttm_object_file
+ *
+ * @tdev: Pointer to the ttm_object_device.
+ *
+ * @lock: Lock that protects the ref_list list and the
+ * ref_hash hash tables.
+ *
+ * @ref_list: List of ttm_ref_objects to be destroyed at
+ * file release.
+ *
+ * @ref_hash: Hash tables of ref objects, one per ttm_ref_type,
+ * for fast lookup of ref objects given a base object.
+ */
+
+#include "ttm/ttm_object.h"
+#include "ttm/ttm_module.h"
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <asm/atomic.h>
+
+struct ttm_object_file {
+       struct ttm_object_device *tdev;
+       rwlock_t lock;
+       struct list_head ref_list;
+       struct drm_open_hash ref_hash[TTM_REF_NUM];
+       struct kref refcount;
+};
+
+/**
+ * struct ttm_object_device
+ *
+ * @object_lock: lock that protects the object_hash hash table.
+ *
+ * @object_hash: hash table for fast lookup of object global names.
+ *
+ * @object_count: Per device object count.
+ *
+ * This is the per-device data structure needed for ttm object management.
+ */
+
+struct ttm_object_device {
+       rwlock_t object_lock;
+       struct drm_open_hash object_hash;
+       atomic_t object_count;
+       struct ttm_mem_global *mem_glob;
+};
+
+/**
+ * struct ttm_ref_object
+ *
+ * @hash: Hash entry for the per-file object reference hash.
+ *
+ * @head: List entry for the per-file list of ref-objects.
+ *
+ * @kref: Ref count.
+ *
+ * @obj: Base object this ref object is referencing.
+ *
+ * @ref_type: Type of ref object.
+ *
+ * This is similar to an idr object, but it also has a hash table entry
+ * that allows lookup with a pointer to the referenced object as a key. In
+ * that way, one can easily detect whether a base object is referenced by
+ * a particular ttm_object_file. It also carries a ref count to avoid creating
+ * multiple ref objects if a ttm_object_file references the same base
+ * object more than once.
+ */
+
+struct ttm_ref_object {
+       struct drm_hash_item hash;
+       struct list_head head;
+       struct kref kref;
+       struct ttm_base_object *obj;
+       enum ttm_ref_type ref_type;
+       struct ttm_object_file *tfile;
+};
+
+static inline struct ttm_object_file *
+ttm_object_file_ref(struct ttm_object_file *tfile)
+{
+       kref_get(&tfile->refcount);
+       return tfile;
+}
+
+static void ttm_object_file_destroy(struct kref *kref)
+{
+       struct ttm_object_file *tfile =
+               container_of(kref, struct ttm_object_file, refcount);
+
+       kfree(tfile);
+}
+
+
+static inline void ttm_object_file_unref(struct ttm_object_file **p_tfile)
+{
+       struct ttm_object_file *tfile = *p_tfile;
+
+       *p_tfile = NULL;
+       kref_put(&tfile->refcount, ttm_object_file_destroy);
+}
+
+
+int ttm_base_object_init(struct ttm_object_file *tfile,
+                        struct ttm_base_object *base,
+                        bool shareable,
+                        enum ttm_object_type object_type,
+                        void (*refcount_release) (struct ttm_base_object **),
+                        void (*ref_obj_release) (struct ttm_base_object *,
+                                                 enum ttm_ref_type ref_type))
+{
+       struct ttm_object_device *tdev = tfile->tdev;
+       int ret;
+
+       base->shareable = shareable;
+       base->tfile = ttm_object_file_ref(tfile);
+       base->refcount_release = refcount_release;
+       base->ref_obj_release = ref_obj_release;
+       base->object_type = object_type;
+       write_lock(&tdev->object_lock);
+       kref_init(&base->refcount);
+       ret = drm_ht_just_insert_please(&tdev->object_hash,
+                                       &base->hash,
+                                       (unsigned long)base, 31, 0, 0);
+       write_unlock(&tdev->object_lock);
+       if (unlikely(ret != 0))
+               goto out_err0;
+
+       ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+       if (unlikely(ret != 0))
+               goto out_err1;
+
+       ttm_base_object_unref(&base);
+
+       return 0;
+out_err1:
+       (void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
+out_err0:
+       return ret;
+}
+EXPORT_SYMBOL(ttm_base_object_init);
+
+static void ttm_release_base(struct kref *kref)
+{
+       struct ttm_base_object *base =
+           container_of(kref, struct ttm_base_object, refcount);
+       struct ttm_object_device *tdev = base->tfile->tdev;
+
+       (void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
+       write_unlock(&tdev->object_lock);
+       if (base->refcount_release) {
+               ttm_object_file_unref(&base->tfile);
+               base->refcount_release(&base);
+       }
+       write_lock(&tdev->object_lock);
+}
+
+void ttm_base_object_unref(struct ttm_base_object **p_base)
+{
+       struct ttm_base_object *base = *p_base;
+       struct ttm_object_device *tdev = base->tfile->tdev;
+
+       *p_base = NULL;
+
+       /*
+        * Need to take the lock here to avoid racing with
+        * users trying to look up the object.
+        */
+
+       write_lock(&tdev->object_lock);
+       (void)kref_put(&base->refcount, &ttm_release_base);
+       write_unlock(&tdev->object_lock);
+}
+EXPORT_SYMBOL(ttm_base_object_unref);
+
+struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
+                                              uint32_t key)
+{
+       struct ttm_object_device *tdev = tfile->tdev;
+       struct ttm_base_object *base;
+       struct drm_hash_item *hash;
+       int ret;
+
+       read_lock(&tdev->object_lock);
+       ret = drm_ht_find_item(&tdev->object_hash, key, &hash);
+
+       if (likely(ret == 0)) {
+               base = drm_hash_entry(hash, struct ttm_base_object, hash);
+               kref_get(&base->refcount);
+       }
+       read_unlock(&tdev->object_lock);
+
+       if (unlikely(ret != 0))
+               return NULL;
+
+       if (tfile != base->tfile && !base->shareable) {
+               printk(KERN_ERR TTM_PFX
+                      "Attempted access of non-shareable object.\n");
+               ttm_base_object_unref(&base);
+               return NULL;
+       }
+
+       return base;
+}
+EXPORT_SYMBOL(ttm_base_object_lookup);
+
+int ttm_ref_object_add(struct ttm_object_file *tfile,
+                      struct ttm_base_object *base,
+                      enum ttm_ref_type ref_type, bool *existed)
+{
+       struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
+       struct ttm_ref_object *ref;
+       struct drm_hash_item *hash;
+       struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
+       int ret = -EINVAL;
+
+       if (existed != NULL)
+               *existed = true;
+
+       while (ret == -EINVAL) {
+               read_lock(&tfile->lock);
+               ret = drm_ht_find_item(ht, base->hash.key, &hash);
+
+               if (ret == 0) {
+                       ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
+                       kref_get(&ref->kref);
+                       read_unlock(&tfile->lock);
+                       break;
+               }
+
+               read_unlock(&tfile->lock);
+               ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
+                                          false, false);
+               if (unlikely(ret != 0))
+                       return ret;
+               ref = kmalloc(sizeof(*ref), GFP_KERNEL);
+               if (unlikely(ref == NULL)) {
+                       ttm_mem_global_free(mem_glob, sizeof(*ref));
+                       return -ENOMEM;
+               }
+
+               ref->hash.key = base->hash.key;
+               ref->obj = base;
+               ref->tfile = tfile;
+               ref->ref_type = ref_type;
+               kref_init(&ref->kref);
+
+               write_lock(&tfile->lock);
+               ret = drm_ht_insert_item(ht, &ref->hash);
+
+               if (likely(ret == 0)) {
+                       list_add_tail(&ref->head, &tfile->ref_list);
+                       kref_get(&base->refcount);
+                       write_unlock(&tfile->lock);
+                       if (existed != NULL)
+                               *existed = false;
+                       break;
+               }
+
+               write_unlock(&tfile->lock);
+               BUG_ON(ret != -EINVAL);
+
+               ttm_mem_global_free(mem_glob, sizeof(*ref));
+               kfree(ref);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(ttm_ref_object_add);
+
+static void ttm_ref_object_release(struct kref *kref)
+{
+       struct ttm_ref_object *ref =
+           container_of(kref, struct ttm_ref_object, kref);
+       struct ttm_base_object *base = ref->obj;
+       struct ttm_object_file *tfile = ref->tfile;
+       struct drm_open_hash *ht;
+       struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
+
+       ht = &tfile->ref_hash[ref->ref_type];
+       (void)drm_ht_remove_item(ht, &ref->hash);
+       list_del(&ref->head);
+       write_unlock(&tfile->lock);
+
+       if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release)
+               base->ref_obj_release(base, ref->ref_type);
+
+       ttm_base_object_unref(&ref->obj);
+       ttm_mem_global_free(mem_glob, sizeof(*ref));
+       kfree(ref);
+       write_lock(&tfile->lock);
+}
+
+int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
+                             unsigned long key, enum ttm_ref_type ref_type)
+{
+       struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
+       struct ttm_ref_object *ref;
+       struct drm_hash_item *hash;
+       int ret;
+
+       write_lock(&tfile->lock);
+       ret = drm_ht_find_item(ht, key, &hash);
+       if (unlikely(ret != 0)) {
+               write_unlock(&tfile->lock);
+               return -EINVAL;
+       }
+       ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
+       kref_put(&ref->kref, ttm_ref_object_release);
+       write_unlock(&tfile->lock);
+       return 0;
+}
+EXPORT_SYMBOL(ttm_ref_object_base_unref);
+
+void ttm_object_file_release(struct ttm_object_file **p_tfile)
+{
+       struct ttm_ref_object *ref;
+       struct list_head *list;
+       unsigned int i;
+       struct ttm_object_file *tfile = *p_tfile;
+
+       *p_tfile = NULL;
+       write_lock(&tfile->lock);
+
+       /*
+        * Since we release the lock within the loop, we have to
+        * restart it from the beginning each time.
+        */
+
+       while (!list_empty(&tfile->ref_list)) {
+               list = tfile->ref_list.next;
+               ref = list_entry(list, struct ttm_ref_object, head);
+               ttm_ref_object_release(&ref->kref);
+       }
+
+       for (i = 0; i < TTM_REF_NUM; ++i)
+               drm_ht_remove(&tfile->ref_hash[i]);
+
+       write_unlock(&tfile->lock);
+       ttm_object_file_unref(&tfile);
+}
+EXPORT_SYMBOL(ttm_object_file_release);
+
+struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev,
+                                            unsigned int hash_order)
+{
+       struct ttm_object_file *tfile = kmalloc(sizeof(*tfile), GFP_KERNEL);
+       unsigned int i;
+       unsigned int j = 0;
+       int ret;
+
+       if (unlikely(tfile == NULL))
+               return NULL;
+
+       rwlock_init(&tfile->lock);
+       tfile->tdev = tdev;
+       kref_init(&tfile->refcount);
+       INIT_LIST_HEAD(&tfile->ref_list);
+
+       for (i = 0; i < TTM_REF_NUM; ++i) {
+               ret = drm_ht_create(&tfile->ref_hash[i], hash_order);
+               if (ret) {
+                       j = i;
+                       goto out_err;
+               }
+       }
+
+       return tfile;
+out_err:
+       for (i = 0; i < j; ++i)
+               drm_ht_remove(&tfile->ref_hash[i]);
+
+       kfree(tfile);
+
+       return NULL;
+}
+EXPORT_SYMBOL(ttm_object_file_init);
+
+struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global
+                                                *mem_glob,
+                                                unsigned int hash_order)
+{
+       struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL);
+       int ret;
+
+       if (unlikely(tdev == NULL))
+               return NULL;
+
+       tdev->mem_glob = mem_glob;
+       rwlock_init(&tdev->object_lock);
+       atomic_set(&tdev->object_count, 0);
+       ret = drm_ht_create(&tdev->object_hash, hash_order);
+
+       if (likely(ret == 0))
+               return tdev;
+
+       kfree(tdev);
+       return NULL;
+}
+EXPORT_SYMBOL(ttm_object_device_init);
+
+void ttm_object_device_release(struct ttm_object_device **p_tdev)
+{
+       struct ttm_object_device *tdev = *p_tdev;
+
+       *p_tdev = NULL;
+
+       write_lock(&tdev->object_lock);
+       drm_ht_remove(&tdev->object_hash);
+       write_unlock(&tdev->object_lock);
+
+       kfree(tdev);
+}
+EXPORT_SYMBOL(ttm_object_device_release);
index 7bcb89f..9c2b1cc 100644 (file)
@@ -192,6 +192,7 @@ int ttm_tt_populate(struct ttm_tt *ttm)
        ttm->state = tt_unbound;
        return 0;
 }
+EXPORT_SYMBOL(ttm_tt_populate);
 
 #ifdef CONFIG_X86
 static inline int ttm_tt_set_page_caching(struct page *p,
index 7d05c4b..80792d3 100644 (file)
@@ -39,8 +39,6 @@
  * Version Information
  */
 
-#define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
 #define DRIVER_DESC "HID core driver"
 #define DRIVER_LICENSE "GPL"
 
@@ -1294,6 +1292,7 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
        { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
@@ -1326,6 +1325,8 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
        { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
@@ -1620,6 +1621,7 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) },
        { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) },
@@ -1918,5 +1920,8 @@ static void __exit hid_exit(void)
 module_init(hid_init);
 module_exit(hid_exit);
 
+MODULE_AUTHOR("Andreas Gal");
+MODULE_AUTHOR("Vojtech Pavlik");
+MODULE_AUTHOR("Jiri Kosina");
 MODULE_LICENSE(DRIVER_LICENSE);
 
index 62e9cb1..998b6f4 100644 (file)
@@ -126,6 +126,8 @@ static const struct hid_device_id cp_devices[] = {
                .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
                .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3),
+               .driver_data = CP_RDESC_SWAPPED_MIN_MAX },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
                .driver_data = CP_2WHEEL_MOUSE_HACK },
        { }
index adbef5d..3839340 100644 (file)
 #define USB_DEVICE_ID_CH_PRO_PEDALS    0x00f2
 #define USB_DEVICE_ID_CH_COMBATSTICK   0x00f4
 #define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE       0x00ff
+#define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK   0x00d3
 
 #define USB_VENDOR_ID_CHERRY           0x046a
 #define USB_DEVICE_ID_CHERRY_CYMOTION  0x0023
 #define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE       0x7417
 #define USB_DEVICE_ID_CYPRESS_BARCODE_1        0xde61
 #define USB_DEVICE_ID_CYPRESS_BARCODE_2        0xde64
+#define USB_DEVICE_ID_CYPRESS_BARCODE_3        0xbca1
 
 #define USB_VENDOR_ID_DEALEXTREAME     0x10c5
 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701        0x819a
 #define USB_DEVICE_ID_S510_RECEIVER_2  0xc517
 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500  0xc512
 #define USB_DEVICE_ID_MX3000_RECEIVER  0xc513
+#define USB_DEVICE_ID_SPACETRAVELLER   0xc623
+#define USB_DEVICE_ID_SPACENAVIGATOR   0xc626
 #define USB_DEVICE_ID_DINOVO_DESKTOP   0xc704
 #define USB_DEVICE_ID_DINOVO_EDGE      0xc714
 #define USB_DEVICE_ID_DINOVO_MINI      0xc71f
 #define USB_VENDOR_ID_NEC              0x073e
 #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
 
+#define USB_VENDOR_ID_NEXTWINDOW       0x1926
+#define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN   0x0003
+
 #define USB_VENDOR_ID_NTRIG                0x1b96
 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN   0x0001
 
index 0f870a3..9fcd3d0 100644 (file)
@@ -33,6 +33,7 @@
 #define LG_NOGET               0x100
 #define LG_FF                  0x200
 #define LG_FF2                 0x400
+#define LG_RDESC_REL_ABS       0x800
 
 /*
  * Certain Logitech keyboards send in report #3 keys which are far
@@ -51,6 +52,13 @@ static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                rdesc[84] = rdesc[89] = 0x4d;
                rdesc[85] = rdesc[90] = 0x10;
        }
+       if ((quirks & LG_RDESC_REL_ABS) && rsize >= 50 &&
+                       rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
+                       rdesc[49] == 0x81 && rdesc[50] == 0x06) {
+               dev_info(&hdev->dev, "fixing up rel/abs in Logitech "
+                               "report descriptor\n");
+               rdesc[33] = rdesc[50] = 0x02;
+       }
 }
 
 #define lg_map_key_clear(c)    hid_map_usage_clear(hi, usage, bit, max, \
@@ -89,6 +97,22 @@ static int lg_ultrax_remote_mapping(struct hid_input *hi,
        return 1;
 }
 
+static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+
+       case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
+       default:
+               return 0;
+
+       }
+       return 1;
+}
+
 static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
@@ -164,6 +188,10 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        lg_ultrax_remote_mapping(hi, usage, bit, max))
                return 1;
 
+       if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
+                       lg_dinovo_mapping(hi, usage, bit, max))
+               return 1;
+
        if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
                return 1;
 
@@ -303,8 +331,13 @@ static const struct hid_device_id lg_devices[] = {
                .driver_data = LG_FF },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
                .driver_data = LG_FF2 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
+               .driver_data = LG_RDESC_REL_ABS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
+               .driver_data = LG_RDESC_REL_ABS },
        { }
 };
+
 MODULE_DEVICE_TABLE(hid, lg_devices);
 
 static struct hid_driver lg_driver = {
index 03bd703..0258289 100644 (file)
@@ -41,8 +41,6 @@
  * Version Information
  */
 
-#define DRIVER_VERSION "v2.6"
-#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
 #define DRIVER_DESC "USB HID core driver"
 #define DRIVER_LICENSE "GPL"
 
@@ -998,7 +996,8 @@ static int usbhid_start(struct hid_device *hid)
        usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
        usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
 
-       usbhid_init_reports(hid);
+       if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
+               usbhid_init_reports(hid);
 
        set_bit(HID_STARTED, &usbhid->iofl);
 
@@ -1395,8 +1394,7 @@ static int __init hid_init(void)
        retval = usb_register(&hid_driver);
        if (retval)
                goto usb_register_fail;
-       printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
-                       DRIVER_DESC "\n");
+       printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
 
        return 0;
 usb_register_fail:
@@ -1423,6 +1421,8 @@ static void __exit hid_exit(void)
 module_init(hid_init);
 module_exit(hid_exit);
 
-MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_AUTHOR("Andreas Gal");
+MODULE_AUTHOR("Vojtech Pavlik");
+MODULE_AUTHOR("Jiri Kosina");
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE(DRIVER_LICENSE);
index 484e3ee..e565dbe 100644 (file)
@@ -1181,12 +1181,11 @@ static void pidff_reset(struct pidff_device *pidff)
        usbhid_wait_io(hid);
 
        if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
-               int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0];
-               while (sim_effects < 2) {
+               while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
                        if (i++ > 20) {
                                printk(KERN_WARNING "hid-pidff: device reports "
                                       "%d simultaneous effects\n",
-                                      sim_effects);
+                                      pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
                                break;
                        }
                        debug("pid_pool requested again");
index 0d9045a..38773dc 100644 (file)
@@ -37,6 +37,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
        { USB_VENDOR_ID_NATSU, USB_DEVICE_ID_NATSU_GAMEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
+       { USB_VENDOR_ID_NEXTWINDOW, USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN, HID_QUIRK_MULTI_INPUT},
        { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
        { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
@@ -53,6 +54,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
@@ -280,7 +282,7 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
        if (idVendor == USB_VENDOR_ID_NCR &&
                        idProduct >= USB_DEVICE_ID_NCR_FIRST &&
                        idProduct <= USB_DEVICE_ID_NCR_LAST)
-                       return HID_QUIRK_NOGET;
+                       return HID_QUIRK_NO_INIT_REPORTS;
 
        down_read(&dquirks_rwsem);
        bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
index 8b6ee24..867e084 100644 (file)
@@ -450,7 +450,6 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
        uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
        if (!uref_multi)
                return -ENOMEM;
-       lock_kernel();
        uref = &uref_multi->uref;
        if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
                if (copy_from_user(uref_multi, user_arg,
@@ -528,7 +527,6 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
 
                case HIDIOCGCOLLECTIONINDEX:
                        i = field->usage[uref->usage_index].collection_index;
-                       unlock_kernel();
                        kfree(uref_multi);
                        return i;
                case HIDIOCGUSAGES:
@@ -547,15 +545,12 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
                }
 
 goodreturn:
-               unlock_kernel();
                kfree(uref_multi);
                return 0;
 fault:
-               unlock_kernel();
                kfree(uref_multi);
                return -EFAULT;
 inval:
-               unlock_kernel();
                kfree(uref_multi);
                return -EINVAL;
        }
index b342926..f843443 100644 (file)
@@ -266,7 +266,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
                         le16_to_cpu(dev->descriptor.idProduct));
 
        usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
-       strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
+       strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
 
        input_dev->name = kbd->name;
        input_dev->phys = kbd->phys;
index 700e93a..9e640c6 100644 (file)
@@ -191,21 +191,27 @@ config SENSORS_ADT7470
          will be called adt7470.
 
 config SENSORS_ADT7473
-       tristate "Analog Devices ADT7473"
+       tristate "Analog Devices ADT7473 (DEPRECATED)"
        depends on I2C && EXPERIMENTAL
+       select SENSORS_ADT7475
        help
          If you say yes here you get support for the Analog Devices
          ADT7473 temperature monitoring chips.
 
+         This driver is deprecated, you should use the adt7475 driver
+         instead.
+
          This driver can also be built as a module. If so, the module
          will be called adt7473.
 
 config SENSORS_ADT7475
-       tristate "Analog Devices ADT7475"
+       tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490"
        depends on I2C && EXPERIMENTAL
+       select HWMON_VID
        help
          If you say yes here you get support for the Analog Devices
-         ADT7475 hardware monitoring chips.
+         ADT7473, ADT7475, ADT7476 and ADT7490 hardware monitoring
+         chips.
 
          This driver can also be build as a module.  If so, the module
          will be called adt7475.
@@ -305,12 +311,12 @@ config SENSORS_F71805F
          will be called f71805f.
 
 config SENSORS_F71882FG
-       tristate "Fintek F71858FG, F71862FG, F71882FG and F8000"
+       tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000"
        depends on EXPERIMENTAL
        help
          If you say yes here you get support for hardware monitoring
-         features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG
-         and F8000 Super-I/O chips.
+         features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG,
+         F71889FG and F8000 Super-I/O chips.
 
          This driver can also be built as a module.  If so, the module
          will be called f71882fg.
@@ -442,6 +448,15 @@ config SENSORS_LM70
          This driver can also be built as a module.  If so, the module
          will be called lm70.
 
+config SENSORS_LM73
+       tristate "National Semiconductor LM73"
+       depends on I2C
+       help
+         If you say yes here you get support for National Semiconductor LM73
+         sensor chips.
+         This driver can also be built as a module.  If so, the module
+         will be called lm73.
+
 config SENSORS_LM75
        tristate "National Semiconductor LM75 and compatibles"
        depends on I2C
@@ -841,7 +856,7 @@ config SENSORS_W83781D
 
 config SENSORS_W83791D
        tristate "Winbond W83791D"
-       depends on I2C && EXPERIMENTAL
+       depends on I2C
        select HWMON_VID
        help
          If you say yes here you get support for the Winbond W83791D chip.
@@ -1008,6 +1023,12 @@ config SENSORS_APPLESMC
          Say Y here if you have an applicable laptop and want to experience
          the awesome power of applesmc.
 
+config SENSORS_MC13783_ADC
+        tristate "Freescale MC13783 ADC"
+        depends on MFD_MC13783
+        help
+          Support for the A/D converter on MC13783 PMIC.
+
 if ACPI
 
 comment "ACPI drivers"
index 9f46cb0..33c2ee1 100644 (file)
@@ -57,6 +57,7 @@ obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
 obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o
 obj-$(CONFIG_SENSORS_LM63)     += lm63.o
 obj-$(CONFIG_SENSORS_LM70)     += lm70.o
+obj-$(CONFIG_SENSORS_LM73)     += lm73.o
 obj-$(CONFIG_SENSORS_LM75)     += lm75.o
 obj-$(CONFIG_SENSORS_LM77)     += lm77.o
 obj-$(CONFIG_SENSORS_LM78)     += lm78.o
@@ -73,6 +74,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
 obj-$(CONFIG_SENSORS_MAX1111)  += max1111.o
 obj-$(CONFIG_SENSORS_MAX1619)  += max1619.o
 obj-$(CONFIG_SENSORS_MAX6650)  += max6650.o
+obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
 obj-$(CONFIG_SENSORS_PC87360)  += pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)  += pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
index afc5943..33acf29 100644 (file)
@@ -288,9 +288,8 @@ static int adm1021_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
-       int i;
-       const char *type_name = "";
-       int conv_rate, status, config;
+       const char *type_name;
+       int conv_rate, status, config, man_id, dev_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                pr_debug("adm1021: detect failed, "
@@ -303,62 +302,37 @@ static int adm1021_detect(struct i2c_client *client, int kind,
                                             ADM1021_REG_CONV_RATE_R);
        config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R);
 
-       /* Now, we do the remaining detection. */
-       if (kind < 0) {
-               if ((status & 0x03) != 0x00 || (config & 0x3F) != 0x00
-                   || (conv_rate & 0xF8) != 0x00) {
-                       pr_debug("adm1021: detect failed, "
-                                "chip not detected!\n");
-                       return -ENODEV;
-               }
+       /* Check unused bits */
+       if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) {
+               pr_debug("adm1021: detect failed, chip not detected!\n");
+               return -ENODEV;
        }
 
        /* Determine the chip type. */
-       if (kind <= 0) {
-               i = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
-               if (i == 0x41)
-                       if ((i2c_smbus_read_byte_data(client,
-                                       ADM1021_REG_DEV_ID) & 0xF0) == 0x30)
-                               kind = adm1023;
-                       else
-                               kind = adm1021;
-               else if (i == 0x49)
-                       kind = thmc10;
-               else if (i == 0x23)
-                       kind = gl523sm;
-               else if ((i == 0x4d) &&
-                        (i2c_smbus_read_byte_data(client,
-                                                  ADM1021_REG_DEV_ID) == 0x01))
-                       kind = max1617a;
-               else if (i == 0x54)
-                       kind = mc1066;
-               /* LM84 Mfr ID in a different place, and it has more unused bits */
-               else if (conv_rate == 0x00
-                        && (kind == 0 /* skip extra detection */
-                            || ((config & 0x7F) == 0x00
-                                && (status & 0xAB) == 0x00)))
-                       kind = lm84;
-               else
-                       kind = max1617;
-       }
+       man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
+       dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID);
 
-       if (kind == max1617) {
-               type_name = "max1617";
-       } else if (kind == max1617a) {
+       if (man_id == 0x4d && dev_id == 0x01)
                type_name = "max1617a";
-       } else if (kind == adm1021) {
-               type_name = "adm1021";
-       } else if (kind == adm1023) {
-               type_name = "adm1023";
-       } else if (kind == thmc10) {
+       else if (man_id == 0x41) {
+               if ((dev_id & 0xF0) == 0x30)
+                       type_name = "adm1023";
+               else
+                       type_name = "adm1021";
+       } else if (man_id == 0x49)
                type_name = "thmc10";
-       } else if (kind == lm84) {
-               type_name = "lm84";
-       } else if (kind == gl523sm) {
+       else if (man_id == 0x23)
                type_name = "gl523sm";
-       } else if (kind == mc1066) {
+       else if (man_id == 0x54)
                type_name = "mc1066";
-       }
+       /* LM84 Mfr ID in a different place, and it has more unused bits */
+       else if (conv_rate == 0x00
+                && (config & 0x7F) == 0x00
+                && (status & 0xAB) == 0x00)
+               type_name = "lm84";
+       else
+               type_name = "max1617";
+
        pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n",
                 type_name, i2c_adapter_id(adapter), client->addr);
        strlcpy(info->type, type_name, I2C_NAME_SIZE);
index 4db04d6..db6ac2b 100644 (file)
@@ -2,7 +2,7 @@
  * adm1025.c
  *
  * Copyright (C) 2000       Chen-Yuan Wu <gwu@esoft.com>
- * Copyright (C) 2003-2008  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009  Jean Delvare <khali@linux-fr.org>
  *
  * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6
  * voltages (including its own power source) and up to two temperatures
@@ -413,67 +413,34 @@ static int adm1025_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
-       const char *name = "";
-       u8 config;
+       const char *name;
+       u8 man_id, chip_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip. A zero kind means that
-        * the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        */
-       config = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG);
-       if (kind < 0) { /* detection */
-               if ((config & 0x80) != 0x00
-                || (i2c_smbus_read_byte_data(client,
-                    ADM1025_REG_STATUS1) & 0xC0) != 0x00
-                || (i2c_smbus_read_byte_data(client,
-                    ADM1025_REG_STATUS2) & 0xBC) != 0x00) {
-                       dev_dbg(&adapter->dev,
-                               "ADM1025 detection failed at 0x%02x.\n",
-                               client->addr);
-                       return -ENODEV;
-               }
+       /* Check for unused bits */
+       if ((i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG) & 0x80)
+        || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0xC0)
+        || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0xBC)) {
+               dev_dbg(&adapter->dev, "ADM1025 detection failed at 0x%02x\n",
+                       client->addr);
+               return -ENODEV;
        }
 
-       if (kind <= 0) { /* identification */
-               u8 man_id, chip_id;
-
-               man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID);
-               chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID);
-
-               if (man_id == 0x41) { /* Analog Devices */
-                       if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */
-                               kind = adm1025;
-                       }
-               } else
-               if (man_id == 0xA1) { /* Philips */
-                       if (client->addr != 0x2E
-                        && (chip_id & 0xF0) == 0x20) { /* NE1619 */
-                               kind = ne1619;
-                       }
-               }
-
-               if (kind <= 0) { /* identification failed */
-                       dev_info(&adapter->dev,
-                           "Unsupported chip (man_id=0x%02X, "
-                           "chip_id=0x%02X).\n", man_id, chip_id);
-                       return -ENODEV;
-               }
-       }
+       /* Identification */
+       chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID);
+       if ((chip_id & 0xF0) != 0x20)
+               return -ENODEV;
 
-       if (kind == adm1025) {
+       man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID);
+       if (man_id == 0x41)
                name = "adm1025";
-       } else if (kind == ne1619) {
+       else if (man_id == 0xA1 && client->addr != 0x2E)
                name = "ne1619";
-       }
+       else
+               return -ENODEV;
+
        strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
index ff7de40..fb53639 100644 (file)
@@ -1672,35 +1672,26 @@ static int adm1026_detect(struct i2c_client *client, int kind,
                i2c_adapter_id(client->adapter), client->addr,
                company, verstep);
 
-       /* If auto-detecting, Determine the chip type. */
-       if (kind <= 0) {
-               dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x "
-                       "...\n", i2c_adapter_id(adapter), address);
-               if (company == ADM1026_COMPANY_ANALOG_DEV
-                   && verstep == ADM1026_VERSTEP_ADM1026) {
-                       kind = adm1026;
-               } else if (company == ADM1026_COMPANY_ANALOG_DEV
-                       && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
-                       dev_err(&adapter->dev, "Unrecognized stepping "
-                               "0x%02x. Defaulting to ADM1026.\n", verstep);
-                       kind = adm1026;
-               } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
-                       dev_err(&adapter->dev, "Found version/stepping "
-                               "0x%02x. Assuming generic ADM1026.\n",
-                               verstep);
-                       kind = any_chip;
-               } else {
-                       dev_dbg(&adapter->dev, "Autodetection failed\n");
-                       /* Not an ADM1026 ... */
-                       if (kind == 0) { /* User used force=x,y */
-                               dev_err(&adapter->dev, "Generic ADM1026 not "
-                                       "found at %d,0x%02x.  Try "
-                                       "force_adm1026.\n",
-                                       i2c_adapter_id(adapter), address);
-                       }
-                       return -ENODEV;
-               }
+       /* Determine the chip type. */
+       dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x...\n",
+               i2c_adapter_id(adapter), address);
+       if (company == ADM1026_COMPANY_ANALOG_DEV
+           && verstep == ADM1026_VERSTEP_ADM1026) {
+               /* Analog Devices ADM1026 */
+       } else if (company == ADM1026_COMPANY_ANALOG_DEV
+               && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+               dev_err(&adapter->dev, "Unrecognized stepping "
+                       "0x%02x. Defaulting to ADM1026.\n", verstep);
+       } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) {
+               dev_err(&adapter->dev, "Found version/stepping "
+                       "0x%02x. Assuming generic ADM1026.\n",
+                       verstep);
+       } else {
+               dev_dbg(&adapter->dev, "Autodetection failed\n");
+               /* Not an ADM1026... */
+               return -ENODEV;
        }
+
        strlcpy(info->type, "adm1026", I2C_NAME_SIZE);
 
        return 0;
index 3671815..ef91e2a 100644 (file)
@@ -301,59 +301,36 @@ static int adm1029_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /* Now we do the detection and identification. A negative kind
-        * means that the driver was loaded with no force parameter
-        * (default), so we must both detect and identify the chip
-        * (actually there is only one possible kind of chip for now, adm1029).
-        * A zero kind means that the driver was loaded with the force
-        * parameter, the detection step shall be skipped. A positive kind
-        * means that the driver was loaded with the force parameter and a
-        * given kind of chip is requested, so both the detection and the
-        * identification steps are skipped. */
-
-       /* Default to an adm1029 if forced */
-       if (kind == 0)
-               kind = adm1029;
-
        /* ADM1029 doesn't have CHIP ID, check just MAN ID
         * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED,
         * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values
         * documented
         */
 
-       if (kind <= 0) {        /* identification */
-               u8 man_id, chip_id, temp_devices_installed, nb_fan_support;
-
-               man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
-               chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
-               temp_devices_installed = i2c_smbus_read_byte_data(client,
+       man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID);
+       chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID);
+       temp_devices_installed = i2c_smbus_read_byte_data(client,
                                        ADM1029_REG_TEMP_DEVICES_INSTALLED);
-               nb_fan_support = i2c_smbus_read_byte_data(client,
+       nb_fan_support = i2c_smbus_read_byte_data(client,
                                                ADM1029_REG_NB_FAN_SUPPORT);
-               /* 0x41 is Analog Devices */
-               if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01
-                   && nb_fan_support == 0x03) {
-                       if ((chip_id & 0xF0) == 0x00) {
-                               kind = adm1029;
-                       } else {
-                               /* There are no "official" CHIP ID, so actually
-                                * we use Major/Minor revision for that */
-                               printk(KERN_INFO
-                                      "adm1029: Unknown major revision %x, "
-                                      "please let us know\n", chip_id);
-                       }
-               }
+       /* 0x41 is Analog Devices */
+       if (man_id != 0x41 || (temp_devices_installed & 0xf9) != 0x01
+           || nb_fan_support != 0x03)
+               return -ENODEV;
 
-               if (kind <= 0) {        /* identification failed */
-                       pr_debug("adm1029: Unsupported chip (man_id=0x%02X, "
-                                "chip_id=0x%02X)\n", man_id, chip_id);
-                       return -ENODEV;
-               }
+       if ((chip_id & 0xF0) != 0x00) {
+               /* There are no "official" CHIP ID, so actually
+                * we use Major/Minor revision for that */
+               pr_info("adm1029: Unknown major revision %x, "
+                       "please let us know\n", chip_id);
+               return -ENODEV;
        }
+
        strlcpy(info->type, "adm1029", I2C_NAME_SIZE);
 
        return 0;
@@ -432,7 +409,7 @@ static int adm1029_remove(struct i2c_client *client)
 }
 
 /*
-function that update the status of the chips (temperature for exemple)
+function that update the status of the chips (temperature for example)
 */
 static struct adm1029_data *adm1029_update_device(struct device *dev)
 {
index 5690595..0e72217 100644 (file)
@@ -817,31 +817,19 @@ static int adm1031_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
-       const char *name = "";
+       const char *name;
+       int id, co;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind < 0) {
-               int id, co;
-               id = i2c_smbus_read_byte_data(client, 0x3d);
-               co = i2c_smbus_read_byte_data(client, 0x3e);
+       id = i2c_smbus_read_byte_data(client, 0x3d);
+       co = i2c_smbus_read_byte_data(client, 0x3e);
 
-               if (!((id == 0x31 || id == 0x30) && co == 0x41))
-                       return -ENODEV;
-               kind = (id == 0x30) ? adm1030 : adm1031;
-       }
-
-       if (kind <= 0)
-               kind = adm1031;
+       if (!((id == 0x31 || id == 0x30) && co == 0x41))
+               return -ENODEV;
+       name = (id == 0x30) ? "adm1030" : "adm1031";
 
-       /* Given the detected chip type, set the chip name and the
-        * auto fan control helper table. */
-       if (kind == adm1030) {
-               name = "adm1030";
-       } else if (kind == adm1031) {
-               name = "adm1031";
-       }
        strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
index 2444b15..20e0481 100644 (file)
@@ -556,51 +556,34 @@ static int adm9240_detect(struct i2c_client *new_client, int kind,
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind == 0) {
-               kind = adm9240;
-       }
-
-       if (kind < 0) {
-
-               /* verify chip: reg address should match i2c address */
-               if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR)
-                               != address) {
-                       dev_err(&adapter->dev, "detect fail: address match, "
-                                       "0x%02x\n", address);
-                       return -ENODEV;
-               }
-
-               /* check known chip manufacturer */
-               man_id = i2c_smbus_read_byte_data(new_client,
-                               ADM9240_REG_MAN_ID);
-               if (man_id == 0x23) {
-                       kind = adm9240;
-               } else if (man_id == 0xda) {
-                       kind = ds1780;
-               } else if (man_id == 0x01) {
-                       kind = lm81;
-               } else {
-                       dev_err(&adapter->dev, "detect fail: unknown manuf, "
-                                       "0x%02x\n", man_id);
-                       return -ENODEV;
-               }
-
-               /* successful detect, print chip info */
-               die_rev = i2c_smbus_read_byte_data(new_client,
-                               ADM9240_REG_DIE_REV);
-               dev_info(&adapter->dev, "found %s revision %u\n",
-                               man_id == 0x23 ? "ADM9240" :
-                               man_id == 0xda ? "DS1780" : "LM81", die_rev);
+       /* verify chip: reg address should match i2c address */
+       if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR)
+                       != address) {
+               dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n",
+                       address);
+               return -ENODEV;
        }
 
-       /* either forced or detected chip kind */
-       if (kind == adm9240) {
+       /* check known chip manufacturer */
+       man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID);
+       if (man_id == 0x23) {
                name = "adm9240";
-       } else if (kind == ds1780) {
+       } else if (man_id == 0xda) {
                name = "ds1780";
-       } else if (kind == lm81) {
+       } else if (man_id == 0x01) {
                name = "lm81";
+       } else {
+               dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n",
+                       man_id);
+               return -ENODEV;
        }
+
+       /* successful detect, print chip info */
+       die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV);
+       dev_info(&adapter->dev, "found %s revision %u\n",
+                man_id == 0x23 ? "ADM9240" :
+                man_id == 0xda ? "DS1780" : "LM81", die_rev);
+
        strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
index 5c39b4a..451977b 100644 (file)
@@ -191,6 +191,7 @@ static int ads7828_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int ch;
 
        /* Check we have a valid client */
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
@@ -202,20 +203,17 @@ static int ads7828_detect(struct i2c_client *client, int kind,
        - Read from the 8 channel addresses
        - Check the top 4 bits of each result are not set (12 data bits)
        */
-       if (kind < 0) {
-               int ch;
-               for (ch = 0; ch < ADS7828_NCH; ch++) {
-                       u16 in_data;
-                       u8 cmd = channel_cmd_byte(ch);
-                       in_data = ads7828_read_value(client, cmd);
-                       if (in_data & 0xF000) {
-                               printk(KERN_DEBUG
-                               "%s : Doesn't look like an ads7828 device\n",
-                               __func__);
-                               return -ENODEV;
-                       }
+       for (ch = 0; ch < ADS7828_NCH; ch++) {
+               u16 in_data;
+               u8 cmd = channel_cmd_byte(ch);
+               in_data = ads7828_read_value(client, cmd);
+               if (in_data & 0xF000) {
+                       pr_debug("%s : Doesn't look like an ads7828 device\n",
+                                __func__);
+                       return -ENODEV;
                }
        }
+
        strlcpy(info->type, "ads7828", I2C_NAME_SIZE);
 
        return 0;
index 1852f27..f9c9562 100644 (file)
@@ -1906,27 +1906,22 @@ static int adt7462_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int vendor, device, revision;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind <= 0) {
-               int vendor, device, revision;
-
-               vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR);
-               if (vendor != ADT7462_VENDOR)
-                       return -ENODEV;
+       vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR);
+       if (vendor != ADT7462_VENDOR)
+               return -ENODEV;
 
-               device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE);
-               if (device != ADT7462_DEVICE)
-                       return -ENODEV;
+       device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE);
+       if (device != ADT7462_DEVICE)
+               return -ENODEV;
 
-               revision = i2c_smbus_read_byte_data(client,
-                                                   ADT7462_REG_REVISION);
-               if (revision != ADT7462_REVISION)
-                       return -ENODEV;
-       } else
-               dev_dbg(&adapter->dev, "detection forced\n");
+       revision = i2c_smbus_read_byte_data(client, ADT7462_REG_REVISION);
+       if (revision != ADT7462_REVISION)
+               return -ENODEV;
 
        strlcpy(info->type, "adt7462", I2C_NAME_SIZE);
 
index 633e1a1..32b1750 100644 (file)
@@ -1229,27 +1229,22 @@ static int adt7470_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int vendor, device, revision;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind <= 0) {
-               int vendor, device, revision;
-
-               vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
-               if (vendor != ADT7470_VENDOR)
-                       return -ENODEV;
+       vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR);
+       if (vendor != ADT7470_VENDOR)
+               return -ENODEV;
 
-               device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
-               if (device != ADT7470_DEVICE)
-                       return -ENODEV;
+       device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE);
+       if (device != ADT7470_DEVICE)
+               return -ENODEV;
 
-               revision = i2c_smbus_read_byte_data(client,
-                                                   ADT7470_REG_REVISION);
-               if (revision != ADT7470_REVISION)
-                       return -ENODEV;
-       } else
-               dev_dbg(&adapter->dev, "detection forced\n");
+       revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION);
+       if (revision != ADT7470_REVISION)
+               return -ENODEV;
 
        strlcpy(info->type, "adt7470", I2C_NAME_SIZE);
 
index 0a6ce23..aea244d 100644 (file)
@@ -174,7 +174,6 @@ static const struct i2c_device_id adt7473_id[] = {
        { "adt7473", adt7473 },
        { }
 };
-MODULE_DEVICE_TABLE(i2c, adt7473_id);
 
 static struct i2c_driver adt7473_driver = {
        .class          = I2C_CLASS_HWMON,
@@ -1090,27 +1089,22 @@ static int adt7473_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int vendor, device, revision;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind <= 0) {
-               int vendor, device, revision;
-
-               vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR);
-               if (vendor != ADT7473_VENDOR)
-                       return -ENODEV;
+       vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR);
+       if (vendor != ADT7473_VENDOR)
+               return -ENODEV;
 
-               device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE);
-               if (device != ADT7473_DEVICE)
-                       return -ENODEV;
+       device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE);
+       if (device != ADT7473_DEVICE)
+               return -ENODEV;
 
-               revision = i2c_smbus_read_byte_data(client,
-                                                   ADT7473_REG_REVISION);
-               if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69)
-                       return -ENODEV;
-       } else
-               dev_dbg(&adapter->dev, "detection forced\n");
+       revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION);
+       if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69)
+               return -ENODEV;
 
        strlcpy(info->type, "adt7473", I2C_NAME_SIZE);
 
@@ -1171,6 +1165,8 @@ static int adt7473_remove(struct i2c_client *client)
 
 static int __init adt7473_init(void)
 {
+       pr_notice("The adt7473 driver is deprecated, please use the adt7475 "
+                 "driver instead\n");
        return i2c_add_driver(&adt7473_driver);
 }
 
index b5a9519..99abfdd 100644 (file)
@@ -3,7 +3,8 @@
  * Copyright (C) 2007-2008, Advanced Micro Devices, Inc.
  * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
  * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com>
-
+ * Copyright (C) 2009 Jean Delvare <khali@linux-fr.org>
+ *
  * Derived from the lm83 driver by Jean Delvare
  *
  * This program is free software; you can redistribute it and/or modify
@@ -17,6 +18,7 @@
 #include <linux/i2c.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
 #include <linux/err.h>
 
 /* Indexes for the sysfs hooks */
 
 /* 7475 Common Registers */
 
-#define REG_VOLTAGE_BASE       0x21
+#define REG_DEVREV2            0x12    /* ADT7490 only */
+
+#define REG_VTT                        0x1E    /* ADT7490 only */
+#define REG_EXTEND3            0x1F    /* ADT7490 only */
+
+#define REG_VOLTAGE_BASE       0x20
 #define REG_TEMP_BASE          0x25
 #define REG_TACH_BASE          0x28
 #define REG_PWM_BASE           0x30
 
 #define REG_DEVID              0x3D
 #define REG_VENDID             0x3E
+#define REG_DEVID2             0x3F
 
 #define REG_STATUS1            0x41
 #define REG_STATUS2            0x42
 
-#define REG_VOLTAGE_MIN_BASE   0x46
-#define REG_VOLTAGE_MAX_BASE   0x47
+#define REG_VID                        0x43    /* ADT7476 only */
+
+#define REG_VOLTAGE_MIN_BASE   0x44
+#define REG_VOLTAGE_MAX_BASE   0x45
 
 #define REG_TEMP_MIN_BASE      0x4E
 #define REG_TEMP_MAX_BASE      0x4F
 
 #define REG_TEMP_OFFSET_BASE   0x70
 
+#define REG_CONFIG2            0x73
+
 #define REG_EXTEND1            0x76
 #define REG_EXTEND2            0x77
+
+#define REG_CONFIG3            0x78
 #define REG_CONFIG5            0x7C
+#define REG_CONFIG4            0x7D
+
+#define REG_STATUS4            0x81    /* ADT7490 only */
+
+#define REG_VTT_MIN            0x84    /* ADT7490 only */
+#define REG_VTT_MAX            0x86    /* ADT7490 only */
+
+#define VID_VIDSEL             0x80    /* ADT7476 only */
+
+#define CONFIG2_ATTN           0x20
+
+#define CONFIG3_SMBALERT       0x01
+#define CONFIG3_THERM          0x02
+
+#define CONFIG4_PINFUNC                0x03
+#define CONFIG4_MAXDUTY                0x08
+#define CONFIG4_ATTN_IN10      0x30
+#define CONFIG4_ATTN_IN43      0xC0
 
 #define CONFIG5_TWOSCOMP       0x01
 #define CONFIG5_TEMPOFFSET     0x02
+#define CONFIG5_VIDGPIO                0x10    /* ADT7476 only */
 
 /* ADT7475 Settings */
 
-#define ADT7475_VOLTAGE_COUNT  2
+#define ADT7475_VOLTAGE_COUNT  5       /* Not counting Vtt */
 #define ADT7475_TEMP_COUNT     3
 #define ADT7475_TACH_COUNT     4
 #define ADT7475_PWM_COUNT      3
 #define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx))
 #define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx))
 
-static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END };
+static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
-I2C_CLIENT_INSMOD_1(adt7475);
+I2C_CLIENT_INSMOD_4(adt7473, adt7475, adt7476, adt7490);
 
 static const struct i2c_device_id adt7475_id[] = {
+       { "adt7473", adt7473 },
        { "adt7475", adt7475 },
+       { "adt7476", adt7476 },
+       { "adt7490", adt7490 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adt7475_id);
@@ -131,15 +167,24 @@ struct adt7475_data {
        unsigned long limits_updated;
        char valid;
 
+       u8 config4;
        u8 config5;
-       u16 alarms;
-       u16 voltage[3][3];
+       u8 has_voltage;
+       u8 bypass_attn;         /* Bypass voltage attenuator */
+       u8 has_pwm2:1;
+       u8 has_fan4:1;
+       u8 has_vid:1;
+       u32 alarms;
+       u16 voltage[3][6];
        u16 temp[7][3];
        u16 tach[2][4];
        u8 pwm[4][3];
        u8 range[3];
        u8 pwmctl[3];
        u8 pwmchan[3];
+
+       u8 vid;
+       u8 vrm;
 };
 
 static struct i2c_driver adt7475_driver;
@@ -196,26 +241,35 @@ static inline u16 rpm2tach(unsigned long rpm)
        return SENSORS_LIMIT((90000 * 60) / rpm, 1, 0xFFFF);
 }
 
-static inline int reg2vcc(u16 reg)
-{
-       return (4296 * reg) / 1000;
-}
+/* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */
+static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = {
+       { 45, 94 },     /* +2.5V */
+       { 175, 525 },   /* Vccp */
+       { 68, 71 },     /* Vcc */
+       { 93, 47 },     /* +5V */
+       { 120, 20 },    /* +12V */
+       { 45, 45 },     /* Vtt */
+};
 
-static inline int reg2vccp(u16 reg)
+static inline int reg2volt(int channel, u16 reg, u8 bypass_attn)
 {
-       return (2929 * reg) / 1000;
-}
+       const int *r = adt7473_in_scaling[channel];
 
-static inline u16 vcc2reg(long vcc)
-{
-       vcc = SENSORS_LIMIT(vcc, 0, 4396);
-       return (vcc * 1000) / 4296;
+       if (bypass_attn & (1 << channel))
+               return DIV_ROUND_CLOSEST(reg * 2250, 1024);
+       return DIV_ROUND_CLOSEST(reg * (r[0] + r[1]) * 2250, r[1] * 1024);
 }
 
-static inline u16 vccp2reg(long vcc)
+static inline u16 volt2reg(int channel, long volt, u8 bypass_attn)
 {
-       vcc = SENSORS_LIMIT(vcc, 0, 2998);
-       return (vcc * 1000) / 2929;
+       const int *r = adt7473_in_scaling[channel];
+       long reg;
+
+       if (bypass_attn & (1 << channel))
+               reg = (volt * 1024) / 2250;
+       else
+               reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250);
+       return SENSORS_LIMIT(reg, 0, 1023) & (0xff << 2);
 }
 
 static u16 adt7475_read_word(struct i2c_client *client, int reg)
@@ -271,12 +325,11 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr,
        switch (sattr->nr) {
        case ALARM:
                return sprintf(buf, "%d\n",
-                              (data->alarms >> (sattr->index + 1)) & 1);
+                              (data->alarms >> sattr->index) & 1);
        default:
                val = data->voltage[sattr->nr][sattr->index];
                return sprintf(buf, "%d\n",
-                              sattr->index ==
-                              0 ? reg2vccp(val) : reg2vcc(val));
+                              reg2volt(sattr->index, val, data->bypass_attn));
        }
 }
 
@@ -296,12 +349,19 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr,
        mutex_lock(&data->lock);
 
        data->voltage[sattr->nr][sattr->index] =
-               sattr->index ? vcc2reg(val) : vccp2reg(val);
+                               volt2reg(sattr->index, val, data->bypass_attn);
 
-       if (sattr->nr == MIN)
-               reg = VOLTAGE_MIN_REG(sattr->index);
-       else
-               reg = VOLTAGE_MAX_REG(sattr->index);
+       if (sattr->index < ADT7475_VOLTAGE_COUNT) {
+               if (sattr->nr == MIN)
+                       reg = VOLTAGE_MIN_REG(sattr->index);
+               else
+                       reg = VOLTAGE_MAX_REG(sattr->index);
+       } else {
+               if (sattr->nr == MIN)
+                       reg = REG_VTT_MIN;
+               else
+                       reg = REG_VTT_MAX;
+       }
 
        i2c_smbus_write_byte_data(client, reg,
                                  data->voltage[sattr->nr][sattr->index] >> 2);
@@ -778,18 +838,103 @@ static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 0);
-static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage,
+static ssize_t show_pwm_at_crit(struct device *dev,
+                               struct device_attribute *devattr, char *buf)
+{
+       struct adt7475_data *data = adt7475_update_device(dev);
+       return sprintf(buf, "%d\n", !!(data->config4 & CONFIG4_MAXDUTY));
+}
+
+static ssize_t set_pwm_at_crit(struct device *dev,
+                              struct device_attribute *devattr,
+                              const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct adt7475_data *data = i2c_get_clientdata(client);
+       long val;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       mutex_lock(&data->lock);
+       data->config4 = i2c_smbus_read_byte_data(client, REG_CONFIG4);
+       if (val)
+               data->config4 |= CONFIG4_MAXDUTY;
+       else
+               data->config4 &= ~CONFIG4_MAXDUTY;
+       i2c_smbus_write_byte_data(client, REG_CONFIG4, data->config4);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct adt7475_data *data = dev_get_drvdata(dev);
+       return sprintf(buf, "%d\n", (int)data->vrm);
+}
+
+static ssize_t set_vrm(struct device *dev, struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       struct adt7475_data *data = dev_get_drvdata(dev);
+       long val;
+
+       if (strict_strtol(buf, 10, &val))
+               return -EINVAL;
+       if (val < 0 || val > 255)
+               return -EINVAL;
+       data->vrm = val;
+
+       return count;
+}
+
+static ssize_t show_vid(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct adt7475_data *data = adt7475_update_device(dev);
+       return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_voltage, NULL, INPUT, 0);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_voltage,
                            set_voltage, MAX, 0);
-static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage,
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_voltage,
                            set_voltage, MIN, 0);
-static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0);
-static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 1);
-static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage,
+static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0);
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 1);
+static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage,
                            set_voltage, MAX, 1);
-static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage,
+static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage,
                            set_voltage, MIN, 1);
-static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1);
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 2);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MAX, 2);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MIN, 2);
+static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 2);
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_voltage, NULL, INPUT, 3);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MAX, 3);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MIN, 3);
+static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_voltage, NULL, ALARM, 3);
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_voltage, NULL, INPUT, 4);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MAX, 4);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MIN, 4);
+static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_voltage, NULL, ALARM, 8);
+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_voltage, NULL, INPUT, 5);
+static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MAX, 5);
+static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_voltage,
+                           set_voltage, MIN, 5);
+static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, show_voltage, NULL, ALARM, 31);
 static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, INPUT, 0);
 static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, show_temp, NULL, ALARM, 0);
 static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_temp, NULL, FAULT, 0);
@@ -893,6 +1038,13 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm,
 static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm,
                            set_pwm, MAX, 2);
 
+/* Non-standard name, might need revisiting */
+static DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO,
+                  show_pwm_at_crit, set_pwm_at_crit);
+
+static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, set_vrm);
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
 static struct attribute *adt7475_attrs[] = {
        &sensor_dev_attr_in1_input.dev_attr.attr,
        &sensor_dev_attr_in1_max.dev_attr.attr,
@@ -940,60 +1092,156 @@ static struct attribute *adt7475_attrs[] = {
        &sensor_dev_attr_fan3_input.dev_attr.attr,
        &sensor_dev_attr_fan3_min.dev_attr.attr,
        &sensor_dev_attr_fan3_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan4_input.dev_attr.attr,
-       &sensor_dev_attr_fan4_min.dev_attr.attr,
-       &sensor_dev_attr_fan4_alarm.dev_attr.attr,
        &sensor_dev_attr_pwm1.dev_attr.attr,
        &sensor_dev_attr_pwm1_freq.dev_attr.attr,
        &sensor_dev_attr_pwm1_enable.dev_attr.attr,
        &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
        &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm2.dev_attr.attr,
-       &sensor_dev_attr_pwm2_freq.dev_attr.attr,
-       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
-       &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
-       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
-       &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm3.dev_attr.attr,
        &sensor_dev_attr_pwm3_freq.dev_attr.attr,
        &sensor_dev_attr_pwm3_enable.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
        &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
+       &dev_attr_pwm_use_point2_pwm_at_crit.attr,
        NULL,
 };
 
-struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
+static struct attribute *fan4_attrs[] = {
+       &sensor_dev_attr_fan4_input.dev_attr.attr,
+       &sensor_dev_attr_fan4_min.dev_attr.attr,
+       &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *pwm2_attrs[] = {
+       &sensor_dev_attr_pwm2.dev_attr.attr,
+       &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+       &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+       &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *in0_attrs[] = {
+       &sensor_dev_attr_in0_input.dev_attr.attr,
+       &sensor_dev_attr_in0_max.dev_attr.attr,
+       &sensor_dev_attr_in0_min.dev_attr.attr,
+       &sensor_dev_attr_in0_alarm.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *in3_attrs[] = {
+       &sensor_dev_attr_in3_input.dev_attr.attr,
+       &sensor_dev_attr_in3_max.dev_attr.attr,
+       &sensor_dev_attr_in3_min.dev_attr.attr,
+       &sensor_dev_attr_in3_alarm.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *in4_attrs[] = {
+       &sensor_dev_attr_in4_input.dev_attr.attr,
+       &sensor_dev_attr_in4_max.dev_attr.attr,
+       &sensor_dev_attr_in4_min.dev_attr.attr,
+       &sensor_dev_attr_in4_alarm.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *in5_attrs[] = {
+       &sensor_dev_attr_in5_input.dev_attr.attr,
+       &sensor_dev_attr_in5_max.dev_attr.attr,
+       &sensor_dev_attr_in5_min.dev_attr.attr,
+       &sensor_dev_attr_in5_alarm.dev_attr.attr,
+       NULL
+};
+
+static struct attribute *vid_attrs[] = {
+       &dev_attr_cpu0_vid.attr,
+       &dev_attr_vrm.attr,
+       NULL
+};
+
+static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs };
+static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs };
+static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs };
+static struct attribute_group in0_attr_group = { .attrs = in0_attrs };
+static struct attribute_group in3_attr_group = { .attrs = in3_attrs };
+static struct attribute_group in4_attr_group = { .attrs = in4_attrs };
+static struct attribute_group in5_attr_group = { .attrs = in5_attrs };
+static struct attribute_group vid_attr_group = { .attrs = vid_attrs };
 
 static int adt7475_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int vendid, devid, devid2;
+       const char *name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind <= 0) {
-               if (adt7475_read(REG_VENDID) != 0x41 ||
-                   adt7475_read(REG_DEVID) != 0x75) {
-                       dev_err(&adapter->dev,
-                               "Couldn't detect a adt7475 part at 0x%02x\n",
-                               (unsigned int)client->addr);
-                       return -ENODEV;
-               }
+       vendid = adt7475_read(REG_VENDID);
+       devid2 = adt7475_read(REG_DEVID2);
+       if (vendid != 0x41 ||           /* Analog Devices */
+           (devid2 & 0xf8) != 0x68)
+               return -ENODEV;
+
+       devid = adt7475_read(REG_DEVID);
+       if (devid == 0x73)
+               name = "adt7473";
+       else if (devid == 0x75 && client->addr == 0x2e)
+               name = "adt7475";
+       else if (devid == 0x76)
+               name = "adt7476";
+       else if ((devid2 & 0xfc) == 0x6c)
+               name = "adt7490";
+       else {
+               dev_dbg(&adapter->dev,
+                       "Couldn't detect an ADT7473/75/76/90 part at "
+                       "0x%02x\n", (unsigned int)client->addr);
+               return -ENODEV;
        }
 
-       strlcpy(info->type, adt7475_id[0].name, I2C_NAME_SIZE);
+       strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
 }
 
+static void adt7475_remove_files(struct i2c_client *client,
+                                struct adt7475_data *data)
+{
+       sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
+       if (data->has_fan4)
+               sysfs_remove_group(&client->dev.kobj, &fan4_attr_group);
+       if (data->has_pwm2)
+               sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group);
+       if (data->has_voltage & (1 << 0))
+               sysfs_remove_group(&client->dev.kobj, &in0_attr_group);
+       if (data->has_voltage & (1 << 3))
+               sysfs_remove_group(&client->dev.kobj, &in3_attr_group);
+       if (data->has_voltage & (1 << 4))
+               sysfs_remove_group(&client->dev.kobj, &in4_attr_group);
+       if (data->has_voltage & (1 << 5))
+               sysfs_remove_group(&client->dev.kobj, &in5_attr_group);
+       if (data->has_vid)
+               sysfs_remove_group(&client->dev.kobj, &vid_attr_group);
+}
+
 static int adt7475_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
+       static const char *names[] = {
+               [adt7473] = "ADT7473",
+               [adt7475] = "ADT7475",
+               [adt7476] = "ADT7476",
+               [adt7490] = "ADT7490",
+       };
+
        struct adt7475_data *data;
-       int i, ret = 0;
+       int i, ret = 0, revision;
+       u8 config2, config3;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (data == NULL)
@@ -1002,6 +1250,70 @@ static int adt7475_probe(struct i2c_client *client,
        mutex_init(&data->lock);
        i2c_set_clientdata(client, data);
 
+       /* Initialize device-specific values */
+       switch (id->driver_data) {
+       case adt7476:
+               data->has_voltage = 0x0e;       /* in1 to in3 */
+               revision = adt7475_read(REG_DEVID2) & 0x07;
+               break;
+       case adt7490:
+               data->has_voltage = 0x3e;       /* in1 to in5 */
+               revision = adt7475_read(REG_DEVID2) & 0x03;
+               if (revision == 0x03)
+                       revision += adt7475_read(REG_DEVREV2);
+               break;
+       default:
+               data->has_voltage = 0x06;       /* in1, in2 */
+               revision = adt7475_read(REG_DEVID2) & 0x07;
+       }
+
+       config3 = adt7475_read(REG_CONFIG3);
+       /* Pin PWM2 may alternatively be used for ALERT output */
+       if (!(config3 & CONFIG3_SMBALERT))
+               data->has_pwm2 = 1;
+       /* Meaning of this bit is inverted for the ADT7473-1 */
+       if (id->driver_data == adt7473 && revision >= 1)
+               data->has_pwm2 = !data->has_pwm2;
+
+       data->config4 = adt7475_read(REG_CONFIG4);
+       /* Pin TACH4 may alternatively be used for THERM */
+       if ((data->config4 & CONFIG4_PINFUNC) == 0x0)
+               data->has_fan4 = 1;
+
+       /* THERM configuration is more complex on the ADT7476 and ADT7490,
+          because 2 different pins (TACH4 and +2.5 Vin) can be used for
+          this function */
+       if (id->driver_data == adt7490) {
+               if ((data->config4 & CONFIG4_PINFUNC) == 0x1 &&
+                   !(config3 & CONFIG3_THERM))
+                       data->has_fan4 = 1;
+       }
+       if (id->driver_data == adt7476 || id->driver_data == adt7490) {
+               if (!(config3 & CONFIG3_THERM) ||
+                   (data->config4 & CONFIG4_PINFUNC) == 0x1)
+                       data->has_voltage |= (1 << 0);          /* in0 */
+       }
+
+       /* On the ADT7476, the +12V input pin may instead be used as VID5,
+          and VID pins may alternatively be used as GPIO */
+       if (id->driver_data == adt7476) {
+               u8 vid = adt7475_read(REG_VID);
+               if (!(vid & VID_VIDSEL))
+                       data->has_voltage |= (1 << 4);          /* in4 */
+
+               data->has_vid = !(adt7475_read(REG_CONFIG5) & CONFIG5_VIDGPIO);
+       }
+
+       /* Voltage attenuators can be bypassed, globally or individually */
+       config2 = adt7475_read(REG_CONFIG2);
+       if (config2 & CONFIG2_ATTN) {
+               data->bypass_attn = (0x3 << 3) | 0x3;
+       } else {
+               data->bypass_attn = ((data->config4 & CONFIG4_ATTN_IN10) >> 4) |
+                                   ((data->config4 & CONFIG4_ATTN_IN43) >> 3);
+       }
+       data->bypass_attn &= data->has_voltage;
+
        /* Call adt7475_read_pwm for all pwm's as this will reprogram any
           pwm's which are disabled to manual mode with 0% duty cycle */
        for (i = 0; i < ADT7475_PWM_COUNT; i++)
@@ -1011,16 +1323,70 @@ static int adt7475_probe(struct i2c_client *client,
        if (ret)
                goto efree;
 
+       /* Features that can be disabled individually */
+       if (data->has_fan4) {
+               ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+       if (data->has_pwm2) {
+               ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+       if (data->has_voltage & (1 << 0)) {
+               ret = sysfs_create_group(&client->dev.kobj, &in0_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+       if (data->has_voltage & (1 << 3)) {
+               ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+       if (data->has_voltage & (1 << 4)) {
+               ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+       if (data->has_voltage & (1 << 5)) {
+               ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+       if (data->has_vid) {
+               data->vrm = vid_which_vrm();
+               ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group);
+               if (ret)
+                       goto eremove;
+       }
+
        data->hwmon_dev = hwmon_device_register(&client->dev);
        if (IS_ERR(data->hwmon_dev)) {
                ret = PTR_ERR(data->hwmon_dev);
                goto eremove;
        }
 
+       dev_info(&client->dev, "%s device, revision %d\n",
+                names[id->driver_data], revision);
+       if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2)
+               dev_info(&client->dev, "Optional features:%s%s%s%s%s\n",
+                        (data->has_voltage & (1 << 0)) ? " in0" : "",
+                        (data->has_voltage & (1 << 4)) ? " in4" : "",
+                        data->has_fan4 ? " fan4" : "",
+                        data->has_pwm2 ? " pwm2" : "",
+                        data->has_vid ? " vid" : "");
+       if (data->bypass_attn)
+               dev_info(&client->dev, "Bypassing attenuators on:%s%s%s%s\n",
+                        (data->bypass_attn & (1 << 0)) ? " in0" : "",
+                        (data->bypass_attn & (1 << 1)) ? " in1" : "",
+                        (data->bypass_attn & (1 << 3)) ? " in3" : "",
+                        (data->bypass_attn & (1 << 4)) ? " in4" : "");
+
        return 0;
 
 eremove:
-       sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
+       adt7475_remove_files(client, data);
 efree:
        kfree(data);
        return ret;
@@ -1031,7 +1397,7 @@ static int adt7475_remove(struct i2c_client *client)
        struct adt7475_data *data = i2c_get_clientdata(client);
 
        hwmon_device_unregister(data->hwmon_dev);
-       sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group);
+       adt7475_remove_files(client, data);
        kfree(data);
 
        return 0;
@@ -1116,7 +1482,7 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct adt7475_data *data = i2c_get_clientdata(client);
-       u8 ext;
+       u16 ext;
        int i;
 
        mutex_lock(&data->lock);
@@ -1127,25 +1493,44 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
                data->alarms = adt7475_read(REG_STATUS2) << 8;
                data->alarms |= adt7475_read(REG_STATUS1);
 
-               ext = adt7475_read(REG_EXTEND1);
-               for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++)
+               ext = (adt7475_read(REG_EXTEND2) << 8) |
+                       adt7475_read(REG_EXTEND1);
+               for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
+                       if (!(data->has_voltage & (1 << i)))
+                               continue;
                        data->voltage[INPUT][i] =
                                (adt7475_read(VOLTAGE_REG(i)) << 2) |
-                               ((ext >> ((i + 1) * 2)) & 3);
+                               ((ext >> (i * 2)) & 3);
+               }
 
-               ext = adt7475_read(REG_EXTEND2);
                for (i = 0; i < ADT7475_TEMP_COUNT; i++)
                        data->temp[INPUT][i] =
                                (adt7475_read(TEMP_REG(i)) << 2) |
-                               ((ext >> ((i + 1) * 2)) & 3);
+                               ((ext >> ((i + 5) * 2)) & 3);
 
-               for (i = 0; i < ADT7475_TACH_COUNT; i++)
+               if (data->has_voltage & (1 << 5)) {
+                       data->alarms |= adt7475_read(REG_STATUS4) << 24;
+                       ext = adt7475_read(REG_EXTEND3);
+                       data->voltage[INPUT][5] = adt7475_read(REG_VTT) << 2 |
+                               ((ext >> 4) & 3);
+               }
+
+               for (i = 0; i < ADT7475_TACH_COUNT; i++) {
+                       if (i == 3 && !data->has_fan4)
+                               continue;
                        data->tach[INPUT][i] =
                                adt7475_read_word(client, TACH_REG(i));
+               }
 
                /* Updated by hw when in auto mode */
-               for (i = 0; i < ADT7475_PWM_COUNT; i++)
+               for (i = 0; i < ADT7475_PWM_COUNT; i++) {
+                       if (i == 1 && !data->has_pwm2)
+                               continue;
                        data->pwm[INPUT][i] = adt7475_read(PWM_REG(i));
+               }
+
+               if (data->has_vid)
+                       data->vid = adt7475_read(REG_VID) & 0x3f;
 
                data->measure_updated = jiffies;
        }
@@ -1153,9 +1538,12 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
        /* Limits and settings, should never change update every 60 seconds */
        if (time_after(jiffies, data->limits_updated + HZ * 60) ||
            !data->valid) {
+               data->config4 = adt7475_read(REG_CONFIG4);
                data->config5 = adt7475_read(REG_CONFIG5);
 
                for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) {
+                       if (!(data->has_voltage & (1 << i)))
+                               continue;
                        /* Adjust values so they match the input precision */
                        data->voltage[MIN][i] =
                                adt7475_read(VOLTAGE_MIN_REG(i)) << 2;
@@ -1163,6 +1551,11 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
                                adt7475_read(VOLTAGE_MAX_REG(i)) << 2;
                }
 
+               if (data->has_voltage & (1 << 5)) {
+                       data->voltage[MIN][5] = adt7475_read(REG_VTT_MIN) << 2;
+                       data->voltage[MAX][5] = adt7475_read(REG_VTT_MAX) << 2;
+               }
+
                for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
                        /* Adjust values so they match the input precision */
                        data->temp[MIN][i] =
@@ -1178,11 +1571,16 @@ static struct adt7475_data *adt7475_update_device(struct device *dev)
                }
                adt7475_read_hystersis(client);
 
-               for (i = 0; i < ADT7475_TACH_COUNT; i++)
+               for (i = 0; i < ADT7475_TACH_COUNT; i++) {
+                       if (i == 3 && !data->has_fan4)
+                               continue;
                        data->tach[MIN][i] =
                                adt7475_read_word(client, TACH_MIN_REG(i));
+               }
 
                for (i = 0; i < ADT7475_PWM_COUNT; i++) {
+                       if (i == 1 && !data->has_pwm2)
+                               continue;
                        data->pwm[MAX][i] = adt7475_read(PWM_MAX_REG(i));
                        data->pwm[MIN][i] = adt7475_read(PWM_MIN_REG(i));
                        /* Set the channel and control information */
index 8acf829..480f80e 100644 (file)
@@ -701,6 +701,7 @@ static int asb100_detect(struct i2c_client *client, int kind,
                         struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int val1, val2;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                pr_debug("asb100.o: detect failed, "
@@ -708,50 +709,30 @@ static int asb100_detect(struct i2c_client *client, int kind,
                return -ENODEV;
        }
 
-       /* The chip may be stuck in some other bank than bank 0. This may
-          make reading other information impossible. Specify a force=... or
-          force_*=... parameter, and the chip will be reset to the right
-          bank. */
-       if (kind < 0) {
-
-               int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK);
-               int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
-
-               /* If we're in bank 0 */
-               if ((!(val1 & 0x07)) &&
-                               /* Check for ASB100 ID (low byte) */
-                               (((!(val1 & 0x80)) && (val2 != 0x94)) ||
-                               /* Check for ASB100 ID (high byte ) */
-                               ((val1 & 0x80) && (val2 != 0x06)))) {
-                       pr_debug("asb100.o: detect failed, "
-                                       "bad chip id 0x%02x!\n", val2);
-                       return -ENODEV;
-               }
+       val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK);
+       val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
 
-       } /* kind < 0 */
+       /* If we're in bank 0 */
+       if ((!(val1 & 0x07)) &&
+                       /* Check for ASB100 ID (low byte) */
+                       (((!(val1 & 0x80)) && (val2 != 0x94)) ||
+                       /* Check for ASB100 ID (high byte ) */
+                       ((val1 & 0x80) && (val2 != 0x06)))) {
+               pr_debug("asb100: detect failed, bad chip id 0x%02x!\n", val2);
+               return -ENODEV;
+       }
 
-       /* We have either had a force parameter, or we have already detected
-          Winbond. Put it now into bank 0 and Vendor ID High Byte */
+       /* Put it now into bank 0 and Vendor ID High Byte */
        i2c_smbus_write_byte_data(client, ASB100_REG_BANK,
                (i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78)
                | 0x80);
 
        /* Determine the chip type. */
-       if (kind <= 0) {
-               int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID);
-               int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
-
-               if ((val1 == 0x31) && (val2 == 0x06))
-                       kind = asb100;
-               else {
-                       if (kind == 0)
-                               dev_warn(&adapter->dev, "ignoring "
-                                       "'force' parameter for unknown chip "
-                                       "at adapter %d, address 0x%02x.\n",
-                                       i2c_adapter_id(adapter), client->addr);
-                       return -ENODEV;
-               }
-       }
+       val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID);
+       val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN);
+
+       if (val1 != 0x31 || val2 != 0x06)
+               return -ENODEV;
 
        strlcpy(info->type, "asb100", I2C_NAME_SIZE);
 
index 27d6257..4377bb0 100644 (file)
@@ -2220,33 +2220,23 @@ static int dme1737_i2c_detect(struct i2c_client *client, int kind,
                return -ENODEV;
        }
 
-       /* A negative kind means that the driver was loaded with no force
-        * parameter (default), so we must identify the chip. */
-       if (kind < 0) {
-               company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY);
-               verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP);
-
-               if (company == DME1737_COMPANY_SMSC &&
-                   (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
-                       kind = dme1737;
-               } else if (company == DME1737_COMPANY_SMSC &&
-                          verstep == SCH5027_VERSTEP) {
-                       kind = sch5027;
-               } else {
-                       return -ENODEV;
-               }
-       }
+       company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY);
+       verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP);
 
-       if (kind == sch5027) {
+       if (company == DME1737_COMPANY_SMSC &&
+           verstep == SCH5027_VERSTEP) {
                name = "sch5027";
-       } else {
-               kind = dme1737;
+
+       } else if (company == DME1737_COMPANY_SMSC &&
+                  (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) {
                name = "dme1737";
+       } else {
+               return -ENODEV;
        }
 
        dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
-                kind == sch5027 ? "SCH5027" : "DME1737", client->addr,
-                verstep);
+                verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737",
+                client->addr, verstep);
        strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
index 53f88f5..2a4c6a0 100644 (file)
@@ -237,20 +237,16 @@ static int ds1621_detect(struct i2c_client *client, int kind,
                return -ENODEV;
 
        /* Now, we do the remaining detection. It is lousy. */
-       if (kind < 0) {
-               /* The NVB bit should be low if no EEPROM write has been 
-                  requested during the latest 10ms, which is highly 
-                  improbable in our case. */
-               conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
-               if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
+       /* The NVB bit should be low if no EEPROM write has been  requested
+          during the latest 10ms, which is highly improbable in our case. */
+       conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF);
+       if (conf < 0 || conf & DS1621_REG_CONFIG_NVB)
+               return -ENODEV;
+       /* The 7 lowest bits of a temperature should always be 0. */
+       for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
+               temp = i2c_smbus_read_word_data(client, DS1621_REG_TEMP[i]);
+               if (temp < 0 || (temp & 0x7f00))
                        return -ENODEV;
-               /* The 7 lowest bits of a temperature should always be 0. */
-               for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) {
-                       temp = i2c_smbus_read_word_data(client,
-                                                       DS1621_REG_TEMP[i]);
-                       if (temp < 0 || (temp & 0x7f00))
-                               return -ENODEV;
-               }
        }
 
        strlcpy(info->type, "ds1621", I2C_NAME_SIZE);
index 4146105..a95fa42 100644 (file)
@@ -48,6 +48,7 @@
 #define SIO_F71858_ID          0x0507  /* Chipset ID */
 #define SIO_F71862_ID          0x0601  /* Chipset ID */
 #define SIO_F71882_ID          0x0541  /* Chipset ID */
+#define SIO_F71889_ID          0x0723  /* Chipset ID */
 #define SIO_F8000_ID           0x0581  /* Chipset ID */
 
 #define REGION_LENGTH          8
@@ -95,12 +96,13 @@ static unsigned short force_id;
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
-enum chips { f71858fg, f71862fg, f71882fg, f8000 };
+enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 };
 
 static const char *f71882fg_names[] = {
        "f71858fg",
        "f71862fg",
        "f71882fg",
+       "f71889fg",
        "f8000",
 };
 
@@ -155,7 +157,7 @@ struct f71882fg_data {
        u8      pwm_auto_point_hyst[2];
        u8      pwm_auto_point_mapping[4];
        u8      pwm_auto_point_pwm[4][5];
-       u8      pwm_auto_point_temp[4][4];
+       s8      pwm_auto_point_temp[4][4];
 };
 
 /* Sysfs in */
@@ -258,7 +260,9 @@ static struct platform_driver f71882fg_driver = {
 
 static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
 
-/* Temp and in attr for the f71858fg */
+/* Temp and in attr for the f71858fg, the f71858fg is special as it
+   has its temperature indexes start at 0 (the others start at 1) and
+   it only has 3 voltage inputs */
 static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
        SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
        SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
@@ -302,8 +306,8 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = {
        SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2),
 };
 
-/* Temp and in attr common to both the f71862fg and f71882fg */
-static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
+/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */
+static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = {
        SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
        SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1),
        SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2),
@@ -371,8 +375,8 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = {
        SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3),
 };
 
-/* Temp and in attr found only on the f71882fg */
-static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = {
+/* For models with in1 alarm capability */
+static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = {
        SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max,
                0, 1),
        SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep,
@@ -383,6 +387,7 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = {
 /* Temp and in attr for the f8000
    Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max)
    is used as hysteresis value to clear alarms
+   Also like the f71858fg its temperature indexes start at 0
  */
 static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
        SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0),
@@ -413,61 +418,70 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = {
 };
 
 /* Fan / PWM attr common to all models */
-static struct sensor_device_attribute_2 fxxxx_fan_attr[] = {
+static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { {
        SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0),
        SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR,
                      show_fan_full_speed,
                      store_fan_full_speed, 0, 0),
        SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0),
-       SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1),
-       SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR,
-                     show_fan_full_speed,
-                     store_fan_full_speed, 0, 1),
-       SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1),
-       SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
-       SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR,
-                     show_fan_full_speed,
-                     store_fan_full_speed, 0, 2),
-       SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
-
        SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0),
        SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
                      store_pwm_enable, 0, 0),
        SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR,
                      show_pwm_interpolate, store_pwm_interpolate, 0, 0),
-       SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
-                     show_pwm_auto_point_channel,
-                     store_pwm_auto_point_channel, 0, 0),
-
+}, {
+       SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1),
+       SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR,
+                     show_fan_full_speed,
+                     store_fan_full_speed, 0, 1),
+       SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1),
        SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1),
        SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
                      store_pwm_enable, 0, 1),
        SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR,
                      show_pwm_interpolate, store_pwm_interpolate, 0, 1),
-       SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
-                     show_pwm_auto_point_channel,
-                     store_pwm_auto_point_channel, 0, 1),
-
+}, {
+       SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2),
+       SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR,
+                     show_fan_full_speed,
+                     store_fan_full_speed, 0, 2),
+       SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2),
        SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2),
        SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
                      store_pwm_enable, 0, 2),
        SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR,
                      show_pwm_interpolate, store_pwm_interpolate, 0, 2),
-       SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
-                     show_pwm_auto_point_channel,
-                     store_pwm_auto_point_channel, 0, 2),
-};
+}, {
+       SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+       SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
+                     show_fan_full_speed,
+                     store_fan_full_speed, 0, 3),
+       SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
+       SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
+       SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
+                     store_pwm_enable, 0, 3),
+       SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR,
+                     show_pwm_interpolate, store_pwm_interpolate, 0, 3),
+} };
 
-/* Fan / PWM attr for the f71862fg, less pwms and less zones per pwm than the
-   f71882fg */
-static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
+/* Attr for models which can beep on Fan alarm */
+static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = {
        SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
                store_fan_beep, 0, 0),
        SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
                store_fan_beep, 0, 1),
        SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
                store_fan_beep, 0, 2),
+       SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
+               store_fan_beep, 0, 3),
+};
 
+/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the
+   f71858fg / f71882fg / f71889fg */
+static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = {
+       SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 0),
        SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      1, 0),
@@ -487,6 +501,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
        SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 0),
 
+       SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 1),
        SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      1, 1),
@@ -506,6 +523,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
        SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 1),
 
+       SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 2),
        SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      1, 2),
@@ -526,8 +546,11 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 3, 2),
 };
 
-/* Fan / PWM attr common to both the f71882fg and f71858fg */
-static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
+/* PWM attr common to the f71858fg, f71882fg and f71889fg */
+static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { {
+       SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 0),
        SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 0),
@@ -565,7 +588,10 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 2, 0),
        SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 0),
-
+}, {
+       SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 1),
        SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 1),
@@ -603,7 +629,10 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 2, 1),
        SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 1),
-
+}, {
+       SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 2),
        SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 2),
@@ -641,30 +670,7 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 2, 2),
        SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 2),
-};
-
-/* Fan / PWM attr found on the f71882fg but not on the f71858fg */
-static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
-       SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 0),
-       SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 1),
-       SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 2),
-
-       SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
-       SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR,
-                     show_fan_full_speed,
-                     store_fan_full_speed, 0, 3),
-       SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep,
-               store_fan_beep, 0, 3),
-       SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3),
-
-       SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3),
-       SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable,
-                     store_pwm_enable, 0, 3),
-       SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR,
-                     show_pwm_interpolate, store_pwm_interpolate, 0, 3),
+}, {
        SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_channel,
                      store_pwm_auto_point_channel, 0, 3),
@@ -705,14 +711,20 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = {
                      show_pwm_auto_point_temp_hyst, NULL, 2, 3),
        SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 3),
-};
+} };
 
-/* Fan / PWM attr for the f8000, zones mapped to temp instead of to pwm!
-   Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
-   F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */
+/* Fan attr specific to the f8000 (4th fan input can only measure speed) */
 static struct sensor_device_attribute_2 f8000_fan_attr[] = {
        SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3),
+};
 
+/* PWM attr for the f8000, zones mapped to temp instead of to pwm!
+   Also the register block at offset A0 maps to TEMP1 (so our temp2, as the
+   F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */
+static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = {
+       SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 0),
        SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 2),
@@ -751,6 +763,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = {
        SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 2),
 
+       SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 1),
        SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 0),
@@ -789,6 +804,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = {
        SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO,
                      show_pwm_auto_point_temp_hyst, NULL, 3, 0),
 
+       SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR,
+                     show_pwm_auto_point_channel,
+                     store_pwm_auto_point_channel, 0, 2),
        SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR,
                      show_pwm_auto_point_pwm, store_pwm_auto_point_pwm,
                      0, 1),
@@ -929,7 +947,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
        /* Update once every 60 seconds */
        if ( time_after(jiffies, data->last_limits + 60 * HZ ) ||
                        !data->valid) {
-               if (data->type == f71882fg) {
+               if (data->type == f71882fg || data->type == f71889fg) {
                        data->in1_max =
                                f71882fg_read8(data, F71882FG_REG_IN1_HIGH);
                        data->in_beep =
@@ -951,7 +969,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                                                F71882FG_REG_TEMP_HYST(1));
                }
 
-               if (data->type == f71862fg || data->type == f71882fg) {
+               if (data->type == f71862fg || data->type == f71882fg ||
+                   data->type == f71889fg) {
                        data->fan_beep = f71882fg_read8(data,
                                                F71882FG_REG_FAN_BEEP);
                        data->temp_beep = f71882fg_read8(data,
@@ -961,15 +980,33 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                        data->temp_type[2] = (reg & 0x04) ? 2 : 4;
                        data->temp_type[3] = (reg & 0x08) ? 2 : 4;
                }
-               reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
-               if ((reg2 & 0x03) == 0x01)
-                       data->temp_type[1] = 6 /* PECI */;
-               else if ((reg2 & 0x03) == 0x02)
-                       data->temp_type[1] = 5 /* AMDSI */;
-               else if (data->type == f71862fg || data->type == f71882fg)
-                       data->temp_type[1] = (reg & 0x02) ? 2 : 4;
-               else
-                       data->temp_type[1] = 2; /* Only supports BJT */
+               /* Determine temp index 1 sensor type */
+               if (data->type == f71889fg) {
+                       reg2 = f71882fg_read8(data, F71882FG_REG_START);
+                       switch ((reg2 & 0x60) >> 5) {
+                       case 0x00: /* BJT / Thermistor */
+                               data->temp_type[1] = (reg & 0x02) ? 2 : 4;
+                               break;
+                       case 0x01: /* AMDSI */
+                               data->temp_type[1] = 5;
+                               break;
+                       case 0x02: /* PECI */
+                       case 0x03: /* Ibex Peak ?? Report as PECI for now */
+                               data->temp_type[1] = 6;
+                               break;
+                       }
+               } else {
+                       reg2 = f71882fg_read8(data, F71882FG_REG_PECI);
+                       if ((reg2 & 0x03) == 0x01)
+                               data->temp_type[1] = 6; /* PECI */
+                       else if ((reg2 & 0x03) == 0x02)
+                               data->temp_type[1] = 5; /* AMDSI */
+                       else if (data->type == f71862fg ||
+                                data->type == f71882fg)
+                               data->temp_type[1] = (reg & 0x02) ? 2 : 4;
+                       else /* f71858fg and f8000 only support BJT */
+                               data->temp_type[1] = 2;
+               }
 
                data->pwm_enable = f71882fg_read8(data,
                                                  F71882FG_REG_PWM_ENABLE);
@@ -1046,7 +1083,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev)
                if (data->type == f8000)
                        data->fan[3] = f71882fg_read16(data,
                                                F71882FG_REG_FAN(3));
-               if (data->type == f71882fg)
+               if (data->type == f71882fg || data->type == f71889fg)
                        data->in_status = f71882fg_read8(data,
                                                F71882FG_REG_IN_STATUS);
                for (nr = 0; nr < nr_ins; nr++)
@@ -1764,7 +1801,11 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev,
        int pwm = to_sensor_dev_attr_2(devattr)->index;
        int point = to_sensor_dev_attr_2(devattr)->nr;
        long val = simple_strtol(buf, NULL, 10) / 1000;
-       val = SENSORS_LIMIT(val, 0, 255);
+
+       if (data->type == f71889fg)
+               val = SENSORS_LIMIT(val, -128, 127);
+       else
+               val = SENSORS_LIMIT(val, 0, 127);
 
        mutex_lock(&data->update_lock);
        f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val);
@@ -1794,6 +1835,15 @@ static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev,
        return 0;
 }
 
+static void f71882fg_remove_sysfs_files(struct platform_device *pdev,
+       struct sensor_device_attribute_2 *attr, int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++)
+               device_remove_file(&pdev->dev, &attr[i].dev_attr);
+}
+
 static int __devinit f71882fg_probe(struct platform_device *pdev)
 {
        struct f71882fg_data *data;
@@ -1846,16 +1896,17 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                                        ARRAY_SIZE(f71858fg_in_temp_attr));
                        break;
                case f71882fg:
+               case f71889fg:
                        err = f71882fg_create_sysfs_files(pdev,
-                                       f71882fg_in_temp_attr,
-                                       ARRAY_SIZE(f71882fg_in_temp_attr));
+                                       fxxxx_in1_alarm_attr,
+                                       ARRAY_SIZE(fxxxx_in1_alarm_attr));
                        if (err)
                                goto exit_unregister_sysfs;
                        /* fall through! */
                case f71862fg:
                        err = f71882fg_create_sysfs_files(pdev,
-                                       f718x2fg_in_temp_attr,
-                                       ARRAY_SIZE(f718x2fg_in_temp_attr));
+                                       fxxxx_in_temp_attr,
+                                       ARRAY_SIZE(fxxxx_in_temp_attr));
                        break;
                case f8000:
                        err = f71882fg_create_sysfs_files(pdev,
@@ -1883,6 +1934,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                        err = (data->pwm_enable & 0x15) != 0x15;
                        break;
                case f71882fg:
+               case f71889fg:
                        err = 0;
                        break;
                case f8000:
@@ -1897,34 +1949,55 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
                        goto exit_unregister_sysfs;
                }
 
-               err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr,
-                                       ARRAY_SIZE(fxxxx_fan_attr));
+               err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
+                               ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
                if (err)
                        goto exit_unregister_sysfs;
 
-               switch (data->type) {
-               case f71862fg:
-                       err = f71882fg_create_sysfs_files(pdev,
-                                       f71862fg_fan_attr,
-                                       ARRAY_SIZE(f71862fg_fan_attr));
-                       break;
-               case f71882fg:
+               if (data->type == f71862fg || data->type == f71882fg ||
+                   data->type == f71889fg) {
                        err = f71882fg_create_sysfs_files(pdev,
-                                       f71882fg_fan_attr,
-                                       ARRAY_SIZE(f71882fg_fan_attr));
+                                       fxxxx_fan_beep_attr, nr_fans);
                        if (err)
                                goto exit_unregister_sysfs;
-                       /* fall through! */
-               case f71858fg:
+               }
+
+               switch (data->type) {
+               case f71862fg:
                        err = f71882fg_create_sysfs_files(pdev,
-                                       f71882fg_f71858fg_fan_attr,
-                                       ARRAY_SIZE(f71882fg_f71858fg_fan_attr));
+                                       f71862fg_auto_pwm_attr,
+                                       ARRAY_SIZE(f71862fg_auto_pwm_attr));
                        break;
                case f8000:
                        err = f71882fg_create_sysfs_files(pdev,
                                        f8000_fan_attr,
                                        ARRAY_SIZE(f8000_fan_attr));
+                       if (err)
+                               goto exit_unregister_sysfs;
+                       err = f71882fg_create_sysfs_files(pdev,
+                                       f8000_auto_pwm_attr,
+                                       ARRAY_SIZE(f8000_auto_pwm_attr));
                        break;
+               case f71889fg:
+                       for (i = 0; i < nr_fans; i++) {
+                               data->pwm_auto_point_mapping[i] =
+                                       f71882fg_read8(data,
+                                               F71882FG_REG_POINT_MAPPING(i));
+                               if (data->pwm_auto_point_mapping[i] & 0x80)
+                                       break;
+                       }
+                       if (i != nr_fans) {
+                               dev_warn(&pdev->dev,
+                                        "Auto pwm controlled by raw digital "
+                                        "data, disabling pwm auto_point "
+                                        "sysfs attributes\n");
+                               break;
+                       }
+                       /* fall through */
+               default: /* f71858fg / f71882fg */
+                       err = f71882fg_create_sysfs_files(pdev,
+                               &fxxxx_auto_pwm_attr[0][0],
+                               ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
                }
                if (err)
                        goto exit_unregister_sysfs;
@@ -1954,33 +2027,76 @@ exit_free:
 
 static int f71882fg_remove(struct platform_device *pdev)
 {
-       int i;
        struct f71882fg_data *data = platform_get_drvdata(pdev);
+       int nr_fans = (data->type == f71882fg) ? 4 : 3;
+       u8 start_reg = f71882fg_read8(data, F71882FG_REG_START);
 
        platform_set_drvdata(pdev, NULL);
        if (data->hwmon_dev)
                hwmon_device_unregister(data->hwmon_dev);
 
-       /* Note we are not looping over all attr arrays we have as the ones
-          below are supersets of the ones skipped. */
        device_remove_file(&pdev->dev, &dev_attr_name);
 
-       for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++)
-               device_remove_file(&pdev->dev,
-                                       &f718x2fg_in_temp_attr[i].dev_attr);
-
-       for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++)
-               device_remove_file(&pdev->dev,
-                                       &f71882fg_in_temp_attr[i].dev_attr);
+       if (start_reg & 0x01) {
+               switch (data->type) {
+               case f71858fg:
+                       if (data->temp_config & 0x10)
+                               f71882fg_remove_sysfs_files(pdev,
+                                       f8000_in_temp_attr,
+                                       ARRAY_SIZE(f8000_in_temp_attr));
+                       else
+                               f71882fg_remove_sysfs_files(pdev,
+                                       f71858fg_in_temp_attr,
+                                       ARRAY_SIZE(f71858fg_in_temp_attr));
+                       break;
+               case f71882fg:
+               case f71889fg:
+                       f71882fg_remove_sysfs_files(pdev,
+                                       fxxxx_in1_alarm_attr,
+                                       ARRAY_SIZE(fxxxx_in1_alarm_attr));
+                       /* fall through! */
+               case f71862fg:
+                       f71882fg_remove_sysfs_files(pdev,
+                                       fxxxx_in_temp_attr,
+                                       ARRAY_SIZE(fxxxx_in_temp_attr));
+                       break;
+               case f8000:
+                       f71882fg_remove_sysfs_files(pdev,
+                                       f8000_in_temp_attr,
+                                       ARRAY_SIZE(f8000_in_temp_attr));
+                       break;
+               }
+       }
 
-       for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++)
-               device_remove_file(&pdev->dev, &fxxxx_fan_attr[i].dev_attr);
+       if (start_reg & 0x02) {
+               f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0],
+                               ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans);
 
-       for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++)
-               device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr);
+               if (data->type == f71862fg || data->type == f71882fg ||
+                   data->type == f71889fg)
+                       f71882fg_remove_sysfs_files(pdev,
+                                       fxxxx_fan_beep_attr, nr_fans);
 
-       for (i = 0; i < ARRAY_SIZE(f8000_fan_attr); i++)
-               device_remove_file(&pdev->dev, &f8000_fan_attr[i].dev_attr);
+               switch (data->type) {
+               case f71862fg:
+                       f71882fg_remove_sysfs_files(pdev,
+                                       f71862fg_auto_pwm_attr,
+                                       ARRAY_SIZE(f71862fg_auto_pwm_attr));
+                       break;
+               case f8000:
+                       f71882fg_remove_sysfs_files(pdev,
+                                       f8000_fan_attr,
+                                       ARRAY_SIZE(f8000_fan_attr));
+                       f71882fg_remove_sysfs_files(pdev,
+                                       f8000_auto_pwm_attr,
+                                       ARRAY_SIZE(f8000_auto_pwm_attr));
+                       break;
+               default: /* f71858fg / f71882fg / f71889fg */
+                       f71882fg_remove_sysfs_files(pdev,
+                               &fxxxx_auto_pwm_attr[0][0],
+                               ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
+               }
+       }
 
        kfree(data);
 
@@ -2012,11 +2128,15 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
        case SIO_F71882_ID:
                sio_data->type = f71882fg;
                break;
+       case SIO_F71889_ID:
+               sio_data->type = f71889fg;
+               break;
        case SIO_F8000_ID:
                sio_data->type = f8000;
                break;
        default:
-               printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n");
+               printk(KERN_INFO DRVNAME ": Unsupported Fintek device: %04x\n",
+                      (unsigned int)devid);
                goto exit;
        }
 
index e2107e5..40dfbcd 100644 (file)
@@ -681,30 +681,20 @@ static int f75375_detect(struct i2c_client *client, int kind,
                         struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
-       u8 version = 0;
-       const char *name = "";
-
-       if (kind < 0) {
-               u16 vendid = f75375_read16(client, F75375_REG_VENDOR);
-               u16 chipid = f75375_read16(client, F75375_CHIP_ID);
-               version = f75375_read8(client, F75375_REG_VERSION);
-               if (chipid == 0x0306 && vendid == 0x1934) {
-                       kind = f75375;
-               } else if (chipid == 0x0204 && vendid == 0x1934) {
-                       kind = f75373;
-               } else {
-                       dev_err(&adapter->dev,
-                               "failed,%02X,%02X,%02X\n",
-                               chipid, version, vendid);
-                       return -ENODEV;
-               }
-       }
+       u16 vendid, chipid;
+       u8 version;
+       const char *name;
 
-       if (kind == f75375) {
+       vendid = f75375_read16(client, F75375_REG_VENDOR);
+       chipid = f75375_read16(client, F75375_CHIP_ID);
+       if (chipid == 0x0306 && vendid == 0x1934)
                name = "f75375";
-       } else if (kind == f75373) {
+       else if (chipid == 0x0204 && vendid == 0x1934)
                name = "f75373";
-       }
+       else
+               return -ENODEV;
+
+       version = f75375_read8(client, F75375_REG_VERSION);
        dev_info(&adapter->dev, "found %s version: %02X\n", name, version);
        strlcpy(info->type, name, I2C_NAME_SIZE);
 
index da1b1f9..281829c 100644 (file)
@@ -1000,43 +1000,38 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy)
        }
 }
 
-static int fschmd_detect(struct i2c_client *client, int kind,
+static int fschmd_detect(struct i2c_client *client, int _kind,
                         struct i2c_board_info *info)
 {
+       enum chips kind;
        struct i2c_adapter *adapter = client->adapter;
+       char id[4];
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
        /* Detect & Identify the chip */
-       if (kind <= 0) {
-               char id[4];
-
-               id[0] = i2c_smbus_read_byte_data(client,
-                               FSCHMD_REG_IDENT_0);
-               id[1] = i2c_smbus_read_byte_data(client,
-                               FSCHMD_REG_IDENT_1);
-               id[2] = i2c_smbus_read_byte_data(client,
-                               FSCHMD_REG_IDENT_2);
-               id[3] = '\0';
-
-               if (!strcmp(id, "PEG"))
-                       kind = fscpos;
-               else if (!strcmp(id, "HER"))
-                       kind = fscher;
-               else if (!strcmp(id, "SCY"))
-                       kind = fscscy;
-               else if (!strcmp(id, "HRC"))
-                       kind = fschrc;
-               else if (!strcmp(id, "HMD"))
-                       kind = fschmd;
-               else if (!strcmp(id, "HDS"))
-                       kind = fschds;
-               else if (!strcmp(id, "SYL"))
-                       kind = fscsyl;
-               else
-                       return -ENODEV;
-       }
+       id[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_0);
+       id[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_1);
+       id[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_2);
+       id[3] = '\0';
+
+       if (!strcmp(id, "PEG"))
+               kind = fscpos;
+       else if (!strcmp(id, "HER"))
+               kind = fscher;
+       else if (!strcmp(id, "SCY"))
+               kind = fscscy;
+       else if (!strcmp(id, "HRC"))
+               kind = fschrc;
+       else if (!strcmp(id, "HMD"))
+               kind = fschmd;
+       else if (!strcmp(id, "HDS"))
+               kind = fschds;
+       else if (!strcmp(id, "SYL"))
+               kind = fscsyl;
+       else
+               return -ENODEV;
 
        strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE);
 
index 7820df4..1d69458 100644 (file)
@@ -488,36 +488,21 @@ static int gl518_detect(struct i2c_client *client, int kind,
                        struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
-       int i;
+       int rev;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
                                     I2C_FUNC_SMBUS_WORD_DATA))
                return -ENODEV;
 
        /* Now, we do the remaining detection. */
-
-       if (kind < 0) {
-               if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80)
-                || (gl518_read_value(client, GL518_REG_CONF) & 0x80))
-                       return -ENODEV;
-       }
+       if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80)
+        || (gl518_read_value(client, GL518_REG_CONF) & 0x80))
+               return -ENODEV;
 
        /* Determine the chip type. */
-       if (kind <= 0) {
-               i = gl518_read_value(client, GL518_REG_REVISION);
-               if (i == 0x00) {
-                       kind = gl518sm_r00;
-               } else if (i == 0x80) {
-                       kind = gl518sm_r80;
-               } else {
-                       if (kind <= 0)
-                               dev_info(&adapter->dev,
-                                   "Ignoring 'force' parameter for unknown "
-                                   "chip at adapter %d, address 0x%02x\n",
-                                   i2c_adapter_id(adapter), client->addr);
-                       return -ENODEV;
-               }
-       }
+       rev = gl518_read_value(client, GL518_REG_REVISION);
+       if (rev != 0x00 && rev != 0x80)
+               return -ENODEV;
 
        strlcpy(info->type, "gl518sm", I2C_NAME_SIZE);
 
index 19616f2..92b5720 100644 (file)
@@ -691,13 +691,11 @@ static int gl520_detect(struct i2c_client *client, int kind,
                return -ENODEV;
 
        /* Determine the chip type. */
-       if (kind < 0) {
-               if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) ||
-                   ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
-                   ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) {
-                       dev_dbg(&client->dev, "Unknown chip type, skipping\n");
-                       return -ENODEV;
-               }
+       if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) ||
+           ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) ||
+           ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) {
+               dev_dbg(&client->dev, "Unknown chip type, skipping\n");
+               return -ENODEV;
        }
 
        strlcpy(info->type, "gl520sm", I2C_NAME_SIZE);
index a3749cb..0ffe84d 100644 (file)
@@ -124,6 +124,8 @@ superio_exit(void)
 #define IT87_BASE_REG 0x60
 
 /* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_GPIO3_REG     0x27
+#define IT87_SIO_GPIO5_REG     0x29
 #define IT87_SIO_PINX2_REG     0x2c    /* Pin selection */
 #define IT87_SIO_VID_REG       0xfc    /* VID value */
 
@@ -244,7 +246,9 @@ struct it87_sio_data {
        /* Values read from Super-I/O config space */
        u8 revision;
        u8 vid_value;
-       /* Values set based on DMI strings */
+       /* Features skipped based on config or DMI */
+       u8 skip_vid;
+       u8 skip_fan;
        u8 skip_pwm;
 };
 
@@ -1028,11 +1032,35 @@ static int __init it87_find(unsigned short *address,
                chip_type, *address, sio_data->revision);
 
        /* Read GPIO config and VID value from LDN 7 (GPIO) */
-       if (sio_data->type != it87) {
+       if (sio_data->type == it87) {
+               /* The IT8705F doesn't have VID pins at all */
+               sio_data->skip_vid = 1;
+       } else {
                int reg;
 
                superio_select(GPIO);
-               if (sio_data->type == it8718 || sio_data->type == it8720)
+               /* We need at least 4 VID pins */
+               reg = superio_inb(IT87_SIO_GPIO3_REG);
+               if (reg & 0x0f) {
+                       pr_info("it87: VID is disabled (pins used for GPIO)\n");
+                       sio_data->skip_vid = 1;
+               }
+
+               /* Check if fan3 is there or not */
+               if (reg & (1 << 6))
+                       sio_data->skip_pwm |= (1 << 2);
+               if (reg & (1 << 7))
+                       sio_data->skip_fan |= (1 << 2);
+
+               /* Check if fan2 is there or not */
+               reg = superio_inb(IT87_SIO_GPIO5_REG);
+               if (reg & (1 << 1))
+                       sio_data->skip_pwm |= (1 << 1);
+               if (reg & (1 << 2))
+                       sio_data->skip_fan |= (1 << 1);
+
+               if ((sio_data->type == it8718 || sio_data->type == it8720)
+                && !(sio_data->skip_vid))
                        sio_data->vid_value = superio_inb(IT87_SIO_VID_REG);
 
                reg = superio_inb(IT87_SIO_PINX2_REG);
@@ -1236,8 +1264,7 @@ static int __devinit it87_probe(struct platform_device *pdev)
                }
        }
 
-       if (data->type == it8712 || data->type == it8716
-        || data->type == it8718 || data->type == it8720) {
+       if (!sio_data->skip_vid) {
                data->vrm = vid_which_vrm();
                /* VID reading from Super-I/O config space if available */
                data->vid = sio_data->vid_value;
@@ -1355,8 +1382,10 @@ static int __devinit it87_check_pwm(struct device *dev)
 /* Called when we have found a new IT87. */
 static void __devinit it87_init_device(struct platform_device *pdev)
 {
+       struct it87_sio_data *sio_data = pdev->dev.platform_data;
        struct it87_data *data = platform_get_drvdata(pdev);
        int tmp, i;
+       u8 mask;
 
        /* initialize to sane defaults:
         * - if the chip is in manual pwm mode, this will be overwritten with
@@ -1402,10 +1431,11 @@ static void __devinit it87_init_device(struct platform_device *pdev)
        }
 
        /* Check if tachometers are reset manually or by some reason */
+       mask = 0x70 & ~(sio_data->skip_fan << 4);
        data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL);
-       if ((data->fan_main_ctrl & 0x70) == 0) {
+       if ((data->fan_main_ctrl & mask) == 0) {
                /* Enable all fan tachometers */
-               data->fan_main_ctrl |= 0x70;
+               data->fan_main_ctrl |= mask;
                it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
        }
        data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
@@ -1428,6 +1458,9 @@ static void __devinit it87_init_device(struct platform_device *pdev)
                }
        }
 
+       /* Fan input pins may be used for alternative functions */
+       data->has_fan &= ~sio_data->skip_fan;
+
        /* Set current fan mode registers and the default settings for the
         * other mode registers */
        for (i = 0; i < 3; i++) {
index 3195a26..5da66ab 100644 (file)
@@ -427,40 +427,34 @@ static int lm63_detect(struct i2c_client *new_client, int kind,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
+       u8 man_id, chip_id, reg_config1, reg_config2;
+       u8 reg_alert_status, reg_alert_mask;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind < 0) { /* must identify */
-               u8 man_id, chip_id, reg_config1, reg_config2;
-               u8 reg_alert_status, reg_alert_mask;
-
-               man_id = i2c_smbus_read_byte_data(new_client,
-                        LM63_REG_MAN_ID);
-               chip_id = i2c_smbus_read_byte_data(new_client,
-                         LM63_REG_CHIP_ID);
-               reg_config1 = i2c_smbus_read_byte_data(new_client,
-                             LM63_REG_CONFIG1);
-               reg_config2 = i2c_smbus_read_byte_data(new_client,
-                             LM63_REG_CONFIG2);
-               reg_alert_status = i2c_smbus_read_byte_data(new_client,
-                                  LM63_REG_ALERT_STATUS);
-               reg_alert_mask = i2c_smbus_read_byte_data(new_client,
-                                LM63_REG_ALERT_MASK);
-
-               if (man_id == 0x01 /* National Semiconductor */
-                && chip_id == 0x41 /* LM63 */
-                && (reg_config1 & 0x18) == 0x00
-                && (reg_config2 & 0xF8) == 0x00
-                && (reg_alert_status & 0x20) == 0x00
-                && (reg_alert_mask & 0xA4) == 0xA4) {
-                       kind = lm63;
-               } else { /* failed */
-                       dev_dbg(&adapter->dev, "Unsupported chip "
-                               "(man_id=0x%02X, chip_id=0x%02X).\n",
-                               man_id, chip_id);
-                       return -ENODEV;
-               }
+       man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID);
+       chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID);
+
+       reg_config1 = i2c_smbus_read_byte_data(new_client,
+                     LM63_REG_CONFIG1);
+       reg_config2 = i2c_smbus_read_byte_data(new_client,
+                     LM63_REG_CONFIG2);
+       reg_alert_status = i2c_smbus_read_byte_data(new_client,
+                          LM63_REG_ALERT_STATUS);
+       reg_alert_mask = i2c_smbus_read_byte_data(new_client,
+                        LM63_REG_ALERT_MASK);
+
+       if (man_id != 0x01 /* National Semiconductor */
+        || chip_id != 0x41 /* LM63 */
+        || (reg_config1 & 0x18) != 0x00
+        || (reg_config2 & 0xF8) != 0x00
+        || (reg_alert_status & 0x20) != 0x00
+        || (reg_alert_mask & 0xA4) != 0xA4) {
+               dev_dbg(&adapter->dev,
+                       "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n",
+                       man_id, chip_id);
+               return -ENODEV;
        }
 
        strlcpy(info->type, "lm63", I2C_NAME_SIZE);
diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c
new file mode 100644 (file)
index 0000000..0bf8b2a
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * LM73 Sensor driver
+ * Based on LM75
+ *
+ * Copyright (C) 2007, CenoSYS (www.cenosys.com).
+ * Copyright (C) 2009, Bollore telecom (www.bolloretelecom.eu).
+ *
+ * Guillaume Ligneul <guillaume.ligneul@gmail.com>
+ * Adrien Demarez <adrien.demarez@bolloretelecom.eu>
+ * Jeremy Laine <jeremy.laine@bolloretelecom.eu>
+ *
+ * This software program is licensed subject to the GNU General Public License
+ * (GPL).Version 2,June 1991, available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c,
+                                       0x4d, 0x4e, I2C_CLIENT_END };
+
+/* Insmod parameters */
+I2C_CLIENT_INSMOD_1(lm73);
+
+/* LM73 registers */
+#define LM73_REG_INPUT         0x00
+#define LM73_REG_CONF          0x01
+#define LM73_REG_MAX           0x02
+#define LM73_REG_MIN           0x03
+#define LM73_REG_CTRL          0x04
+#define LM73_REG_ID            0x07
+
+#define LM73_ID                        0x9001 /* or 0x190 after a swab16() */
+#define DRVNAME                        "lm73"
+#define LM73_TEMP_MIN          (-40)
+#define LM73_TEMP_MAX          150
+
+/*-----------------------------------------------------------------------*/
+
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *da,
+                       const char *buf, size_t count)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct i2c_client *client = to_i2c_client(dev);
+       long temp;
+       short value;
+
+       int status = strict_strtol(buf, 10, &temp);
+       if (status < 0)
+               return status;
+
+       /* Write value */
+       value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4),
+               (LM73_TEMP_MAX*4)) << 5;
+       i2c_smbus_write_word_data(client, attr->index, swab16(value));
+       return count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *da,
+                        char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+       struct i2c_client *client = to_i2c_client(dev);
+       /* use integer division instead of equivalent right shift to
+          guarantee arithmetic shift and preserve the sign */
+       int temp = ((s16) (swab16(i2c_smbus_read_word_data(client,
+               attr->index)))*250) / 32;
+       return sprintf(buf, "%d\n", temp);
+}
+
+
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+                       show_temp, set_temp, LM73_REG_MAX);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+                       show_temp, set_temp, LM73_REG_MIN);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+                       show_temp, NULL, LM73_REG_INPUT);
+
+
+static struct attribute *lm73_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+
+       NULL
+};
+
+static const struct attribute_group lm73_group = {
+       .attrs = lm73_attributes,
+};
+
+/*-----------------------------------------------------------------------*/
+
+/* device probe and removal */
+
+static int
+lm73_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       struct device *hwmon_dev;
+       int status;
+
+       /* Register sysfs hooks */
+       status = sysfs_create_group(&client->dev.kobj, &lm73_group);
+       if (status)
+               return status;
+
+       hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(hwmon_dev)) {
+               status = PTR_ERR(hwmon_dev);
+               goto exit_remove;
+       }
+       i2c_set_clientdata(client, hwmon_dev);
+
+       dev_info(&client->dev, "%s: sensor '%s'\n",
+                dev_name(hwmon_dev), client->name);
+
+       return 0;
+
+exit_remove:
+       sysfs_remove_group(&client->dev.kobj, &lm73_group);
+       return status;
+}
+
+static int lm73_remove(struct i2c_client *client)
+{
+       struct device *hwmon_dev = i2c_get_clientdata(client);
+
+       hwmon_device_unregister(hwmon_dev);
+       sysfs_remove_group(&client->dev.kobj, &lm73_group);
+       i2c_set_clientdata(client, NULL);
+       return 0;
+}
+
+static const struct i2c_device_id lm73_ids[] = {
+       { "lm73", lm73 },
+       { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm73_ids);
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int lm73_detect(struct i2c_client *new_client, int kind,
+                       struct i2c_board_info *info)
+{
+       struct i2c_adapter *adapter = new_client->adapter;
+       u16 id;
+       u8 ctrl;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+                                       I2C_FUNC_SMBUS_WORD_DATA))
+               return -ENODEV;
+
+       /* Check device ID */
+       id = i2c_smbus_read_word_data(new_client, LM73_REG_ID);
+       ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL);
+       if ((id != LM73_ID) || (ctrl & 0x10))
+               return -ENODEV;
+
+       strlcpy(info->type, "lm73", I2C_NAME_SIZE);
+
+       return 0;
+}
+
+static struct i2c_driver lm73_driver = {
+       .class          = I2C_CLASS_HWMON,
+       .driver = {
+               .name   = "lm73",
+       },
+       .probe          = lm73_probe,
+       .remove         = lm73_remove,
+       .id_table       = lm73_ids,
+       .detect         = lm73_detect,
+       .address_data   = &addr_data,
+};
+
+/* module glue */
+
+static int __init sensors_lm73_init(void)
+{
+       return i2c_add_driver(&lm73_driver);
+}
+
+static void __exit sensors_lm73_exit(void)
+{
+       i2c_del_driver(&lm73_driver);
+}
+
+MODULE_AUTHOR("Guillaume Ligneul <guillaume.ligneul@gmail.com>");
+MODULE_DESCRIPTION("LM73 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm73_init);
+module_exit(sensors_lm73_exit);
index 55bd87c..e392548 100644 (file)
@@ -239,6 +239,7 @@ static int lm75_detect(struct i2c_client *new_client, int kind,
 {
        struct i2c_adapter *adapter = new_client->adapter;
        int i;
+       int cur, conf, hyst, os;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
                                     I2C_FUNC_SMBUS_WORD_DATA))
@@ -251,40 +252,35 @@ static int lm75_detect(struct i2c_client *new_client, int kind,
           The cycling+unused addresses combination is not tested,
           since it would significantly slow the detection down and would
           hardly add any value. */
-       if (kind < 0) {
-               int cur, conf, hyst, os;
-
-               /* Unused addresses */
-               cur = i2c_smbus_read_word_data(new_client, 0);
-               conf = i2c_smbus_read_byte_data(new_client, 1);
-               hyst = i2c_smbus_read_word_data(new_client, 2);
-               if (i2c_smbus_read_word_data(new_client, 4) != hyst
-                || i2c_smbus_read_word_data(new_client, 5) != hyst
-                || i2c_smbus_read_word_data(new_client, 6) != hyst
-                || i2c_smbus_read_word_data(new_client, 7) != hyst)
-                       return -ENODEV;
-               os = i2c_smbus_read_word_data(new_client, 3);
-               if (i2c_smbus_read_word_data(new_client, 4) != os
-                || i2c_smbus_read_word_data(new_client, 5) != os
-                || i2c_smbus_read_word_data(new_client, 6) != os
-                || i2c_smbus_read_word_data(new_client, 7) != os)
-                       return -ENODEV;
 
-               /* Unused bits */
-               if (conf & 0xe0)
-                       return -ENODEV;
+       /* Unused addresses */
+       cur = i2c_smbus_read_word_data(new_client, 0);
+       conf = i2c_smbus_read_byte_data(new_client, 1);
+       hyst = i2c_smbus_read_word_data(new_client, 2);
+       if (i2c_smbus_read_word_data(new_client, 4) != hyst
+        || i2c_smbus_read_word_data(new_client, 5) != hyst
+        || i2c_smbus_read_word_data(new_client, 6) != hyst
+        || i2c_smbus_read_word_data(new_client, 7) != hyst)
+               return -ENODEV;
+       os = i2c_smbus_read_word_data(new_client, 3);
+       if (i2c_smbus_read_word_data(new_client, 4) != os
+        || i2c_smbus_read_word_data(new_client, 5) != os
+        || i2c_smbus_read_word_data(new_client, 6) != os
+        || i2c_smbus_read_word_data(new_client, 7) != os)
+               return -ENODEV;
 
-               /* Addresses cycling */
-               for (i = 8; i < 0xff; i += 8)
-                       if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
-                        || i2c_smbus_read_word_data(new_client, i + 2) != hyst
-                        || i2c_smbus_read_word_data(new_client, i + 3) != os)
-                               return -ENODEV;
+       /* Unused bits */
+       if (conf & 0xe0)
+               return -ENODEV;
+
+       /* Addresses cycling */
+       for (i = 8; i < 0xff; i += 8) {
+               if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+                || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+                || i2c_smbus_read_word_data(new_client, i + 3) != os)
+                       return -ENODEV;
        }
 
-       /* NOTE: we treat "force=..." and "force_lm75=..." the same.
-        * Only new-style driver binding distinguishes chip types.
-        */
        strlcpy(info->type, "lm75", I2C_NAME_SIZE);
 
        return 0;
index 866b401..ac067fd 100644 (file)
@@ -249,6 +249,7 @@ static int lm77_detect(struct i2c_client *new_client, int kind,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
+       int i, cur, conf, hyst, crit, min, max;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
                                     I2C_FUNC_SMBUS_WORD_DATA))
@@ -265,51 +266,48 @@ static int lm77_detect(struct i2c_client *new_client, int kind,
           4. registers cycling over 8-address boundaries
 
           Word-sized registers are high-byte first. */
-       if (kind < 0) {
-               int i, cur, conf, hyst, crit, min, max;
-
-               /* addresses cycling */
-               cur = i2c_smbus_read_word_data(new_client, 0);
-               conf = i2c_smbus_read_byte_data(new_client, 1);
-               hyst = i2c_smbus_read_word_data(new_client, 2);
-               crit = i2c_smbus_read_word_data(new_client, 3);
-               min = i2c_smbus_read_word_data(new_client, 4);
-               max = i2c_smbus_read_word_data(new_client, 5);
-               for (i = 8; i <= 0xff; i += 8)
-                       if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
-                           || i2c_smbus_read_word_data(new_client, i + 2) != hyst
-                           || i2c_smbus_read_word_data(new_client, i + 3) != crit
-                           || i2c_smbus_read_word_data(new_client, i + 4) != min
-                           || i2c_smbus_read_word_data(new_client, i + 5) != max)
-                               return -ENODEV;
-
-               /* sign bits */
-               if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
-                   || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
-                   || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
-                   || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
-                   || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
-                       return -ENODEV;
 
-               /* unused bits */
-               if (conf & 0xe0)
+       /* addresses cycling */
+       cur = i2c_smbus_read_word_data(new_client, 0);
+       conf = i2c_smbus_read_byte_data(new_client, 1);
+       hyst = i2c_smbus_read_word_data(new_client, 2);
+       crit = i2c_smbus_read_word_data(new_client, 3);
+       min = i2c_smbus_read_word_data(new_client, 4);
+       max = i2c_smbus_read_word_data(new_client, 5);
+       for (i = 8; i <= 0xff; i += 8) {
+               if (i2c_smbus_read_byte_data(new_client, i + 1) != conf
+                || i2c_smbus_read_word_data(new_client, i + 2) != hyst
+                || i2c_smbus_read_word_data(new_client, i + 3) != crit
+                || i2c_smbus_read_word_data(new_client, i + 4) != min
+                || i2c_smbus_read_word_data(new_client, i + 5) != max)
                        return -ENODEV;
+       }
 
-               /* 0x06 and 0x07 return the last read value */
-               cur = i2c_smbus_read_word_data(new_client, 0);
-               if (i2c_smbus_read_word_data(new_client, 6) != cur
-                   || i2c_smbus_read_word_data(new_client, 7) != cur)
-                       return -ENODEV;
-               hyst = i2c_smbus_read_word_data(new_client, 2);
-               if (i2c_smbus_read_word_data(new_client, 6) != hyst
-                   || i2c_smbus_read_word_data(new_client, 7) != hyst)
-                       return -ENODEV;
-               min = i2c_smbus_read_word_data(new_client, 4);
-               if (i2c_smbus_read_word_data(new_client, 6) != min
-                   || i2c_smbus_read_word_data(new_client, 7) != min)
-                       return -ENODEV;
+       /* sign bits */
+       if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0)
+        || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0)
+        || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0)
+        || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0)
+        || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0))
+               return -ENODEV;
 
-       }
+       /* unused bits */
+       if (conf & 0xe0)
+               return -ENODEV;
+
+       /* 0x06 and 0x07 return the last read value */
+       cur = i2c_smbus_read_word_data(new_client, 0);
+       if (i2c_smbus_read_word_data(new_client, 6) != cur
+        || i2c_smbus_read_word_data(new_client, 7) != cur)
+               return -ENODEV;
+       hyst = i2c_smbus_read_word_data(new_client, 2);
+       if (i2c_smbus_read_word_data(new_client, 6) != hyst
+        || i2c_smbus_read_word_data(new_client, 7) != hyst)
+               return -ENODEV;
+       min = i2c_smbus_read_word_data(new_client, 4);
+       if (i2c_smbus_read_word_data(new_client, 6) != min
+        || i2c_smbus_read_word_data(new_client, 7) != min)
+               return -ENODEV;
 
        strlcpy(info->type, "lm77", I2C_NAME_SIZE);
 
index f7e7016..5978291 100644 (file)
@@ -576,52 +576,34 @@ static int lm78_i2c_detect(struct i2c_client *client, int kind,
        if (isa)
                mutex_lock(&isa->update_lock);
 
-       if (kind < 0) {
-               if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
-                || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR)
-                   != address)
-                       goto err_nodev;
-
-               /* Explicitly prevent the misdetection of Winbond chips */
-               i = i2c_smbus_read_byte_data(client, 0x4f);
-               if (i == 0xa3 || i == 0x5c)
-                       goto err_nodev;
-       }
+       if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80)
+        || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address)
+               goto err_nodev;
+
+       /* Explicitly prevent the misdetection of Winbond chips */
+       i = i2c_smbus_read_byte_data(client, 0x4f);
+       if (i == 0xa3 || i == 0x5c)
+               goto err_nodev;
 
        /* Determine the chip type. */
-       if (kind <= 0) {
-               i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
-               if (i == 0x00 || i == 0x20      /* LM78 */
-                || i == 0x40)                  /* LM78-J */
-                       kind = lm78;
-               else if ((i & 0xfe) == 0xc0)
-                       kind = lm79;
-               else {
-                       if (kind == 0)
-                               dev_warn(&adapter->dev, "Ignoring 'force' "
-                                       "parameter for unknown chip at "
-                                       "adapter %d, address 0x%02x\n",
-                                       i2c_adapter_id(adapter), address);
-                       goto err_nodev;
-               }
+       i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID);
+       if (i == 0x00 || i == 0x20      /* LM78 */
+        || i == 0x40)                  /* LM78-J */
+               client_name = "lm78";
+       else if ((i & 0xfe) == 0xc0)
+               client_name = "lm79";
+       else
+               goto err_nodev;
 
-               if (lm78_alias_detect(client, i)) {
-                       dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
-                               "be the same as ISA device\n", address);
-                       goto err_nodev;
-               }
+       if (lm78_alias_detect(client, i)) {
+               dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
+                       "be the same as ISA device\n", address);
+               goto err_nodev;
        }
 
        if (isa)
                mutex_unlock(&isa->update_lock);
 
-       switch (kind) {
-       case lm79:
-               client_name = "lm79";
-               break;
-       default:
-               client_name = "lm78";
-       }
        strlcpy(info->type, client_name, I2C_NAME_SIZE);
 
        return 0;
index e59e2d1..08b03e6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
  *          monitoring
- * Copyright (C) 2003-2008  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009  Jean Delvare <khali@linux-fr.org>
  *
  * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
  * a sensor chip made by National Semiconductor. It reports up to four
@@ -295,69 +295,40 @@ static int lm83_detect(struct i2c_client *new_client, int kind,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
-       const char *name = "";
+       const char *name;
+       u8 man_id, chip_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /* Now we do the detection and identification. A negative kind
-        * means that the driver was loaded with no force parameter
-        * (default), so we must both detect and identify the chip
-        * (actually there is only one possible kind of chip for now, LM83).
-        * A zero kind means that the driver was loaded with the force
-        * parameter, the detection step shall be skipped. A positive kind
-        * means that the driver was loaded with the force parameter and a
-        * given kind of chip is requested, so both the detection and the
-        * identification steps are skipped. */
-
-       /* Default to an LM83 if forced */
-       if (kind == 0)
-               kind = lm83;
-
-       if (kind < 0) { /* detection */
-               if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
-                   & 0xA8) != 0x00) ||
-                   ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
-                   & 0x48) != 0x00) ||
-                   ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
-                   & 0x41) != 0x00)) {
-                       dev_dbg(&adapter->dev,
-                               "LM83 detection failed at 0x%02x.\n",
-                               new_client->addr);
-                       return -ENODEV;
-               }
+       /* Detection */
+       if ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) & 0xA8) ||
+           (i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) & 0x48) ||
+           (i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) & 0x41)) {
+               dev_dbg(&adapter->dev, "LM83 detection failed at 0x%02x\n",
+                       new_client->addr);
+               return -ENODEV;
        }
 
-       if (kind <= 0) { /* identification */
-               u8 man_id, chip_id;
-
-               man_id = i2c_smbus_read_byte_data(new_client,
-                   LM83_REG_R_MAN_ID);
-               chip_id = i2c_smbus_read_byte_data(new_client,
-                   LM83_REG_R_CHIP_ID);
-
-               if (man_id == 0x01) { /* National Semiconductor */
-                       if (chip_id == 0x03) {
-                               kind = lm83;
-                       } else
-                       if (chip_id == 0x01) {
-                               kind = lm82;
-                       }
-               }
-
-               if (kind <= 0) { /* identification failed */
-                       dev_info(&adapter->dev,
-                           "Unsupported chip (man_id=0x%02X, "
-                           "chip_id=0x%02X).\n", man_id, chip_id);
-                       return -ENODEV;
-               }
-       }
+       /* Identification */
+       man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID);
+       if (man_id != 0x01)     /* National Semiconductor */
+               return -ENODEV;
 
-       if (kind == lm83) {
+       chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID);
+       switch (chip_id) {
+       case 0x03:
                name = "lm83";
-       } else
-       if (kind == lm82) {
+               break;
+       case 0x01:
                name = "lm82";
+               break;
+       default:
+               /* identification failed */
+               dev_info(&adapter->dev,
+                        "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n",
+                        man_id, chip_id);
+               return -ENODEV;
        }
 
        strlcpy(info->type, name, I2C_NAME_SIZE);
index 6c53d98..d56da2e 100644 (file)
@@ -5,7 +5,7 @@
     Copyright (c) 2002, 2003  Philip Pokorny <ppokorny@penguincomputing.com>
     Copyright (c) 2003        Margit Schubert-While <margitsw@t-online.de>
     Copyright (c) 2004        Justin Thiessen <jthiessen@penguincomputing.com>
-    Copyright (C) 2007, 2008  Jean Delvare <khali@linux-fr.org>
+    Copyright (C) 2007--2009  Jean Delvare <khali@linux-fr.org>
 
     Chip details at          <http://www.national.com/ds/LM/LM85.pdf>
 
@@ -1162,107 +1162,80 @@ static int lm85_detect(struct i2c_client *client, int kind,
        struct i2c_adapter *adapter = client->adapter;
        int address = client->addr;
        const char *type_name;
+       int company, verstep;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                /* We need to be able to do byte I/O */
                return -ENODEV;
        }
 
-       /* If auto-detecting, determine the chip type */
-       if (kind < 0) {
-               int company = lm85_read_value(client, LM85_REG_COMPANY);
-               int verstep = lm85_read_value(client, LM85_REG_VERSTEP);
-
-               dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
-                       "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
-                       address, company, verstep);
-
-               /* All supported chips have the version in common */
-               if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC &&
-                   (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) {
-                       dev_dbg(&adapter->dev, "Autodetection failed: "
-                               "unsupported version\n");
-                       return -ENODEV;
-               }
-               kind = any_chip;
-
-               /* Now, refine the detection */
-               if (company == LM85_COMPANY_NATIONAL) {
-                       switch (verstep) {
-                       case LM85_VERSTEP_LM85C:
-                               kind = lm85c;
-                               break;
-                       case LM85_VERSTEP_LM85B:
-                               kind = lm85b;
-                               break;
-                       case LM85_VERSTEP_LM96000_1:
-                       case LM85_VERSTEP_LM96000_2:
-                               /* Check for Winbond WPCD377I */
-                               if (lm85_is_fake(client)) {
-                                       dev_dbg(&adapter->dev,
-                                               "Found Winbond WPCD377I, "
-                                               "ignoring\n");
-                                       return -ENODEV;
-                               }
-                               break;
-                       }
-               } else if (company == LM85_COMPANY_ANALOG_DEV) {
-                       switch (verstep) {
-                       case LM85_VERSTEP_ADM1027:
-                               kind = adm1027;
-                               break;
-                       case LM85_VERSTEP_ADT7463:
-                       case LM85_VERSTEP_ADT7463C:
-                               kind = adt7463;
-                               break;
-                       case LM85_VERSTEP_ADT7468_1:
-                       case LM85_VERSTEP_ADT7468_2:
-                               kind = adt7468;
-                               break;
-                       }
-               } else if (company == LM85_COMPANY_SMSC) {
-                       switch (verstep) {
-                       case LM85_VERSTEP_EMC6D100_A0:
-                       case LM85_VERSTEP_EMC6D100_A1:
-                               /* Note: we can't tell a '100 from a '101 */
-                               kind = emc6d100;
-                               break;
-                       case LM85_VERSTEP_EMC6D102:
-                               kind = emc6d102;
-                               break;
+       /* Determine the chip type */
+       company = lm85_read_value(client, LM85_REG_COMPANY);
+       verstep = lm85_read_value(client, LM85_REG_VERSTEP);
+
+       dev_dbg(&adapter->dev, "Detecting device at 0x%02x with "
+               "COMPANY: 0x%02x and VERSTEP: 0x%02x\n",
+               address, company, verstep);
+
+       /* All supported chips have the version in common */
+       if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC &&
+           (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) {
+               dev_dbg(&adapter->dev,
+                       "Autodetection failed: unsupported version\n");
+               return -ENODEV;
+       }
+       type_name = "lm85";
+
+       /* Now, refine the detection */
+       if (company == LM85_COMPANY_NATIONAL) {
+               switch (verstep) {
+               case LM85_VERSTEP_LM85C:
+                       type_name = "lm85c";
+                       break;
+               case LM85_VERSTEP_LM85B:
+                       type_name = "lm85b";
+                       break;
+               case LM85_VERSTEP_LM96000_1:
+               case LM85_VERSTEP_LM96000_2:
+                       /* Check for Winbond WPCD377I */
+                       if (lm85_is_fake(client)) {
+                               dev_dbg(&adapter->dev,
+                                       "Found Winbond WPCD377I, ignoring\n");
+                               return -ENODEV;
                        }
-               } else {
-                       dev_dbg(&adapter->dev, "Autodetection failed: "
-                               "unknown vendor\n");
-                       return -ENODEV;
+                       break;
+               }
+       } else if (company == LM85_COMPANY_ANALOG_DEV) {
+               switch (verstep) {
+               case LM85_VERSTEP_ADM1027:
+                       type_name = "adm1027";
+                       break;
+               case LM85_VERSTEP_ADT7463:
+               case LM85_VERSTEP_ADT7463C:
+                       type_name = "adt7463";
+                       break;
+               case LM85_VERSTEP_ADT7468_1:
+               case LM85_VERSTEP_ADT7468_2:
+                       type_name = "adt7468";
+                       break;
                }
+       } else if (company == LM85_COMPANY_SMSC) {
+               switch (verstep) {
+               case LM85_VERSTEP_EMC6D100_A0:
+               case LM85_VERSTEP_EMC6D100_A1:
+                       /* Note: we can't tell a '100 from a '101 */
+                       type_name = "emc6d100";
+                       break;
+               case LM85_VERSTEP_EMC6D102:
+                       type_name = "emc6d102";
+                       break;
+               }
+       } else {
+               dev_dbg(&adapter->dev,
+                       "Autodetection failed: unknown vendor\n");
+               return -ENODEV;
        }
 
-       switch (kind) {
-       case lm85b:
-               type_name = "lm85b";
-               break;
-       case lm85c:
-               type_name = "lm85c";
-               break;
-       case adm1027:
-               type_name = "adm1027";
-               break;
-       case adt7463:
-               type_name = "adt7463";
-               break;
-       case adt7468:
-               type_name = "adt7468";
-               break;
-       case emc6d100:
-               type_name = "emc6d100";
-               break;
-       case emc6d102:
-               type_name = "emc6d102";
-               break;
-       default:
-               type_name = "lm85";
-       }
        strlcpy(info->type, type_name, I2C_NAME_SIZE);
 
        return 0;
index 2e4a3ce..4929b18 100644 (file)
@@ -666,37 +666,32 @@ static int lm87_detect(struct i2c_client *new_client, int kind,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
-       static const char *names[] = { "lm87", "adm1024" };
+       const char *name;
+       u8 cid, rev;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /* Default to an LM87 if forced */
-       if (kind == 0)
-               kind = lm87;
+       if (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)
+               return -ENODEV;
 
        /* Now, we do the remaining detection. */
-       if (kind < 0) {
-               u8 cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID);
-               u8 rev = lm87_read_value(new_client, LM87_REG_REVISION);
-
-               if (cid == 0x02                 /* National Semiconductor */
-                && (rev >= 0x01 && rev <= 0x08))
-                       kind = lm87;
-               else if (cid == 0x41            /* Analog Devices */
-                     && (rev & 0xf0) == 0x10)
-                       kind = adm1024;
-
-               if (kind < 0
-                || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)) {
-                       dev_dbg(&adapter->dev,
-                               "LM87 detection failed at 0x%02x.\n",
-                               new_client->addr);
-                       return -ENODEV;
-               }
+       cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID);
+       rev = lm87_read_value(new_client, LM87_REG_REVISION);
+
+       if (cid == 0x02                 /* National Semiconductor */
+        && (rev >= 0x01 && rev <= 0x08))
+               name = "lm87";
+       else if (cid == 0x41            /* Analog Devices */
+             && (rev & 0xf0) == 0x10)
+               name = "adm1024";
+       else {
+               dev_dbg(&adapter->dev, "LM87 detection failed at 0x%02x\n",
+                       new_client->addr);
+               return -ENODEV;
        }
 
-       strlcpy(info->type, names[kind - 1], I2C_NAME_SIZE);
+       strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
 }
index 1aff757..b7c905f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lm90.c - Part of lm_sensors, Linux kernel modules for hardware
  *          monitoring
- * Copyright (C) 2003-2008  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009  Jean Delvare <khali@linux-fr.org>
  *
  * Based on the lm83 driver. The LM90 is a sensor chip made by National
  * Semiconductor. It reports up to two temperatures (its own plus up to
@@ -661,154 +661,118 @@ static int lm90_detect(struct i2c_client *new_client, int kind,
 {
        struct i2c_adapter *adapter = new_client->adapter;
        int address = new_client->addr;
-       const char *name = "";
+       const char *name = NULL;
+       int man_id, chip_id, reg_config1, reg_convrate;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip. A zero kind means that
-        * the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        */
-
-       /* Default to an LM90 if forced */
-       if (kind == 0)
-               kind = lm90;
-
-       if (kind < 0) { /* detection and identification */
-               int man_id, chip_id, reg_config1, reg_convrate;
-
-               if ((man_id = i2c_smbus_read_byte_data(new_client,
+       /* detection and identification */
+       if ((man_id = i2c_smbus_read_byte_data(new_client,
                                                LM90_REG_R_MAN_ID)) < 0
-                || (chip_id = i2c_smbus_read_byte_data(new_client,
+        || (chip_id = i2c_smbus_read_byte_data(new_client,
                                                LM90_REG_R_CHIP_ID)) < 0
-                || (reg_config1 = i2c_smbus_read_byte_data(new_client,
+        || (reg_config1 = i2c_smbus_read_byte_data(new_client,
                                                LM90_REG_R_CONFIG1)) < 0
-                || (reg_convrate = i2c_smbus_read_byte_data(new_client,
+        || (reg_convrate = i2c_smbus_read_byte_data(new_client,
                                                LM90_REG_R_CONVRATE)) < 0)
+               return -ENODEV;
+
+       if ((address == 0x4C || address == 0x4D)
+        && man_id == 0x01) { /* National Semiconductor */
+               int reg_config2;
+
+               reg_config2 = i2c_smbus_read_byte_data(new_client,
+                                               LM90_REG_R_CONFIG2);
+               if (reg_config2 < 0)
                        return -ENODEV;
-               
-               if ((address == 0x4C || address == 0x4D)
-                && man_id == 0x01) { /* National Semiconductor */
-                       int reg_config2;
-
-                       if ((reg_config2 = i2c_smbus_read_byte_data(new_client,
-                                               LM90_REG_R_CONFIG2)) < 0)
-                               return -ENODEV;
-
-                       if ((reg_config1 & 0x2A) == 0x00
-                        && (reg_config2 & 0xF8) == 0x00
-                        && reg_convrate <= 0x09) {
-                               if (address == 0x4C
-                                && (chip_id & 0xF0) == 0x20) { /* LM90 */
-                                       kind = lm90;
-                               } else
-                               if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
-                                       kind = lm99;
-                                       dev_info(&adapter->dev,
-                                                "Assuming LM99 chip at "
-                                                "0x%02x\n", address);
-                                       dev_info(&adapter->dev,
-                                                "If it is an LM89, pass "
-                                                "force_lm86=%d,0x%02x when "
-                                                "loading the lm90 driver\n",
-                                                i2c_adapter_id(adapter),
-                                                address);
-                               } else
-                               if (address == 0x4C
-                                && (chip_id & 0xF0) == 0x10) { /* LM86 */
-                                       kind = lm86;
-                               }
-                       }
-               } else
-               if ((address == 0x4C || address == 0x4D)
-                && man_id == 0x41) { /* Analog Devices */
-                       if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
-                        && (reg_config1 & 0x3F) == 0x00
-                        && reg_convrate <= 0x0A) {
-                               kind = adm1032;
-                       } else
-                       if (chip_id == 0x51 /* ADT7461 */
-                        && (reg_config1 & 0x1B) == 0x00
-                        && reg_convrate <= 0x0A) {
-                               kind = adt7461;
-                       }
-               } else
-               if (man_id == 0x4D) { /* Maxim */
-                       /*
-                        * The MAX6657, MAX6658 and MAX6659 do NOT have a
-                        * chip_id register. Reading from that address will
-                        * return the last read value, which in our case is
-                        * those of the man_id register. Likewise, the config1
-                        * register seems to lack a low nibble, so the value
-                        * will be those of the previous read, so in our case
-                        * those of the man_id register.
-                        */
-                       if (chip_id == man_id
-                        && (address == 0x4C || address == 0x4D)
-                        && (reg_config1 & 0x1F) == (man_id & 0x0F)
-                        && reg_convrate <= 0x09) {
-                               kind = max6657;
+
+               if ((reg_config1 & 0x2A) == 0x00
+                && (reg_config2 & 0xF8) == 0x00
+                && reg_convrate <= 0x09) {
+                       if (address == 0x4C
+                        && (chip_id & 0xF0) == 0x20) { /* LM90 */
+                               name = "lm90";
                        } else
-                       /* The chip_id register of the MAX6680 and MAX6681
-                        * holds the revision of the chip.
-                        * the lowest bit of the config1 register is unused
-                        * and should return zero when read, so should the
-                        * second to last bit of config1 (software reset)
-                        */
-                       if (chip_id == 0x01
-                        && (reg_config1 & 0x03) == 0x00
-                        && reg_convrate <= 0x07) {
-                               kind = max6680;
+                       if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */
+                               name = "lm99";
+                               dev_info(&adapter->dev,
+                                        "Assuming LM99 chip at 0x%02x\n",
+                                        address);
+                               dev_info(&adapter->dev,
+                                        "If it is an LM89, instantiate it "
+                                        "with the new_device sysfs "
+                                        "interface\n");
                        } else
-                       /* The chip_id register of the MAX6646/6647/6649
-                        * holds the revision of the chip.
-                        * The lowest 6 bits of the config1 register are
-                        * unused and should return zero when read.
-                        */
-                       if (chip_id == 0x59
-                        && (reg_config1 & 0x3f) == 0x00
-                        && reg_convrate <= 0x07) {
-                               kind = max6646;
+                       if (address == 0x4C
+                        && (chip_id & 0xF0) == 0x10) { /* LM86 */
+                               name = "lm86";
                        }
                }
-
-               if (kind <= 0) { /* identification failed */
-                       dev_dbg(&adapter->dev,
-                               "Unsupported chip at 0x%02x (man_id=0x%02X, "
-                               "chip_id=0x%02X)\n", address, man_id, chip_id);
-                       return -ENODEV;
+       } else
+       if ((address == 0x4C || address == 0x4D)
+        && man_id == 0x41) { /* Analog Devices */
+               if ((chip_id & 0xF0) == 0x40 /* ADM1032 */
+                && (reg_config1 & 0x3F) == 0x00
+                && reg_convrate <= 0x0A) {
+                       name = "adm1032";
+                       /* The ADM1032 supports PEC, but only if combined
+                          transactions are not used. */
+                       if (i2c_check_functionality(adapter,
+                                                   I2C_FUNC_SMBUS_BYTE))
+                               info->flags |= I2C_CLIENT_PEC;
+               } else
+               if (chip_id == 0x51 /* ADT7461 */
+                && (reg_config1 & 0x1B) == 0x00
+                && reg_convrate <= 0x0A) {
+                       name = "adt7461";
+               }
+       } else
+       if (man_id == 0x4D) { /* Maxim */
+               /*
+                * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id
+                * register. Reading from that address will return the last
+                * read value, which in our case is those of the man_id
+                * register. Likewise, the config1 register seems to lack a
+                * low nibble, so the value will be those of the previous
+                * read, so in our case those of the man_id register.
+                */
+               if (chip_id == man_id
+                && (address == 0x4C || address == 0x4D)
+                && (reg_config1 & 0x1F) == (man_id & 0x0F)
+                && reg_convrate <= 0x09) {
+                       name = "max6657";
+               } else
+               /*
+                * The chip_id register of the MAX6680 and MAX6681 holds the
+                * revision of the chip. The lowest bit of the config1 register
+                * is unused and should return zero when read, so should the
+                * second to last bit of config1 (software reset).
+                */
+               if (chip_id == 0x01
+                && (reg_config1 & 0x03) == 0x00
+                && reg_convrate <= 0x07) {
+                       name = "max6680";
+               } else
+               /*
+                * The chip_id register of the MAX6646/6647/6649 holds the
+                * revision of the chip. The lowest 6 bits of the config1
+                * register are unused and should return zero when read.
+                */
+               if (chip_id == 0x59
+                && (reg_config1 & 0x3f) == 0x00
+                && reg_convrate <= 0x07) {
+                       name = "max6646";
                }
        }
 
-       /* Fill the i2c board info */
-       if (kind == lm90) {
-               name = "lm90";
-       } else if (kind == adm1032) {
-               name = "adm1032";
-               /* The ADM1032 supports PEC, but only if combined
-                  transactions are not used. */
-               if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
-                       info->flags |= I2C_CLIENT_PEC;
-       } else if (kind == lm99) {
-               name = "lm99";
-       } else if (kind == lm86) {
-               name = "lm86";
-       } else if (kind == max6657) {
-               name = "max6657";
-       } else if (kind == max6680) {
-               name = "max6680";
-       } else if (kind == adt7461) {
-               name = "adt7461";
-       } else if (kind == max6646) {
-               name = "max6646";
+       if (!name) { /* identification failed */
+               dev_dbg(&adapter->dev,
+                       "Unsupported chip at 0x%02x (man_id=0x%02X, "
+                       "chip_id=0x%02X)\n", address, man_id, chip_id);
+               return -ENODEV;
        }
+
        strlcpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
index b2e00c5..47ac698 100644 (file)
@@ -323,31 +323,22 @@ static int lm92_detect(struct i2c_client *new_client, int kind,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = new_client->adapter;
+       u8 config;
+       u16 man_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
                                            | I2C_FUNC_SMBUS_WORD_DATA))
                return -ENODEV;
 
-       /* A negative kind means that the driver was loaded with no force
-          parameter (default), so we must identify the chip. */
-       if (kind < 0) {
-               u8 config = i2c_smbus_read_byte_data(new_client,
-                            LM92_REG_CONFIG);
-               u16 man_id = i2c_smbus_read_word_data(new_client,
-                            LM92_REG_MAN_ID);
-
-               if ((config & 0xe0) == 0x00
-                && man_id == 0x0180) {
-                       pr_info("lm92: Found National Semiconductor LM92 chip\n");
-                       kind = lm92;
-               } else
-               if (max6635_check(new_client)) {
-                       pr_info("lm92: Found Maxim MAX6635 chip\n");
-                       kind = lm92; /* No separate prefix */
-               }
-               else
-                       return -ENODEV;
-       }
+       config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG);
+       man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID);
+
+       if ((config & 0xe0) == 0x00 && man_id == 0x0180)
+               pr_info("lm92: Found National Semiconductor LM92 chip\n");
+       else if (max6635_check(new_client))
+               pr_info("lm92: Found Maxim MAX6635 chip\n");
+       else
+               return -ENODEV;
 
        strlcpy(info->type, "lm92", I2C_NAME_SIZE);
 
index fc36cad..124dd7c 100644 (file)
@@ -928,7 +928,7 @@ static void lm93_update_client_common(struct lm93_data *data,
        data->prochot_interval = lm93_read_byte(client,
                        LM93_REG_PROCHOT_INTERVAL);
 
-       /* Fan Boost Termperature registers */
+       /* Fan Boost Temperature registers */
        for (i = 0; i < 4; i++)
                data->boost[i] = lm93_read_byte(client, LM93_REG_BOOST(i));
 
@@ -2505,34 +2505,24 @@ static int lm93_detect(struct i2c_client *client, int kind,
                       struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       int mfr, ver;
 
        if (!i2c_check_functionality(adapter, LM93_SMBUS_FUNC_MIN))
                return -ENODEV;
 
        /* detection */
-       if (kind < 0) {
-               int mfr = lm93_read_byte(client, LM93_REG_MFR_ID);
-
-               if (mfr != 0x01) {
-                       dev_dbg(&adapter->dev,"detect failed, "
-                               "bad manufacturer id 0x%02x!\n", mfr);
-                       return -ENODEV;
-               }
+       mfr = lm93_read_byte(client, LM93_REG_MFR_ID);
+       if (mfr != 0x01) {
+               dev_dbg(&adapter->dev,
+                       "detect failed, bad manufacturer id 0x%02x!\n", mfr);
+               return -ENODEV;
        }
 
-       if (kind <= 0) {
-               int ver = lm93_read_byte(client, LM93_REG_VER);
-
-               if ((ver == LM93_MFR_ID) || (ver == LM93_MFR_ID_PROTOTYPE)) {
-                       kind = lm93;
-               } else {
-                       dev_dbg(&adapter->dev,"detect failed, "
-                               "bad version id 0x%02x!\n", ver);
-                       if (kind == 0)
-                               dev_dbg(&adapter->dev,
-                                       "(ignored 'force' parameter)\n");
-                       return -ENODEV;
-               }
+       ver = lm93_read_byte(client, LM93_REG_VER);
+       if (ver != LM93_MFR_ID && ver != LM93_MFR_ID_PROTOTYPE) {
+               dev_dbg(&adapter->dev,
+                       "detect failed, bad version id 0x%02x!\n", ver);
+               return -ENODEV;
        }
 
        strlcpy(info->type, "lm93", I2C_NAME_SIZE);
index e34f9e4..906b896 100644 (file)
@@ -315,51 +315,23 @@ static int lm95241_detect(struct i2c_client *new_client, int kind,
 {
        struct i2c_adapter *adapter = new_client->adapter;
        int address = new_client->addr;
-       const char *name = "";
+       const char *name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip. A zero kind means that
-        * the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        */
-       if (kind < 0) { /* detection */
-               if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
-                    != MANUFACTURER_ID)
-               || (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
-                   < DEFAULT_REVISION)) {
-                       dev_dbg(&adapter->dev,
-                               "LM95241 detection failed at 0x%02x.\n",
-                               address);
-                       return -ENODEV;
-               }
-       }
-
-       if (kind <= 0) { /* identification */
-               if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
-                    == MANUFACTURER_ID)
-               && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
-                   >= DEFAULT_REVISION)) {
-
-                       kind = lm95241;
-
-                       if (kind <= 0) { /* identification failed */
-                               dev_info(&adapter->dev, "Unsupported chip\n");
-                               return -ENODEV;
-                       }
-               }
+       if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID)
+            == MANUFACTURER_ID)
+        && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID)
+            >= DEFAULT_REVISION)) {
+               name = "lm95241";
+       } else {
+               dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n",
+                       address);
+               return -ENODEV;
        }
 
        /* Fill the i2c board info */
-       if (kind == lm95241)
-               name = "lm95241";
        strlcpy(info->type, name, I2C_NAME_SIZE);
        return 0;
 }
index 7897754..7fcf5ff 100644 (file)
@@ -226,58 +226,34 @@ static const struct attribute_group max1619_group = {
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int max1619_detect(struct i2c_client *new_client, int kind,
+static int max1619_detect(struct i2c_client *client, int kind,
                          struct i2c_board_info *info)
 {
-       struct i2c_adapter *adapter = new_client->adapter;
-       u8 reg_config=0, reg_convrate=0, reg_status=0;
+       struct i2c_adapter *adapter = client->adapter;
+       u8 reg_config, reg_convrate, reg_status, man_id, chip_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip. A zero kind means that
-        * the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        */
-       if (kind < 0) { /* detection */
-               reg_config = i2c_smbus_read_byte_data(new_client,
-                             MAX1619_REG_R_CONFIG);
-               reg_convrate = i2c_smbus_read_byte_data(new_client,
-                              MAX1619_REG_R_CONVRATE);
-               reg_status = i2c_smbus_read_byte_data(new_client,
-                               MAX1619_REG_R_STATUS);
-               if ((reg_config & 0x03) != 0x00
-                || reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) {
-                       dev_dbg(&adapter->dev,
-                               "MAX1619 detection failed at 0x%02x.\n",
-                               new_client->addr);
-                       return -ENODEV;
-               }
+       /* detection */
+       reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
+       reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE);
+       reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS);
+       if ((reg_config & 0x03) != 0x00
+        || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) {
+               dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n",
+                       client->addr);
+               return -ENODEV;
        }
 
-       if (kind <= 0) { /* identification */
-               u8 man_id, chip_id;
-       
-               man_id = i2c_smbus_read_byte_data(new_client,
-                        MAX1619_REG_R_MAN_ID);
-               chip_id = i2c_smbus_read_byte_data(new_client,
-                         MAX1619_REG_R_CHIP_ID);
-               
-               if ((man_id == 0x4D) && (chip_id == 0x04))
-                       kind = max1619;
-
-               if (kind <= 0) { /* identification failed */
-                       dev_info(&adapter->dev,
-                           "Unsupported chip (man_id=0x%02X, "
-                           "chip_id=0x%02X).\n", man_id, chip_id);
-                       return -ENODEV;
-               }
+       /* identification */
+       man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID);
+       chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID);
+       if (man_id != 0x4D || chip_id != 0x04) {
+               dev_info(&adapter->dev,
+                        "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n",
+                        man_id, chip_id);
+               return -ENODEV;
        }
 
        strlcpy(info->type, "max1619", I2C_NAME_SIZE);
index 58f66be..1da561e 100644 (file)
@@ -534,7 +534,7 @@ static int max6650_detect(struct i2c_client *client, int kind,
        struct i2c_adapter *adapter = client->adapter;
        int address = client->addr;
 
-       dev_dbg(&adapter->dev, "max6650_detect called, kind = %d\n", kind);
+       dev_dbg(&adapter->dev, "max6650_detect called\n");
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support "
@@ -542,23 +542,7 @@ static int max6650_detect(struct i2c_client *client, int kind,
                return -ENODEV;
        }
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip (actually there is only
-        * one possible kind of chip for now, max6650). A zero kind means that
-        * the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        *
-        * Currently I can find no way to distinguish between a MAX6650 and
-        * a MAX6651. This driver has only been tried on the former.
-        */
-
-       if ((kind < 0) &&
-          (  (i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0)
+       if (((i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0)
            ||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0)
            ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0)
            ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0)
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
new file mode 100644 (file)
index 0000000..883fa81
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Driver for the Freescale Semiconductor MC13783 adc.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2009 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/mfd/mc13783-private.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+#define MC13783_ADC_NAME       "mc13783-adc"
+
+struct mc13783_adc_priv {
+       struct mc13783 *mc13783;
+       struct device *hwmon_dev;
+};
+
+static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute
+                             *devattr, char *buf)
+{
+       return sprintf(buf, "mc13783_adc\n");
+}
+
+static int mc13783_adc_read(struct device *dev,
+               struct device_attribute *devattr, unsigned int *val)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       unsigned int channel = attr->index;
+       unsigned int sample[4];
+       int ret;
+
+       ret = mc13783_adc_do_conversion(priv->mc13783,
+                       MC13783_ADC_MODE_MULT_CHAN,
+                       channel, sample);
+       if (ret)
+               return ret;
+
+       channel &= 0x7;
+
+       *val = (sample[channel % 4] >> (channel > 3 ? 14 : 2)) & 0x3ff;
+
+       return 0;
+}
+
+static ssize_t mc13783_adc_read_bp(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       unsigned val;
+       int ret = mc13783_adc_read(dev, devattr, &val);
+
+       if (ret)
+               return ret;
+
+       /*
+        * BP (channel 2) reports with offset 2.4V to the actual value to fit
+        * the input range of the ADC.  unit = 2.25mV = 9/4 mV.
+        */
+       val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400;
+
+       return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t mc13783_adc_read_gp(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       unsigned val;
+       int ret = mc13783_adc_read(dev, devattr, &val);
+
+       if (ret)
+               return ret;
+
+       /*
+        * input range is [0, 2.3V], val has 10 bits, so each bit
+        * is worth 9/4 mV.
+        */
+       val = DIV_ROUND_CLOSEST(val * 9, 4);
+
+       return sprintf(buf, "%u\n", val);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, mc13783_adc_read_bp, NULL, 2);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, mc13783_adc_read_gp, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, mc13783_adc_read_gp, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, mc13783_adc_read_gp, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, mc13783_adc_read_gp, NULL, 8);
+static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, mc13783_adc_read_gp, NULL, 9);
+static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, mc13783_adc_read_gp, NULL, 10);
+static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, mc13783_adc_read_gp, NULL, 11);
+static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, mc13783_adc_read_gp, NULL, 12);
+static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13);
+static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14);
+static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15);
+
+static struct attribute *mc13783_attr[] = {
+       &dev_attr_name.attr,
+       &sensor_dev_attr_in2_input.dev_attr.attr,
+       &sensor_dev_attr_in5_input.dev_attr.attr,
+       &sensor_dev_attr_in6_input.dev_attr.attr,
+       &sensor_dev_attr_in7_input.dev_attr.attr,
+       &sensor_dev_attr_in8_input.dev_attr.attr,
+       &sensor_dev_attr_in9_input.dev_attr.attr,
+       &sensor_dev_attr_in10_input.dev_attr.attr,
+       &sensor_dev_attr_in11_input.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group mc13783_group = {
+       .attrs = mc13783_attr,
+};
+
+/* last four channels may be occupied by the touchscreen */
+static struct attribute *mc13783_attr_ts[] = {
+       &sensor_dev_attr_in12_input.dev_attr.attr,
+       &sensor_dev_attr_in13_input.dev_attr.attr,
+       &sensor_dev_attr_in14_input.dev_attr.attr,
+       &sensor_dev_attr_in15_input.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group mc13783_group_ts = {
+       .attrs = mc13783_attr_ts,
+};
+
+static int __init mc13783_adc_probe(struct platform_device *pdev)
+{
+       struct mc13783_adc_priv *priv;
+       int ret;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
+
+       platform_set_drvdata(pdev, priv);
+
+       /* Register sysfs hooks */
+       ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group);
+       if (ret)
+               goto out_err_create1;
+
+       if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+               ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
+               if (ret)
+                       goto out_err_create2;
+
+       priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(priv->hwmon_dev)) {
+               ret = PTR_ERR(priv->hwmon_dev);
+               dev_err(&pdev->dev,
+                               "hwmon_device_register failed with %d.\n", ret);
+               goto out_err_register;
+       }
+
+
+       return 0;
+
+out_err_register:
+
+       if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+               sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
+out_err_create2:
+
+       sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
+out_err_create1:
+
+       platform_set_drvdata(pdev, NULL);
+       kfree(priv);
+
+       return ret;
+}
+
+static int __devexit mc13783_adc_remove(struct platform_device *pdev)
+{
+       struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+
+       hwmon_device_unregister(priv->hwmon_dev);
+
+       if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN))
+               sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
+
+       sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
+
+       platform_set_drvdata(pdev, NULL);
+       kfree(priv);
+
+       return 0;
+}
+
+static struct platform_driver mc13783_adc_driver = {
+       .remove         = __devexit_p(mc13783_adc_remove),
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = MC13783_ADC_NAME,
+       },
+};
+
+static int __init mc13783_adc_init(void)
+{
+       return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe);
+}
+
+static void __exit mc13783_adc_exit(void)
+{
+       platform_driver_unregister(&mc13783_adc_driver);
+}
+
+module_init(mc13783_adc_init);
+module_exit(mc13783_adc_exit);
+
+MODULE_DESCRIPTION("MC13783 ADC driver");
+MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MC13783_ADC_NAME);
index 7183541..3f3f9a4 100644 (file)
@@ -323,7 +323,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev)
        }
 
        for (i = 0; i < ARRAY_SIZE(pdata->in); i++) {
-               struct s3c24xx_adc_hwmon_incfg *cfg = pdata->in[i];
+               struct s3c_hwmon_chcfg *cfg = pdata->in[i];
 
                if (!cfg)
                        continue;
@@ -333,7 +333,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev)
                                 "channel %d multiplier too large\n",
                                 i);
 
-               if (cfg->divider == 0) {
+               if (cfg->div == 0) {
                        dev_err(&dev->dev, "channel %d divider zero\n", i);
                        continue;
                }
index 8bb5cb5..4d88c04 100644 (file)
@@ -491,24 +491,22 @@ static int smsc47m192_detect(struct i2c_client *client, int kind,
                return -ENODEV;
 
        /* Detection criteria from sensors_detect script */
-       if (kind < 0) {
-               if (i2c_smbus_read_byte_data(client,
+       version = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VERSION);
+       if (i2c_smbus_read_byte_data(client,
                                SMSC47M192_REG_COMPANY_ID) == 0x55
-                && ((version = i2c_smbus_read_byte_data(client,
-                               SMSC47M192_REG_VERSION)) & 0xf0) == 0x20
-                && (i2c_smbus_read_byte_data(client,
+        && (version & 0xf0) == 0x20
+        && (i2c_smbus_read_byte_data(client,
                                SMSC47M192_REG_VID) & 0x70) == 0x00
-                && (i2c_smbus_read_byte_data(client,
+        && (i2c_smbus_read_byte_data(client,
                                SMSC47M192_REG_VID4) & 0xfe) == 0x80) {
-                       dev_info(&adapter->dev,
-                                "found SMSC47M192 or compatible, "
-                                "version 2, stepping A%d\n", version & 0x0f);
-               } else {
-                       dev_dbg(&adapter->dev,
-                               "SMSC47M192 detection failed at 0x%02x\n",
-                               client->addr);
-                       return -ENODEV;
-               }
+               dev_info(&adapter->dev,
+                        "found SMSC47M192 or compatible, "
+                        "version 2, stepping A%d\n", version & 0x0f);
+       } else {
+               dev_dbg(&adapter->dev,
+                       "SMSC47M192 detection failed at 0x%02x\n",
+                       client->addr);
+               return -ENODEV;
        }
 
        strlcpy(info->type, "smsc47m192", I2C_NAME_SIZE);
index 7d97431..4b79384 100644 (file)
@@ -36,7 +36,11 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
 
 /* Insmod parameters */
 I2C_CLIENT_INSMOD_2(thmc50, adm1022);
-I2C_CLIENT_MODULE_PARM(adm1022_temp3, "List of adapter,address pairs "
+
+static unsigned short adm1022_temp3[16];
+static unsigned int adm1022_temp3_num;
+module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0);
+MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs "
                        "to enable 3rd temperature (ADM1022 only)");
 
 /* Many THMC50 constants specified below */
@@ -289,7 +293,6 @@ static int thmc50_detect(struct i2c_client *client, int kind,
        unsigned revision;
        unsigned config;
        struct i2c_adapter *adapter = client->adapter;
-       int err = 0;
        const char *type_name;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
@@ -301,31 +304,13 @@ static int thmc50_detect(struct i2c_client *client, int kind,
        pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n",
                 client->addr, i2c_adapter_id(client->adapter));
 
-       /* Now, we do the remaining detection. */
        company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID);
        revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE);
        config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF);
+       if (revision < 0xc0 || (config & 0x10))
+               return -ENODEV;
 
-       if (kind == 0)
-               kind = thmc50;
-       else if (kind < 0) {
-               err = -ENODEV;
-               if (revision >= 0xc0 && ((config & 0x10) == 0)) {
-                       if (company == 0x49) {
-                               kind = thmc50;
-                               err = 0;
-                       } else if (company == 0x41) {
-                               kind = adm1022;
-                               err = 0;
-                       }
-               }
-       }
-       if (err == -ENODEV) {
-               pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
-               return err;
-       }
-
-       if (kind == adm1022) {
+       if (company == 0x41) {
                int id = i2c_adapter_id(client->adapter);
                int i;
 
@@ -340,9 +325,13 @@ static int thmc50_detect(struct i2c_client *client, int kind,
                                                          config);
                                break;
                        }
-       } else {
+       } else if (company == 0x49) {
                type_name = "thmc50";
+       } else {
+               pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n");
+               return -ENODEV;
        }
+
        pr_debug("thmc50: Detected %s (version %x, revision %x)\n",
                 type_name, (revision >> 4) - 0xc, revision & 0xf);
 
index 7b34f2c..ee96734 100644 (file)
@@ -488,46 +488,43 @@ static void tmp401_init_client(struct i2c_client *client)
                i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
 }
 
-static int tmp401_detect(struct i2c_client *client, int kind,
+static int tmp401_detect(struct i2c_client *client, int _kind,
                         struct i2c_board_info *info)
 {
+       enum chips kind;
        struct i2c_adapter *adapter = client->adapter;
+       u8 reg;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
        /* Detect and identify the chip */
-       if (kind <= 0) {
-               u8 reg;
-
-               reg = i2c_smbus_read_byte_data(client,
-                                              TMP401_MANUFACTURER_ID_REG);
-               if (reg != TMP401_MANUFACTURER_ID)
-                       return -ENODEV;
-
-               reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
-
-               switch (reg) {
-               case TMP401_DEVICE_ID:
-                       kind = tmp401;
-                       break;
-               case TMP411_DEVICE_ID:
-                       kind = tmp411;
-                       break;
-               default:
-                       return -ENODEV;
-               }
+       reg = i2c_smbus_read_byte_data(client, TMP401_MANUFACTURER_ID_REG);
+       if (reg != TMP401_MANUFACTURER_ID)
+               return -ENODEV;
 
-               reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
-               if (reg & 0x1b)
-                       return -ENODEV;
+       reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG);
 
-               reg = i2c_smbus_read_byte_data(client,
-                                              TMP401_CONVERSION_RATE_READ);
-               /* Datasheet says: 0x1-0x6 */
-               if (reg > 15)
-                       return -ENODEV;
+       switch (reg) {
+       case TMP401_DEVICE_ID:
+               kind = tmp401;
+               break;
+       case TMP411_DEVICE_ID:
+               kind = tmp411;
+               break;
+       default:
+               return -ENODEV;
        }
+
+       reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
+       if (reg & 0x1b)
+               return -ENODEV;
+
+       reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE_READ);
+       /* Datasheet says: 0x1-0x6 */
+       if (reg > 15)
+               return -ENODEV;
+
        strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE);
 
        return 0;
index 2092434..bb5464a 100644 (file)
@@ -223,39 +223,36 @@ static int tmp421_init_client(struct i2c_client *client)
        return 0;
 }
 
-static int tmp421_detect(struct i2c_client *client, int kind,
+static int tmp421_detect(struct i2c_client *client, int _kind,
                         struct i2c_board_info *info)
 {
+       enum chips kind;
        struct i2c_adapter *adapter = client->adapter;
        const char *names[] = { "TMP421", "TMP422", "TMP423" };
+       u8 reg;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       if (kind <= 0) {
-               u8 reg;
-
-               reg = i2c_smbus_read_byte_data(client,
-                                              TMP421_MANUFACTURER_ID_REG);
-               if (reg != TMP421_MANUFACTURER_ID)
-                       return -ENODEV;
-
-               reg = i2c_smbus_read_byte_data(client,
-                                              TMP421_DEVICE_ID_REG);
-               switch (reg) {
-               case TMP421_DEVICE_ID:
-                       kind = tmp421;
-                       break;
-               case TMP422_DEVICE_ID:
-                       kind = tmp422;
-                       break;
-               case TMP423_DEVICE_ID:
-                       kind = tmp423;
-                       break;
-               default:
-                       return -ENODEV;
-               }
+       reg = i2c_smbus_read_byte_data(client, TMP421_MANUFACTURER_ID_REG);
+       if (reg != TMP421_MANUFACTURER_ID)
+               return -ENODEV;
+
+       reg = i2c_smbus_read_byte_data(client, TMP421_DEVICE_ID_REG);
+       switch (reg) {
+       case TMP421_DEVICE_ID:
+               kind = tmp421;
+               break;
+       case TMP422_DEVICE_ID:
+               kind = tmp422;
+               break;
+       case TMP423_DEVICE_ID:
+               kind = tmp423;
+               break;
+       default:
+               return -ENODEV;
        }
+
        strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE);
        dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n",
                 names[kind - 1], client->addr);
index 2be28ac..b257c72 100644 (file)
@@ -59,10 +59,11 @@ static struct platform_device *pdev;
 #define DRVNAME "w83627hf"
 enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf };
 
-static u16 force_addr;
-module_param(force_addr, ushort, 0);
-MODULE_PARM_DESC(force_addr,
-                "Initialize the base address of the sensors");
+struct w83627hf_sio_data {
+       enum chips type;
+       int sioaddr;
+};
+
 static u8 force_i2c = 0x1f;
 module_param(force_i2c, byte, 0);
 MODULE_PARM_DESC(force_i2c,
@@ -77,9 +78,7 @@ module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
 /* modified from kernel/include/traps.c */
-static int REG;                /* The register to read/write */
 #define        DEV     0x07    /* Register: Logical device select */
-static int VAL;                /* The value to read/write */
 
 /* logical device numbers for superio_select (below) */
 #define W83627HF_LD_FDC                0x00
@@ -109,37 +108,37 @@ static int VAL;           /* The value to read/write */
 #define W83687THF_VID_DATA     0xF1 /* w83687thf only */
 
 static inline void
-superio_outb(int reg, int val)
+superio_outb(struct w83627hf_sio_data *sio, int reg, int val)
 {
-       outb(reg, REG);
-       outb(val, VAL);
+       outb(reg, sio->sioaddr);
+       outb(val, sio->sioaddr + 1);
 }
 
 static inline int
-superio_inb(int reg)
+superio_inb(struct w83627hf_sio_data *sio, int reg)
 {
-       outb(reg, REG);
-       return inb(VAL);
+       outb(reg, sio->sioaddr);
+       return inb(sio->sioaddr + 1);
 }
 
 static inline void
-superio_select(int ld)
+superio_select(struct w83627hf_sio_data *sio, int ld)
 {
-       outb(DEV, REG);
-       outb(ld, VAL);
+       outb(DEV, sio->sioaddr);
+       outb(ld,  sio->sioaddr + 1);
 }
 
 static inline void
-superio_enter(void)
+superio_enter(struct w83627hf_sio_data *sio)
 {
-       outb(0x87, REG);
-       outb(0x87, REG);
+       outb(0x87, sio->sioaddr);
+       outb(0x87, sio->sioaddr);
 }
 
 static inline void
-superio_exit(void)
+superio_exit(struct w83627hf_sio_data *sio)
 {
-       outb(0xAA, REG);
+       outb(0xAA, sio->sioaddr);
 }
 
 #define W627_DEVID 0x52
@@ -380,10 +379,6 @@ struct w83627hf_data {
        u8 vrm_ovt;             /* Register value, 627THF/637HF/687THF only */
 };
 
-struct w83627hf_sio_data {
-       enum chips type;
-};
-
 
 static int w83627hf_probe(struct platform_device *pdev);
 static int __devexit w83627hf_remove(struct platform_device *pdev);
@@ -1140,11 +1135,8 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
                "W83687THF",
        };
 
-       REG = sioaddr;
-       VAL = sioaddr + 1;
-
-       superio_enter();
-       val = force_id ? force_id : superio_inb(DEVID);
+       superio_enter(sio_data);
+       val = force_id ? force_id : superio_inb(sio_data, DEVID);
        switch (val) {
        case W627_DEVID:
                sio_data->type = w83627hf;
@@ -1168,16 +1160,9 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
                goto exit;
        }
 
-       superio_select(W83627HF_LD_HWM);
-       force_addr &= WINB_ALIGNMENT;
-       if (force_addr) {
-               printk(KERN_WARNING DRVNAME ": Forcing address 0x%x\n",
-                      force_addr);
-               superio_outb(WINB_BASE_REG, force_addr >> 8);
-               superio_outb(WINB_BASE_REG + 1, force_addr & 0xff);
-       }
-       val = (superio_inb(WINB_BASE_REG) << 8) |
-              superio_inb(WINB_BASE_REG + 1);
+       superio_select(sio_data, W83627HF_LD_HWM);
+       val = (superio_inb(sio_data, WINB_BASE_REG) << 8) |
+              superio_inb(sio_data, WINB_BASE_REG + 1);
        *addr = val & WINB_ALIGNMENT;
        if (*addr == 0) {
                printk(KERN_WARNING DRVNAME ": Base address not set, "
@@ -1185,18 +1170,19 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
                goto exit;
        }
 
-       val = superio_inb(WINB_ACT_REG);
+       val = superio_inb(sio_data, WINB_ACT_REG);
        if (!(val & 0x01)) {
                printk(KERN_WARNING DRVNAME ": Enabling HWM logical device\n");
-               superio_outb(WINB_ACT_REG, val | 0x01);
+               superio_outb(sio_data, WINB_ACT_REG, val | 0x01);
        }
 
        err = 0;
+       sio_data->sioaddr = sioaddr;
        pr_info(DRVNAME ": Found %s chip at %#x\n",
                names[sio_data->type], *addr);
 
  exit:
-       superio_exit();
+       superio_exit(sio_data);
        return err;
 }
 
@@ -1511,20 +1497,21 @@ static int w83627hf_read_value(struct w83627hf_data *data, u16 reg)
 
 static int __devinit w83627thf_read_gpio5(struct platform_device *pdev)
 {
+       struct w83627hf_sio_data *sio_data = pdev->dev.platform_data;
        int res = 0xff, sel;
 
-       superio_enter();
-       superio_select(W83627HF_LD_GPIO5);
+       superio_enter(sio_data);
+       superio_select(sio_data, W83627HF_LD_GPIO5);
 
        /* Make sure these GPIO pins are enabled */
-       if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) {
+       if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) {
                dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n");
                goto exit;
        }
 
        /* Make sure the pins are configured for input
           There must be at least five (VRM 9), and possibly 6 (VRM 10) */
-       sel = superio_inb(W83627THF_GPIO5_IOSR) & 0x3f;
+       sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f;
        if ((sel & 0x1f) != 0x1f) {
                dev_dbg(&pdev->dev, "GPIO5 not configured for VID "
                        "function\n");
@@ -1532,37 +1519,38 @@ static int __devinit w83627thf_read_gpio5(struct platform_device *pdev)
        }
 
        dev_info(&pdev->dev, "Reading VID from GPIO5\n");
-       res = superio_inb(W83627THF_GPIO5_DR) & sel;
+       res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel;
 
 exit:
-       superio_exit();
+       superio_exit(sio_data);
        return res;
 }
 
 static int __devinit w83687thf_read_vid(struct platform_device *pdev)
 {
+       struct w83627hf_sio_data *sio_data = pdev->dev.platform_data;
        int res = 0xff;
 
-       superio_enter();
-       superio_select(W83627HF_LD_HWM);
+       superio_enter(sio_data);
+       superio_select(sio_data, W83627HF_LD_HWM);
 
        /* Make sure these GPIO pins are enabled */
-       if (!(superio_inb(W83687THF_VID_EN) & (1 << 2))) {
+       if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) {
                dev_dbg(&pdev->dev, "VID disabled, no VID function\n");
                goto exit;
        }
 
        /* Make sure the pins are configured for input */
-       if (!(superio_inb(W83687THF_VID_CFG) & (1 << 4))) {
+       if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) {
                dev_dbg(&pdev->dev, "VID configured as output, "
                        "no VID function\n");
                goto exit;
        }
 
-       res = superio_inb(W83687THF_VID_DATA) & 0x3f;
+       res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f;
 
 exit:
-       superio_exit();
+       superio_exit(sio_data);
        return res;
 }
 
index d27ed1b..7ab7967 100644 (file)
@@ -1054,11 +1054,11 @@ static int
 w83781d_detect(struct i2c_client *client, int kind,
               struct i2c_board_info *info)
 {
-       int val1 = 0, val2;
+       int val1, val2;
        struct w83781d_data *isa = w83781d_data_if_isa();
        struct i2c_adapter *adapter = client->adapter;
        int address = client->addr;
-       const char *client_name = "";
+       const char *client_name;
        enum vendor { winbond, asus } vendid;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -1070,98 +1070,73 @@ w83781d_detect(struct i2c_client *client, int kind,
        if (isa)
                mutex_lock(&isa->update_lock);
 
-       /* The w8378?d may be stuck in some other bank than bank 0. This may
-          make reading other information impossible. Specify a force=... or
-          force_*=... parameter, and the Winbond will be reset to the right
-          bank. */
-       if (kind < 0) {
-               if (i2c_smbus_read_byte_data
-                   (client, W83781D_REG_CONFIG) & 0x80) {
-                       dev_dbg(&adapter->dev, "Detection of w83781d chip "
-                               "failed at step 3\n");
-                       goto err_nodev;
-               }
-               val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
-               val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
-               /* Check for Winbond or Asus ID if in bank 0 */
-               if ((!(val1 & 0x07)) &&
-                   (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
-                    || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
-                       dev_dbg(&adapter->dev, "Detection of w83781d chip "
-                               "failed at step 4\n");
+       if (i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG) & 0x80) {
+               dev_dbg(&adapter->dev,
+                       "Detection of w83781d chip failed at step 3\n");
+               goto err_nodev;
+       }
+
+       val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK);
+       val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
+       /* Check for Winbond or Asus ID if in bank 0 */
+       if (!(val1 & 0x07) &&
+           ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) ||
+            ( (val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) {
+               dev_dbg(&adapter->dev,
+                       "Detection of w83781d chip failed at step 4\n");
+               goto err_nodev;
+       }
+       /* If Winbond SMBus, check address at 0x48.
+          Asus doesn't support, except for as99127f rev.2 */
+       if ((!(val1 & 0x80) && val2 == 0xa3) ||
+           ( (val1 & 0x80) && val2 == 0x5c)) {
+               if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR)
+                   != address) {
+                       dev_dbg(&adapter->dev,
+                               "Detection of w83781d chip failed at step 5\n");
                        goto err_nodev;
                }
-               /* If Winbond SMBus, check address at 0x48.
-                  Asus doesn't support, except for as99127f rev.2 */
-               if ((!(val1 & 0x80) && (val2 == 0xa3)) ||
-                   ((val1 & 0x80) && (val2 == 0x5c))) {
-                       if (i2c_smbus_read_byte_data
-                           (client, W83781D_REG_I2C_ADDR) != address) {
-                               dev_dbg(&adapter->dev, "Detection of w83781d "
-                                       "chip failed at step 5\n");
-                               goto err_nodev;
-                       }
-               }
        }
 
-       /* We have either had a force parameter, or we have already detected the
-          Winbond. Put it now into bank 0 and Vendor ID High Byte */
+       /* Put it now into bank 0 and Vendor ID High Byte */
        i2c_smbus_write_byte_data(client, W83781D_REG_BANK,
                (i2c_smbus_read_byte_data(client, W83781D_REG_BANK)
                 & 0x78) | 0x80);
 
-       /* Determine the chip type. */
-       if (kind <= 0) {
-               /* get vendor ID */
-               val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
-               if (val2 == 0x5c)
-                       vendid = winbond;
-               else if (val2 == 0x12)
-                       vendid = asus;
-               else {
-                       dev_dbg(&adapter->dev, "w83781d chip vendor is "
-                               "neither Winbond nor Asus\n");
-                       goto err_nodev;
-               }
-
-               val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
-               if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
-                       kind = w83781d;
-               else if (val1 == 0x30 && vendid == winbond)
-                       kind = w83782d;
-               else if (val1 == 0x40 && vendid == winbond && address == 0x2d)
-                       kind = w83783s;
-               else if (val1 == 0x31)
-                       kind = as99127f;
-               else {
-                       if (kind == 0)
-                               dev_warn(&adapter->dev, "Ignoring 'force' "
-                                        "parameter for unknown chip at "
-                                        "address 0x%02x\n", address);
-                       goto err_nodev;
-               }
-
-               if ((kind == w83781d || kind == w83782d)
-                && w83781d_alias_detect(client, val1)) {
-                       dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
-                               "be the same as ISA device\n", address);
-                       goto err_nodev;
-               }
+       /* Get the vendor ID */
+       val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN);
+       if (val2 == 0x5c)
+               vendid = winbond;
+       else if (val2 == 0x12)
+               vendid = asus;
+       else {
+               dev_dbg(&adapter->dev,
+                       "w83781d chip vendor is neither Winbond nor Asus\n");
+               goto err_nodev;
        }
 
-       if (isa)
-               mutex_unlock(&isa->update_lock);
-
-       if (kind == w83781d) {
+       /* Determine the chip type. */
+       val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID);
+       if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
                client_name = "w83781d";
-       } else if (kind == w83782d) {
+       else if (val1 == 0x30 && vendid == winbond)
                client_name = "w83782d";
-       } else if (kind == w83783s) {
+       else if (val1 == 0x40 && vendid == winbond && address == 0x2d)
                client_name = "w83783s";
-       } else if (kind == as99127f) {
+       else if (val1 == 0x31)
                client_name = "as99127f";
+       else
+               goto err_nodev;
+
+       if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) {
+               dev_dbg(&adapter->dev, "Device at 0x%02x appears to "
+                       "be the same as ISA device\n", address);
+               goto err_nodev;
        }
 
+       if (isa)
+               mutex_unlock(&isa->update_lock);
+
        strlcpy(info->type, client_name, I2C_NAME_SIZE);
 
        return 0;
index 97851c5..0410bf1 100644 (file)
@@ -1270,56 +1270,32 @@ static int w83791d_detect(struct i2c_client *client, int kind,
                return -ENODEV;
        }
 
-       /* The w83791d may be stuck in some other bank than bank 0. This may
-          make reading other information impossible. Specify a force=...
-          parameter, and the Winbond will be reset to the right bank. */
-       if (kind < 0) {
-               if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) {
-                       return -ENODEV;
-               }
-               val1 = w83791d_read(client, W83791D_REG_BANK);
-               val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
-               /* Check for Winbond ID if in bank 0 */
-               if (!(val1 & 0x07)) {
-                       /* yes it is Bank0 */
-                       if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
-                           ((val1 & 0x80) && (val2 != 0x5c))) {
-                               return -ENODEV;
-                       }
-               }
-               /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
-                  should match */
-               if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) {
+       if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80)
+               return -ENODEV;
+
+       val1 = w83791d_read(client, W83791D_REG_BANK);
+       val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
+       /* Check for Winbond ID if in bank 0 */
+       if (!(val1 & 0x07)) {
+               if ((!(val1 & 0x80) && val2 != 0xa3) ||
+                   ( (val1 & 0x80) && val2 != 0x5c)) {
                        return -ENODEV;
                }
        }
+       /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR
+          should match */
+       if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address)
+               return -ENODEV;
 
-       /* We either have a force parameter or we have reason to
-          believe it is a Winbond chip. Either way, we want bank 0 and
-          Vendor ID high byte */
+       /* We want bank 0 and Vendor ID high byte */
        val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78;
        w83791d_write(client, W83791D_REG_BANK, val1 | 0x80);
 
        /* Verify it is a Winbond w83791d */
-       if (kind <= 0) {
-               /* get vendor ID */
-               val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
-               if (val2 != 0x5c) {     /* the vendor is NOT Winbond */
-                       return -ENODEV;
-               }
-               val1 = w83791d_read(client, W83791D_REG_WCHIPID);
-               if (val1 == 0x71) {
-                       kind = w83791d;
-               } else {
-                       if (kind == 0)
-                               dev_warn(&adapter->dev,
-                                       "w83791d: Ignoring 'force' parameter "
-                                       "for unknown chip at adapter %d, "
-                                       "address 0x%02x\n",
-                                       i2c_adapter_id(adapter), address);
-                       return -ENODEV;
-               }
-       }
+       val1 = w83791d_read(client, W83791D_REG_WCHIPID);
+       val2 = w83791d_read(client, W83791D_REG_CHIPMAN);
+       if (val1 != 0x71 || val2 != 0x5c)
+               return -ENODEV;
 
        strlcpy(info->type, "w83791d", I2C_NAME_SIZE);
 
index 2be1619..3897885 100644 (file)
@@ -1273,58 +1273,33 @@ w83792d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
                return -ENODEV;
        }
 
-       /* The w83792d may be stuck in some other bank than bank 0. This may
-          make reading other information impossible. Specify a force=... or
-          force_*=... parameter, and the Winbond will be reset to the right
-          bank. */
-       if (kind < 0) {
-               if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) {
-                       return -ENODEV;
-               }
-               val1 = w83792d_read_value(client, W83792D_REG_BANK);
-               val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
-               /* Check for Winbond ID if in bank 0 */
-               if (!(val1 & 0x07)) {  /* is Bank0 */
-                       if (((!(val1 & 0x80)) && (val2 != 0xa3)) ||
-                            ((val1 & 0x80) && (val2 != 0x5c))) {
-                               return -ENODEV;
-                       }
-               }
-               /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR
-                  should match */
-               if (w83792d_read_value(client,
-                                       W83792D_REG_I2C_ADDR) != address) {
+       if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80)
+               return -ENODEV;
+
+       val1 = w83792d_read_value(client, W83792D_REG_BANK);
+       val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
+       /* Check for Winbond ID if in bank 0 */
+       if (!(val1 & 0x07)) {  /* is Bank0 */
+               if ((!(val1 & 0x80) && val2 != 0xa3) ||
+                   ( (val1 & 0x80) && val2 != 0x5c))
                        return -ENODEV;
-               }
        }
+       /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR
+          should match */
+       if (w83792d_read_value(client, W83792D_REG_I2C_ADDR) != address)
+               return -ENODEV;
 
-       /* We have either had a force parameter, or we have already detected the
-          Winbond. Put it now into bank 0 and Vendor ID High Byte */
+       /*  Put it now into bank 0 and Vendor ID High Byte */
        w83792d_write_value(client,
                            W83792D_REG_BANK,
                            (w83792d_read_value(client,
                                W83792D_REG_BANK) & 0x78) | 0x80);
 
        /* Determine the chip type. */
-       if (kind <= 0) {
-               /* get vendor ID */
-               val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
-               if (val2 != 0x5c) {  /* the vendor is NOT Winbond */
-                       return -ENODEV;
-               }
-               val1 = w83792d_read_value(client, W83792D_REG_WCHIPID);
-               if (val1 == 0x7a) {
-                       kind = w83792d;
-               } else {
-                       if (kind == 0)
-                               dev_warn(&adapter->dev,
-                                       "w83792d: Ignoring 'force' parameter for"
-                                       " unknown chip at adapter %d, address"
-                                       " 0x%02x\n", i2c_adapter_id(adapter),
-                                       address);
-                       return -ENODEV;
-               }
-       }
+       val1 = w83792d_read_value(client, W83792D_REG_WCHIPID);
+       val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN);
+       if (val1 != 0x7a || val2 != 0x5c)
+               return -ENODEV;
 
        strlcpy(info->type, "w83792d", I2C_NAME_SIZE);
 
index 47dd398..80a2191 100644 (file)
@@ -1164,7 +1164,7 @@ ERROR_SC_0:
 static int w83793_detect(struct i2c_client *client, int kind,
                         struct i2c_board_info *info)
 {
-       u8 tmp, bank;
+       u8 tmp, bank, chip_id;
        struct i2c_adapter *adapter = client->adapter;
        unsigned short address = client->addr;
 
@@ -1174,44 +1174,27 @@ static int w83793_detect(struct i2c_client *client, int kind,
 
        bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL);
 
-       if (kind < 0) {
-               tmp = bank & 0x80 ? 0x5c : 0xa3;
-               /* Check Winbond vendor ID */
-               if (tmp != i2c_smbus_read_byte_data(client,
-                                                       W83793_REG_VENDORID)) {
-                       pr_debug("w83793: Detection failed at check "
-                                "vendor id\n");
-                       return -ENODEV;
-               }
-
-               /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR
-                  should match */
-               if ((bank & 0x07) == 0
-                && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) !=
-                   (address << 1)) {
-                       pr_debug("w83793: Detection failed at check "
-                                "i2c addr\n");
-                       return -ENODEV;
-               }
-
+       tmp = bank & 0x80 ? 0x5c : 0xa3;
+       /* Check Winbond vendor ID */
+       if (tmp != i2c_smbus_read_byte_data(client, W83793_REG_VENDORID)) {
+               pr_debug("w83793: Detection failed at check vendor id\n");
+               return -ENODEV;
        }
 
-       /* We have either had a force parameter, or we have already detected the
-          Winbond. Determine the chip type now */
-
-       if (kind <= 0) {
-               if (0x7b == i2c_smbus_read_byte_data(client,
-                                                    W83793_REG_CHIPID)) {
-                       kind = w83793;
-               } else {
-                       if (kind == 0)
-                               dev_warn(&adapter->dev, "w83793: Ignoring "
-                                        "'force' parameter for unknown chip "
-                                        "at address 0x%02x\n", address);
-                       return -ENODEV;
-               }
+       /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR
+          should match */
+       if ((bank & 0x07) == 0
+        && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) !=
+           (address << 1)) {
+               pr_debug("w83793: Detection failed at check i2c addr\n");
+               return -ENODEV;
        }
 
+       /* Determine the chip type now */
+       chip_id = i2c_smbus_read_byte_data(client, W83793_REG_CHIPID);
+       if (chip_id != 0x7b)
+               return -ENODEV;
+
        strlcpy(info->type, "w83793", I2C_NAME_SIZE);
 
        return 0;
index ea295b9..9b6c4c1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware
  *               monitoring
- * Copyright (C) 2003-2004  Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2009  Jean Delvare <khali@linux-fr.org>
  *
  * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made
  * by Winbond. It reports a single external temperature with a 1 deg
@@ -146,60 +146,36 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1);
  */
 
 /* Return 0 if detection is successful, -ENODEV otherwise */
-static int w83l785ts_detect(struct i2c_client *new_client, int kind,
+static int w83l785ts_detect(struct i2c_client *client, int kind,
                            struct i2c_board_info *info)
 {
-       struct i2c_adapter *adapter = new_client->adapter;
+       struct i2c_adapter *adapter = client->adapter;
+       u16 man_id;
+       u8 chip_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                return -ENODEV;
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip (actually there is only
-        * one possible kind of chip for now, W83L785TS-S). A zero kind means
-        * that the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        */
-       if (kind < 0) { /* detection */
-               if (((w83l785ts_read_value(new_client,
-                     W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00)
-                || ((w83l785ts_read_value(new_client,
-                     W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) {
-                       dev_dbg(&adapter->dev,
-                               "W83L785TS-S detection failed at 0x%02x.\n",
-                               new_client->addr);
-                       return -ENODEV;
-               }
+       /* detection */
+       if ((w83l785ts_read_value(client, W83L785TS_REG_CONFIG, 0) & 0x80)
+        || (w83l785ts_read_value(client, W83L785TS_REG_TYPE, 0) & 0xFC)) {
+               dev_dbg(&adapter->dev,
+                       "W83L785TS-S detection failed at 0x%02x\n",
+                       client->addr);
+               return -ENODEV;
        }
 
-       if (kind <= 0) { /* identification */
-               u16 man_id;
-               u8 chip_id;
-
-               man_id = (w83l785ts_read_value(new_client,
-                        W83L785TS_REG_MAN_ID1, 0) << 8) +
-                        w83l785ts_read_value(new_client,
-                        W83L785TS_REG_MAN_ID2, 0);
-               chip_id = w83l785ts_read_value(new_client,
-                         W83L785TS_REG_CHIP_ID, 0);
-
-               if (man_id == 0x5CA3) { /* Winbond */
-                       if (chip_id == 0x70) { /* W83L785TS-S */
-                               kind = w83l785ts;                       
-                       }
-               }
-       
-               if (kind <= 0) { /* identification failed */
-                       dev_info(&adapter->dev,
-                                "Unsupported chip (man_id=0x%04X, "
-                                "chip_id=0x%02X).\n", man_id, chip_id);
-                       return -ENODEV;
-               }
+       /* Identification */
+       man_id = (w83l785ts_read_value(client, W83L785TS_REG_MAN_ID1, 0) << 8)
+              + w83l785ts_read_value(client, W83L785TS_REG_MAN_ID2, 0);
+       chip_id = w83l785ts_read_value(client, W83L785TS_REG_CHIP_ID, 0);
+
+       if (man_id != 0x5CA3            /* Winbond */
+        || chip_id != 0x70) {          /* W83L785TS-S */
+               dev_dbg(&adapter->dev,
+                       "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
+                       man_id, chip_id);
+               return -ENODEV;
        }
 
        strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE);
index badca76..27da7d2 100644 (file)
@@ -590,53 +590,31 @@ w83l786ng_detect(struct i2c_client *client, int kind,
                 struct i2c_board_info *info)
 {
        struct i2c_adapter *adapter = client->adapter;
+       u16 man_id;
+       u8 chip_id;
 
        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
                return -ENODEV;
        }
 
-       /*
-        * Now we do the remaining detection. A negative kind means that
-        * the driver was loaded with no force parameter (default), so we
-        * must both detect and identify the chip (actually there is only
-        * one possible kind of chip for now, W83L786NG). A zero kind means
-        * that the driver was loaded with the force parameter, the detection
-        * step shall be skipped. A positive kind means that the driver
-        * was loaded with the force parameter and a given kind of chip is
-        * requested, so both the detection and the identification steps
-        * are skipped.
-        */
-       if (kind < 0) { /* detection */
-               if (((w83l786ng_read_value(client,
-                   W83L786NG_REG_CONFIG) & 0x80) != 0x00)) {
-                       dev_dbg(&adapter->dev,
-                               "W83L786NG detection failed at 0x%02x.\n",
-                               client->addr);
-                       return -ENODEV;
-               }
+       /* Detection */
+       if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) {
+               dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n",
+                       client->addr);
+               return -ENODEV;
        }
 
-       if (kind <= 0) { /* identification */
-               u16 man_id;
-               u8 chip_id;
-
-               man_id = (w83l786ng_read_value(client,
-                   W83L786NG_REG_MAN_ID1) << 8) +
-                   w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
-               chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
-
-               if (man_id == 0x5CA3) { /* Winbond */
-                       if (chip_id == 0x80) { /* W83L786NG */
-                               kind = w83l786ng;
-                       }
-               }
+       /* Identification */
+       man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) +
+                w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2);
+       chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID);
 
-               if (kind <= 0) { /* identification failed */
-                       dev_info(&adapter->dev,
-                           "Unsupported chip (man_id=0x%04X, "
-                           "chip_id=0x%02X).\n", man_id, chip_id);
-                       return -ENODEV;
-               }
+       if (man_id != 0x5CA3 ||         /* Winbond */
+           chip_id != 0x80) {          /* W83L786NG */
+               dev_dbg(&adapter->dev,
+                       "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n",
+                       man_id, chip_id);
+               return -ENODEV;
        }
 
        strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE);
index b444762..9e18ef9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Synopsys Designware I2C adapter driver (master only).
+ * Synopsys DesignWare I2C adapter driver (master only).
  *
  * Based on the TI DAVINCI I2C adapter driver.
  *
 #define DW_IC_FS_SCL_LCNT      0x20
 #define DW_IC_INTR_STAT                0x2c
 #define DW_IC_INTR_MASK                0x30
+#define DW_IC_RAW_INTR_STAT    0x34
+#define DW_IC_RX_TL            0x38
+#define DW_IC_TX_TL            0x3c
 #define DW_IC_CLR_INTR         0x40
+#define DW_IC_CLR_RX_UNDER     0x44
+#define DW_IC_CLR_RX_OVER      0x48
+#define DW_IC_CLR_TX_OVER      0x4c
+#define DW_IC_CLR_RD_REQ       0x50
+#define DW_IC_CLR_TX_ABRT      0x54
+#define DW_IC_CLR_RX_DONE      0x58
+#define DW_IC_CLR_ACTIVITY     0x5c
+#define DW_IC_CLR_STOP_DET     0x60
+#define DW_IC_CLR_START_DET    0x64
+#define DW_IC_CLR_GEN_CALL     0x68
 #define DW_IC_ENABLE           0x6c
 #define DW_IC_STATUS           0x70
 #define DW_IC_TXFLR            0x74
 #define DW_IC_CON_RESTART_EN           0x20
 #define DW_IC_CON_SLAVE_DISABLE                0x40
 
-#define DW_IC_INTR_TX_EMPTY    0x10
-#define DW_IC_INTR_TX_ABRT     0x40
+#define DW_IC_INTR_RX_UNDER    0x001
+#define DW_IC_INTR_RX_OVER     0x002
+#define DW_IC_INTR_RX_FULL     0x004
+#define DW_IC_INTR_TX_OVER     0x008
+#define DW_IC_INTR_TX_EMPTY    0x010
+#define DW_IC_INTR_RD_REQ      0x020
+#define DW_IC_INTR_TX_ABRT     0x040
+#define DW_IC_INTR_RX_DONE     0x080
+#define DW_IC_INTR_ACTIVITY    0x100
 #define DW_IC_INTR_STOP_DET    0x200
+#define DW_IC_INTR_START_DET   0x400
+#define DW_IC_INTR_GEN_CALL    0x800
+
+#define DW_IC_INTR_DEFAULT_MASK                (DW_IC_INTR_RX_FULL | \
+                                        DW_IC_INTR_TX_EMPTY | \
+                                        DW_IC_INTR_TX_ABRT | \
+                                        DW_IC_INTR_STOP_DET)
 
 #define DW_IC_STATUS_ACTIVITY  0x1
 
 #define ABRT_SBYTE_ACKDET      7
 #define ABRT_SBYTE_NORSTRT     9
 #define ABRT_10B_RD_NORSTRT    10
-#define ARB_MASTER_DIS         11
+#define ABRT_MASTER_DIS                11
 #define ARB_LOST               12
 
+#define DW_IC_TX_ABRT_7B_ADDR_NOACK    (1UL << ABRT_7B_ADDR_NOACK)
+#define DW_IC_TX_ABRT_10ADDR1_NOACK    (1UL << ABRT_10ADDR1_NOACK)
+#define DW_IC_TX_ABRT_10ADDR2_NOACK    (1UL << ABRT_10ADDR2_NOACK)
+#define DW_IC_TX_ABRT_TXDATA_NOACK     (1UL << ABRT_TXDATA_NOACK)
+#define DW_IC_TX_ABRT_GCALL_NOACK      (1UL << ABRT_GCALL_NOACK)
+#define DW_IC_TX_ABRT_GCALL_READ       (1UL << ABRT_GCALL_READ)
+#define DW_IC_TX_ABRT_SBYTE_ACKDET     (1UL << ABRT_SBYTE_ACKDET)
+#define DW_IC_TX_ABRT_SBYTE_NORSTRT    (1UL << ABRT_SBYTE_NORSTRT)
+#define DW_IC_TX_ABRT_10B_RD_NORSTRT   (1UL << ABRT_10B_RD_NORSTRT)
+#define DW_IC_TX_ABRT_MASTER_DIS       (1UL << ABRT_MASTER_DIS)
+#define DW_IC_TX_ARB_LOST              (1UL << ARB_LOST)
+
+#define DW_IC_TX_ABRT_NOACK            (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
+                                        DW_IC_TX_ABRT_10ADDR1_NOACK | \
+                                        DW_IC_TX_ABRT_10ADDR2_NOACK | \
+                                        DW_IC_TX_ABRT_TXDATA_NOACK | \
+                                        DW_IC_TX_ABRT_GCALL_NOACK)
+
 static char *abort_sources[] = {
-       [ABRT_7B_ADDR_NOACK]    =
+       [ABRT_7B_ADDR_NOACK] =
                "slave address not acknowledged (7bit mode)",
-       [ABRT_10ADDR1_NOACK]    =
+       [ABRT_10ADDR1_NOACK] =
                "first address byte not acknowledged (10bit mode)",
-       [ABRT_10ADDR2_NOACK]    =
+       [ABRT_10ADDR2_NOACK] =
                "second address byte not acknowledged (10bit mode)",
-       [ABRT_TXDATA_NOACK]             =
+       [ABRT_TXDATA_NOACK] =
                "data not acknowledged",
-       [ABRT_GCALL_NOACK]              =
+       [ABRT_GCALL_NOACK] =
                "no acknowledgement for a general call",
-       [ABRT_GCALL_READ]               =
+       [ABRT_GCALL_READ] =
                "read after general call",
-       [ABRT_SBYTE_ACKDET]             =
+       [ABRT_SBYTE_ACKDET] =
                "start byte acknowledged",
-       [ABRT_SBYTE_NORSTRT]    =
+       [ABRT_SBYTE_NORSTRT] =
                "trying to send start byte when restart is disabled",
-       [ABRT_10B_RD_NORSTRT]   =
+       [ABRT_10B_RD_NORSTRT] =
                "trying to read when restart is disabled (10bit mode)",
-       [ARB_MASTER_DIS]                =
+       [ABRT_MASTER_DIS] =
                "trying to use disabled adapter",
-       [ARB_LOST]                      =
+       [ARB_LOST] =
                "lost arbitration",
 };
 
@@ -129,7 +174,6 @@ static char *abort_sources[] = {
  * @dev: driver model device node
  * @base: IO registers pointer
  * @cmd_complete: tx completion indicator
- * @pump_msg: continue in progress transfers
  * @lock: protect this struct and IO registers
  * @clk: input reference clock
  * @cmd_err: run time hadware error code
@@ -155,27 +199,81 @@ struct dw_i2c_dev {
        struct device           *dev;
        void __iomem            *base;
        struct completion       cmd_complete;
-       struct tasklet_struct   pump_msg;
        struct mutex            lock;
        struct clk              *clk;
        int                     cmd_err;
        struct i2c_msg          *msgs;
        int                     msgs_num;
        int                     msg_write_idx;
-       u16                     tx_buf_len;
+       u32                     tx_buf_len;
        u8                      *tx_buf;
        int                     msg_read_idx;
-       u16                     rx_buf_len;
+       u32                     rx_buf_len;
        u8                      *rx_buf;
        int                     msg_err;
        unsigned int            status;
-       u16                     abort_source;
+       u32                     abort_source;
        int                     irq;
        struct i2c_adapter      adapter;
        unsigned int            tx_fifo_depth;
        unsigned int            rx_fifo_depth;
 };
 
+static u32
+i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
+{
+       /*
+        * DesignWare I2C core doesn't seem to have solid strategy to meet
+        * the tHD;STA timing spec.  Configuring _HCNT based on tHIGH spec
+        * will result in violation of the tHD;STA spec.
+        */
+       if (cond)
+               /*
+                * Conditional expression:
+                *
+                *   IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
+                *
+                * This is based on the DW manuals, and represents an ideal
+                * configuration.  The resulting I2C bus speed will be
+                * faster than any of the others.
+                *
+                * If your hardware is free from tHD;STA issue, try this one.
+                */
+               return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset;
+       else
+               /*
+                * Conditional expression:
+                *
+                *   IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
+                *
+                * This is just experimental rule; the tHD;STA period turned
+                * out to be proportinal to (_HCNT + 3).  With this setting,
+                * we could meet both tHIGH and tHD;STA timing specs.
+                *
+                * If unsure, you'd better to take this alternative.
+                *
+                * The reason why we need to take into account "tf" here,
+                * is the same as described in i2c_dw_scl_lcnt().
+                */
+               return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset;
+}
+
+static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
+{
+       /*
+        * Conditional expression:
+        *
+        *   IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf)
+        *
+        * DW I2C core starts counting the SCL CNTs for the LOW period
+        * of the SCL clock (tLOW) as soon as it pulls the SCL line.
+        * In order to meet the tLOW timing spec, we need to take into
+        * account the fall time of SCL signal (tf).  Default tf value
+        * should be 0.3 us, for safety.
+        */
+       return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset;
+}
+
 /**
  * i2c_dw_init() - initialize the designware i2c master hardware
  * @dev: device private data
@@ -187,25 +285,49 @@ struct dw_i2c_dev {
 static void i2c_dw_init(struct dw_i2c_dev *dev)
 {
        u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
-       u16 ic_con;
+       u32 ic_con, hcnt, lcnt;
 
        /* Disable the adapter */
-       writeb(0, dev->base + DW_IC_ENABLE);
+       writel(0, dev->base + DW_IC_ENABLE);
 
        /* set standard and fast speed deviders for high/low periods */
-       writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */
-                       dev->base + DW_IC_SS_SCL_HCNT);
-       writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */
-                       dev->base + DW_IC_SS_SCL_LCNT);
-       writew((input_clock_khz *  6 / 10000)+1, /* fast speed high, 0.6us */
-                       dev->base + DW_IC_FS_SCL_HCNT);
-       writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */
-                       dev->base + DW_IC_FS_SCL_LCNT);
+
+       /* Standard-mode */
+       hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+                               40,     /* tHD;STA = tHIGH = 4.0 us */
+                               3,      /* tf = 0.3 us */
+                               0,      /* 0: DW default, 1: Ideal */
+                               0);     /* No offset */
+       lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+                               47,     /* tLOW = 4.7 us */
+                               3,      /* tf = 0.3 us */
+                               0);     /* No offset */
+       writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT);
+       writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT);
+       dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+
+       /* Fast-mode */
+       hcnt = i2c_dw_scl_hcnt(input_clock_khz,
+                               6,      /* tHD;STA = tHIGH = 0.6 us */
+                               3,      /* tf = 0.3 us */
+                               0,      /* 0: DW default, 1: Ideal */
+                               0);     /* No offset */
+       lcnt = i2c_dw_scl_lcnt(input_clock_khz,
+                               13,     /* tLOW = 1.3 us */
+                               3,      /* tf = 0.3 us */
+                               0);     /* No offset */
+       writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT);
+       writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
+       dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+
+       /* Configure Tx/Rx FIFO threshold levels */
+       writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL);
+       writel(0, dev->base + DW_IC_RX_TL);
 
        /* configure the i2c master */
        ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
                DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
-       writew(ic_con, dev->base + DW_IC_CON);
+       writel(ic_con, dev->base + DW_IC_CON);
 }
 
 /*
@@ -215,7 +337,7 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
 {
        int timeout = TIMEOUT;
 
-       while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+       while (readl(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
                if (timeout <= 0) {
                        dev_warn(dev->dev, "timeout waiting for bus ready\n");
                        return -ETIMEDOUT;
@@ -227,106 +349,125 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
        return 0;
 }
 
+static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
+{
+       struct i2c_msg *msgs = dev->msgs;
+       u32 ic_con;
+
+       /* Disable the adapter */
+       writel(0, dev->base + DW_IC_ENABLE);
+
+       /* set the slave (target) address */
+       writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+
+       /* if the slave address is ten bit address, enable 10BITADDR */
+       ic_con = readl(dev->base + DW_IC_CON);
+       if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
+               ic_con |= DW_IC_CON_10BITADDR_MASTER;
+       else
+               ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
+       writel(ic_con, dev->base + DW_IC_CON);
+
+       /* Enable the adapter */
+       writel(1, dev->base + DW_IC_ENABLE);
+
+       /* Enable interrupts */
+       writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK);
+}
+
 /*
- * Initiate low level master read/write transaction.
- * This function is called from i2c_dw_xfer when starting a transfer.
- * This function is also called from dw_i2c_pump_msg to continue a transfer
- * that is longer than the size of the TX FIFO.
+ * Initiate (and continue) low level master read/write transaction.
+ * This function is only called from i2c_dw_isr, and pumping i2c_msg
+ * messages into the tx buffer.  Even if the size of i2c_msg data is
+ * longer than the size of the tx buffer, it handles everything.
  */
 static void
-i2c_dw_xfer_msg(struct i2c_adapter *adap)
+i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
 {
-       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
        struct i2c_msg *msgs = dev->msgs;
-       int num = dev->msgs_num;
-       u16 ic_con, intr_mask;
-       int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR);
-       int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR);
-       u16 addr = msgs[dev->msg_write_idx].addr;
-       u16 buf_len = dev->tx_buf_len;
-
-       if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
-               /* Disable the adapter */
-               writeb(0, dev->base + DW_IC_ENABLE);
-
-               /* set the slave (target) address */
-               writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+       u32 intr_mask;
+       int tx_limit, rx_limit;
+       u32 addr = msgs[dev->msg_write_idx].addr;
+       u32 buf_len = dev->tx_buf_len;
+       u8 *buf = dev->tx_buf;;
 
-               /* if the slave address is ten bit address, enable 10BITADDR */
-               ic_con = readw(dev->base + DW_IC_CON);
-               if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
-                       ic_con |= DW_IC_CON_10BITADDR_MASTER;
-               else
-                       ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
-               writew(ic_con, dev->base + DW_IC_CON);
+       intr_mask = DW_IC_INTR_DEFAULT_MASK;
 
-               /* Enable the adapter */
-               writeb(1, dev->base + DW_IC_ENABLE);
-       }
-
-       for (; dev->msg_write_idx < num; dev->msg_write_idx++) {
-               /* if target address has changed, we need to
+       for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
+               /*
+                * if target address has changed, we need to
                 * reprogram the target address in the i2c
                 * adapter when we are done with this transfer
                 */
-               if (msgs[dev->msg_write_idx].addr != addr)
-                       return;
+               if (msgs[dev->msg_write_idx].addr != addr) {
+                       dev_err(dev->dev,
+                               "%s: invalid target address\n", __func__);
+                       dev->msg_err = -EINVAL;
+                       break;
+               }
 
                if (msgs[dev->msg_write_idx].len == 0) {
                        dev_err(dev->dev,
                                "%s: invalid message length\n", __func__);
                        dev->msg_err = -EINVAL;
-                       return;
+                       break;
                }
 
                if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
                        /* new i2c_msg */
-                       dev->tx_buf = msgs[dev->msg_write_idx].buf;
+                       buf = msgs[dev->msg_write_idx].buf;
                        buf_len = msgs[dev->msg_write_idx].len;
                }
 
+               tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
+               rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
+
                while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
                        if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
-                               writew(0x100, dev->base + DW_IC_DATA_CMD);
+                               writel(0x100, dev->base + DW_IC_DATA_CMD);
                                rx_limit--;
                        } else
-                               writew(*(dev->tx_buf++),
-                                               dev->base + DW_IC_DATA_CMD);
+                               writel(*buf++, dev->base + DW_IC_DATA_CMD);
                        tx_limit--; buf_len--;
                }
+
+               dev->tx_buf = buf;
+               dev->tx_buf_len = buf_len;
+
+               if (buf_len > 0) {
+                       /* more bytes to be written */
+                       dev->status |= STATUS_WRITE_IN_PROGRESS;
+                       break;
+               } else
+                       dev->status &= ~STATUS_WRITE_IN_PROGRESS;
        }
 
-       intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
-       if (buf_len > 0) { /* more bytes to be written */
-               intr_mask |= DW_IC_INTR_TX_EMPTY;
-               dev->status |= STATUS_WRITE_IN_PROGRESS;
-       } else
-               dev->status &= ~STATUS_WRITE_IN_PROGRESS;
-       writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+       /*
+        * If i2c_msg index search is completed, we don't need TX_EMPTY
+        * interrupt any more.
+        */
+       if (dev->msg_write_idx == dev->msgs_num)
+               intr_mask &= ~DW_IC_INTR_TX_EMPTY;
+
+       if (dev->msg_err)
+               intr_mask = 0;
 
-       dev->tx_buf_len = buf_len;
+       writel(intr_mask, dev->base + DW_IC_INTR_MASK);
 }
 
 static void
-i2c_dw_read(struct i2c_adapter *adap)
+i2c_dw_read(struct dw_i2c_dev *dev)
 {
-       struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
        struct i2c_msg *msgs = dev->msgs;
-       int num = dev->msgs_num;
-       u16 addr = msgs[dev->msg_read_idx].addr;
-       int rx_valid = readw(dev->base + DW_IC_RXFLR);
+       int rx_valid;
 
-       for (; dev->msg_read_idx < num; dev->msg_read_idx++) {
-               u16 len;
+       for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
+               u32 len;
                u8 *buf;
 
                if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
                        continue;
 
-               /* different i2c client, reprogram the i2c adapter */
-               if (msgs[dev->msg_read_idx].addr != addr)
-                       return;
-
                if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
                        len = msgs[dev->msg_read_idx].len;
                        buf = msgs[dev->msg_read_idx].buf;
@@ -335,8 +476,10 @@ i2c_dw_read(struct i2c_adapter *adap)
                        buf = dev->rx_buf;
                }
 
+               rx_valid = readl(dev->base + DW_IC_RXFLR);
+
                for (; len > 0 && rx_valid > 0; len--, rx_valid--)
-                       *buf++ = readb(dev->base + DW_IC_DATA_CMD);
+                       *buf++ = readl(dev->base + DW_IC_DATA_CMD);
 
                if (len > 0) {
                        dev->status |= STATUS_READ_IN_PROGRESS;
@@ -348,6 +491,29 @@ i2c_dw_read(struct i2c_adapter *adap)
        }
 }
 
+static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
+{
+       unsigned long abort_source = dev->abort_source;
+       int i;
+
+       if (abort_source & DW_IC_TX_ABRT_NOACK) {
+               for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+                       dev_dbg(dev->dev,
+                               "%s: %s\n", __func__, abort_sources[i]);
+               return -EREMOTEIO;
+       }
+
+       for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+               dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
+
+       if (abort_source & DW_IC_TX_ARB_LOST)
+               return -EAGAIN;
+       else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
+               return -EINVAL; /* wrong msgs[] data */
+       else
+               return -EIO;
+}
+
 /*
  * Prepare controller for a transaction and call i2c_dw_xfer_msg
  */
@@ -369,13 +535,14 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        dev->msg_read_idx = 0;
        dev->msg_err = 0;
        dev->status = STATUS_IDLE;
+       dev->abort_source = 0;
 
        ret = i2c_dw_wait_bus_not_busy(dev);
        if (ret < 0)
                goto done;
 
        /* start the transfers */
-       i2c_dw_xfer_msg(adap);
+       i2c_dw_xfer_init(dev);
 
        /* wait for tx to complete */
        ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
@@ -394,23 +561,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 
        /* no error */
        if (likely(!dev->cmd_err)) {
-               /* read rx fifo, and disable the adapter */
-               do {
-                       i2c_dw_read(adap);
-               } while (dev->status & STATUS_READ_IN_PROGRESS);
-               writeb(0, dev->base + DW_IC_ENABLE);
+               /* Disable the adapter */
+               writel(0, dev->base + DW_IC_ENABLE);
                ret = num;
                goto done;
        }
 
        /* We have an error */
        if (dev->cmd_err == DW_IC_ERR_TX_ABRT) {
-               unsigned long abort_source = dev->abort_source;
-               int i;
-
-               for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) {
-                   dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
-               }
+               ret = i2c_dw_handle_tx_abort(dev);
+               goto done;
        }
        ret = -EIO;
 
@@ -422,21 +582,67 @@ done:
 
 static u32 i2c_dw_func(struct i2c_adapter *adap)
 {
-       return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+       return  I2C_FUNC_I2C |
+               I2C_FUNC_10BIT_ADDR |
+               I2C_FUNC_SMBUS_BYTE |
+               I2C_FUNC_SMBUS_BYTE_DATA |
+               I2C_FUNC_SMBUS_WORD_DATA |
+               I2C_FUNC_SMBUS_I2C_BLOCK;
 }
 
-static void dw_i2c_pump_msg(unsigned long data)
+static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
 {
-       struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
-       u16 intr_mask;
-
-       i2c_dw_read(&dev->adapter);
-       i2c_dw_xfer_msg(&dev->adapter);
-
-       intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
-       if (dev->status & STATUS_WRITE_IN_PROGRESS)
-               intr_mask |= DW_IC_INTR_TX_EMPTY;
-       writew(intr_mask, dev->base + DW_IC_INTR_MASK);
+       u32 stat;
+
+       /*
+        * The IC_INTR_STAT register just indicates "enabled" interrupts.
+        * Ths unmasked raw version of interrupt status bits are available
+        * in the IC_RAW_INTR_STAT register.
+        *
+        * That is,
+        *   stat = readl(IC_INTR_STAT);
+        * equals to,
+        *   stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK);
+        *
+        * The raw version might be useful for debugging purposes.
+        */
+       stat = readl(dev->base + DW_IC_INTR_STAT);
+
+       /*
+        * Do not use the IC_CLR_INTR register to clear interrupts, or
+        * you'll miss some interrupts, triggered during the period from
+        * readl(IC_INTR_STAT) to readl(IC_CLR_INTR).
+        *
+        * Instead, use the separately-prepared IC_CLR_* registers.
+        */
+       if (stat & DW_IC_INTR_RX_UNDER)
+               readl(dev->base + DW_IC_CLR_RX_UNDER);
+       if (stat & DW_IC_INTR_RX_OVER)
+               readl(dev->base + DW_IC_CLR_RX_OVER);
+       if (stat & DW_IC_INTR_TX_OVER)
+               readl(dev->base + DW_IC_CLR_TX_OVER);
+       if (stat & DW_IC_INTR_RD_REQ)
+               readl(dev->base + DW_IC_CLR_RD_REQ);
+       if (stat & DW_IC_INTR_TX_ABRT) {
+               /*
+                * The IC_TX_ABRT_SOURCE register is cleared whenever
+                * the IC_CLR_TX_ABRT is read.  Preserve it beforehand.
+                */
+               dev->abort_source = readl(dev->base + DW_IC_TX_ABRT_SOURCE);
+               readl(dev->base + DW_IC_CLR_TX_ABRT);
+       }
+       if (stat & DW_IC_INTR_RX_DONE)
+               readl(dev->base + DW_IC_CLR_RX_DONE);
+       if (stat & DW_IC_INTR_ACTIVITY)
+               readl(dev->base + DW_IC_CLR_ACTIVITY);
+       if (stat & DW_IC_INTR_STOP_DET)
+               readl(dev->base + DW_IC_CLR_STOP_DET);
+       if (stat & DW_IC_INTR_START_DET)
+               readl(dev->base + DW_IC_CLR_START_DET);
+       if (stat & DW_IC_INTR_GEN_CALL)
+               readl(dev->base + DW_IC_CLR_GEN_CALL);
+
+       return stat;
 }
 
 /*
@@ -446,20 +652,37 @@ static void dw_i2c_pump_msg(unsigned long data)
 static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
 {
        struct dw_i2c_dev *dev = dev_id;
-       u16 stat;
+       u32 stat;
 
-       stat = readw(dev->base + DW_IC_INTR_STAT);
+       stat = i2c_dw_read_clear_intrbits(dev);
        dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
+
        if (stat & DW_IC_INTR_TX_ABRT) {
-               dev->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE);
                dev->cmd_err |= DW_IC_ERR_TX_ABRT;
                dev->status = STATUS_IDLE;
-       } else if (stat & DW_IC_INTR_TX_EMPTY)
-               tasklet_schedule(&dev->pump_msg);
 
-       readb(dev->base + DW_IC_CLR_INTR);      /* clear interrupts */
-       writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
-       if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
+               /*
+                * Anytime TX_ABRT is set, the contents of the tx/rx
+                * buffers are flushed.  Make sure to skip them.
+                */
+               writel(0, dev->base + DW_IC_INTR_MASK);
+               goto tx_aborted;
+       }
+
+       if (stat & DW_IC_INTR_RX_FULL)
+               i2c_dw_read(dev);
+
+       if (stat & DW_IC_INTR_TX_EMPTY)
+               i2c_dw_xfer_msg(dev);
+
+       /*
+        * No need to modify or disable the interrupt mask here.
+        * i2c_dw_xfer_msg() will take care of it according to
+        * the current transmit status.
+        */
+
+tx_aborted:
+       if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
                complete(&dev->cmd_complete);
 
        return IRQ_HANDLED;
@@ -474,8 +697,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
 {
        struct dw_i2c_dev *dev;
        struct i2c_adapter *adap;
-       struct resource *mem, *irq, *ioarea;
-       int r;
+       struct resource *mem, *ioarea;
+       int irq, r;
 
        /* NOTE: driver uses the static register mapping */
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -484,10 +707,10 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!irq) {
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
                dev_err(&pdev->dev, "no irq resource?\n");
-               return -EINVAL;
+               return irq; /* -ENXIO */
        }
 
        ioarea = request_mem_region(mem->start, resource_size(mem),
@@ -504,10 +727,9 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
        }
 
        init_completion(&dev->cmd_complete);
-       tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
        mutex_init(&dev->lock);
        dev->dev = get_device(&pdev->dev);
-       dev->irq = irq->start;
+       dev->irq = irq;
        platform_set_drvdata(pdev, dev);
 
        dev->clk = clk_get(&pdev->dev, NULL);
@@ -531,8 +753,8 @@ static int __devinit dw_i2c_probe(struct platform_device *pdev)
        }
        i2c_dw_init(dev);
 
-       writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
-       r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev);
+       writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
+       r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
        if (r) {
                dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
                goto err_iounmap;
@@ -587,7 +809,7 @@ static int __devexit dw_i2c_remove(struct platform_device *pdev)
        clk_put(dev->clk);
        dev->clk = NULL;
 
-       writeb(0, dev->base + DW_IC_ENABLE);
+       writel(0, dev->base + DW_IC_ENABLE);
        free_irq(dev->irq, dev);
        kfree(dev);
 
index 827da08..75bf3ad 100644 (file)
@@ -178,6 +178,12 @@ struct omap_i2c_dev {
        unsigned                b_hw:1;         /* bad h/w fixes */
        unsigned                idle:1;
        u16                     iestate;        /* Saved interrupt register */
+       u16                     pscstate;
+       u16                     scllstate;
+       u16                     sclhstate;
+       u16                     bufstate;
+       u16                     syscstate;
+       u16                     westate;
 };
 
 static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
@@ -230,9 +236,18 @@ static void omap_i2c_unidle(struct omap_i2c_dev *dev)
 
        clk_enable(dev->iclk);
        clk_enable(dev->fclk);
+       if (cpu_is_omap34xx()) {
+               omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+               omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate);
+               omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate);
+               omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate);
+               omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, dev->bufstate);
+               omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, dev->syscstate);
+               omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
+               omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+       }
        dev->idle = 0;
-       if (dev->iestate)
-               omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
+       omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
 }
 
 static void omap_i2c_idle(struct omap_i2c_dev *dev)
@@ -258,7 +273,7 @@ static void omap_i2c_idle(struct omap_i2c_dev *dev)
 
 static int omap_i2c_init(struct omap_i2c_dev *dev)
 {
-       u16 psc = 0, scll = 0, sclh = 0;
+       u16 psc = 0, scll = 0, sclh = 0, buf = 0;
        u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0;
        unsigned long fclk_rate = 12000000;
        unsigned long timeout;
@@ -287,24 +302,22 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
                                           SYSC_AUTOIDLE_MASK);
 
                } else if (dev->rev >= OMAP_I2C_REV_ON_3430) {
-                       u32 v;
-
-                       v = SYSC_AUTOIDLE_MASK;
-                       v |= SYSC_ENAWAKEUP_MASK;
-                       v |= (SYSC_IDLEMODE_SMART <<
+                       dev->syscstate = SYSC_AUTOIDLE_MASK;
+                       dev->syscstate |= SYSC_ENAWAKEUP_MASK;
+                       dev->syscstate |= (SYSC_IDLEMODE_SMART <<
                              __ffs(SYSC_SIDLEMODE_MASK));
-                       v |= (SYSC_CLOCKACTIVITY_FCLK <<
+                       dev->syscstate |= (SYSC_CLOCKACTIVITY_FCLK <<
                              __ffs(SYSC_CLOCKACTIVITY_MASK));
 
-                       omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, v);
+                       omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG,
+                                                       dev->syscstate);
                        /*
                         * Enabling all wakup sources to stop I2C freezing on
                         * WFI instruction.
                         * REVISIT: Some wkup sources might not be needed.
                         */
-                       omap_i2c_write_reg(dev, OMAP_I2C_WE_REG,
-                                                       OMAP_I2C_WE_ALL);
-
+                       dev->westate = OMAP_I2C_WE_ALL;
+                       omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
                }
        }
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
@@ -394,23 +407,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
        omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
        omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
 
-       if (dev->fifo_size)
-               /* Note: setup required fifo size - 1 */
-               omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
-                                       (dev->fifo_size - 1) << 8 | /* RTRSH */
-                                       OMAP_I2C_BUF_RXFIF_CLR |
-                                       (dev->fifo_size - 1) | /* XTRSH */
-                                       OMAP_I2C_BUF_TXFIF_CLR);
+       if (dev->fifo_size) {
+               /* Note: setup required fifo size - 1. RTRSH and XTRSH */
+               buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR |
+                       (dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR;
+               omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
+       }
 
        /* Take the I2C module out of reset: */
        omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
 
        /* Enable interrupts */
-       omap_i2c_write_reg(dev, OMAP_I2C_IE_REG,
-                       (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
+       dev->iestate = (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
                        OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
                        OMAP_I2C_IE_AL)  | ((dev->fifo_size) ?
-                               (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0));
+                               (OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
+       omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
+       if (cpu_is_omap34xx()) {
+               dev->pscstate = psc;
+               dev->scllstate = scll;
+               dev->sclhstate = sclh;
+               dev->bufstate = buf;
+       }
        return 0;
 }
 
index fbab684..5d1c260 100644 (file)
@@ -638,7 +638,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev)
 
        /* Register this adapter with the I2C subsystem */
        i2c_pnx->adapter->dev.parent = &pdev->dev;
-       ret = i2c_add_adapter(i2c_pnx->adapter);
+       i2c_pnx->adapter->nr = pdev->id;
+       ret = i2c_add_numbered_adapter(i2c_pnx->adapter);
        if (ret < 0) {
                dev_err(&pdev->dev, "I2C: Failed to add bus\n");
                goto out_irq;
index b4a55d4..365e0be 100644 (file)
@@ -363,7 +363,7 @@ static int acpi_smbus_cmi_add(struct acpi_device *device)
        smbus_cmi->cap_write = 0;
 
        acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1,
-                           acpi_smbus_cmi_query_methods, smbus_cmi, NULL);
+                           acpi_smbus_cmi_query_methods, NULL, smbus_cmi, NULL);
 
        if (smbus_cmi->cap_info == 0)
                goto err;
index 9a5d0aa..98ccfeb 100644 (file)
@@ -7,50 +7,25 @@ config HAVE_IDE
        bool
 
 menuconfig IDE
-       tristate "ATA/ATAPI/MFM/RLL support"
+       tristate "ATA/ATAPI/MFM/RLL support (DEPRECATED)"
        depends on HAVE_IDE
        depends on BLOCK
        ---help---
-         If you say Y here, your kernel will be able to manage low cost mass
-         storage units such as ATA/(E)IDE and ATAPI units. The most common
-         cases are IDE hard drives and ATAPI CD-ROM drives.
-
-         If your system is pure SCSI and doesn't use these interfaces, you
-         can say N here.
-
-         Integrated Disk Electronics (IDE aka ATA-1) is a connecting standard
-         for mass storage units such as hard disks. It was designed by
-         Western Digital and Compaq Computer in 1984. It was then named
-         ST506. Quite a number of disks use the IDE interface.
-
-         AT Attachment (ATA) is the superset of the IDE specifications.
-         ST506 was also called ATA-1.
-
-         Fast-IDE is ATA-2 (also named Fast ATA), Enhanced IDE (EIDE) is
-         ATA-3. It provides support for larger disks (up to 8.4GB by means of
-         the LBA standard), more disks (4 instead of 2) and for other mass
-         storage units such as tapes and cdrom. UDMA/33 (aka UltraDMA/33) is
-         ATA-4 and provides faster (and more CPU friendly) transfer modes
-         than previous PIO (Programmed processor Input/Output) from previous
-         ATA/IDE standards by means of fast DMA controllers.
-
-         ATA Packet Interface (ATAPI) is a protocol used by EIDE tape and
-         CD-ROM drives, similar in many respects to the SCSI protocol.
-
-         SMART IDE (Self Monitoring, Analysis and Reporting Technology) was
-         designed in order to prevent data corruption and disk crash by
-         detecting pre hardware failure conditions (heat, access time, and
-         the like...). Disks built since June 1995 may follow this standard.
-         The kernel itself doesn't manage this; however there are quite a
-         number of user programs such as smart that can query the status of
-         SMART parameters from disk drives.
+         If you say Y here, your kernel will be able to manage ATA/(E)IDE and
+         ATAPI units. The most common cases are IDE hard drives and ATAPI
+         CD-ROM drives.
+
+         This subsystem is currently in maintenance mode with only bug fix
+         changes applied. Users of ATA hardware are encouraged to migrate to
+         the newer ATA subsystem ("Serial ATA (prod) and Parallel ATA
+         (experimental) drivers") which is more actively maintained.
 
          To compile this driver as a module, choose M here: the
          module will be called ide-core.
 
          For further information, please read <file:Documentation/ide/ide.txt>.
 
-         If unsure, say Y.
+         If unsure, say N.
 
 if IDE
 
index e59b6de..0abc43f 100644 (file)
 
 #define DRV_NAME "alim15x3"
 
-/*
- * Allow UDMA on M1543C-E chipset for WDC disks that ignore CRC checking
- * (this is DANGEROUS and could result in data corruption).
- */
-static int wdc_udma;
-
-module_param(wdc_udma, bool, 0);
-MODULE_PARM_DESC(wdc_udma,
-                "allow UDMA on M1543C-E chipset for WDC disks (DANGEROUS)");
-
 /*
  *     ALi devices are not plug in. Otherwise these static values would
  *     need to go. They ought to go away anyway
@@ -132,7 +122,7 @@ static u8 ali_udma_filter(ide_drive_t *drive)
        if (m5229_revision > 0x20 && m5229_revision < 0xC2) {
                if (drive->media != ide_disk)
                        return 0;
-               if (wdc_udma == 0 && chip_is_1543c_e &&
+               if (chip_is_1543c_e &&
                    strstr((char *)&drive->id[ATA_ID_PROD], "WDC "))
                        return 0;
        }
index 58121bd..87cef0c 100644 (file)
@@ -532,14 +532,13 @@ static int au_ide_probe(struct platform_device *dev)
                goto out;
        }
 
-       if (!request_mem_region(res->start, res->end - res->start + 1,
-                               dev->name)) {
+       if (!request_mem_region(res->start, resource_size(res), dev->name)) {
                pr_debug("%s: request_mem_region failed\n", DRV_NAME);
                ret =  -EBUSY;
                goto out;
        }
 
-       ahwif->regbase = (u32)ioremap(res->start, res->end - res->start + 1);
+       ahwif->regbase = (u32)ioremap(res->start, resource_size(res));
        if (ahwif->regbase == 0) {
                ret = -ENOMEM;
                goto out;
@@ -575,7 +574,7 @@ static int au_ide_remove(struct platform_device *dev)
        iounmap((void *)ahwif->regbase);
 
        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, res->end - res->start + 1);
+       release_mem_region(res->start, resource_size(res));
 
        return 0;
 }
index ca0c46f..f2500c8 100644 (file)
 
 #define DRV_NAME "cmd64x"
 
-#define CMD_DEBUG 0
-
-#if CMD_DEBUG
-#define cmdprintk(x...)        printk(x)
-#else
-#define cmdprintk(x...)
-#endif
-
 /*
  * CMD64x specific registers definition.
  */
@@ -76,9 +68,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_
                {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
        static const u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM2, DRWTIM3};
 
-       cmdprintk("program_cycle_times parameters: total=%d, active=%d\n",
-                 cycle_time, active_time);
-
        cycle_count     = quantize_timing( cycle_time, clock_time);
        active_count    = quantize_timing(active_time, clock_time);
        recovery_count  = cycle_count - active_count;
@@ -94,9 +83,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_
        if (active_count > 16)          /* shouldn't actually happen... */
                active_count = 16;
 
-       cmdprintk("Final counts: total=%d, active=%d, recovery=%d\n",
-                 cycle_count, active_count, recovery_count);
-
        /*
         * Convert values to internal chipset representation
         */
@@ -106,7 +92,6 @@ static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_
        /* Program the active/recovery counts into the DRWTIM register */
        drwtim = (active_count << 4) | recovery_count;
        (void) pci_write_config_byte(dev, drwtim_regs[drive->dn], drwtim);
-       cmdprintk("Write 0x%02x to reg 0x%x\n", drwtim, drwtim_regs[drive->dn]);
 }
 
 /*
@@ -150,7 +135,6 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
 
        if (setup_count > 5)            /* shouldn't actually happen... */
                setup_count = 5;
-       cmdprintk("Final address setup count: %d\n", setup_count);
 
        /*
         * Program the address setup clocks into the ARTTIM registers.
@@ -162,7 +146,6 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
        arttim &= ~0xc0;
        arttim |= setup_values[setup_count];
        (void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim);
-       cmdprintk("Write 0x%02x to reg 0x%x\n", arttim, arttim_regs[drive->dn]);
 }
 
 /*
index 983d957..b883838 100644 (file)
@@ -187,6 +187,7 @@ static int __devinit cs5535_init_one(struct pci_dev *dev,
 
 static const struct pci_device_id cs5535_pci_tbl[] = {
        { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_CS5535_IDE), 0 },
+       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5535_IDE), },
        { 0, },
 };
 
index 74fc540..d6e2cbb 100644 (file)
 
 #define DRV_NAME "cy82c693"
 
-/*
- *     The following are used to debug the driver.
- */
-#define CY82C693_DEBUG_INFO    0
-
 /*
  *     NOTE: the value for busmaster timeout is tricky and I got it by
  *     trial and error!  By using a to low value will cause DMA timeouts
@@ -176,11 +171,6 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode)
        outb(index, CY82_INDEX_PORT);
        outb(data, CY82_DATA_PORT);
 
-#if CY82C693_DEBUG_INFO
-       printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n",
-               drive->name, hwif->channel, drive->dn & 1, mode & 3, single);
-#endif /* CY82C693_DEBUG_INFO */
-
        /*
         * note: below we set the value for Bus Master IDE TimeOut Register
         * I'm not absolutly sure what this does, but it solved my problem
@@ -194,11 +184,6 @@ static void cy82c693_set_dma_mode(ide_drive_t *drive, const u8 mode)
        data = BUSMASTER_TIMEOUT;
        outb(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
        outb(data, CY82_DATA_PORT);
-
-#if CY82C693_DEBUG_INFO
-       printk(KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n",
-               drive->name, data);
-#endif /* CY82C693_DEBUG_INFO */
 }
 
 static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
@@ -239,8 +224,6 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
                pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
                pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
                pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
-
-               addrCtrl &= 0xF;
        } else {
                /*
                 * set slave drive
@@ -257,17 +240,7 @@ static void cy82c693_set_pio_mode(ide_drive_t *drive, const u8 pio)
                pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
                pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
                pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
-
-               addrCtrl >>= 4;
-               addrCtrl &= 0xF;
        }
-
-#if CY82C693_DEBUG_INFO
-       printk(KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to "
-               "(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n",
-               drive->name, hwif->channel, drive->dn & 1,
-               addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
-#endif /* CY82C693_DEBUG_INFO */
 }
 
 static void __devinit init_iops_cy82c693(ide_hwif_t *hwif)
index 7ce68ef..4d90ac2 100644 (file)
@@ -297,68 +297,6 @@ static u32 twenty_five_base_hpt36x[] = {
        /* XFER_PIO_0 */        0xc0d08585
 };
 
-#if 0
-/* These are the timing tables from the HighPoint open source drivers... */
-static u32 thirty_three_base_hpt37x[] = {
-       /* XFER_UDMA_6 */       0x12446231,     /* 0x12646231 ?? */
-       /* XFER_UDMA_5 */       0x12446231,
-       /* XFER_UDMA_4 */       0x12446231,
-       /* XFER_UDMA_3 */       0x126c6231,
-       /* XFER_UDMA_2 */       0x12486231,
-       /* XFER_UDMA_1 */       0x124c6233,
-       /* XFER_UDMA_0 */       0x12506297,
-
-       /* XFER_MW_DMA_2 */     0x22406c31,
-       /* XFER_MW_DMA_1 */     0x22406c33,
-       /* XFER_MW_DMA_0 */     0x22406c97,
-
-       /* XFER_PIO_4 */        0x06414e31,
-       /* XFER_PIO_3 */        0x06414e42,
-       /* XFER_PIO_2 */        0x06414e53,
-       /* XFER_PIO_1 */        0x06814e93,
-       /* XFER_PIO_0 */        0x06814ea7
-};
-
-static u32 fifty_base_hpt37x[] = {
-       /* XFER_UDMA_6 */       0x12848242,
-       /* XFER_UDMA_5 */       0x12848242,
-       /* XFER_UDMA_4 */       0x12ac8242,
-       /* XFER_UDMA_3 */       0x128c8242,
-       /* XFER_UDMA_2 */       0x120c8242,
-       /* XFER_UDMA_1 */       0x12148254,
-       /* XFER_UDMA_0 */       0x121882ea,
-
-       /* XFER_MW_DMA_2 */     0x22808242,
-       /* XFER_MW_DMA_1 */     0x22808254,
-       /* XFER_MW_DMA_0 */     0x228082ea,
-
-       /* XFER_PIO_4 */        0x0a81f442,
-       /* XFER_PIO_3 */        0x0a81f443,
-       /* XFER_PIO_2 */        0x0a81f454,
-       /* XFER_PIO_1 */        0x0ac1f465,
-       /* XFER_PIO_0 */        0x0ac1f48a
-};
-
-static u32 sixty_six_base_hpt37x[] = {
-       /* XFER_UDMA_6 */       0x1c869c62,
-       /* XFER_UDMA_5 */       0x1cae9c62,     /* 0x1c8a9c62 */
-       /* XFER_UDMA_4 */       0x1c8a9c62,
-       /* XFER_UDMA_3 */       0x1c8e9c62,
-       /* XFER_UDMA_2 */       0x1c929c62,
-       /* XFER_UDMA_1 */       0x1c9a9c62,
-       /* XFER_UDMA_0 */       0x1c829c62,
-
-       /* XFER_MW_DMA_2 */     0x2c829c62,
-       /* XFER_MW_DMA_1 */     0x2c829c66,
-       /* XFER_MW_DMA_0 */     0x2c829d2e,
-
-       /* XFER_PIO_4 */        0x0c829c62,
-       /* XFER_PIO_3 */        0x0c829c84,
-       /* XFER_PIO_2 */        0x0c829ca6,
-       /* XFER_PIO_1 */        0x0d029d26,
-       /* XFER_PIO_0 */        0x0d029d5e
-};
-#else
 /*
  * The following are the new timing tables with PIO mode data/taskfile transfer
  * overclocking fixed...
@@ -424,16 +362,13 @@ static u32 sixty_six_base_hpt37x[] = {
        /* XFER_PIO_1 */        0x0d02ff26,
        /* XFER_PIO_0 */        0x0d42ff7f
 };
-#endif
 
-#define HPT366_DEBUG_DRIVE_INFO                0
 #define HPT371_ALLOW_ATA133_6          1
 #define HPT302_ALLOW_ATA133_6          1
 #define HPT372_ALLOW_ATA133_6          1
 #define HPT370_ALLOW_ATA100_5          0
 #define HPT366_ALLOW_ATA66_4           1
 #define HPT366_ALLOW_ATA66_3           1
-#define HPT366_MAX_DEVS                        8
 
 /* Supported ATA clock frequencies */
 enum ata_clock {
index 58fc920..6a0e625 100644 (file)
@@ -221,6 +221,8 @@ typedef struct ide_tape_obj {
 
 static DEFINE_MUTEX(idetape_ref_mutex);
 
+static DEFINE_MUTEX(idetape_chrdev_mutex);
+
 static struct class *idetape_sysfs_class;
 
 static void ide_tape_release(struct device *);
@@ -1457,10 +1459,11 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
        if (i >= MAX_HWIFS * MAX_DRIVES)
                return -ENXIO;
 
-       lock_kernel();
+       mutex_lock(&idetape_chrdev_mutex);
+
        tape = ide_tape_get(NULL, true, i);
        if (!tape) {
-               unlock_kernel();
+               mutex_unlock(&idetape_chrdev_mutex);
                return -ENXIO;
        }
 
@@ -1519,12 +1522,15 @@ static int idetape_chrdev_open(struct inode *inode, struct file *filp)
                                tape->door_locked = DOOR_LOCKED;
                }
        }
-       unlock_kernel();
+       mutex_unlock(&idetape_chrdev_mutex);
+
        return 0;
 
 out_put_tape:
        ide_tape_put(tape);
-       unlock_kernel();
+
+       mutex_unlock(&idetape_chrdev_mutex);
+
        return retval;
 }
 
@@ -1551,7 +1557,8 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp)
        ide_drive_t *drive = tape->drive;
        unsigned int minor = iminor(inode);
 
-       lock_kernel();
+       mutex_lock(&idetape_chrdev_mutex);
+
        tape = drive->driver_data;
 
        ide_debug_log(IDE_DBG_FUNC, "enter");
@@ -1575,7 +1582,9 @@ static int idetape_chrdev_release(struct inode *inode, struct file *filp)
        }
        clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags);
        ide_tape_put(tape);
-       unlock_kernel();
+
+       mutex_unlock(&idetape_chrdev_mutex);
+
        return 0;
 }
 
index b579fbe..42965b3 100644 (file)
@@ -81,14 +81,14 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
 
        if (mmio) {
                base = devm_ioremap(&pdev->dev,
-                       res_base->start, res_base->end - res_base->start + 1);
+                       res_base->start, resource_size(res_base));
                alt_base = devm_ioremap(&pdev->dev,
-                       res_alt->start, res_alt->end - res_alt->start + 1);
+                       res_alt->start, resource_size(res_alt));
        } else {
                base = devm_ioport_map(&pdev->dev,
-                       res_base->start, res_base->end - res_base->start + 1);
+                       res_base->start, resource_size(res_base));
                alt_base = devm_ioport_map(&pdev->dev,
-                       res_alt->start, res_alt->end - res_alt->start + 1);
+                       res_alt->start, resource_size(res_alt));
        }
 
        memset(&hw, 0, sizeof(hw));
index cb812f3..35161dd 100644 (file)
@@ -21,8 +21,6 @@
 
 #define DRV_NAME "pdc202xx_old"
 
-#define PDC202XX_DEBUG_DRIVE_INFO      0
-
 static void pdc_old_disable_66MHz_clock(ide_hwif_t *);
 
 static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
@@ -34,11 +32,6 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
        u8                      AP = 0, BP = 0, CP = 0;
        u8                      TA = 0, TB = 0, TC = 0;
 
-#if PDC202XX_DEBUG_DRIVE_INFO
-       u32                     drive_conf = 0;
-       pci_read_config_dword(dev, drive_pci, &drive_conf);
-#endif
-
        /*
         * TODO: do this once per channel
         */
@@ -89,14 +82,6 @@ static void pdc202xx_set_mode(ide_drive_t *drive, const u8 speed)
                pci_write_config_byte(dev, drive_pci + 1, BP | TB);
                pci_write_config_byte(dev, drive_pci + 2, CP | TC);
        }
-
-#if PDC202XX_DEBUG_DRIVE_INFO
-       printk(KERN_DEBUG "%s: %s drive%d 0x%08x ",
-               drive->name, ide_xfer_verbose(speed),
-               drive->dn, drive_conf);
-       pci_read_config_dword(dev, drive_pci, &drive_conf);
-       printk("0x%08x\n", drive_conf);
-#endif
 }
 
 static void pdc202xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
index 3b88eba..4687060 100644 (file)
@@ -632,12 +632,3 @@ module_exit(sis5513_ide_exit);
 MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik");
 MODULE_DESCRIPTION("PCI driver module for SIS IDE");
 MODULE_LICENSE("GPL");
-
-/*
- * TODO:
- *     - CLEANUP
- *     - More checks in the config registers (force values instead of
- *       relying on the BIOS setting them correctly).
- *     - Further optimisations ?
- *       . for example ATA66+ regs 0x48 & 0x4A
- */
index d698da4..3c2bbf0 100644 (file)
 
 #define DRV_NAME "sl82c105"
 
-#undef DEBUG
-
-#ifdef DEBUG
-#define DBG(arg) printk arg
-#else
-#define DBG(fmt,...)
-#endif
 /*
  * SL82C105 PCI config register 0x40 bits.
  */
@@ -104,9 +97,6 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
        unsigned long timings = (unsigned long)ide_get_drivedata(drive);
        u16 drv_ctrl;
 
-       DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n",
-            drive->name, ide_xfer_verbose(speed)));
-
        drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0];
 
        /*
@@ -196,8 +186,6 @@ static void sl82c105_dma_start(ide_drive_t *drive)
        struct pci_dev *dev     = to_pci_dev(hwif->dev);
        int reg                 = 0x44 + drive->dn * 4;
 
-       DBG(("%s(drive:%s)\n", __func__, drive->name));
-
        pci_write_config_word(dev, reg,
                              (unsigned long)ide_get_drivedata(drive) >> 16);
 
@@ -209,8 +197,6 @@ static void sl82c105_dma_clear(ide_drive_t *drive)
 {
        struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
 
-       DBG(("sl82c105_dma_clear(drive:%s)\n", drive->name));
-
        sl82c105_reset_host(dev);
 }
 
@@ -218,11 +204,7 @@ static int sl82c105_dma_end(ide_drive_t *drive)
 {
        struct pci_dev *dev     = to_pci_dev(drive->hwif->dev);
        int reg                 = 0x44 + drive->dn * 4;
-       int ret;
-
-       DBG(("%s(drive:%s)\n", __func__, drive->name));
-
-       ret = ide_dma_end(drive);
+       int ret                 = ide_dma_end(drive);
 
        pci_write_config_word(dev, reg,
                              (unsigned long)ide_get_drivedata(drive));
@@ -239,8 +221,6 @@ static void sl82c105_resetproc(ide_drive_t *drive)
        struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
        u32 val;
 
-       DBG(("sl82c105_resetproc(drive:%s)\n", drive->name));
-
        pci_read_config_dword(dev, 0x40, &val);
        val |= (CTRL_P1F16 | CTRL_P0F16);
        pci_write_config_dword(dev, 0x40, val);
@@ -291,8 +271,6 @@ static int init_chipset_sl82c105(struct pci_dev *dev)
 {
        u32 val;
 
-       DBG(("init_chipset_sl82c105()\n"));
-
        pci_read_config_dword(dev, 0x40, &val);
        val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
        pci_write_config_dword(dev, 0x40, val);
index 9aec78d..1ccfb40 100644 (file)
@@ -91,8 +91,7 @@ static void slc90e66_set_dma_mode(ide_drive_t *drive, const u8 speed)
 
                if (!(reg48 & u_flag))
                        pci_write_config_word(dev, 0x48, reg48|u_flag);
-               /* FIXME: (reg4a & a_speed) ? */
-               if ((reg4a & u_speed) != u_speed) {
+               if ((reg4a & a_speed) != u_speed) {
                        pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
                        pci_read_config_word(dev, 0x4a, &reg4a);
                        pci_write_config_word(dev, 0x4a, reg4a|u_speed);
index ea89fdd..fd59c0d 100644 (file)
@@ -146,7 +146,7 @@ static int __init tx4938ide_probe(struct platform_device *pdev)
                return -ENODEV;
 
        if (!devm_request_mem_region(&pdev->dev, res->start,
-                                    res->end - res->start + 1, "tx4938ide"))
+                                    resource_size(res), "tx4938ide"))
                return -EBUSY;
        mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start,
                                              8 << pdata->ioport_shift);
index 2cd00b5..9fd4a0d 100644 (file)
    0 - no debugging messages
    1 - some debugging messages, but none during DMA frame transmission
    2 - lots of messages, including during DMA frame transmission
-       (will cause undeflows if your machine is too slow!)
+       (will cause underflows if your machine is too slow!)
 */
 
 #define DV1394_DEBUG_LEVEL 0
index 72ed339..0677fc7 100644 (file)
@@ -589,7 +589,7 @@ static int cxio_hal_destroy_ctrl_qp(struct cxio_rdev *rdev_p)
 
 /* write len bytes of data into addr (32B aligned address)
  * If data is NULL, clear len byte of memory to zero.
- * caller aquires the ctrl_qp lock before the call
+ * caller acquires the ctrl_qp lock before the call
  */
 static int cxio_hal_ctrl_qp_write_mem(struct cxio_rdev *rdev_p, u32 addr,
                                      u32 len, void *data)
index 4bd39c8..37d12e5 100644 (file)
@@ -381,7 +381,7 @@ static const ipath_err_t infinipath_hwe_htclnkbbyte1crcerr =
 #define IPATH_GPIO_SCL \
        (1ULL << (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
 
-/* keep the code below somewhat more readonable; not used elsewhere */
+/* keep the code below somewhat more readable; not used elsewhere */
 #define _IPATH_HTLINK0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr |    \
                                infinipath_hwe_htclnkabyte1crcerr)
 #define _IPATH_HTLINK1_CRCBITS (infinipath_hwe_htclnkbbyte0crcerr |    \
index aa47eb5..2a68d9f 100644 (file)
@@ -614,7 +614,7 @@ static int epb_trans(struct ipath_devdata *dd, u16 reg, u64 i_val, u64 *o_vp)
  * @wd: Write Data - value to set in register
  * @mask: ones where data should be spliced into reg.
  *
- * Basic register read/modify/write, with un-needed acesses elided. That is,
+ * Basic register read/modify/write, with un-needed accesses elided. That is,
  * a mask of zero will prevent write, while a mask of 0xFF will prevent read.
  * returns current (presumed, if a write was done) contents of selected
  * register, or <0 if errors.
@@ -989,7 +989,7 @@ static struct rxeq_init {
        /* Set DFELTHFDR/HDR thresholds */
        RXEQ_VAL(7, 8,    0, 0, 0, 0), /* FDR */
        RXEQ_VAL(7, 0x21, 0, 0, 0, 0), /* HDR */
-       /* Set TLTHFDR/HDR theshold */
+       /* Set TLTHFDR/HDR threshold */
        RXEQ_VAL(7, 9,    2, 2, 2, 2), /* FDR */
        RXEQ_VAL(7, 0x23, 2, 2, 2, 2), /* HDR */
        /* Set Preamp setting 2 (ZFR/ZCNT) */
index 219b103..256a00c 100644 (file)
@@ -352,7 +352,7 @@ static int set_kernel_sq_size(struct mlx4_ib_dev *dev, struct ib_qp_cap *cap,
         * anymore, so we do this only if selective signaling is off.
         *
         * Further, on 32-bit platforms, we can't use vmap() to make
-        * the QP buffer virtually contigious.  Thus we have to use
+        * the QP buffer virtually contiguous.  Thus we have to use
         * constant-sized WRs to make sure a WR is always fully within
         * a single page-sized chunk.
         *
index add9188..5f7a6fc 100644 (file)
@@ -625,6 +625,7 @@ static struct iscsi_transport iscsi_iser_transport = {
                                  ISCSI_USERNAME | ISCSI_PASSWORD |
                                  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
                                  ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+                                 ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
                                  ISCSI_PING_TMO | ISCSI_RECV_TMO |
                                  ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
        .host_param_mask        = ISCSI_HOST_HWADDRESS |
index ea9e155..8579f32 100644 (file)
@@ -499,7 +499,7 @@ void iser_conn_init(struct iser_conn *ib_conn)
 
  /**
  * starts the process of connecting to the target
- * sleeps untill the connection is established or rejected
+ * sleeps until the connection is established or rejected
  */
 int iser_connect(struct iser_conn   *ib_conn,
                 struct sockaddr_in *src_addr,
index cd50c00..07c2cd4 100644 (file)
@@ -8,7 +8,7 @@ menu "Input device support"
 config INPUT
        tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED
        default y
-       ---help---
+       help
          Say Y here if you have any input device (mouse, keyboard, tablet,
          joystick, steering wheel ...) connected to your system and want
          it to be available to applications. This includes standard PS/2
@@ -27,8 +27,7 @@ if INPUT
 
 config INPUT_FF_MEMLESS
        tristate "Support for memoryless force-feedback devices"
-       default n
-       ---help---
+       help
          Say Y here if you have memoryless force-feedback input device
          such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual
          Power 2, or similar. You will also need to enable hardware-specific
@@ -52,12 +51,25 @@ config INPUT_POLLDEV
          To compile this driver as a module, choose M here: the
          module will be called input-polldev.
 
+config INPUT_SPARSEKMAP
+       tristate "Sparse keymap support library"
+       help
+         Say Y here if you are using a driver for an input
+         device that uses sparse keymap. This option is only
+         useful for out-of-tree drivers since in-tree drivers
+         select it automatically.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called sparse-keymap.
+
 comment "Userland interfaces"
 
 config INPUT_MOUSEDEV
        tristate "Mouse interface" if EMBEDDED
        default y
-       ---help---
+       help
          Say Y here if you want your mouse to be accessible as char devices
          13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
          emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
@@ -73,7 +85,7 @@ config INPUT_MOUSEDEV_PSAUX
        bool "Provide legacy /dev/psaux device"
        default y
        depends on INPUT_MOUSEDEV
-       ---help---
+       help
          Say Y here if you want your mouse also be accessible as char device
          10:1 - /dev/psaux. The data available through /dev/psaux is exactly
          the same as the data from /dev/input/mice.
@@ -103,7 +115,7 @@ config INPUT_MOUSEDEV_SCREEN_Y
 
 config INPUT_JOYDEV
        tristate "Joystick interface"
-       ---help---
+       help
          Say Y here if you want your joystick or gamepad to be
          accessible as char device 13:0+ - /dev/input/jsX device.
 
@@ -125,7 +137,7 @@ config INPUT_EVDEV
 
 config INPUT_EVBUG
        tristate "Event debugging"
-       ---help---
+       help
          Say Y here if you have a problem with the input subsystem and
          want all events (keypresses, mouse movements), to be output to
          the system log. While this is useful for debugging, it's also
@@ -140,7 +152,7 @@ config INPUT_EVBUG
 config INPUT_APMPOWER
        tristate "Input Power Event -> APM Bridge" if EMBEDDED
        depends on INPUT && APM_EMULATION
-       ---help---
+       help
          Say Y here if you want suspend key events to trigger a user
          requested suspend through APM. This is useful on embedded
          systems where such behaviour is desired without userspace
@@ -153,6 +165,7 @@ config XEN_KBDDEV_FRONTEND
        tristate "Xen virtual keyboard and mouse support"
        depends on XEN_FBDEV_FRONTEND
        default y
+       select XEN_XENBUS_FRONTEND
        help
          This driver implements the front-end of the Xen virtual
          keyboard and mouse device driver.  It communicates with a back-end
index 4c9c745..7ad212d 100644 (file)
@@ -9,6 +9,7 @@ input-core-objs := input.o input-compat.o ff-core.o
 
 obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
 obj-$(CONFIG_INPUT_POLLDEV)    += input-polldev.o
+obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
 
 obj-$(CONFIG_INPUT_MOUSEDEV)   += mousedev.o
 obj-$(CONFIG_INPUT_JOYDEV)     += joydev.o
index 38df81f..b2f07aa 100644 (file)
@@ -353,7 +353,7 @@ int input_ff_create(struct input_dev *dev, int max_effects)
 EXPORT_SYMBOL_GPL(input_ff_create);
 
 /**
- * input_ff_free() - frees force feedback portion of input device
+ * input_ff_destroy() - frees force feedback portion of input device
  * @dev: input device supporting force feedback
  *
  * This function is only needed in error path as input core will
@@ -369,6 +369,7 @@ void input_ff_destroy(struct input_dev *dev)
                if (ff->destroy)
                        ff->destroy(ff);
                kfree(ff->private);
+               kfree(ff->effects);
                kfree(ff);
                dev->ff = NULL;
        }
index 0d3ce7a..aa6713b 100644 (file)
@@ -56,14 +56,10 @@ static void input_polldev_stop_workqueue(void)
        mutex_unlock(&polldev_mutex);
 }
 
-static void input_polled_device_work(struct work_struct *work)
+static void input_polldev_queue_work(struct input_polled_dev *dev)
 {
-       struct input_polled_dev *dev =
-               container_of(work, struct input_polled_dev, work.work);
        unsigned long delay;
 
-       dev->poll(dev);
-
        delay = msecs_to_jiffies(dev->poll_interval);
        if (delay >= HZ)
                delay = round_jiffies_relative(delay);
@@ -71,6 +67,15 @@ static void input_polled_device_work(struct work_struct *work)
        queue_delayed_work(polldev_wq, &dev->work, delay);
 }
 
+static void input_polled_device_work(struct work_struct *work)
+{
+       struct input_polled_dev *dev =
+               container_of(work, struct input_polled_dev, work.work);
+
+       dev->poll(dev);
+       input_polldev_queue_work(dev);
+}
+
 static int input_open_polled_device(struct input_dev *input)
 {
        struct input_polled_dev *dev = input_get_drvdata(input);
@@ -80,11 +85,12 @@ static int input_open_polled_device(struct input_dev *input)
        if (error)
                return error;
 
-       if (dev->flush)
-               dev->flush(dev);
+       if (dev->open)
+               dev->open(dev);
 
-       queue_delayed_work(polldev_wq, &dev->work,
-                          msecs_to_jiffies(dev->poll_interval));
+       /* Only start polling if polling is enabled */
+       if (dev->poll_interval > 0)
+               queue_delayed_work(polldev_wq, &dev->work, 0);
 
        return 0;
 }
@@ -95,8 +101,88 @@ static void input_close_polled_device(struct input_dev *input)
 
        cancel_delayed_work_sync(&dev->work);
        input_polldev_stop_workqueue();
+
+       if (dev->close)
+               dev->close(dev);
 }
 
+/* SYSFS interface */
+
+static ssize_t input_polldev_get_poll(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", polldev->poll_interval);
+}
+
+static ssize_t input_polldev_set_poll(struct device *dev,
+                               struct device_attribute *attr, const char *buf,
+                               size_t count)
+{
+       struct input_polled_dev *polldev = dev_get_drvdata(dev);
+       struct input_dev *input = polldev->input;
+       unsigned long interval;
+
+       if (strict_strtoul(buf, 0, &interval))
+               return -EINVAL;
+
+       if (interval < polldev->poll_interval_min)
+               return -EINVAL;
+
+       if (interval > polldev->poll_interval_max)
+               return -EINVAL;
+
+       mutex_lock(&input->mutex);
+
+       polldev->poll_interval = interval;
+
+       if (input->users) {
+               cancel_delayed_work_sync(&polldev->work);
+               if (polldev->poll_interval > 0)
+                       input_polldev_queue_work(polldev);
+       }
+
+       mutex_unlock(&input->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll,
+                                           input_polldev_set_poll);
+
+
+static ssize_t input_polldev_get_max(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", polldev->poll_interval_max);
+}
+
+static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL);
+
+static ssize_t input_polldev_get_min(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct input_polled_dev *polldev = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", polldev->poll_interval_min);
+}
+
+static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL);
+
+static struct attribute *sysfs_attrs[] = {
+       &dev_attr_poll.attr,
+       &dev_attr_max.attr,
+       &dev_attr_min.attr,
+       NULL
+};
+
+static struct attribute_group input_polldev_attribute_group = {
+       .attrs = sysfs_attrs
+};
+
 /**
  * input_allocate_polled_device - allocated memory polled device
  *
@@ -126,7 +212,7 @@ EXPORT_SYMBOL(input_allocate_polled_device);
  * @dev: device to free
  *
  * The function frees memory allocated for polling device and drops
- * reference to the associated input device (if present).
+ * reference to the associated input device.
  */
 void input_free_polled_device(struct input_polled_dev *dev)
 {
@@ -150,15 +236,38 @@ EXPORT_SYMBOL(input_free_polled_device);
 int input_register_polled_device(struct input_polled_dev *dev)
 {
        struct input_dev *input = dev->input;
+       int error;
 
        input_set_drvdata(input, dev);
        INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
        if (!dev->poll_interval)
                dev->poll_interval = 500;
+       if (!dev->poll_interval_max)
+               dev->poll_interval_max = dev->poll_interval;
        input->open = input_open_polled_device;
        input->close = input_close_polled_device;
 
-       return input_register_device(input);
+       error = input_register_device(input);
+       if (error)
+               return error;
+
+       error = sysfs_create_group(&input->dev.kobj,
+                                  &input_polldev_attribute_group);
+       if (error) {
+               input_unregister_device(input);
+               return error;
+       }
+
+       /*
+        * Take extra reference to the underlying input device so
+        * that it survives call to input_unregister_polled_device()
+        * and is deleted only after input_free_polled_device()
+        * has been invoked. This is needed to ease task of freeing
+        * sparse keymaps.
+        */
+       input_get_device(input);
+
+       return 0;
 }
 EXPORT_SYMBOL(input_register_polled_device);
 
@@ -169,13 +278,13 @@ EXPORT_SYMBOL(input_register_polled_device);
  * The function unregisters previously registered polled input
  * device from input layer. Polling is stopped and device is
  * ready to be freed with call to input_free_polled_device().
- * Callers should not attempt to access dev->input pointer
- * after calling this function.
  */
 void input_unregister_polled_device(struct input_polled_dev *dev)
 {
+       sysfs_remove_group(&dev->input->dev.kobj,
+                          &input_polldev_attribute_group);
+
        input_unregister_device(dev->input);
-       dev->input = NULL;
 }
 EXPORT_SYMBOL(input_unregister_polled_device);
 
index 2266ecb..5c16001 100644 (file)
@@ -1657,6 +1657,38 @@ void input_unregister_handler(struct input_handler *handler)
 }
 EXPORT_SYMBOL(input_unregister_handler);
 
+/**
+ * input_handler_for_each_handle - handle iterator
+ * @handler: input handler to iterate
+ * @data: data for the callback
+ * @fn: function to be called for each handle
+ *
+ * Iterate over @bus's list of devices, and call @fn for each, passing
+ * it @data and stop when @fn returns a non-zero value. The function is
+ * using RCU to traverse the list and therefore may be usind in atonic
+ * contexts. The @fn callback is invoked from RCU critical section and
+ * thus must not sleep.
+ */
+int input_handler_for_each_handle(struct input_handler *handler, void *data,
+                                 int (*fn)(struct input_handle *, void *))
+{
+       struct input_handle *handle;
+       int retval = 0;
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(handle, &handler->h_list, h_node) {
+               retval = fn(handle, data);
+               if (retval)
+                       break;
+       }
+
+       rcu_read_unlock();
+
+       return retval;
+}
+EXPORT_SYMBOL(input_handler_for_each_handle);
+
 /**
  * input_register_handle - register a new input handle
  * @handle: handle to register
@@ -1690,7 +1722,7 @@ int input_register_handle(struct input_handle *handle)
         * we can't be racing with input_unregister_handle()
         * and so separate lock is not needed here.
         */
-       list_add_tail(&handle->h_node, &handler->h_list);
+       list_add_tail_rcu(&handle->h_node, &handler->h_list);
 
        if (handler->start)
                handler->start(handle);
@@ -1713,7 +1745,7 @@ void input_unregister_handle(struct input_handle *handle)
 {
        struct input_dev *dev = handle->dev;
 
-       list_del_init(&handle->h_node);
+       list_del_rcu(&handle->h_node);
 
        /*
         * Take dev->mutex to prevent race with input_release_device().
@@ -1721,6 +1753,7 @@ void input_unregister_handle(struct input_handle *handle)
        mutex_lock(&dev->mutex);
        list_del_rcu(&handle->d_node);
        mutex_unlock(&dev->mutex);
+
        synchronize_rcu();
 }
 EXPORT_SYMBOL(input_unregister_handle);
index 79e3edc..482cb12 100644 (file)
@@ -125,6 +125,7 @@ static const struct xpad_device {
        { 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
+       { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x0c12, 0x8802, "Zeroplus Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x0c12, 0x880a, "Pelican Eclipse PL-2023", MAP_DPAD_TO_AXES, XTYPE_XBOX },
@@ -146,6 +147,7 @@ static const struct xpad_device {
        { 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+       { 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN }
 };
@@ -212,6 +214,7 @@ static struct usb_device_id xpad_table [] = {
        XPAD_XBOX360_VENDOR(0x1430),            /* RedOctane X-Box 360 controllers */
        XPAD_XBOX360_VENDOR(0x146b),            /* BigBen Interactive Controllers */
        XPAD_XBOX360_VENDOR(0x1bad),            /* Rock Band Drums */
+       XPAD_XBOX360_VENDOR(0x0f0d),            /* Hori Controllers */
        { }
 };
 
index ee98b1b..203b88a 100644 (file)
@@ -361,6 +361,16 @@ config KEYBOARD_SH_KEYSC
          To compile this driver as a module, choose M here: the
          module will be called sh_keysc.
 
+config KEYBOARD_DAVINCI
+       tristate "TI DaVinci Key Scan"
+       depends on ARCH_DAVINCI_DM365
+       help
+         Say Y to enable keypad module support for the TI DaVinci
+         platforms (DM365).
+
+         To compile this driver as a module, choose M here: the
+         module will be called davinci_keyscan.
+
 config KEYBOARD_OMAP
        tristate "TI OMAP keypad support"
        depends on (ARCH_OMAP1 || ARCH_OMAP2)
index babad5e..68c0172 100644 (file)
@@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_ATARI)          += atakbd.o
 obj-$(CONFIG_KEYBOARD_ATKBD)           += atkbd.o
 obj-$(CONFIG_KEYBOARD_BFIN)            += bf54x-keys.o
 obj-$(CONFIG_KEYBOARD_CORGI)           += corgikbd.o
+obj-$(CONFIG_KEYBOARD_DAVINCI)         += davinci_keyscan.o
 obj-$(CONFIG_KEYBOARD_EP93XX)          += ep93xx_keypad.o
 obj-$(CONFIG_KEYBOARD_GPIO)            += gpio_keys.o
 obj-$(CONFIG_KEYBOARD_HIL)             += hil_kbd.o
index 28e6110..a357357 100644 (file)
@@ -1567,9 +1567,8 @@ static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id)
        return 0;
 }
 
-static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
+static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
        {
-               .ident = "Dell Laptop",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
@@ -1578,7 +1577,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_dell_laptop_forced_release_keys,
        },
        {
-               .ident = "Dell Laptop",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
@@ -1587,7 +1585,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_dell_laptop_forced_release_keys,
        },
        {
-               .ident = "HP 2133",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"),
@@ -1596,7 +1593,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_hp_forced_release_keys,
        },
        {
-               .ident = "HP Pavilion ZV6100",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"),
@@ -1605,7 +1601,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_volume_forced_release_keys,
        },
        {
-               .ident = "HP Presario R4000",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4000"),
@@ -1614,7 +1609,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_volume_forced_release_keys,
        },
        {
-               .ident = "HP Presario R4100",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4100"),
@@ -1623,7 +1617,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_volume_forced_release_keys,
        },
        {
-               .ident = "HP Presario R4200",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Presario R4200"),
@@ -1632,7 +1625,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_volume_forced_release_keys,
        },
        {
-               .ident = "Inventec Symphony",
+               /* Inventec Symphony */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"),
@@ -1641,7 +1634,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_volume_forced_release_keys,
        },
        {
-               .ident = "Samsung NC10",
+               /* Samsung NC10 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
@@ -1650,7 +1643,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_samsung_forced_release_keys,
        },
        {
-               .ident = "Samsung NC20",
+               /* Samsung NC20 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "NC20"),
@@ -1659,7 +1652,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_samsung_forced_release_keys,
        },
        {
-               .ident = "Samsung SQ45S70S",
+               /* Samsung SQ45S70S */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
@@ -1668,7 +1661,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_samsung_forced_release_keys,
        },
        {
-               .ident = "Fujitsu Amilo PA 1510",
+               /* Fujitsu Amilo PA 1510 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"),
@@ -1677,7 +1670,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_volume_forced_release_keys,
        },
        {
-               .ident = "Fujitsu Amilo Pi 3525",
+               /* Fujitsu Amilo Pi 3525 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pi 3525"),
@@ -1686,7 +1679,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_amilo_pi3525_forced_release_keys,
        },
        {
-               .ident = "Fujitsu Amilo Xi 3650",
+               /* Fujitsu Amilo Xi 3650 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 3650"),
@@ -1695,7 +1688,6 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkbd_amilo_xi3650_forced_release_keys,
        },
        {
-               .ident = "Soltech Corporation TA12",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Soltech Corporation"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TA12"),
@@ -1704,7 +1696,7 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                .driver_data = atkdb_soltech_ta12_forced_release_keys,
        },
        {
-               .ident = "OQO Model 01+",
+               /* OQO Model 01+ */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c
new file mode 100644 (file)
index 0000000..6e52d85
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * DaVinci Key Scan Driver for TI platforms
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * Intial Code: Sandeep Paulraj <s-paulraj@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/keyscan.h>
+
+/* Key scan registers */
+#define DAVINCI_KEYSCAN_KEYCTRL                0x0000
+#define DAVINCI_KEYSCAN_INTENA         0x0004
+#define DAVINCI_KEYSCAN_INTFLAG                0x0008
+#define DAVINCI_KEYSCAN_INTCLR         0x000c
+#define DAVINCI_KEYSCAN_STRBWIDTH      0x0010
+#define DAVINCI_KEYSCAN_INTERVAL       0x0014
+#define DAVINCI_KEYSCAN_CONTTIME       0x0018
+#define DAVINCI_KEYSCAN_CURRENTST      0x001c
+#define DAVINCI_KEYSCAN_PREVSTATE      0x0020
+#define DAVINCI_KEYSCAN_EMUCTRL                0x0024
+#define DAVINCI_KEYSCAN_IODFTCTRL      0x002c
+
+/* Key Control Register (KEYCTRL) */
+#define DAVINCI_KEYSCAN_KEYEN          0x00000001
+#define DAVINCI_KEYSCAN_PREVMODE       0x00000002
+#define DAVINCI_KEYSCAN_CHATOFF                0x00000004
+#define DAVINCI_KEYSCAN_AUTODET                0x00000008
+#define DAVINCI_KEYSCAN_SCANMODE       0x00000010
+#define DAVINCI_KEYSCAN_OUTTYPE                0x00000020
+
+/* Masks for the interrupts */
+#define DAVINCI_KEYSCAN_INT_CONT       0x00000008
+#define DAVINCI_KEYSCAN_INT_OFF                0x00000004
+#define DAVINCI_KEYSCAN_INT_ON         0x00000002
+#define DAVINCI_KEYSCAN_INT_CHANGE     0x00000001
+#define DAVINCI_KEYSCAN_INT_ALL                0x0000000f
+
+struct davinci_ks {
+       struct input_dev                *input;
+       struct davinci_ks_platform_data *pdata;
+       int                             irq;
+       void __iomem                    *base;
+       resource_size_t                 pbase;
+       size_t                          base_size;
+       unsigned short                  keymap[];
+};
+
+/* Initializing the kp Module */
+static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks)
+{
+       struct device *dev = &davinci_ks->input->dev;
+       struct davinci_ks_platform_data *pdata = davinci_ks->pdata;
+       u32 matrix_ctrl;
+
+       /* Enable all interrupts */
+       __raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+                    davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+       /* Clear interrupts if any */
+       __raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+                    davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
+
+       /* Setup the scan period = strobe + interval */
+       __raw_writel(pdata->strobe,
+                    davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH);
+       __raw_writel(pdata->interval,
+                    davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL);
+       __raw_writel(0x01,
+                    davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME);
+
+       /* Define matrix type */
+       switch (pdata->matrix_type) {
+       case DAVINCI_KEYSCAN_MATRIX_4X4:
+               matrix_ctrl = 0;
+               break;
+       case DAVINCI_KEYSCAN_MATRIX_5X3:
+               matrix_ctrl = (1 << 6);
+               break;
+       default:
+               dev_err(dev->parent, "wrong matrix type\n");
+               return -EINVAL;
+       }
+
+       /* Enable key scan module and set matrix type */
+       __raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN |
+                    matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL);
+
+       return 0;
+}
+
+static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id)
+{
+       struct davinci_ks *davinci_ks = dev_id;
+       struct device *dev = &davinci_ks->input->dev;
+       unsigned short *keymap = davinci_ks->keymap;
+       int keymapsize = davinci_ks->pdata->keymapsize;
+       u32 prev_status, new_status, changed;
+       bool release;
+       int keycode = KEY_UNKNOWN;
+       int i;
+
+       /* Disable interrupt */
+       __raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+       /* Reading previous and new status of the key scan */
+       prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE);
+       new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST);
+
+       changed = prev_status ^ new_status;
+
+       if (changed) {
+               /*
+                * It goes through all bits in 'changed' to ensure
+                * that no key changes are being missed
+                */
+               for (i = 0 ; i < keymapsize; i++) {
+                       if ((changed>>i) & 0x1) {
+                               keycode = keymap[i];
+                               release = (new_status >> i) & 0x1;
+                               dev_dbg(dev->parent, "key %d %s\n", keycode,
+                                       release ? "released" : "pressed");
+                               input_report_key(davinci_ks->input, keycode,
+                                                !release);
+                               input_sync(davinci_ks->input);
+                       }
+               }
+               /* Clearing interrupt */
+               __raw_writel(DAVINCI_KEYSCAN_INT_ALL,
+                            davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
+       }
+
+       /* Enable interrupts */
+       __raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
+
+       return IRQ_HANDLED;
+}
+
+static int __init davinci_ks_probe(struct platform_device *pdev)
+{
+       struct davinci_ks *davinci_ks;
+       struct input_dev *key_dev;
+       struct resource *res, *mem;
+       struct device *dev = &pdev->dev;
+       struct davinci_ks_platform_data *pdata = pdev->dev.platform_data;
+       int error, i;
+
+       if (!pdata->keymap) {
+               dev_dbg(dev, "no keymap from pdata\n");
+               return -EINVAL;
+       }
+
+       davinci_ks = kzalloc(sizeof(struct davinci_ks) +
+               sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL);
+       if (!davinci_ks) {
+               dev_dbg(dev, "could not allocate memory for private data\n");
+               return -ENOMEM;
+       }
+
+       memcpy(davinci_ks->keymap, pdata->keymap,
+               sizeof(unsigned short) * pdata->keymapsize);
+
+       key_dev = input_allocate_device();
+       if (!key_dev) {
+               dev_dbg(dev, "could not allocate input device\n");
+               error = -ENOMEM;
+               goto fail1;
+       }
+
+       davinci_ks->input = key_dev;
+
+       davinci_ks->irq = platform_get_irq(pdev, 0);
+       if (davinci_ks->irq < 0) {
+               dev_err(dev, "no key scan irq\n");
+               error = davinci_ks->irq;
+               goto fail2;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "no mem resource\n");
+               error = -EINVAL;
+               goto fail2;
+       }
+
+       davinci_ks->pbase = res->start;
+       davinci_ks->base_size = resource_size(res);
+
+       mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size,
+                                pdev->name);
+       if (!mem) {
+               dev_err(dev, "key scan registers at %08x are not free\n",
+                       davinci_ks->pbase);
+               error = -EBUSY;
+               goto fail2;
+       }
+
+       davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size);
+       if (!davinci_ks->base) {
+               dev_err(dev, "can't ioremap MEM resource.\n");
+               error = -ENOMEM;
+               goto fail3;
+       }
+
+       /* Enable auto repeat feature of Linux input subsystem */
+       if (pdata->rep)
+               __set_bit(EV_REP, key_dev->evbit);
+
+       /* Setup input device */
+       __set_bit(EV_KEY, key_dev->evbit);
+
+       /* Setup the platform data */
+       davinci_ks->pdata = pdata;
+
+       for (i = 0; i < davinci_ks->pdata->keymapsize; i++)
+               __set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit);
+
+       key_dev->name = "davinci_keyscan";
+       key_dev->phys = "davinci_keyscan/input0";
+       key_dev->dev.parent = &pdev->dev;
+       key_dev->id.bustype = BUS_HOST;
+       key_dev->id.vendor = 0x0001;
+       key_dev->id.product = 0x0001;
+       key_dev->id.version = 0x0001;
+       key_dev->keycode = davinci_ks->keymap;
+       key_dev->keycodesize = sizeof(davinci_ks->keymap[0]);
+       key_dev->keycodemax = davinci_ks->pdata->keymapsize;
+
+       error = input_register_device(davinci_ks->input);
+       if (error < 0) {
+               dev_err(dev, "unable to register davinci key scan device\n");
+               goto fail4;
+       }
+
+       error = request_irq(davinci_ks->irq, davinci_ks_interrupt,
+                         IRQF_DISABLED, pdev->name, davinci_ks);
+       if (error < 0) {
+               dev_err(dev, "unable to register davinci key scan interrupt\n");
+               goto fail5;
+       }
+
+       error = davinci_ks_initialize(davinci_ks);
+       if (error < 0) {
+               dev_err(dev, "unable to initialize davinci key scan device\n");
+               goto fail6;
+       }
+
+       platform_set_drvdata(pdev, davinci_ks);
+       return 0;
+
+fail6:
+       free_irq(davinci_ks->irq, davinci_ks);
+fail5:
+       input_unregister_device(davinci_ks->input);
+       key_dev = NULL;
+fail4:
+       iounmap(davinci_ks->base);
+fail3:
+       release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
+fail2:
+       input_free_device(key_dev);
+fail1:
+       kfree(davinci_ks);
+
+       return error;
+}
+
+static int __devexit davinci_ks_remove(struct platform_device *pdev)
+{
+       struct davinci_ks *davinci_ks = platform_get_drvdata(pdev);
+
+       free_irq(davinci_ks->irq, davinci_ks);
+
+       input_unregister_device(davinci_ks->input);
+
+       iounmap(davinci_ks->base);
+       release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
+
+       platform_set_drvdata(pdev, NULL);
+
+       kfree(davinci_ks);
+
+       return 0;
+}
+
+static struct platform_driver davinci_ks_driver = {
+       .driver = {
+               .name = "davinci_keyscan",
+               .owner = THIS_MODULE,
+       },
+       .remove = __devexit_p(davinci_ks_remove),
+};
+
+static int __init davinci_ks_init(void)
+{
+       return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe);
+}
+module_init(davinci_ks_init);
+
+static void __exit davinci_ks_exit(void)
+{
+       platform_driver_unregister(&davinci_ks_driver);
+}
+module_exit(davinci_ks_exit);
+
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
+MODULE_LICENSE("GPL");
index 77d1309..1aff3b7 100644 (file)
@@ -23,8 +23,7 @@
 #include <linux/input.h>
 #include <linux/gpio_keys.h>
 #include <linux/workqueue.h>
-
-#include <asm/gpio.h>
+#include <linux/gpio.h>
 
 struct gpio_button_data {
        struct gpio_keys_button *button;
@@ -38,10 +37,8 @@ struct gpio_keys_drvdata {
        struct gpio_button_data data[0];
 };
 
-static void gpio_keys_report_event(struct work_struct *work)
+static void gpio_keys_report_event(struct gpio_button_data *bdata)
 {
-       struct gpio_button_data *bdata =
-               container_of(work, struct gpio_button_data, work);
        struct gpio_keys_button *button = bdata->button;
        struct input_dev *input = bdata->input;
        unsigned int type = button->type ?: EV_KEY;
@@ -51,6 +48,14 @@ static void gpio_keys_report_event(struct work_struct *work)
        input_sync(input);
 }
 
+static void gpio_keys_work_func(struct work_struct *work)
+{
+       struct gpio_button_data *bdata =
+               container_of(work, struct gpio_button_data, work);
+
+       gpio_keys_report_event(bdata);
+}
+
 static void gpio_keys_timer(unsigned long _data)
 {
        struct gpio_button_data *data = (struct gpio_button_data *)_data;
@@ -74,10 +79,62 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int __devinit gpio_keys_setup_key(struct device *dev,
+                                        struct gpio_button_data *bdata,
+                                        struct gpio_keys_button *button)
+{
+       char *desc = button->desc ? button->desc : "gpio_keys";
+       int irq, error;
+
+       setup_timer(&bdata->timer, gpio_keys_timer, (unsigned long)bdata);
+       INIT_WORK(&bdata->work, gpio_keys_work_func);
+
+       error = gpio_request(button->gpio, desc);
+       if (error < 0) {
+               dev_err(dev, "failed to request GPIO %d, error %d\n",
+                       button->gpio, error);
+               goto fail2;
+       }
+
+       error = gpio_direction_input(button->gpio);
+       if (error < 0) {
+               dev_err(dev, "failed to configure"
+                       " direction for GPIO %d, error %d\n",
+                       button->gpio, error);
+               goto fail3;
+       }
+
+       irq = gpio_to_irq(button->gpio);
+       if (irq < 0) {
+               error = irq;
+               dev_err(dev, "Unable to get irq number for GPIO %d, error %d\n",
+                       button->gpio, error);
+               goto fail3;
+       }
+
+       error = request_irq(irq, gpio_keys_isr,
+                           IRQF_SHARED |
+                           IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                           desc, bdata);
+       if (error) {
+               dev_err(dev, "Unable to claim irq %d; error %d\n",
+                       irq, error);
+               goto fail3;
+       }
+
+       return 0;
+
+fail3:
+       gpio_free(button->gpio);
+fail2:
+       return error;
+}
+
 static int __devinit gpio_keys_probe(struct platform_device *pdev)
 {
        struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
        struct gpio_keys_drvdata *ddata;
+       struct device *dev = &pdev->dev;
        struct input_dev *input;
        int i, error;
        int wakeup = 0;
@@ -87,6 +144,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
                        GFP_KERNEL);
        input = input_allocate_device();
        if (!ddata || !input) {
+               dev_err(dev, "failed to allocate state\n");
                error = -ENOMEM;
                goto fail1;
        }
@@ -111,52 +169,14 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
        for (i = 0; i < pdata->nbuttons; i++) {
                struct gpio_keys_button *button = &pdata->buttons[i];
                struct gpio_button_data *bdata = &ddata->data[i];
-               int irq;
                unsigned int type = button->type ?: EV_KEY;
 
                bdata->input = input;
                bdata->button = button;
-               setup_timer(&bdata->timer,
-                           gpio_keys_timer, (unsigned long)bdata);
-               INIT_WORK(&bdata->work, gpio_keys_report_event);
-
-               error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
-               if (error < 0) {
-                       pr_err("gpio-keys: failed to request GPIO %d,"
-                               " error %d\n", button->gpio, error);
-                       goto fail2;
-               }
-
-               error = gpio_direction_input(button->gpio);
-               if (error < 0) {
-                       pr_err("gpio-keys: failed to configure input"
-                               " direction for GPIO %d, error %d\n",
-                               button->gpio, error);
-                       gpio_free(button->gpio);
-                       goto fail2;
-               }
-
-               irq = gpio_to_irq(button->gpio);
-               if (irq < 0) {
-                       error = irq;
-                       pr_err("gpio-keys: Unable to get irq number"
-                               " for GPIO %d, error %d\n",
-                               button->gpio, error);
-                       gpio_free(button->gpio);
-                       goto fail2;
-               }
 
-               error = request_irq(irq, gpio_keys_isr,
-                                   IRQF_SHARED |
-                                   IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-                                   button->desc ? button->desc : "gpio_keys",
-                                   bdata);
-               if (error) {
-                       pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
-                               irq, error);
-                       gpio_free(button->gpio);
+               error = gpio_keys_setup_key(dev, bdata, button);
+               if (error)
                        goto fail2;
-               }
 
                if (button->wakeup)
                        wakeup = 1;
@@ -166,11 +186,16 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
 
        error = input_register_device(input);
        if (error) {
-               pr_err("gpio-keys: Unable to register input device, "
+               dev_err(dev, "Unable to register input device, "
                        "error: %d\n", error);
                goto fail2;
        }
 
+       /* get current state of buttons */
+       for (i = 0; i < pdata->nbuttons; i++)
+               gpio_keys_report_event(&ddata->data[i]);
+       input_sync(input);
+
        device_init_wakeup(&pdev->dev, wakeup);
 
        return 0;
@@ -239,18 +264,21 @@ static int gpio_keys_suspend(struct device *dev)
 static int gpio_keys_resume(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
+       struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
        struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
        int i;
 
-       if (device_may_wakeup(&pdev->dev)) {
-               for (i = 0; i < pdata->nbuttons; i++) {
-                       struct gpio_keys_button *button = &pdata->buttons[i];
-                       if (button->wakeup) {
-                               int irq = gpio_to_irq(button->gpio);
-                               disable_irq_wake(irq);
-                       }
+       for (i = 0; i < pdata->nbuttons; i++) {
+
+               struct gpio_keys_button *button = &pdata->buttons[i];
+               if (button->wakeup && device_may_wakeup(&pdev->dev)) {
+                       int irq = gpio_to_irq(button->gpio);
+                       disable_irq_wake(irq);
                }
+
+               gpio_keys_report_event(&ddata->data[i]);
        }
+       input_sync(ddata->input);
 
        return 0;
 }
index f9847e0..fa9bb6d 100644 (file)
@@ -72,9 +72,9 @@
 
 #define DRIVER_DESC    "LK keyboard driver"
 
-MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
 
 /*
  * Known parameters:
@@ -85,27 +85,27 @@ MODULE_LICENSE ("GPL");
  * Please notice that there's not yet an API to set these at runtime.
  */
 static int bell_volume = 100; /* % */
-module_param (bell_volume, int, 0);
-MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
+module_param(bell_volume, int, 0);
+MODULE_PARM_DESC(bell_volume, "Bell volume (in %). default is 100%");
 
 static int keyclick_volume = 100; /* % */
-module_param (keyclick_volume, int, 0);
-MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
+module_param(keyclick_volume, int, 0);
+MODULE_PARM_DESC(keyclick_volume, "Keyclick volume (in %), default is 100%");
 
 static int ctrlclick_volume = 100; /* % */
-module_param (ctrlclick_volume, int, 0);
-MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
+module_param(ctrlclick_volume, int, 0);
+MODULE_PARM_DESC(ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
 
 static int lk201_compose_is_alt;
-module_param (lk201_compose_is_alt, int, 0);
-MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
-               "will act as an Alt key");
+module_param(lk201_compose_is_alt, int, 0);
+MODULE_PARM_DESC(lk201_compose_is_alt,
+                "If set non-zero, LK201' Compose key will act as an Alt key");
 
 
 
 #undef LKKBD_DEBUG
 #ifdef LKKBD_DEBUG
-#define DBG(x...) printk (x)
+#define DBG(x...) printk(x)
 #else
 #define DBG(x...) do {} while (0)
 #endif
@@ -122,7 +122,7 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
 #define LK_MODE_DOWN           0x80
 #define LK_MODE_AUTODOWN       0x82
 #define LK_MODE_UPDOWN         0x86
-#define LK_CMD_SET_MODE(mode,div)      ((mode) | ((div) << 3))
+#define LK_CMD_SET_MODE(mode, div)     ((mode) | ((div) << 3))
 
 /* Misc commands */
 #define LK_CMD_ENABLE_KEYCLICK 0x1b
@@ -152,11 +152,8 @@ MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
 
 #define LK_NUM_KEYCODES                256
 #define LK_NUM_IGNORE_BYTES    6
-typedef u_int16_t lk_keycode_t;
 
-
-
-static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
+static unsigned short lkkbd_keycode[LK_NUM_KEYCODES] = {
        [0x56] = KEY_F1,
        [0x57] = KEY_F2,
        [0x58] = KEY_F3,
@@ -268,7 +265,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
 };
 
 #define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do {         \
-       if (test_bit (LED, (LK)->dev->led))                     \
+       if (test_bit(LED, (LK)->dev->led))                      \
                VAR_ON |= BITS;                                 \
        else                                                    \
                VAR_OFF |= BITS;                                \
@@ -278,7 +275,7 @@ static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
  * Per-keyboard data
  */
 struct lkkbd {
-       lk_keycode_t keycode[LK_NUM_KEYCODES];
+       unsigned short keycode[LK_NUM_KEYCODES];
        int ignore_bytes;
        unsigned char id[LK_NUM_IGNORE_BYTES];
        struct input_dev *dev;
@@ -301,26 +298,25 @@ static struct {
        unsigned char *name;
 } lk_response[] = {
 #define RESPONSE(x) { .value = (x), .name = #x, }
-       RESPONSE (LK_STUCK_KEY),
-       RESPONSE (LK_SELFTEST_FAILED),
-       RESPONSE (LK_ALL_KEYS_UP),
-       RESPONSE (LK_METRONOME),
-       RESPONSE (LK_OUTPUT_ERROR),
-       RESPONSE (LK_INPUT_ERROR),
-       RESPONSE (LK_KBD_LOCKED),
-       RESPONSE (LK_KBD_TEST_MODE_ACK),
-       RESPONSE (LK_PREFIX_KEY_DOWN),
-       RESPONSE (LK_MODE_CHANGE_ACK),
-       RESPONSE (LK_RESPONSE_RESERVED),
+       RESPONSE(LK_STUCK_KEY),
+       RESPONSE(LK_SELFTEST_FAILED),
+       RESPONSE(LK_ALL_KEYS_UP),
+       RESPONSE(LK_METRONOME),
+       RESPONSE(LK_OUTPUT_ERROR),
+       RESPONSE(LK_INPUT_ERROR),
+       RESPONSE(LK_KBD_LOCKED),
+       RESPONSE(LK_KBD_TEST_MODE_ACK),
+       RESPONSE(LK_PREFIX_KEY_DOWN),
+       RESPONSE(LK_MODE_CHANGE_ACK),
+       RESPONSE(LK_RESPONSE_RESERVED),
 #undef RESPONSE
 };
 
-static unsigned char *
-response_name (unsigned char value)
+static unsigned char *response_name(unsigned char value)
 {
        int i;
 
-       for (i = 0; i < ARRAY_SIZE (lk_response); i++)
+       for (i = 0; i < ARRAY_SIZE(lk_response); i++)
                if (lk_response[i].value == value)
                        return lk_response[i].name;
 
@@ -331,8 +327,7 @@ response_name (unsigned char value)
 /*
  * Calculate volume parameter byte for a given volume.
  */
-static unsigned char
-volume_to_hw (int volume_percent)
+static unsigned char volume_to_hw(int volume_percent)
 {
        unsigned char ret = 0;
 
@@ -363,8 +358,7 @@ volume_to_hw (int volume_percent)
        return ret;
 }
 
-static void
-lkkbd_detection_done (struct lkkbd *lk)
+static void lkkbd_detection_done(struct lkkbd *lk)
 {
        int i;
 
@@ -377,190 +371,202 @@ lkkbd_detection_done (struct lkkbd *lk)
         * Print keyboard name and modify Compose=Alt on user's request.
         */
        switch (lk->id[4]) {
-               case 1:
-                       strlcpy (lk->name, "DEC LK201 keyboard",
-                                sizeof (lk->name));
-
-                       if (lk201_compose_is_alt)
-                               lk->keycode[0xb1] = KEY_LEFTALT;
-                       break;
-
-               case 2:
-                       strlcpy (lk->name, "DEC LK401 keyboard",
-                                sizeof (lk->name));
-                       break;
-
-               default:
-                       strlcpy (lk->name, "Unknown DEC keyboard",
-                                sizeof (lk->name));
-                       printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
-                                       "please report to Jan-Benedict Glaw "
-                                       "<jbglaw@lug-owl.de>\n", lk->phys);
-                       printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
-                       for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
-                               printk (" 0x%02x", lk->id[i]);
-                       printk ("\n");
-                       break;
+       case 1:
+               strlcpy(lk->name, "DEC LK201 keyboard", sizeof(lk->name));
+
+               if (lk201_compose_is_alt)
+                       lk->keycode[0xb1] = KEY_LEFTALT;
+               break;
+
+       case 2:
+               strlcpy(lk->name, "DEC LK401 keyboard", sizeof(lk->name));
+               break;
+
+       default:
+               strlcpy(lk->name, "Unknown DEC keyboard", sizeof(lk->name));
+               printk(KERN_ERR
+                       "lkkbd: keyboard on %s is unknown, please report to "
+                       "Jan-Benedict Glaw <jbglaw@lug-owl.de>\n", lk->phys);
+               printk(KERN_ERR "lkkbd: keyboard ID'ed as:");
+               for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
+                       printk(" 0x%02x", lk->id[i]);
+               printk("\n");
+               break;
        }
-       printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
-                       lk->phys, lk->name);
+
+       printk(KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
+               lk->phys, lk->name);
 
        /*
         * Report errors during keyboard boot-up.
         */
        switch (lk->id[2]) {
-               case 0x00:
-                       /* All okay */
-                       break;
-
-               case LK_STUCK_KEY:
-                       printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
-                                       "%s\n", lk->phys);
-                       break;
-
-               case LK_SELFTEST_FAILED:
-                       printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
-                                       "at %s, keyboard may not work "
-                                       "properly\n", lk->phys);
-                       break;
-
-               default:
-                       printk (KERN_ERR "lkkbd: Unknown error %02x on "
-                                       "keyboard at %s\n", lk->id[2],
-                                       lk->phys);
-                       break;
+       case 0x00:
+               /* All okay */
+               break;
+
+       case LK_STUCK_KEY:
+               printk(KERN_ERR "lkkbd: Stuck key on keyboard at %s\n",
+                       lk->phys);
+               break;
+
+       case LK_SELFTEST_FAILED:
+               printk(KERN_ERR
+                       "lkkbd: Selftest failed on keyboard at %s, "
+                       "keyboard may not work properly\n", lk->phys);
+               break;
+
+       default:
+               printk(KERN_ERR
+                       "lkkbd: Unknown error %02x on keyboard at %s\n",
+                       lk->id[2], lk->phys);
+               break;
        }
 
        /*
         * Try to hint user if there's a stuck key.
         */
        if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
-               printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
-                               "is 0x%04x\n", lk->id[3],
-                               lk->keycode[lk->id[3]]);
-
-       return;
+               printk(KERN_ERR
+                       "Scancode of stuck key is 0x%02x, keycode is 0x%04x\n",
+                       lk->id[3], lk->keycode[lk->id[3]]);
 }
 
 /*
  * lkkbd_interrupt() is called by the low level driver when a character
  * is received.
  */
-static irqreturn_t
-lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
+static irqreturn_t lkkbd_interrupt(struct serio *serio,
+                                  unsigned char data, unsigned int flags)
 {
-       struct lkkbd *lk = serio_get_drvdata (serio);
+       struct lkkbd *lk = serio_get_drvdata(serio);
+       struct input_dev *input_dev = lk->dev;
+       unsigned int keycode;
        int i;
 
-       DBG (KERN_INFO "Got byte 0x%02x\n", data);
+       DBG(KERN_INFO "Got byte 0x%02x\n", data);
 
        if (lk->ignore_bytes > 0) {
-               DBG (KERN_INFO "Ignoring a byte on %s\n", lk->name);
+               DBG(KERN_INFO "Ignoring a byte on %s\n", lk->name);
                lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
 
                if (lk->ignore_bytes == 0)
-                       lkkbd_detection_done (lk);
+                       lkkbd_detection_done(lk);
 
                return IRQ_HANDLED;
        }
 
        switch (data) {
-               case LK_ALL_KEYS_UP:
-                       for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
-                               if (lk->keycode[i] != KEY_RESERVED)
-                                       input_report_key (lk->dev, lk->keycode[i], 0);
-                       input_sync (lk->dev);
-                       break;
-
-               case 0x01:
-                       DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
-                       lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
-                       lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
-                       schedule_work (&lk->tq);
-                       break;
-
-               case LK_METRONOME:
-               case LK_OUTPUT_ERROR:
-               case LK_INPUT_ERROR:
-               case LK_KBD_LOCKED:
-               case LK_KBD_TEST_MODE_ACK:
-               case LK_PREFIX_KEY_DOWN:
-               case LK_MODE_CHANGE_ACK:
-               case LK_RESPONSE_RESERVED:
-                       DBG (KERN_INFO "Got %s and don't know how to handle...\n",
-                                       response_name (data));
-                       break;
-
-               default:
-                       if (lk->keycode[data] != KEY_RESERVED) {
-                               if (!test_bit (lk->keycode[data], lk->dev->key))
-                                       input_report_key (lk->dev, lk->keycode[data], 1);
-                               else
-                                       input_report_key (lk->dev, lk->keycode[data], 0);
-                               input_sync (lk->dev);
-                        } else
-                                printk (KERN_WARNING "%s: Unknown key with "
-                                               "scancode 0x%02x on %s.\n",
-                                               __FILE__, data, lk->name);
+       case LK_ALL_KEYS_UP:
+               for (i = 0; i < ARRAY_SIZE(lkkbd_keycode); i++)
+                       input_report_key(input_dev, lk->keycode[i], 0);
+               input_sync(input_dev);
+               break;
+
+       case 0x01:
+               DBG(KERN_INFO "Got 0x01, scheduling re-initialization\n");
+               lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
+               lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+               schedule_work(&lk->tq);
+               break;
+
+       case LK_METRONOME:
+       case LK_OUTPUT_ERROR:
+       case LK_INPUT_ERROR:
+       case LK_KBD_LOCKED:
+       case LK_KBD_TEST_MODE_ACK:
+       case LK_PREFIX_KEY_DOWN:
+       case LK_MODE_CHANGE_ACK:
+       case LK_RESPONSE_RESERVED:
+               DBG(KERN_INFO "Got %s and don't know how to handle...\n",
+                       response_name(data));
+               break;
+
+       default:
+               keycode = lk->keycode[data];
+               if (keycode != KEY_RESERVED) {
+                       input_report_key(input_dev, keycode,
+                                        !test_bit(keycode, input_dev->key));
+                       input_sync(input_dev);
+               } else {
+                       printk(KERN_WARNING
+                               "%s: Unknown key with scancode 0x%02x on %s.\n",
+                               __FILE__, data, lk->name);
+               }
        }
 
        return IRQ_HANDLED;
 }
 
+static void lkkbd_toggle_leds(struct lkkbd *lk)
+{
+       struct serio *serio = lk->serio;
+       unsigned char leds_on = 0;
+       unsigned char leds_off = 0;
+
+       CHECK_LED(lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
+       CHECK_LED(lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
+       CHECK_LED(lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
+       CHECK_LED(lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
+       if (leds_on != 0) {
+               serio_write(serio, LK_CMD_LED_ON);
+               serio_write(serio, leds_on);
+       }
+       if (leds_off != 0) {
+               serio_write(serio, LK_CMD_LED_OFF);
+               serio_write(serio, leds_off);
+       }
+}
+
+static void lkkbd_toggle_keyclick(struct lkkbd *lk, bool on)
+{
+       struct serio *serio = lk->serio;
+
+       if (on) {
+               DBG("%s: Activating key clicks\n", __func__);
+               serio_write(serio, LK_CMD_ENABLE_KEYCLICK);
+               serio_write(serio, volume_to_hw(lk->keyclick_volume));
+               serio_write(serio, LK_CMD_ENABLE_CTRCLICK);
+               serio_write(serio, volume_to_hw(lk->ctrlclick_volume));
+       } else {
+               DBG("%s: Deactivating key clicks\n", __func__);
+               serio_write(serio, LK_CMD_DISABLE_KEYCLICK);
+               serio_write(serio, LK_CMD_DISABLE_CTRCLICK);
+       }
+
+}
+
 /*
  * lkkbd_event() handles events from the input module.
  */
-static int
-lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
-               int value)
+static int lkkbd_event(struct input_dev *dev,
+                       unsigned int type, unsigned int code, int value)
 {
-       struct lkkbd *lk = input_get_drvdata (dev);
-       unsigned char leds_on = 0;
-       unsigned char leds_off = 0;
+       struct lkkbd *lk = input_get_drvdata(dev);
 
        switch (type) {
-               case EV_LED:
-                       CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
-                       CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
-                       CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
-                       CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
-                       if (leds_on != 0) {
-                               serio_write (lk->serio, LK_CMD_LED_ON);
-                               serio_write (lk->serio, leds_on);
-                       }
-                       if (leds_off != 0) {
-                               serio_write (lk->serio, LK_CMD_LED_OFF);
-                               serio_write (lk->serio, leds_off);
-                       }
+       case EV_LED:
+               lkkbd_toggle_leds(lk);
+               return 0;
+
+       case EV_SND:
+               switch (code) {
+               case SND_CLICK:
+                       lkkbd_toggle_keyclick(lk, value);
                        return 0;
 
-               case EV_SND:
-                       switch (code) {
-                               case SND_CLICK:
-                                       if (value == 0) {
-                                               DBG ("%s: Deactivating key clicks\n", __func__);
-                                               serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
-                                               serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
-                                       } else {
-                                               DBG ("%s: Activating key clicks\n", __func__);
-                                               serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
-                                               serio_write (lk->serio, volume_to_hw (lk->keyclick_volume));
-                                               serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
-                                               serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
-                                       }
-                                       return 0;
-
-                               case SND_BELL:
-                                       if (value != 0)
-                                               serio_write (lk->serio, LK_CMD_SOUND_BELL);
-
-                                       return 0;
-                       }
-                       break;
-
-               default:
-                       printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n",
-                                       __func__, type, code, value);
+               case SND_BELL:
+                       if (value != 0)
+                               serio_write(lk->serio, LK_CMD_SOUND_BELL);
+
+                       return 0;
+               }
+
+               break;
+
+       default:
+               printk(KERN_ERR "%s(): Got unknown type %d, code %d, value %d\n",
+                       __func__, type, code, value);
        }
 
        return -1;
@@ -570,79 +576,56 @@ lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
  * lkkbd_reinit() sets leds and beeps to a state the computer remembers they
  * were in.
  */
-static void
-lkkbd_reinit (struct work_struct *work)
+static void lkkbd_reinit(struct work_struct *work)
 {
        struct lkkbd *lk = container_of(work, struct lkkbd, tq);
        int division;
-       unsigned char leds_on = 0;
-       unsigned char leds_off = 0;
 
        /* Ask for ID */
-       serio_write (lk->serio, LK_CMD_REQUEST_ID);
+       serio_write(lk->serio, LK_CMD_REQUEST_ID);
 
        /* Reset parameters */
-       serio_write (lk->serio, LK_CMD_SET_DEFAULTS);
+       serio_write(lk->serio, LK_CMD_SET_DEFAULTS);
 
        /* Set LEDs */
-       CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
-       CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
-       CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
-       CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
-       if (leds_on != 0) {
-               serio_write (lk->serio, LK_CMD_LED_ON);
-               serio_write (lk->serio, leds_on);
-       }
-       if (leds_off != 0) {
-               serio_write (lk->serio, LK_CMD_LED_OFF);
-               serio_write (lk->serio, leds_off);
-       }
+       lkkbd_toggle_leds(lk);
 
        /*
         * Try to activate extended LK401 mode. This command will
         * only work with a LK401 keyboard and grants access to
         * LAlt, RAlt, RCompose and RShift.
         */
-       serio_write (lk->serio, LK_CMD_ENABLE_LK401);
+       serio_write(lk->serio, LK_CMD_ENABLE_LK401);
 
        /* Set all keys to UPDOWN mode */
        for (division = 1; division <= 14; division++)
-               serio_write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
-                                       division));
+               serio_write(lk->serio,
+                           LK_CMD_SET_MODE(LK_MODE_UPDOWN, division));
 
        /* Enable bell and set volume */
-       serio_write (lk->serio, LK_CMD_ENABLE_BELL);
-       serio_write (lk->serio, volume_to_hw (lk->bell_volume));
+       serio_write(lk->serio, LK_CMD_ENABLE_BELL);
+       serio_write(lk->serio, volume_to_hw(lk->bell_volume));
 
        /* Enable/disable keyclick (and possibly set volume) */
-       if (test_bit (SND_CLICK, lk->dev->snd)) {
-               serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
-               serio_write (lk->serio, volume_to_hw (lk->keyclick_volume));
-               serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
-               serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
-       } else {
-               serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
-               serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
-       }
+       lkkbd_toggle_keyclick(lk, test_bit(SND_CLICK, lk->dev->snd));
 
        /* Sound the bell if needed */
-       if (test_bit (SND_BELL, lk->dev->snd))
-               serio_write (lk->serio, LK_CMD_SOUND_BELL);
+       if (test_bit(SND_BELL, lk->dev->snd))
+               serio_write(lk->serio, LK_CMD_SOUND_BELL);
 }
 
 /*
  * lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
  */
-static int
-lkkbd_connect (struct serio *serio, struct serio_driver *drv)
+static int lkkbd_connect(struct serio *serio, struct serio_driver *drv)
 {
        struct lkkbd *lk;
        struct input_dev *input_dev;
        int i;
        int err;
 
-       lk = kzalloc (sizeof (struct lkkbd), GFP_KERNEL);
-       input_dev = input_allocate_device ();
+       lk = kzalloc(sizeof(struct lkkbd), GFP_KERNEL);
+       input_dev = input_allocate_device();
        if (!lk || !input_dev) {
                err = -ENOMEM;
                goto fail1;
@@ -650,14 +633,14 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
 
        lk->serio = serio;
        lk->dev = input_dev;
-       INIT_WORK (&lk->tq, lkkbd_reinit);
+       INIT_WORK(&lk->tq, lkkbd_reinit);
        lk->bell_volume = bell_volume;
        lk->keyclick_volume = keyclick_volume;
        lk->ctrlclick_volume = ctrlclick_volume;
-       memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES);
+       memcpy(lk->keycode, lkkbd_keycode, sizeof(lk->keycode));
 
-       strlcpy (lk->name, "DEC LK keyboard", sizeof(lk->name));
-       snprintf (lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
+       strlcpy(lk->name, "DEC LK keyboard", sizeof(lk->name));
+       snprintf(lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
 
        input_dev->name = lk->name;
        input_dev->phys = lk->phys;
@@ -668,62 +651,61 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
        input_dev->dev.parent = &serio->dev;
        input_dev->event = lkkbd_event;
 
-       input_set_drvdata (input_dev, lk);
+       input_set_drvdata(input_dev, lk);
 
-       set_bit (EV_KEY, input_dev->evbit);
-       set_bit (EV_LED, input_dev->evbit);
-       set_bit (EV_SND, input_dev->evbit);
-       set_bit (EV_REP, input_dev->evbit);
-       set_bit (LED_CAPSL, input_dev->ledbit);
-       set_bit (LED_SLEEP, input_dev->ledbit);
-       set_bit (LED_COMPOSE, input_dev->ledbit);
-       set_bit (LED_SCROLLL, input_dev->ledbit);
-       set_bit (SND_BELL, input_dev->sndbit);
-       set_bit (SND_CLICK, input_dev->sndbit);
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_LED, input_dev->evbit);
+       __set_bit(EV_SND, input_dev->evbit);
+       __set_bit(EV_REP, input_dev->evbit);
+       __set_bit(LED_CAPSL, input_dev->ledbit);
+       __set_bit(LED_SLEEP, input_dev->ledbit);
+       __set_bit(LED_COMPOSE, input_dev->ledbit);
+       __set_bit(LED_SCROLLL, input_dev->ledbit);
+       __set_bit(SND_BELL, input_dev->sndbit);
+       __set_bit(SND_CLICK, input_dev->sndbit);
 
        input_dev->keycode = lk->keycode;
-       input_dev->keycodesize = sizeof (lk_keycode_t);
-       input_dev->keycodemax = LK_NUM_KEYCODES;
+       input_dev->keycodesize = sizeof(lk->keycode[0]);
+       input_dev->keycodemax = ARRAY_SIZE(lk->keycode);
 
        for (i = 0; i < LK_NUM_KEYCODES; i++)
-               __set_bit (lk->keycode[i], input_dev->keybit);
+               __set_bit(lk->keycode[i], input_dev->keybit);
        __clear_bit(KEY_RESERVED, input_dev->keybit);
 
-       serio_set_drvdata (serio, lk);
+       serio_set_drvdata(serio, lk);
 
-       err = serio_open (serio, drv);
+       err = serio_open(serio, drv);
        if (err)
                goto fail2;
 
-       err = input_register_device (lk->dev);
+       err = input_register_device(lk->dev);
        if (err)
                goto fail3;
 
-       serio_write (lk->serio, LK_CMD_POWERCYCLE_RESET);
+       serio_write(lk->serio, LK_CMD_POWERCYCLE_RESET);
 
        return 0;
 
- fail3:        serio_close (serio);
- fail2:        serio_set_drvdata (serio, NULL);
- fail1:        input_free_device (input_dev);
-       kfree (lk);
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(lk);
        return err;
 }
 
 /*
  * lkkbd_disconnect() unregisters and closes behind us.
  */
-static void
-lkkbd_disconnect (struct serio *serio)
+static void lkkbd_disconnect(struct serio *serio)
 {
-       struct lkkbd *lk = serio_get_drvdata (serio);
-
-       input_get_device (lk->dev);
-       input_unregister_device (lk->dev);
-       serio_close (serio);
-       serio_set_drvdata (serio, NULL);
-       input_put_device (lk->dev);
-       kfree (lk);
+       struct lkkbd *lk = serio_get_drvdata(serio);
+
+       input_get_device(lk->dev);
+       input_unregister_device(lk->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_put_device(lk->dev);
+       kfree(lk);
 }
 
 static struct serio_device_id lkkbd_serio_ids[] = {
@@ -752,18 +734,16 @@ static struct serio_driver lkkbd_drv = {
 /*
  * The functions for insering/removing us as a module.
  */
-static int __init
-lkkbd_init (void)
+static int __init lkkbd_init(void)
 {
        return serio_register_driver(&lkkbd_drv);
 }
 
-static void __exit
-lkkbd_exit (void)
+static void __exit lkkbd_exit(void)
 {
        serio_unregister_driver(&lkkbd_drv);
 }
 
-module_init (lkkbd_init);
-module_exit (lkkbd_exit);
+module_init(lkkbd_init);
+module_exit(lkkbd_exit);
 
index 91cfe51..34f4a29 100644 (file)
@@ -213,8 +213,9 @@ static void matrix_keypad_stop(struct input_dev *dev)
 }
 
 #ifdef CONFIG_PM
-static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t state)
+static int matrix_keypad_suspend(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct matrix_keypad *keypad = platform_get_drvdata(pdev);
        const struct matrix_keypad_platform_data *pdata = keypad->pdata;
        int i;
@@ -228,8 +229,9 @@ static int matrix_keypad_suspend(struct platform_device *pdev, pm_message_t stat
        return 0;
 }
 
-static int matrix_keypad_resume(struct platform_device *pdev)
+static int matrix_keypad_resume(struct device *dev)
 {
+       struct platform_device *pdev = to_platform_device(dev);
        struct matrix_keypad *keypad = platform_get_drvdata(pdev);
        const struct matrix_keypad_platform_data *pdata = keypad->pdata;
        int i;
@@ -242,9 +244,9 @@ static int matrix_keypad_resume(struct platform_device *pdev)
 
        return 0;
 }
-#else
-#define matrix_keypad_suspend  NULL
-#define matrix_keypad_resume   NULL
+
+static const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
+                               matrix_keypad_suspend, matrix_keypad_resume);
 #endif
 
 static int __devinit init_matrix_gpio(struct platform_device *pdev,
@@ -417,11 +419,12 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev)
 static struct platform_driver matrix_keypad_driver = {
        .probe          = matrix_keypad_probe,
        .remove         = __devexit_p(matrix_keypad_remove),
-       .suspend        = matrix_keypad_suspend,
-       .resume         = matrix_keypad_resume,
        .driver         = {
                .name   = "matrix-keypad",
                .owner  = THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm     = &matrix_keypad_pm_ops,
+#endif
        },
 };
 
index 887af79..076111f 100644 (file)
@@ -18,9 +18,9 @@
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/input.h>
+#include <linux/input/sh_keysc.h>
 #include <linux/clk.h>
 #include <linux/io.h>
-#include <asm/sh_keysc.h>
 
 #define KYCR1_OFFS   0x00
 #define KYCR2_OFFS   0x04
index a9bb254..16ec523 100644 (file)
@@ -80,6 +80,7 @@ config INPUT_WISTRON_BTNS
        tristate "x86 Wistron laptop button interface"
        depends on X86 && !X86_64
        select INPUT_POLLDEV
+       select INPUT_SPARSEKMAP
        select NEW_LEDS
        select LEDS_CLASS
        select CHECK_SIGNATURE
@@ -281,6 +282,7 @@ config INPUT_RB532_BUTTON
 config INPUT_DM355EVM
        tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
        depends on MFD_DM355EVM_MSP
+       select INPUT_SPARSEKMAP
        help
          Supports the pushbuttons and IR remote used with
          the DM355 EVM board.
index e290fde..614b65d 100644 (file)
@@ -766,7 +766,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
        ati_remote->interface = interface;
 
        usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
-       strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
+       strlcat(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
 
        if (udev->manufacturer)
                strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
index f2b67dc..766c069 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 
@@ -33,12 +34,8 @@ struct dm355evm_keys {
        int                     irq;
 };
 
-/* These initial keycodes can be remapped by dm355evm_setkeycode(). */
-static struct {
-       u16     event;
-       u16     keycode;
-} dm355evm_keys[] = {
-
+/* These initial keycodes can be remapped */
+static const struct key_entry dm355evm_keys[] = {
        /*
         * Pushbuttons on the EVM board ... note that the labels for these
         * are SW10/SW11/etc on the PC board.  The left/right orientation
@@ -47,11 +44,11 @@ static struct {
         * is to the right.  (That is, rotate the board counter-clockwise
         * by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
         */
-       { 0x00d8, KEY_OK, },            /* SW12 */
-       { 0x00b8, KEY_UP, },            /* SW13 */
-       { 0x00e8, KEY_DOWN, },          /* SW11 */
-       { 0x0078, KEY_LEFT, },          /* SW14 */
-       { 0x00f0, KEY_RIGHT, },         /* SW10 */
+       { KE_KEY, 0x00d8, { KEY_OK } },         /* SW12 */
+       { KE_KEY, 0x00b8, { KEY_UP } },         /* SW13 */
+       { KE_KEY, 0x00e8, { KEY_DOWN } },       /* SW11 */
+       { KE_KEY, 0x0078, { KEY_LEFT } },       /* SW14 */
+       { KE_KEY, 0x00f0, { KEY_RIGHT } },      /* SW10 */
 
        /*
         * IR buttons ... codes assigned to match the universal remote
@@ -65,35 +62,35 @@ static struct {
         * RC5 codes are 14 bits, with two start bits (0x3 prefix)
         * and a toggle bit (masked out below).
         */
-       { 0x300c, KEY_POWER, },         /* NOTE: docs omit this */
-       { 0x3000, KEY_NUMERIC_0, },
-       { 0x3001, KEY_NUMERIC_1, },
-       { 0x3002, KEY_NUMERIC_2, },
-       { 0x3003, KEY_NUMERIC_3, },
-       { 0x3004, KEY_NUMERIC_4, },
-       { 0x3005, KEY_NUMERIC_5, },
-       { 0x3006, KEY_NUMERIC_6, },
-       { 0x3007, KEY_NUMERIC_7, },
-       { 0x3008, KEY_NUMERIC_8, },
-       { 0x3009, KEY_NUMERIC_9, },
-       { 0x3022, KEY_ENTER, },
-       { 0x30ec, KEY_MODE, },          /* "tv/vcr/..." */
-       { 0x300f, KEY_SELECT, },        /* "info" */
-       { 0x3020, KEY_CHANNELUP, },     /* "up" */
-       { 0x302e, KEY_MENU, },          /* "in/out" */
-       { 0x3011, KEY_VOLUMEDOWN, },    /* "left" */
-       { 0x300d, KEY_MUTE, },          /* "ok" */
-       { 0x3010, KEY_VOLUMEUP, },      /* "right" */
-       { 0x301e, KEY_SUBTITLE, },      /* "cc" */
-       { 0x3021, KEY_CHANNELDOWN, },   /* "down" */
-       { 0x3022, KEY_PREVIOUS, },
-       { 0x3026, KEY_SLEEP, },
-       { 0x3172, KEY_REWIND, },        /* NOTE: docs wrongly say 0x30ca */
-       { 0x3175, KEY_PLAY, },
-       { 0x3174, KEY_FASTFORWARD, },
-       { 0x3177, KEY_RECORD, },
-       { 0x3176, KEY_STOP, },
-       { 0x3169, KEY_PAUSE, },
+       { KE_KEY, 0x300c, { KEY_POWER } },      /* NOTE: docs omit this */
+       { KE_KEY, 0x3000, { KEY_NUMERIC_0 } },
+       { KE_KEY, 0x3001, { KEY_NUMERIC_1 } },
+       { KE_KEY, 0x3002, { KEY_NUMERIC_2 } },
+       { KE_KEY, 0x3003, { KEY_NUMERIC_3 } },
+       { KE_KEY, 0x3004, { KEY_NUMERIC_4 } },
+       { KE_KEY, 0x3005, { KEY_NUMERIC_5 } },
+       { KE_KEY, 0x3006, { KEY_NUMERIC_6 } },
+       { KE_KEY, 0x3007, { KEY_NUMERIC_7 } },
+       { KE_KEY, 0x3008, { KEY_NUMERIC_8 } },
+       { KE_KEY, 0x3009, { KEY_NUMERIC_9 } },
+       { KE_KEY, 0x3022, { KEY_ENTER } },
+       { KE_KEY, 0x30ec, { KEY_MODE } },       /* "tv/vcr/..." */
+       { KE_KEY, 0x300f, { KEY_SELECT } },     /* "info" */
+       { KE_KEY, 0x3020, { KEY_CHANNELUP } },  /* "up" */
+       { KE_KEY, 0x302e, { KEY_MENU } },       /* "in/out" */
+       { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */
+       { KE_KEY, 0x300d, { KEY_MUTE } },       /* "ok" */
+       { KE_KEY, 0x3010, { KEY_VOLUMEUP } },   /* "right" */
+       { KE_KEY, 0x301e, { KEY_SUBTITLE } },   /* "cc" */
+       { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */
+       { KE_KEY, 0x3022, { KEY_PREVIOUS } },
+       { KE_KEY, 0x3026, { KEY_SLEEP } },
+       { KE_KEY, 0x3172, { KEY_REWIND } },     /* NOTE: docs wrongly say 0x30ca */
+       { KE_KEY, 0x3175, { KEY_PLAY } },
+       { KE_KEY, 0x3174, { KEY_FASTFORWARD } },
+       { KE_KEY, 0x3177, { KEY_RECORD } },
+       { KE_KEY, 0x3176, { KEY_STOP } },
+       { KE_KEY, 0x3169, { KEY_PAUSE } },
 };
 
 /*
@@ -105,19 +102,18 @@ static struct {
  */
 static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
 {
-       struct dm355evm_keys    *keys = _keys;
-       int                     status;
+       static u16 last_event;
+       struct dm355evm_keys *keys = _keys;
+       const struct key_entry *ke;
+       unsigned int keycode;
+       int status;
+       u16 event;
 
        /* For simplicity we ignore INPUT_COUNT and just read
         * events until we get the "queue empty" indicator.
         * Reading INPUT_LOW decrements the count.
         */
        for (;;) {
-               static u16      last_event;
-               u16             event;
-               int             keycode;
-               int             i;
-
                status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
                if (status < 0) {
                        dev_dbg(keys->dev, "input high err %d\n",
@@ -156,14 +152,9 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
                /* ignore the RC5 toggle bit */
                event &= ~0x0800;
 
-               /* find the key, or leave it as unknown */
-               keycode = KEY_UNKNOWN;
-               for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
-                       if (dm355evm_keys[i].event != event)
-                               continue;
-                       keycode = dm355evm_keys[i].keycode;
-                       break;
-               }
+               /* find the key, or report it as unknown */
+               ke = sparse_keymap_entry_from_scancode(keys->input, event);
+               keycode = ke ? ke->keycode : KEY_UNKNOWN;
                dev_dbg(keys->dev,
                        "input event 0x%04x--> keycode %d\n",
                        event, keycode);
@@ -174,36 +165,8 @@ static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
                input_report_key(keys->input, keycode, 0);
                input_sync(keys->input);
        }
-       return IRQ_HANDLED;
-}
 
-static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
-{
-       u16             old_keycode;
-       unsigned        i;
-
-       if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
-               return -EINVAL;
-
-       old_keycode = dm355evm_keys[index].keycode;
-       dm355evm_keys[index].keycode = keycode;
-       set_bit(keycode, dev->keybit);
-
-       for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
-               if (dm355evm_keys[index].keycode == old_keycode)
-                       goto done;
-       }
-       clear_bit(old_keycode, dev->keybit);
-done:
-       return 0;
-}
-
-static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode)
-{
-       if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
-               return -EINVAL;
-
-       return dm355evm_keys[index].keycode;
+       return IRQ_HANDLED;
 }
 
 /*----------------------------------------------------------------------*/
@@ -213,7 +176,6 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
        struct dm355evm_keys    *keys;
        struct input_dev        *input;
        int                     status;
-       int                     i;
 
        /* allocate instance struct and input dev */
        keys = kzalloc(sizeof *keys, GFP_KERNEL);
@@ -242,31 +204,30 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
        input->id.product = 0x0355;
        input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
 
-       input->evbit[0] = BIT(EV_KEY);
-       for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++)
-               __set_bit(dm355evm_keys[i].keycode, input->keybit);
-
-       input->setkeycode = dm355evm_setkeycode;
-       input->getkeycode = dm355evm_getkeycode;
+       status = sparse_keymap_setup(input, dm355evm_keys, NULL);
+       if (status)
+               goto fail1;
 
        /* REVISIT:  flush the event queue? */
 
        status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
                        IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys);
        if (status < 0)
-               goto fail1;
+               goto fail2;
 
        /* register */
        status = input_register_device(input);
        if (status < 0)
-               goto fail2;
+               goto fail3;
 
        platform_set_drvdata(pdev, keys);
 
        return 0;
 
-fail2:
+fail3:
        free_irq(keys->irq, keys);
+fail2:
+       sparse_keymap_free(input);
 fail1:
        input_free_device(input);
        kfree(keys);
@@ -280,6 +241,7 @@ static int __devexit dm355evm_keys_remove(struct platform_device *pdev)
        struct dm355evm_keys    *keys = platform_get_drvdata(pdev);
 
        free_irq(keys->irq, keys);
+       sparse_keymap_free(keys->input);
        input_unregister_device(keys->input);
        kfree(keys);
 
index a53c488..668913d 100644 (file)
@@ -338,7 +338,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
        pm->input = input_dev;
 
        usb_make_path(udev, pm->phys, sizeof(pm->phys));
-       strlcpy(pm->phys, "/input0", sizeof(pm->phys));
+       strlcat(pm->phys, "/input0", sizeof(pm->phys));
 
        spin_lock_init(&pm->lock);
 
index a932179..38da6ab 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/dmi.h>
 #include <linux/init.h>
 #include <linux/input-polldev.h>
+#include <linux/input/sparse-keymap.h>
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
@@ -224,19 +225,8 @@ static void bios_set_state(u8 subsys, int enable)
 
 /* Hardware database */
 
-struct key_entry {
-       char type;              /* See KE_* below */
-       u8 code;
-       union {
-               u16 keycode;            /* For KE_KEY */
-               struct {                /* For KE_SW */
-                       u8 code;
-                       u8 value;
-               } sw;
-       };
-};
-
-enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH };
+#define KE_WIFI                (KE_LAST + 1)
+#define KE_BLUETOOTH   (KE_LAST + 2)
 
 #define FE_MAIL_LED 0x01
 #define FE_WIFI_LED 0x02
@@ -644,10 +634,10 @@ static struct key_entry keymap_prestigio[] __initdata = {
  * a list of buttons and their key codes (reported when loading this module
  * with force=1) and the output of dmidecode to $MODULE_AUTHOR.
  */
-static struct dmi_system_id dmi_ids[] __initdata = {
+static const struct dmi_system_id __initconst dmi_ids[] = {
        {
+               /* Fujitsu-Siemens Amilo Pro V2000 */
                .callback = dmi_matched,
-               .ident = "Fujitsu-Siemens Amilo Pro V2000",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"),
@@ -655,8 +645,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fs_amilo_pro_v2000
        },
        {
+               /* Fujitsu-Siemens Amilo Pro Edition V3505 */
                .callback = dmi_matched,
-               .ident = "Fujitsu-Siemens Amilo Pro Edition V3505",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),
@@ -664,8 +654,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fs_amilo_pro_v3505
        },
        {
+               /* Fujitsu-Siemens Amilo M7400 */
                .callback = dmi_matched,
-               .ident = "Fujitsu-Siemens Amilo M7400",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M        "),
@@ -673,8 +663,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fs_amilo_pro_v2000
        },
        {
+               /* Maxdata Pro 7000 DX */
                .callback = dmi_matched,
-               .ident = "Maxdata Pro 7000 DX",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"),
@@ -682,8 +672,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fs_amilo_pro_v2000
        },
        {
+               /* Fujitsu N3510 */
                .callback = dmi_matched,
-               .ident = "Fujitsu N3510",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "N3510"),
@@ -691,8 +681,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fujitsu_n3510
        },
        {
+               /* Acer Aspire 1500 */
                .callback = dmi_matched,
-               .ident = "Acer Aspire 1500",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"),
@@ -700,8 +690,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_aspire_1500
        },
        {
+               /* Acer Aspire 1600 */
                .callback = dmi_matched,
-               .ident = "Acer Aspire 1600",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),
@@ -709,8 +699,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_aspire_1600
        },
        {
+               /* Acer Aspire 3020 */
                .callback = dmi_matched,
-               .ident = "Acer Aspire 3020",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
@@ -718,8 +708,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_aspire_5020
        },
        {
+               /* Acer Aspire 5020 */
                .callback = dmi_matched,
-               .ident = "Acer Aspire 5020",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
@@ -727,8 +717,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_aspire_5020
        },
        {
+               /* Acer TravelMate 2100 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 2100",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),
@@ -736,8 +726,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_aspire_5020
        },
        {
+               /* Acer TravelMate 2410 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 2410",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),
@@ -745,8 +735,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_2410
        },
        {
+               /* Acer TravelMate C300 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate C300",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),
@@ -754,8 +744,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_300
        },
        {
+               /* Acer TravelMate C100 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate C100",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),
@@ -763,8 +753,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_300
        },
        {
+               /* Acer TravelMate C110 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate C110",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),
@@ -772,8 +762,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_110
        },
        {
+               /* Acer TravelMate 380 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 380",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),
@@ -781,8 +771,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_380
        },
        {
+               /* Acer TravelMate 370 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 370",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),
@@ -790,8 +780,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */
        },
        {
+               /* Acer TravelMate 220 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 220",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),
@@ -799,8 +789,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_220
        },
        {
+               /* Acer TravelMate 260 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 260",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),
@@ -808,8 +798,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_220
        },
        {
+               /* Acer TravelMate 230 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 230",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),
@@ -818,8 +808,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_230
        },
        {
+               /* Acer TravelMate 280 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 280",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),
@@ -827,8 +817,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_230
        },
        {
+               /* Acer TravelMate 240 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 240",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"),
@@ -836,8 +826,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_240
        },
        {
+               /* Acer TravelMate 250 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 250",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),
@@ -845,8 +835,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_240
        },
        {
+               /* Acer TravelMate 2424NWXCi */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 2424NWXCi",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
@@ -854,8 +844,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_240
        },
        {
+               /* Acer TravelMate 350 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 350",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),
@@ -863,8 +853,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_350
        },
        {
+               /* Acer TravelMate 360 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 360",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
@@ -872,8 +862,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_360
        },
        {
+               /* Acer TravelMate 610 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 610",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ACER"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),
@@ -881,8 +871,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_610
        },
        {
+               /* Acer TravelMate 620 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 620",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),
@@ -890,8 +880,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_630
        },
        {
+               /* Acer TravelMate 630 */
                .callback = dmi_matched,
-               .ident = "Acer TravelMate 630",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),
@@ -899,8 +889,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_acer_travelmate_630
        },
        {
+               /* AOpen 1559AS */
                .callback = dmi_matched,
-               .ident = "AOpen 1559AS",
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),
                        DMI_MATCH(DMI_BOARD_NAME, "E2U"),
@@ -908,8 +898,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_aopen_1559as
        },
        {
+               /* Medion MD 9783 */
                .callback = dmi_matched,
-               .ident = "Medion MD 9783",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),
@@ -917,8 +907,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_wistron_ms2111
        },
        {
+               /* Medion MD 40100 */
                .callback = dmi_matched,
-               .ident = "Medion MD 40100",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),
@@ -926,8 +916,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_wistron_md40100
        },
        {
+               /* Medion MD 2900 */
                .callback = dmi_matched,
-               .ident = "Medion MD 2900",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),
@@ -935,8 +925,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_wistron_md2900
        },
        {
+               /* Medion MD 42200 */
                .callback = dmi_matched,
-               .ident = "Medion MD 42200",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"),
@@ -944,8 +934,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fs_amilo_pro_v2000
        },
        {
+               /* Medion MD 96500 */
                .callback = dmi_matched,
-               .ident = "Medion MD 96500",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),
@@ -953,8 +943,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_wistron_md96500
        },
        {
+               /* Medion MD 95400 */
                .callback = dmi_matched,
-               .ident = "Medion MD 95400",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),
@@ -962,8 +952,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_wistron_md96500
        },
        {
+               /* Fujitsu Siemens Amilo D7820 */
                .callback = dmi_matched,
-               .ident = "Fujitsu Siemens Amilo D7820",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */
                        DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),
@@ -971,8 +961,8 @@ static struct dmi_system_id dmi_ids[] __initdata = {
                .driver_data = keymap_fs_amilo_d88x0
        },
        {
+               /* Fujitsu Siemens Amilo D88x0 */
                .callback = dmi_matched,
-               .ident = "Fujitsu Siemens Amilo D88x0",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"),
@@ -1037,21 +1027,6 @@ static unsigned long jiffies_last_press;
 static bool wifi_enabled;
 static bool bluetooth_enabled;
 
-static void report_key(struct input_dev *dev, unsigned int keycode)
-{
-       input_report_key(dev, keycode, 1);
-       input_sync(dev);
-       input_report_key(dev, keycode, 0);
-       input_sync(dev);
-}
-
-static void report_switch(struct input_dev *dev, unsigned int code, int value)
-{
-       input_report_switch(dev, code, value);
-       input_sync(dev);
-}
-
-
  /* led management */
 static void wistron_mail_led_set(struct led_classdev *led_cdev,
                                enum led_brightness value)
@@ -1128,43 +1103,13 @@ static inline void wistron_led_resume(void)
                led_classdev_resume(&wistron_wifi_led);
 }
 
-static struct key_entry *wistron_get_entry_by_scancode(int code)
-{
-       struct key_entry *key;
-
-       for (key = keymap; key->type != KE_END; key++)
-               if (code == key->code)
-                       return key;
-
-       return NULL;
-}
-
-static struct key_entry *wistron_get_entry_by_keycode(int keycode)
-{
-       struct key_entry *key;
-
-       for (key = keymap; key->type != KE_END; key++)
-               if (key->type == KE_KEY && keycode == key->keycode)
-                       return key;
-
-       return NULL;
-}
-
 static void handle_key(u8 code)
 {
-       const struct key_entry *key = wistron_get_entry_by_scancode(code);
+       const struct key_entry *key =
+               sparse_keymap_entry_from_scancode(wistron_idev->input, code);
 
        if (key) {
                switch (key->type) {
-               case KE_KEY:
-                       report_key(wistron_idev->input, key->keycode);
-                       break;
-
-               case KE_SW:
-                       report_switch(wistron_idev->input,
-                                     key->sw.code, key->sw.value);
-                       break;
-
                case KE_WIFI:
                        if (have_wifi) {
                                wifi_enabled = !wifi_enabled;
@@ -1180,7 +1125,9 @@ static void handle_key(u8 code)
                        break;
 
                default:
-                       BUG();
+                       sparse_keymap_report_entry(wistron_idev->input,
+                                                  key, 1, true);
+                       break;
                }
                jiffies_last_press = jiffies;
        } else
@@ -1220,42 +1167,39 @@ static void wistron_poll(struct input_polled_dev *dev)
                dev->poll_interval = POLL_INTERVAL_DEFAULT;
 }
 
-static int wistron_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+static int __devinit wistron_setup_keymap(struct input_dev *dev,
+                                         struct key_entry *entry)
 {
-       const struct key_entry *key = wistron_get_entry_by_scancode(scancode);
+       switch (entry->type) {
 
-       if (key && key->type == KE_KEY) {
-               *keycode = key->keycode;
-               return 0;
-       }
-
-       return -EINVAL;
-}
+       /* if wifi or bluetooth are not available, create normal keys */
+       case KE_WIFI:
+               if (!have_wifi) {
+                       entry->type = KE_KEY;
+                       entry->keycode = KEY_WLAN;
+               }
+               break;
 
-static int wistron_setkeycode(struct input_dev *dev, int scancode, int keycode)
-{
-       struct key_entry *key;
-       int old_keycode;
-
-       if (keycode < 0 || keycode > KEY_MAX)
-               return -EINVAL;
-
-       key = wistron_get_entry_by_scancode(scancode);
-       if (key && key->type == KE_KEY) {
-               old_keycode = key->keycode;
-               key->keycode = keycode;
-               set_bit(keycode, dev->keybit);
-               if (!wistron_get_entry_by_keycode(old_keycode))
-                       clear_bit(old_keycode, dev->keybit);
-               return 0;
+       case KE_BLUETOOTH:
+               if (!have_bluetooth) {
+                       entry->type = KE_KEY;
+                       entry->keycode = KEY_BLUETOOTH;
+               }
+               break;
+
+       case KE_END:
+               if (entry->code & FE_UNTESTED)
+                       printk(KERN_WARNING "Untested laptop multimedia keys, "
+                               "please report success or failure to "
+                               "eric.piel@tremplin-utc.net\n");
+               break;
        }
 
-       return -EINVAL;
+       return 0;
 }
 
 static int __devinit setup_input_dev(void)
 {
-       struct key_entry *key;
        struct input_dev *input_dev;
        int error;
 
@@ -1263,7 +1207,7 @@ static int __devinit setup_input_dev(void)
        if (!wistron_idev)
                return -ENOMEM;
 
-       wistron_idev->flush = wistron_flush;
+       wistron_idev->open = wistron_flush;
        wistron_idev->poll = wistron_poll;
        wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;
 
@@ -1273,56 +1217,21 @@ static int __devinit setup_input_dev(void)
        input_dev->id.bustype = BUS_HOST;
        input_dev->dev.parent = &wistron_device->dev;
 
-       input_dev->getkeycode = wistron_getkeycode;
-       input_dev->setkeycode = wistron_setkeycode;
-
-       for (key = keymap; key->type != KE_END; key++) {
-               switch (key->type) {
-                       case KE_KEY:
-                               set_bit(EV_KEY, input_dev->evbit);
-                               set_bit(key->keycode, input_dev->keybit);
-                               break;
-
-                       case KE_SW:
-                               set_bit(EV_SW, input_dev->evbit);
-                               set_bit(key->sw.code, input_dev->swbit);
-                               break;
-
-                       /* if wifi or bluetooth are not available, create normal keys */
-                       case KE_WIFI:
-                               if (!have_wifi) {
-                                       key->type = KE_KEY;
-                                       key->keycode = KEY_WLAN;
-                                       key--;
-                               }
-                               break;
-
-                       case KE_BLUETOOTH:
-                               if (!have_bluetooth) {
-                                       key->type = KE_KEY;
-                                       key->keycode = KEY_BLUETOOTH;
-                                       key--;
-                               }
-                               break;
-
-                       default:
-                               break;
-               }
-       }
-
-       /* reads information flags on KE_END */
-       if (key->code & FE_UNTESTED)
-               printk(KERN_WARNING "Untested laptop multimedia keys, "
-                       "please report success or failure to eric.piel"
-                       "@tremplin-utc.net\n");
+       error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap);
+       if (error)
+               goto err_free_dev;
 
        error = input_register_polled_device(wistron_idev);
-       if (error) {
-               input_free_polled_device(wistron_idev);
-               return error;
-       }
+       if (error)
+               goto err_free_keymap;
 
        return 0;
+
+ err_free_keymap:
+       sparse_keymap_free(input_dev);
+ err_free_dev:
+       input_free_polled_device(wistron_idev);
+       return error;
 }
 
 /* Driver core */
@@ -1371,6 +1280,7 @@ static int __devexit wistron_remove(struct platform_device *dev)
 {
        wistron_led_remove();
        input_unregister_polled_device(wistron_idev);
+       sparse_keymap_free(wistron_idev->input);
        input_free_polled_device(wistron_idev);
        bios_detach();
 
index f361106..a3f492a 100644 (file)
 #define dbg(format, arg...) do {} while (0)
 #endif
 
-#define ALPS_DUALPOINT 0x01
-#define ALPS_WHEEL     0x02
-#define ALPS_FW_BK_1   0x04
-#define ALPS_4BTN      0x08
-#define ALPS_OLDPROTO  0x10
-#define ALPS_PASS      0x20
-#define ALPS_FW_BK_2   0x40
+
+#define ALPS_OLDPROTO          0x01    /* old style input */
+#define ALPS_DUALPOINT         0x02    /* touchpad has trackstick */
+#define ALPS_PASS              0x04    /* device has a pass-through port */
+
+#define ALPS_WHEEL             0x08    /* hardware wheel present */
+#define ALPS_FW_BK_1           0x10    /* front & back buttons present */
+#define ALPS_FW_BK_2           0x20    /* front & back buttons present */
+#define ALPS_FOUR_BUTTONS      0x40    /* 4 direction button present */
+
 
 static const struct alps_model_info alps_model_data[] = {
        { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
@@ -56,7 +59,7 @@ static const struct alps_model_info alps_model_data[] = {
        { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
        { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
        { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */
-       { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 },               /* Dell Vostro 1400 */
+       { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS },          /* Dell Vostro 1400 */
 };
 
 /*
@@ -83,6 +86,7 @@ static const struct alps_model_info alps_model_data[] = {
 static void alps_process_packet(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
+       const struct alps_model_info *model = priv->i;
        unsigned char *packet = psmouse->packet;
        struct input_dev *dev = psmouse->dev;
        struct input_dev *dev2 = priv->dev2;
@@ -101,7 +105,7 @@ static void alps_process_packet(struct psmouse *psmouse)
                return;
        }
 
-       if (priv->i->flags & ALPS_OLDPROTO) {
+       if (model->flags & ALPS_OLDPROTO) {
                left = packet[2] & 0x10;
                right = packet[2] & 0x08;
                middle = 0;
@@ -117,12 +121,12 @@ static void alps_process_packet(struct psmouse *psmouse)
                z = packet[5];
        }
 
-       if (priv->i->flags & ALPS_FW_BK_1) {
+       if (model->flags & ALPS_FW_BK_1) {
                back = packet[0] & 0x10;
                forward = packet[2] & 4;
        }
 
-       if (priv->i->flags & ALPS_FW_BK_2) {
+       if (model->flags & ALPS_FW_BK_2) {
                back = packet[3] & 4;
                forward = packet[2] & 4;
                if ((middle = forward && back))
@@ -132,7 +136,7 @@ static void alps_process_packet(struct psmouse *psmouse)
        ges = packet[2] & 1;
        fin = packet[2] & 2;
 
-       if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) {
+       if ((model->flags & ALPS_DUALPOINT) && z == 127) {
                input_report_rel(dev2, REL_X,  (x > 383 ? (x - 768) : x));
                input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
 
@@ -150,7 +154,8 @@ static void alps_process_packet(struct psmouse *psmouse)
        input_report_key(dev, BTN_MIDDLE, middle);
 
        /* Convert hardware tap to a reasonable Z value */
-       if (ges && !fin) z = 40;
+       if (ges && !fin)
+               z = 40;
 
        /*
         * A "tap and drag" operation is reported by the hardware as a transition
@@ -166,8 +171,10 @@ static void alps_process_packet(struct psmouse *psmouse)
        }
        priv->prev_fin = fin;
 
-       if (z > 30) input_report_key(dev, BTN_TOUCH, 1);
-       if (z < 25) input_report_key(dev, BTN_TOUCH, 0);
+       if (z > 30)
+               input_report_key(dev, BTN_TOUCH, 1);
+       if (z < 25)
+               input_report_key(dev, BTN_TOUCH, 0);
 
        if (z > 0) {
                input_report_abs(dev, ABS_X, x);
@@ -177,14 +184,21 @@ static void alps_process_packet(struct psmouse *psmouse)
        input_report_abs(dev, ABS_PRESSURE, z);
        input_report_key(dev, BTN_TOOL_FINGER, z > 0);
 
-       if (priv->i->flags & ALPS_WHEEL)
+       if (model->flags & ALPS_WHEEL)
                input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07));
 
-       if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+       if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
                input_report_key(dev, BTN_FORWARD, forward);
                input_report_key(dev, BTN_BACK, back);
        }
 
+       if (model->flags & ALPS_FOUR_BUTTONS) {
+               input_report_key(dev, BTN_0, packet[2] & 4);
+               input_report_key(dev, BTN_1, packet[0] & 0x10);
+               input_report_key(dev, BTN_2, packet[3] & 4);
+               input_report_key(dev, BTN_3, packet[0] & 0x20);
+       }
+
        input_sync(dev);
 }
 
@@ -393,15 +407,12 @@ static int alps_poll(struct psmouse *psmouse)
        return 0;
 }
 
-static int alps_hw_init(struct psmouse *psmouse, int *version)
+static int alps_hw_init(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
+       const struct alps_model_info *model = priv->i;
 
-       priv->i = alps_get_model(psmouse, version);
-       if (!priv->i)
-               return -1;
-
-       if ((priv->i->flags & ALPS_PASS) &&
+       if ((model->flags & ALPS_PASS) &&
            alps_passthrough_mode(psmouse, true)) {
                return -1;
        }
@@ -416,7 +427,7 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
                return -1;
        }
 
-       if ((priv->i->flags & ALPS_PASS) &&
+       if ((model->flags & ALPS_PASS) &&
            alps_passthrough_mode(psmouse, false)) {
                return -1;
        }
@@ -432,12 +443,15 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
 
 static int alps_reconnect(struct psmouse *psmouse)
 {
+       const struct alps_model_info *model;
+
        psmouse_reset(psmouse);
 
-       if (alps_hw_init(psmouse, NULL))
+       model = alps_get_model(psmouse, NULL);
+       if (!model)
                return -1;
 
-       return 0;
+       return alps_hw_init(psmouse);
 }
 
 static void alps_disconnect(struct psmouse *psmouse)
@@ -452,6 +466,7 @@ static void alps_disconnect(struct psmouse *psmouse)
 int alps_init(struct psmouse *psmouse)
 {
        struct alps_data *priv;
+       const struct alps_model_info *model;
        struct input_dev *dev1 = psmouse->dev, *dev2;
        int version;
 
@@ -463,33 +478,48 @@ int alps_init(struct psmouse *psmouse)
        priv->dev2 = dev2;
        psmouse->private = priv;
 
-       if (alps_hw_init(psmouse, &version))
+       model = alps_get_model(psmouse, &version);
+       if (!model)
+               goto init_fail;
+
+       priv->i = model;
+
+       if (alps_hw_init(psmouse))
                goto init_fail;
 
        dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY);
        dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
        dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER);
-       dev1->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) |
-               BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+       dev1->keybit[BIT_WORD(BTN_LEFT)] |=
+               BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
 
        dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
        input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0);
        input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
        input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
 
-       if (priv->i->flags & ALPS_WHEEL) {
+       if (model->flags & ALPS_WHEEL) {
                dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
                dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL);
        }
 
-       if (priv->i->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
+       if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) {
                dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD);
                dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK);
        }
 
+       if (model->flags & ALPS_FOUR_BUTTONS) {
+               dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0);
+               dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
+               dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
+               dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
+       } else {
+               dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
+       }
+
        snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
        dev2->phys = priv->phys;
-       dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
+       dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
        dev2->id.bustype = BUS_I8042;
        dev2->id.vendor  = 0x0002;
        dev2->id.product = PSMOUSE_ALPS;
@@ -497,9 +527,9 @@ int alps_init(struct psmouse *psmouse)
        dev2->dev.parent = &psmouse->ps2dev.serio->dev;
 
        dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-       dev2->relbit[BIT_WORD(REL_X)] |= BIT_MASK(REL_X) | BIT_MASK(REL_Y);
-       dev2->keybit[BIT_WORD(BTN_LEFT)] |= BIT_MASK(BTN_LEFT) |
-               BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+       dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+       dev2->keybit[BIT_WORD(BTN_LEFT)] =
+               BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
 
        if (input_register_device(priv->dev2))
                goto init_fail;
index fda35e6..b27684f 100644 (file)
@@ -420,6 +420,7 @@ static void elantech_set_input_params(struct psmouse *psmouse)
 
        __set_bit(EV_KEY, dev->evbit);
        __set_bit(EV_ABS, dev->evbit);
+       __clear_bit(EV_REL, dev->evbit);
 
        __set_bit(BTN_LEFT, dev->keybit);
        __set_bit(BTN_RIGHT, dev->keybit);
index de1e553..b146237 100644 (file)
@@ -430,19 +430,6 @@ static int hgpk_register(struct psmouse *psmouse)
        struct input_dev *dev = psmouse->dev;
        int err;
 
-       /* unset the things that psmouse-base sets which we don't have */
-       __clear_bit(BTN_MIDDLE, dev->keybit);
-
-       /* set the things we do have */
-       __set_bit(EV_KEY, dev->evbit);
-       __set_bit(EV_REL, dev->evbit);
-
-       __set_bit(REL_X, dev->relbit);
-       __set_bit(REL_Y, dev->relbit);
-
-       __set_bit(BTN_LEFT, dev->keybit);
-       __set_bit(BTN_RIGHT, dev->keybit);
-
        /* register handlers */
        psmouse->protocol_handler = hgpk_process_byte;
        psmouse->poll = hgpk_poll;
index 8281155..2e6bdfe 100644 (file)
@@ -25,11 +25,13 @@ struct lifebook_data {
        char phys[32];
 };
 
+static bool lifebook_present;
+
 static const char *desired_serio_phys;
 
-static int lifebook_set_serio_phys(const struct dmi_system_id *d)
+static int lifebook_limit_serio3(const struct dmi_system_id *d)
 {
-       desired_serio_phys = d->driver_data;
+       desired_serio_phys = "isa0060/serio3";
        return 0;
 }
 
@@ -41,53 +43,53 @@ static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
        return 0;
 }
 
-static const struct dmi_system_id lifebook_dmi_table[] = {
+static const struct dmi_system_id __initconst lifebook_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
        {
-               .ident = "FLORA-ie 55mi",
+               /* FLORA-ie 55mi */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"),
                },
        },
        {
-               .ident = "LifeBook B",
+               /* LifeBook B */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"),
                },
        },
        {
-               .ident = "Lifebook B",
+               /* Lifebook B */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"),
                },
        },
        {
-               .ident = "Lifebook B-2130",
+               /* Lifebook B-2130 */
                .matches = {
                        DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"),
                },
        },
        {
-               .ident = "Lifebook B213x/B2150",
+               /* Lifebook B213x/B2150 */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"),
                },
        },
        {
-               .ident = "Zephyr",
+               /* Zephyr */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"),
                },
        },
        {
-               .ident = "CF-18",
+               /* Panasonic CF-18 */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"),
                },
-               .callback = lifebook_set_serio_phys,
-               .driver_data = "isa0060/serio3",
+               .callback = lifebook_limit_serio3,
        },
        {
-               .ident = "Panasonic CF-28",
+               /* Panasonic CF-28 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"),
@@ -95,7 +97,7 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
                .callback = lifebook_set_6byte_proto,
        },
        {
-               .ident = "Panasonic CF-29",
+               /* Panasonic CF-29 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
@@ -103,21 +105,27 @@ static const struct dmi_system_id lifebook_dmi_table[] = {
                .callback = lifebook_set_6byte_proto,
        },
        {
-               .ident = "CF-72",
+               /* Panasonic CF-72 */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"),
                },
                .callback = lifebook_set_6byte_proto,
        },
        {
-               .ident = "Lifebook B142",
+               /* Lifebook B142 */
                .matches = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"),
                },
        },
        { }
+#endif
 };
 
+void __init lifebook_module_init(void)
+{
+       lifebook_present = dmi_check_system(lifebook_dmi_table);
+}
+
 static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
 {
        struct lifebook_data *priv = psmouse->private;
@@ -198,10 +206,10 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
                return -1;
 
        /*
-          Enable absolute output -- ps2_command fails always but if
-          you leave this call out the touchsreen will never send
-          absolute coordinates
-       */
+        * Enable absolute output -- ps2_command fails always but if
+        * you leave this call out the touchsreen will never send
+        * absolute coordinates
+        */
        param = lifebook_use_6byte_proto ? 0x08 : 0x07;
        ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
 
@@ -243,7 +251,7 @@ static void lifebook_disconnect(struct psmouse *psmouse)
 
 int lifebook_detect(struct psmouse *psmouse, bool set_properties)
 {
-        if (!dmi_check_system(lifebook_dmi_table))
+        if (!lifebook_present)
                 return -1;
 
        if (desired_serio_phys &&
@@ -283,8 +291,8 @@ static int lifebook_create_relative_device(struct psmouse *psmouse)
 
        dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
        dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
-       dev2->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) |
-               BIT_MASK(BTN_RIGHT);
+       dev2->keybit[BIT_WORD(BTN_LEFT)] =
+                               BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
 
        error = input_register_device(priv->dev2);
        if (error)
@@ -309,6 +317,7 @@ int lifebook_init(struct psmouse *psmouse)
 
        dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
        dev1->relbit[0] = 0;
+       dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0;
        dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
        input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0);
        input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0);
index 407cb22..4c4326c 100644 (file)
 #define _LIFEBOOK_H
 
 #ifdef CONFIG_MOUSE_PS2_LIFEBOOK
+void lifebook_module_init(void);
 int lifebook_detect(struct psmouse *psmouse, bool set_properties);
 int lifebook_init(struct psmouse *psmouse);
 #else
+inline void lifebook_module_init(void)
+{
+}
 inline int lifebook_detect(struct psmouse *psmouse, bool set_properties)
 {
        return -ENOSYS;
index ab5dc5f..543c240 100644 (file)
@@ -404,8 +404,8 @@ int ps2pp_init(struct psmouse *psmouse, bool set_properties)
                        }
                }
 
-               if (buttons < 3)
-                       __clear_bit(BTN_MIDDLE, psmouse->dev->keybit);
+               if (buttons >= 3)
+                       __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
 
                if (model_info)
                        ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
index 07c5379..fd0bc09 100644 (file)
@@ -425,6 +425,7 @@ static int genius_detect(struct psmouse *psmouse, bool set_properties)
                return -1;
 
        if (set_properties) {
+               __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
                __set_bit(BTN_EXTRA, psmouse->dev->keybit);
                __set_bit(BTN_SIDE, psmouse->dev->keybit);
                __set_bit(REL_WHEEL, psmouse->dev->relbit);
@@ -460,8 +461,10 @@ static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
                __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
                __set_bit(REL_WHEEL, psmouse->dev->relbit);
 
-               if (!psmouse->vendor) psmouse->vendor = "Generic";
-               if (!psmouse->name) psmouse->name = "Wheel Mouse";
+               if (!psmouse->vendor)
+                       psmouse->vendor = "Generic";
+               if (!psmouse->name)
+                       psmouse->name = "Wheel Mouse";
                psmouse->pktsize = 4;
        }
 
@@ -504,8 +507,10 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
                __set_bit(BTN_SIDE, psmouse->dev->keybit);
                __set_bit(BTN_EXTRA, psmouse->dev->keybit);
 
-               if (!psmouse->vendor) psmouse->vendor = "Generic";
-               if (!psmouse->name) psmouse->name = "Explorer Mouse";
+               if (!psmouse->vendor)
+                       psmouse->vendor = "Generic";
+               if (!psmouse->name)
+                       psmouse->name = "Explorer Mouse";
                psmouse->pktsize = 4;
        }
 
@@ -536,6 +541,7 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
                return -1;
 
        if (set_properties) {
+               __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
                __set_bit(BTN_EXTRA, psmouse->dev->keybit);
 
                psmouse->vendor = "Kensington";
@@ -551,8 +557,16 @@ static int thinking_detect(struct psmouse *psmouse, bool set_properties)
 static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
 {
        if (set_properties) {
-               if (!psmouse->vendor) psmouse->vendor = "Generic";
-               if (!psmouse->name) psmouse->name = "Mouse";
+               if (!psmouse->vendor)
+                       psmouse->vendor = "Generic";
+               if (!psmouse->name)
+                       psmouse->name = "Mouse";
+
+/*
+ * We have no way of figuring true number of buttons so let's
+ * assume that the device has 3.
+ */
+               __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
        }
 
        return 0;
@@ -567,6 +581,8 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)
        if (set_properties) {
                psmouse->vendor = "Cortron";
                psmouse->name = "PS/2 Trackball";
+
+               __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
                __set_bit(BTN_SIDE, psmouse->dev->keybit);
        }
 
@@ -1184,15 +1200,16 @@ static void psmouse_disconnect(struct serio *serio)
        mutex_unlock(&psmouse_mutex);
 }
 
-static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto)
+static int psmouse_switch_protocol(struct psmouse *psmouse,
+                                  const struct psmouse_protocol *proto)
 {
        struct input_dev *input_dev = psmouse->dev;
 
        input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
 
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-       input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
-               BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
+       input_dev->keybit[BIT_WORD(BTN_MOUSE)] =
+                               BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
        input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
 
        psmouse->set_rate = psmouse_set_rate;
@@ -1209,8 +1226,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse
                        return -1;
 
                psmouse->type = proto->type;
-       }
-       else
+       } else
                psmouse->type = psmouse_extensions(psmouse,
                                                   psmouse_max_proto, true);
 
@@ -1680,6 +1696,9 @@ static int __init psmouse_init(void)
 {
        int err;
 
+       lifebook_module_init();
+       synaptics_module_init();
+
        kpsmoused_wq = create_singlethread_workqueue("kpsmoused");
        if (!kpsmoused_wq) {
                printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n");
index f84cbd9..77b9fd0 100644 (file)
@@ -836,6 +836,7 @@ int fsp_init(struct psmouse *psmouse)
        priv->flags |= FSPDRV_FLAG_EN_OPC;
 
        /* Set up various supported input event bits */
+       __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
        __set_bit(BTN_BACK, psmouse->dev->keybit);
        __set_bit(BTN_FORWARD, psmouse->dev->keybit);
        __set_bit(REL_WHEEL, psmouse->dev->relbit);
index f4a6125..05689e7 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/dmi.h>
 #include <linux/input.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
@@ -629,25 +630,26 @@ static int synaptics_reconnect(struct psmouse *psmouse)
        return 0;
 }
 
-#if defined(__i386__)
-#include <linux/dmi.h>
-static const struct dmi_system_id toshiba_dmi_table[] = {
+static bool impaired_toshiba_kbc;
+
+static const struct dmi_system_id __initconst toshiba_dmi_table[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
        {
-               .ident = "Toshiba Satellite",
+               /* Toshiba Satellite */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),
                },
        },
        {
-               .ident = "Toshiba Dynabook",
+               /* Toshiba Dynabook */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"),
                },
        },
        {
-               .ident = "Toshiba Portege M300",
+               /* Toshiba Portege M300 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"),
@@ -655,7 +657,7 @@ static const struct dmi_system_id toshiba_dmi_table[] = {
 
        },
        {
-               .ident = "Toshiba Portege M300",
+               /* Toshiba Portege M300 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"),
@@ -664,8 +666,13 @@ static const struct dmi_system_id toshiba_dmi_table[] = {
 
        },
        { }
-};
 #endif
+};
+
+void __init synaptics_module_init(void)
+{
+       impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
+}
 
 int synaptics_init(struct psmouse *psmouse)
 {
@@ -718,18 +725,16 @@ int synaptics_init(struct psmouse *psmouse)
        if (SYN_CAP_PASS_THROUGH(priv->capabilities))
                synaptics_pt_create(psmouse);
 
-#if defined(__i386__)
        /*
         * Toshiba's KBC seems to have trouble handling data from
         * Synaptics as full rate, switch to lower rate which is roughly
         * thye same as rate of standard PS/2 mouse.
         */
-       if (psmouse->rate >= 80 && dmi_check_system(toshiba_dmi_table)) {
+       if (psmouse->rate >= 80 && impaired_toshiba_kbc) {
                printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n",
                        dmi_get_system_info(DMI_PRODUCT_NAME));
                psmouse->rate = 40;
        }
-#endif
 
        return 0;
 
@@ -740,6 +745,10 @@ int synaptics_init(struct psmouse *psmouse)
 
 #else /* CONFIG_MOUSE_PS2_SYNAPTICS */
 
+void __init synaptics_module_init(void)
+{
+}
+
 int synaptics_init(struct psmouse *psmouse)
 {
        return -ENOSYS;
index 871f6fe..838e7f2 100644 (file)
@@ -105,6 +105,7 @@ struct synaptics_data {
        int scroll;
 };
 
+void synaptics_module_init(void);
 int synaptics_detect(struct psmouse *psmouse, bool set_properties);
 int synaptics_init(struct psmouse *psmouse);
 void synaptics_reset(struct psmouse *psmouse);
index 7283c78..9867dfe 100644 (file)
@@ -420,8 +420,8 @@ static void synaptics_i2c_check_params(struct synaptics_i2c *touch)
 }
 
 /* Control the Device polling rate / Work Handler sleep time */
-unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
-                                        bool have_data)
+static unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
+                                               bool have_data)
 {
        unsigned long delay, nodata_count_thres;
 
@@ -520,7 +520,7 @@ static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch)
        __set_bit(BTN_LEFT, input->keybit);
 }
 
-struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
+static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
 {
        struct synaptics_i2c *touch;
 
index 0308a0f..909431c 100644 (file)
@@ -86,7 +86,8 @@ int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties)
 
        if (set_properties) {
                dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-               __set_bit(BTN_TOUCH, dev->keybit);
+               dev->keybit[BIT_WORD(BTN_MOUSE)] = 0;
+               dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
                input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
                input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
 
index e354362..63d4a67 100644 (file)
@@ -284,7 +284,6 @@ static int trackpoint_reconnect(struct psmouse *psmouse)
 
 int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
 {
-       struct trackpoint_data *priv;
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char firmware_id;
        unsigned char button_info;
@@ -301,8 +300,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
                button_info = 0;
        }
 
-       psmouse->private = priv = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
-       if (!priv)
+       psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
+       if (!psmouse->private)
                return -1;
 
        psmouse->vendor = "IBM";
@@ -311,7 +310,10 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
        psmouse->reconnect = trackpoint_reconnect;
        psmouse->disconnect = trackpoint_disconnect;
 
-       trackpoint_defaults(priv);
+       if ((button_info & 0x0f) >= 3)
+               __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+
+       trackpoint_defaults(psmouse->private);
        trackpoint_sync(psmouse);
 
        error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
@@ -319,7 +321,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
                printk(KERN_ERR
                        "trackpoint.c: failed to create sysfs attributes, error: %d\n",
                        error);
-               kfree(priv);
+               kfree(psmouse->private);
+               psmouse->private = NULL;
                return -1;
        }
 
index 7011144..bf2c0c8 100644 (file)
 
 #define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
 
-MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_LICENSE ("GPL");
+MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
 
 #undef VSXXXAA_DEBUG
 #ifdef VSXXXAA_DEBUG
-#define DBG(x...) printk (x)
+#define DBG(x...) printk(x)
 #else
 #define DBG(x...) do {} while (0)
 #endif
 
 #define VSXXXAA_INTRO_MASK     0x80
 #define VSXXXAA_INTRO_HEAD     0x80
-#define IS_HDR_BYTE(x)         (((x) & VSXXXAA_INTRO_MASK)     \
-                                       == VSXXXAA_INTRO_HEAD)
+#define IS_HDR_BYTE(x)                 \
+       (((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD)
 
 #define VSXXXAA_PACKET_MASK    0xe0
 #define VSXXXAA_PACKET_REL     0x80
 #define VSXXXAA_PACKET_ABS     0xc0
 #define VSXXXAA_PACKET_POR     0xa0
-#define MATCH_PACKET_TYPE(data, type)  (((data) & VSXXXAA_PACKET_MASK) == (type))
+#define MATCH_PACKET_TYPE(data, type)  \
+       (((data) & VSXXXAA_PACKET_MASK) == (type))
 
 
 
@@ -123,52 +124,50 @@ struct vsxxxaa {
        char phys[32];
 };
 
-static void
-vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num)
+static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num)
 {
-       if (num >= mouse->count)
+       if (num >= mouse->count) {
                mouse->count = 0;
-       else {
-               memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num);
+       else {
+               memmove(mouse->buf, mouse->buf + num - 1, BUFLEN - num);
                mouse->count -= num;
        }
 }
 
-static void
-vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte)
+static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte)
 {
        if (mouse->count == BUFLEN) {
-               printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
-                               mouse->name, mouse->phys);
-               vsxxxaa_drop_bytes (mouse, 1);
+               printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
+                       mouse->name, mouse->phys);
+               vsxxxaa_drop_bytes(mouse, 1);
        }
-       DBG (KERN_INFO "Queueing byte 0x%02x\n", byte);
+
+       DBG(KERN_INFO "Queueing byte 0x%02x\n", byte);
 
        mouse->buf[mouse->count++] = byte;
 }
 
-static void
-vsxxxaa_detection_done (struct vsxxxaa *mouse)
+static void vsxxxaa_detection_done(struct vsxxxaa *mouse)
 {
        switch (mouse->type) {
-               case 0x02:
-                       strlcpy (mouse->name, "DEC VSXXX-AA/-GA mouse",
-                                sizeof (mouse->name));
-                       break;
-
-               case 0x04:
-                       strlcpy (mouse->name, "DEC VSXXX-AB digitizer",
-                                sizeof (mouse->name));
-                       break;
-
-               default:
-                       snprintf (mouse->name, sizeof (mouse->name),
-                                 "unknown DEC pointer device (type = 0x%02x)",
-                                 mouse->type);
-                       break;
+       case 0x02:
+               strlcpy(mouse->name, "DEC VSXXX-AA/-GA mouse",
+                       sizeof(mouse->name));
+               break;
+
+       case 0x04:
+               strlcpy(mouse->name, "DEC VSXXX-AB digitizer",
+                       sizeof(mouse->name));
+               break;
+
+       default:
+               snprintf(mouse->name, sizeof(mouse->name),
+                        "unknown DEC pointer device (type = 0x%02x)",
+                        mouse->type);
+               break;
        }
 
-       printk (KERN_INFO
+       printk(KERN_INFO
                "Found %s version 0x%02x from country 0x%02x on port %s\n",
                mouse->name, mouse->version, mouse->country, mouse->phys);
 }
@@ -176,42 +175,38 @@ vsxxxaa_detection_done (struct vsxxxaa *mouse)
 /*
  * Returns number of bytes to be dropped, 0 if packet is okay.
  */
-static int
-vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len)
+static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len)
 {
        int i;
 
        /* First byte must be a header byte */
-       if (!IS_HDR_BYTE (mouse->buf[0])) {
-               DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
+       if (!IS_HDR_BYTE(mouse->buf[0])) {
+               DBG("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
                return 1;
        }
 
        /* Check all following bytes */
-       if (packet_len > 1) {
-               for (i = 1; i < packet_len; i++) {
-                       if (IS_HDR_BYTE (mouse->buf[i])) {
-                               printk (KERN_ERR "Need to drop %d bytes "
-                                               "of a broken packet.\n",
-                                               i - 1);
-                               DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
-                                               packet_len, i, mouse->buf[i]);
-                               return i - 1;
-                       }
+       for (i = 1; i < packet_len; i++) {
+               if (IS_HDR_BYTE(mouse->buf[i])) {
+                       printk(KERN_ERR
+                               "Need to drop %d bytes of a broken packet.\n",
+                               i - 1);
+                       DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
+                           packet_len, i, mouse->buf[i]);
+                       return i - 1;
                }
        }
 
        return 0;
 }
 
-static __inline__ int
-vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len)
+static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse,
+                                            unsigned char type, size_t len)
 {
-       return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type);
+       return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type);
 }
 
-static void
-vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse)
+static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse)
 {
        struct input_dev *dev = mouse->dev;
        unsigned char *buf = mouse->buf;
@@ -232,43 +227,42 @@ vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse)
         * 0, bit 4 of byte 0 is direction.
         */
        dx = buf[1] & 0x7f;
-       dx *= ((buf[0] >> 4) & 0x01)? 1: -1;
+       dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1;
 
        /*
         * Low 7 bit of byte 2 are abs(dy), bit 7 is
         * 0, bit 3 of byte 0 is direction.
         */
        dy = buf[2] & 0x7f;
-       dy *= ((buf[0] >> 3) & 0x01)? -1: 1;
+       dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1;
 
        /*
         * Get button state. It's the low three bits
         * (for three buttons) of byte 0.
         */
-       left    = (buf[0] & 0x04)? 1: 0;
-       middle  = (buf[0] & 0x02)? 1: 0;
-       right   = (buf[0] & 0x01)? 1: 0;
+       left    = buf[0] & 0x04;
+       middle  = buf[0] & 0x02;
+       right   = buf[0] & 0x01;
 
-       vsxxxaa_drop_bytes (mouse, 3);
+       vsxxxaa_drop_bytes(mouse, 3);
 
-       DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
-                       mouse->name, mouse->phys, dx, dy,
-                       left? "L": "l", middle? "M": "m", right? "R": "r");
+       DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
+           mouse->name, mouse->phys, dx, dy,
+           left ? "L" : "l", middle ? "M" : "m", right ? "R" : "r");
 
        /*
         * Report what we've found so far...
         */
-       input_report_key (dev, BTN_LEFT, left);
-       input_report_key (dev, BTN_MIDDLE, middle);
-       input_report_key (dev, BTN_RIGHT, right);
-       input_report_key (dev, BTN_TOUCH, 0);
-       input_report_rel (dev, REL_X, dx);
-       input_report_rel (dev, REL_Y, dy);
-       input_sync (dev);
+       input_report_key(dev, BTN_LEFT, left);
+       input_report_key(dev, BTN_MIDDLE, middle);
+       input_report_key(dev, BTN_RIGHT, right);
+       input_report_key(dev, BTN_TOUCH, 0);
+       input_report_rel(dev, REL_X, dx);
+       input_report_rel(dev, REL_Y, dy);
+       input_sync(dev);
 }
 
-static void
-vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse)
+static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse)
 {
        struct input_dev *dev = mouse->dev;
        unsigned char *buf = mouse->buf;
@@ -296,32 +290,31 @@ vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse)
        /*
         * Get button state. It's bits <4..1> of byte 0.
         */
-       left    = (buf[0] & 0x02)? 1: 0;
-       middle  = (buf[0] & 0x04)? 1: 0;
-       right   = (buf[0] & 0x08)? 1: 0;
-       touch   = (buf[0] & 0x10)? 1: 0;
+       left    = buf[0] & 0x02;
+       middle  = buf[0] & 0x04;
+       right   = buf[0] & 0x08;
+       touch   = buf[0] & 0x10;
 
-       vsxxxaa_drop_bytes (mouse, 5);
+       vsxxxaa_drop_bytes(mouse, 5);
 
-       DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
-                       mouse->name, mouse->phys, x, y,
-                       left? "L": "l", middle? "M": "m",
-                       right? "R": "r", touch? "T": "t");
+       DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
+           mouse->name, mouse->phys, x, y,
+           left ? "L" : "l", middle ? "M" : "m",
+           right ? "R" : "r", touch ? "T" : "t");
 
        /*
         * Report what we've found so far...
         */
-       input_report_key (dev, BTN_LEFT, left);
-       input_report_key (dev, BTN_MIDDLE, middle);
-       input_report_key (dev, BTN_RIGHT, right);
-       input_report_key (dev, BTN_TOUCH, touch);
-       input_report_abs (dev, ABS_X, x);
-       input_report_abs (dev, ABS_Y, y);
-       input_sync (dev);
+       input_report_key(dev, BTN_LEFT, left);
+       input_report_key(dev, BTN_MIDDLE, middle);
+       input_report_key(dev, BTN_RIGHT, right);
+       input_report_key(dev, BTN_TOUCH, touch);
+       input_report_abs(dev, ABS_X, x);
+       input_report_abs(dev, ABS_Y, y);
+       input_sync(dev);
 }
 
-static void
-vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
+static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse)
 {
        struct input_dev *dev = mouse->dev;
        unsigned char *buf = mouse->buf;
@@ -356,24 +349,24 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
         * (for three buttons) of byte 0. Maybe even the bit <3>
         * has some meaning if a tablet is attached.
         */
-       left    = (buf[0] & 0x04)? 1: 0;
-       middle  = (buf[0] & 0x02)? 1: 0;
-       right   = (buf[0] & 0x01)? 1: 0;
+       left    = buf[0] & 0x04;
+       middle  = buf[0] & 0x02;
+       right   = buf[0] & 0x01;
 
-       vsxxxaa_drop_bytes (mouse, 4);
-       vsxxxaa_detection_done (mouse);
+       vsxxxaa_drop_bytes(mouse, 4);
+       vsxxxaa_detection_done(mouse);
 
        if (error <= 0x1f) {
                /* No (serious) error. Report buttons */
-               input_report_key (dev, BTN_LEFT, left);
-               input_report_key (dev, BTN_MIDDLE, middle);
-               input_report_key (dev, BTN_RIGHT, right);
-               input_report_key (dev, BTN_TOUCH, 0);
-               input_sync (dev);
+               input_report_key(dev, BTN_LEFT, left);
+               input_report_key(dev, BTN_MIDDLE, middle);
+               input_report_key(dev, BTN_RIGHT, right);
+               input_report_key(dev, BTN_TOUCH, 0);
+               input_sync(dev);
 
                if (error != 0)
-                       printk (KERN_INFO "Your %s on %s reports error=0x%02x\n",
-                                       mouse->name, mouse->phys, error);
+                       printk(KERN_INFO "Your %s on %s reports error=0x%02x\n",
+                               mouse->name, mouse->phys, error);
 
        }
 
@@ -381,18 +374,18 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
         * If the mouse was hot-plugged, we need to force differential mode
         * now... However, give it a second to recover from it's reset.
         */
-       printk (KERN_NOTICE "%s on %s: Forceing standard packet format, "
-                       "incremental streaming mode and 72 samples/sec\n",
-                       mouse->name, mouse->phys);
-       serio_write (mouse->serio, 'S');        /* Standard format */
-       mdelay (50);
-       serio_write (mouse->serio, 'R');        /* Incremental */
-       mdelay (50);
-       serio_write (mouse->serio, 'L');        /* 72 samples/sec */
+       printk(KERN_NOTICE
+               "%s on %s: Forcing standard packet format, "
+               "incremental streaming mode and 72 samples/sec\n",
+               mouse->name, mouse->phys);
+       serio_write(mouse->serio, 'S'); /* Standard format */
+       mdelay(50);
+       serio_write(mouse->serio, 'R'); /* Incremental */
+       mdelay(50);
+       serio_write(mouse->serio, 'L'); /* 72 samples/sec */
 }
 
-static void
-vsxxxaa_parse_buffer (struct vsxxxaa *mouse)
+static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse)
 {
        unsigned char *buf = mouse->buf;
        int stray_bytes;
@@ -409,122 +402,107 @@ vsxxxaa_parse_buffer (struct vsxxxaa *mouse)
                 * activity on the mouse.
                 */
                while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
-                       printk (KERN_ERR "%s on %s: Dropping a byte to regain "
-                                       "sync with mouse data stream...\n",
-                                       mouse->name, mouse->phys);
-                       vsxxxaa_drop_bytes (mouse, 1);
+                       printk(KERN_ERR "%s on %s: Dropping a byte to regain "
+                               "sync with mouse data stream...\n",
+                               mouse->name, mouse->phys);
+                       vsxxxaa_drop_bytes(mouse, 1);
                }
 
                /*
                 * Check for packets we know about.
                 */
 
-               if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) {
+               if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, 3)) {
                        /* Check for broken packet */
-                       stray_bytes = vsxxxaa_check_packet (mouse, 3);
-                       if (stray_bytes > 0) {
-                               printk (KERN_ERR "Dropping %d bytes now...\n",
-                                               stray_bytes);
-                               vsxxxaa_drop_bytes (mouse, stray_bytes);
-                               continue;
-                       }
-
-                       vsxxxaa_handle_REL_packet (mouse);
-                       continue; /* More to parse? */
-               }
+                       stray_bytes = vsxxxaa_check_packet(mouse, 3);
+                       if (!stray_bytes)
+                               vsxxxaa_handle_REL_packet(mouse);
 
-               if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
+               } else if (vsxxxaa_smells_like_packet(mouse,
+                                                     VSXXXAA_PACKET_ABS, 5)) {
                        /* Check for broken packet */
-                       stray_bytes = vsxxxaa_check_packet (mouse, 5);
-                       if (stray_bytes > 0) {
-                               printk (KERN_ERR "Dropping %d bytes now...\n",
-                                               stray_bytes);
-                               vsxxxaa_drop_bytes (mouse, stray_bytes);
-                               continue;
-                       }
-
-                       vsxxxaa_handle_ABS_packet (mouse);
-                       continue; /* More to parse? */
-               }
+                       stray_bytes = vsxxxaa_check_packet(mouse, 5);
+                       if (!stray_bytes)
+                               vsxxxaa_handle_ABS_packet(mouse);
 
-               if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
+               } else if (vsxxxaa_smells_like_packet(mouse,
+                                                     VSXXXAA_PACKET_POR, 4)) {
                        /* Check for broken packet */
-                       stray_bytes = vsxxxaa_check_packet (mouse, 4);
-                       if (stray_bytes > 0) {
-                               printk (KERN_ERR "Dropping %d bytes now...\n",
-                                               stray_bytes);
-                               vsxxxaa_drop_bytes (mouse, stray_bytes);
-                               continue;
-                       }
-
-                       vsxxxaa_handle_POR_packet (mouse);
-                       continue; /* More to parse? */
+                       stray_bytes = vsxxxaa_check_packet(mouse, 4);
+                       if (!stray_bytes)
+                               vsxxxaa_handle_POR_packet(mouse);
+
+               } else {
+                       break; /* No REL, ABS or POR packet found */
+               }
+
+               if (stray_bytes > 0) {
+                       printk(KERN_ERR "Dropping %d bytes now...\n",
+                               stray_bytes);
+                       vsxxxaa_drop_bytes(mouse, stray_bytes);
                }
 
-               break; /* No REL, ABS or POR packet found */
        } while (1);
 }
 
-static irqreturn_t
-vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
+static irqreturn_t vsxxxaa_interrupt(struct serio *serio,
+                                    unsigned char data, unsigned int flags)
 {
-       struct vsxxxaa *mouse = serio_get_drvdata (serio);
+       struct vsxxxaa *mouse = serio_get_drvdata(serio);
 
-       vsxxxaa_queue_byte (mouse, data);
-       vsxxxaa_parse_buffer (mouse);
+       vsxxxaa_queue_byte(mouse, data);
+       vsxxxaa_parse_buffer(mouse);
 
        return IRQ_HANDLED;
 }
 
-static void
-vsxxxaa_disconnect (struct serio *serio)
+static void vsxxxaa_disconnect(struct serio *serio)
 {
-       struct vsxxxaa *mouse = serio_get_drvdata (serio);
+       struct vsxxxaa *mouse = serio_get_drvdata(serio);
 
-       serio_close (serio);
-       serio_set_drvdata (serio, NULL);
-       input_unregister_device (mouse->dev);
-       kfree (mouse);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_unregister_device(mouse->dev);
+       kfree(mouse);
 }
 
-static int
-vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
+static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv)
 {
        struct vsxxxaa *mouse;
        struct input_dev *input_dev;
        int err = -ENOMEM;
 
-       mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL);
-       input_dev = input_allocate_device ();
+       mouse = kzalloc(sizeof(struct vsxxxaa), GFP_KERNEL);
+       input_dev = input_allocate_device();
        if (!mouse || !input_dev)
                goto fail1;
 
        mouse->dev = input_dev;
        mouse->serio = serio;
-       strlcat (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
-                sizeof (mouse->name));
-       snprintf (mouse->phys, sizeof (mouse->phys), "%s/input0", serio->phys);
+       strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
+                sizeof(mouse->name));
+       snprintf(mouse->phys, sizeof(mouse->phys), "%s/input0", serio->phys);
 
        input_dev->name = mouse->name;
        input_dev->phys = mouse->phys;
        input_dev->id.bustype = BUS_RS232;
        input_dev->dev.parent = &serio->dev;
 
-       set_bit (EV_KEY, input_dev->evbit);             /* We have buttons */
-       set_bit (EV_REL, input_dev->evbit);
-       set_bit (EV_ABS, input_dev->evbit);
-       set_bit (BTN_LEFT, input_dev->keybit);          /* We have 3 buttons */
-       set_bit (BTN_MIDDLE, input_dev->keybit);
-       set_bit (BTN_RIGHT, input_dev->keybit);
-       set_bit (BTN_TOUCH, input_dev->keybit);         /* ...and Tablet */
-       set_bit (REL_X, input_dev->relbit);
-       set_bit (REL_Y, input_dev->relbit);
-       input_set_abs_params (input_dev, ABS_X, 0, 1023, 0, 0);
-       input_set_abs_params (input_dev, ABS_Y, 0, 1023, 0, 0);
-
-       serio_set_drvdata (serio, mouse);
-
-       err = serio_open (serio, drv);
+       __set_bit(EV_KEY, input_dev->evbit);            /* We have buttons */
+       __set_bit(EV_REL, input_dev->evbit);
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(BTN_LEFT, input_dev->keybit);         /* We have 3 buttons */
+       __set_bit(BTN_MIDDLE, input_dev->keybit);
+       __set_bit(BTN_RIGHT, input_dev->keybit);
+       __set_bit(BTN_TOUCH, input_dev->keybit);        /* ...and Tablet */
+       __set_bit(REL_X, input_dev->relbit);
+       __set_bit(REL_Y, input_dev->relbit);
+       input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
+
+       serio_set_drvdata(serio, mouse);
+
+       err = serio_open(serio, drv);
        if (err)
                goto fail2;
 
@@ -532,18 +510,18 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
         * Request selftest. Standard packet format and differential
         * mode will be requested after the device ID'ed successfully.
         */
-       serio_write (serio, 'T'); /* Test */
+       serio_write(serio, 'T'); /* Test */
 
-       err = input_register_device (input_dev);
+       err = input_register_device(input_dev);
        if (err)
                goto fail3;
 
        return 0;
 
- fail3:        serio_close (serio);
- fail2:        serio_set_drvdata (serio, NULL);
- fail1:        input_free_device (input_dev);
-       kfree (mouse);
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(mouse);
        return err;
 }
 
@@ -570,18 +548,16 @@ static struct serio_driver vsxxxaa_drv = {
        .disconnect     = vsxxxaa_disconnect,
 };
 
-static int __init
-vsxxxaa_init (void)
+static int __init vsxxxaa_init(void)
 {
        return serio_register_driver(&vsxxxaa_drv);
 }
 
-static void __exit
-vsxxxaa_exit (void)
+static void __exit vsxxxaa_exit(void)
 {
        serio_unregister_driver(&vsxxxaa_drv);
 }
 
-module_init (vsxxxaa_init);
-module_exit (vsxxxaa_exit);
+module_init(vsxxxaa_init);
+module_exit(vsxxxaa_exit);
 
index aa533ce..7e319d6 100644 (file)
@@ -201,4 +201,12 @@ config SERIO_XILINX_XPS_PS2
          To compile this driver as a module, choose M here: the
          module will be called xilinx_ps2.
 
+config SERIO_ALTERA_PS2
+       tristate "Altera UP PS/2 controller"
+       help
+         Say Y here if you have Altera University Program PS/2 ports.
+
+         To compile this driver as a module, choose M here: the
+         module will be called altera_ps2.
+
 endif
index 9b6c813..bf945f7 100644 (file)
@@ -22,3 +22,4 @@ obj-$(CONFIG_SERIO_MACEPS2)   += maceps2.o
 obj-$(CONFIG_SERIO_LIBPS2)     += libps2.o
 obj-$(CONFIG_SERIO_RAW)                += serio_raw.o
 obj-$(CONFIG_SERIO_XILINX_XPS_PS2)     += xilinx_ps2.o
+obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c
new file mode 100644 (file)
index 0000000..f479ea5
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Altera University Program PS2 controller driver
+ *
+ * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on sa1111ps2.c, which is:
+ * Copyright (C) 2002 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#define DRV_NAME "altera_ps2"
+
+struct ps2if {
+       struct serio *io;
+       struct resource *iomem_res;
+       void __iomem *base;
+       unsigned irq;
+};
+
+/*
+ * Read all bytes waiting in the PS2 port.  There should be
+ * at the most one, but we loop for safety.
+ */
+static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
+{
+       struct ps2if *ps2if = dev_id;
+       unsigned int status;
+       int handled = IRQ_NONE;
+
+       while ((status = readl(ps2if->base)) & 0xffff0000) {
+               serio_interrupt(ps2if->io, status & 0xff, 0);
+               handled = IRQ_HANDLED;
+       }
+
+       return handled;
+}
+
+/*
+ * Write a byte to the PS2 port.
+ */
+static int altera_ps2_write(struct serio *io, unsigned char val)
+{
+       struct ps2if *ps2if = io->port_data;
+
+       writel(val, ps2if->base);
+       return 0;
+}
+
+static int altera_ps2_open(struct serio *io)
+{
+       struct ps2if *ps2if = io->port_data;
+
+       /* clear fifo */
+       while (readl(ps2if->base) & 0xffff0000)
+               /* empty */;
+
+       writel(1, ps2if->base + 4); /* enable rx irq */
+       return 0;
+}
+
+static void altera_ps2_close(struct serio *io)
+{
+       struct ps2if *ps2if = io->port_data;
+
+       writel(0, ps2if->base); /* disable rx irq */
+}
+
+/*
+ * Add one device to this driver.
+ */
+static int altera_ps2_probe(struct platform_device *pdev)
+{
+       struct ps2if *ps2if;
+       struct serio *serio;
+       int error;
+
+       ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
+       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!ps2if || !serio) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       serio->id.type          = SERIO_8042;
+       serio->write            = altera_ps2_write;
+       serio->open             = altera_ps2_open;
+       serio->close            = altera_ps2_close;
+       strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
+       strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
+       serio->port_data        = ps2if;
+       serio->dev.parent       = &pdev->dev;
+       ps2if->io               = serio;
+
+       ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (ps2if->iomem_res == NULL) {
+               error = -ENOENT;
+               goto err_free_mem;
+       }
+
+       ps2if->irq  = platform_get_irq(pdev, 0);
+       if (ps2if->irq < 0) {
+               error = -ENXIO;
+               goto err_free_mem;
+       }
+
+       if (!request_mem_region(ps2if->iomem_res->start,
+                               resource_size(ps2if->iomem_res), pdev->name)) {
+               error = -EBUSY;
+               goto err_free_mem;
+       }
+
+       ps2if->base = ioremap(ps2if->iomem_res->start,
+                             resource_size(ps2if->iomem_res));
+       if (!ps2if->base) {
+               error = -ENOMEM;
+               goto err_free_res;
+       }
+
+       error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);
+       if (error) {
+               dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",
+                       ps2if->irq, error);
+               goto err_unmap;
+       }
+
+       dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);
+
+       serio_register_port(ps2if->io);
+       platform_set_drvdata(pdev, ps2if);
+
+       return 0;
+
+ err_unmap:
+       iounmap(ps2if->base);
+ err_free_res:
+       release_mem_region(ps2if->iomem_res->start,
+                          resource_size(ps2if->iomem_res));
+ err_free_mem:
+       kfree(ps2if);
+       kfree(serio);
+       return error;
+}
+
+/*
+ * Remove one device from this driver.
+ */
+static int altera_ps2_remove(struct platform_device *pdev)
+{
+       struct ps2if *ps2if = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+       serio_unregister_port(ps2if->io);
+       free_irq(ps2if->irq, ps2if);
+       iounmap(ps2if->base);
+       release_mem_region(ps2if->iomem_res->start,
+                          resource_size(ps2if->iomem_res));
+       kfree(ps2if);
+
+       return 0;
+}
+
+/*
+ * Our device driver structure
+ */
+static struct platform_driver altera_ps2_driver = {
+       .probe          = altera_ps2_probe,
+       .remove         = altera_ps2_remove,
+       .driver = {
+               .name   = DRV_NAME,
+       },
+};
+
+static int __init altera_ps2_init(void)
+{
+       return platform_driver_register(&altera_ps2_driver);
+}
+
+static void __exit altera_ps2_exit(void)
+{
+       platform_driver_unregister(&altera_ps2_driver);
+}
+
+module_init(altera_ps2_init);
+module_exit(altera_ps2_exit);
+
+MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
index 1c9410d..bcc2d30 100644 (file)
@@ -955,7 +955,7 @@ static int __init hp_sdc_init_hppa(struct parisc_device *d)
        INIT_DELAYED_WORK(&moduleloader_work, request_module_delayed);
 
        ret = hp_sdc_init();
-       /* after sucessfull initialization give SDC some time to settle
+       /* after successfull initialization give SDC some time to settle
         * and then load the hp_sdc_mlc upper layer driver */
        if (!ret)
                schedule_delayed_work(&moduleloader_work,
index 820e516..7d2b820 100644 (file)
@@ -125,7 +125,7 @@ static void hp_sdc_mlc_isr (int irq, void *dev_id,
                break;
 
        default:
-               printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
+               printk(KERN_WARNING PREFIX "Unknown HIL Error status (%x)!\n", data);
                break;
        }
 
index 2bcf1ac..7fbffe4 100644 (file)
@@ -67,10 +67,12 @@ static inline void i8042_write_command(int val)
 
 #include <linux/dmi.h>
 
-static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
        {
-               /* AUX LOOP command does not raise AUX IRQ */
-               .ident = "Arima-Rioworks HDAMB",
+               /*
+                * Arima-Rioworks HDAMB -
+                * AUX LOOP command does not raise AUX IRQ
+                */
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"),
                        DMI_MATCH(DMI_BOARD_NAME, "HDAMB"),
@@ -78,7 +80,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "ASUS G1S",
+               /* ASUS G1S */
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
                        DMI_MATCH(DMI_BOARD_NAME, "G1S"),
@@ -86,8 +88,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               /* AUX LOOP command does not raise AUX IRQ */
-               .ident = "ASUS P65UP5",
+               /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
                        DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"),
@@ -95,7 +96,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "Compaq Proliant 8500",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
                        DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
@@ -103,7 +103,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "Compaq Proliant DL760",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
                        DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
@@ -111,7 +110,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "OQO Model 01",
+               /* OQO Model 01 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
@@ -119,8 +118,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               /* AUX LOOP does not work properly */
-               .ident = "ULI EV4873",
+               /* ULI EV4873 - AUX LOOP does not work properly */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ULI"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"),
@@ -128,7 +126,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "Microsoft Virtual Machine",
+               /* Microsoft Virtual Machine */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
@@ -136,7 +134,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "Medion MAM 2070",
+               /* Medion MAM 2070 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"),
@@ -144,7 +142,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "Blue FB5601",
+               /* Blue FB5601 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "blue"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"),
@@ -152,7 +150,7 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "Gigabyte M912",
+               /* Gigabyte M912 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
@@ -160,7 +158,6 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                },
        },
        {
-               .ident = "HP DV9700",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
@@ -177,72 +174,72 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
  * ... apparently some Toshibas don't like MUX mode either and
  * die horrible death on reboot.
  */
-static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
        {
-               .ident = "Fujitsu Lifebook P7010/P7010D",
+               /* Fujitsu Lifebook P7010/P7010D */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
                },
        },
        {
-               .ident = "Fujitsu Lifebook P7010",
+               /* Fujitsu Lifebook P7010 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"),
                },
        },
        {
-               .ident = "Fujitsu Lifebook P5020D",
+               /* Fujitsu Lifebook P5020D */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
                },
        },
        {
-               .ident = "Fujitsu Lifebook S2000",
+               /* Fujitsu Lifebook S2000 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
                },
        },
        {
-               .ident = "Fujitsu Lifebook S6230",
+               /* Fujitsu Lifebook S6230 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
                },
        },
        {
-               .ident = "Fujitsu T70H",
+               /* Fujitsu T70H */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
                },
        },
        {
-               .ident = "Fujitsu-Siemens Lifebook T3010",
+               /* Fujitsu-Siemens Lifebook T3010 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"),
                },
        },
        {
-               .ident = "Fujitsu-Siemens Lifebook E4010",
+               /* Fujitsu-Siemens Lifebook E4010 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"),
                },
        },
        {
-               .ident = "Fujitsu-Siemens Amilo Pro 2010",
+               /* Fujitsu-Siemens Amilo Pro 2010 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"),
                },
        },
        {
-               .ident = "Fujitsu-Siemens Amilo Pro 2030",
+               /* Fujitsu-Siemens Amilo Pro 2030 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
@@ -253,7 +250,7 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
                 * No data is coming from the touchscreen unless KBC
                 * is in legacy mode.
                 */
-               .ident = "Panasonic CF-29",
+               /* Panasonic CF-29 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
@@ -261,10 +258,10 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
        },
        {
                /*
-                * Errors on MUX ports are reported without raising AUXDATA
+                * HP Pavilion DV4017EA -
+                * errors on MUX ports are reported without raising AUXDATA
                 * causing "spurious NAK" messages.
                 */
-               .ident = "HP Pavilion DV4017EA",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
@@ -272,9 +269,9 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
        },
        {
                /*
-                * Like DV4017EA does not raise AUXERR for errors on MUX ports.
+                * HP Pavilion ZT1000 -
+                * like DV4017EA does not raise AUXERR for errors on MUX ports.
                 */
-               .ident = "HP Pavilion ZT1000",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"),
@@ -283,44 +280,41 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
        },
        {
                /*
-                * Like DV4017EA does not raise AUXERR for errors on MUX ports.
+                * HP Pavilion DV4270ca -
+                * like DV4017EA does not raise AUXERR for errors on MUX ports.
                 */
-               .ident = "HP Pavilion DV4270ca",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"),
                },
        },
        {
-               .ident = "Toshiba P10",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
                },
        },
        {
-               .ident = "Toshiba Equium A110",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
                },
        },
        {
-               .ident = "Alienware Sentia",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
                },
        },
        {
-               .ident = "Sharp Actius MM20",
+               /* Sharp Actius MM20 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"),
                },
        },
        {
-               .ident = "Sony Vaio FS-115b",
+               /* Sony Vaio FS-115b */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
@@ -328,73 +322,72 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
        },
        {
                /*
-                * Reset and GET ID commands issued via KBD port are
+                * Sony Vaio FZ-240E -
+                * reset and GET ID commands issued via KBD port are
                 * sometimes being delivered to AUX3.
                 */
-               .ident = "Sony Vaio FZ-240E",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"),
                },
        },
        {
-               .ident = "Amoi M636/A737",
+               /* Amoi M636/A737 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
                },
        },
        {
-               .ident = "Lenovo 3000 n100",
+               /* Lenovo 3000 n100 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "076804U"),
                },
        },
        {
-               .ident = "Acer Aspire 1360",
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
                },
        },
        {
-               .ident = "Gericom Bellagio",
+               /* Gericom Bellagio */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Gericom"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
                },
        },
        {
-               .ident = "IBM 2656",
+               /* IBM 2656 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
                },
        },
        {
-               .ident = "Dell XPS M1530",
+               /* Dell XPS M1530 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"),
                },
        },
        {
-               .ident = "Compal HEL80I",
+               /* Compal HEL80I */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
                },
        },
        {
-               .ident = "Dell Vostro 1510",
+               /* Dell Vostro 1510 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
                },
        },
        {
-               .ident = "Acer Aspire 5536",
+               /* Acer Aspire 5536 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
@@ -404,65 +397,65 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
        { }
 };
 
-static struct dmi_system_id __initdata i8042_dmi_reset_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
        {
-               .ident = "MSI Wind U-100",
+               /* MSI Wind U-100 */
                .matches = {
                        DMI_MATCH(DMI_BOARD_NAME, "U-100"),
                        DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
                },
        },
        {
-               .ident = "LG Electronics X110",
+               /* LG Electronics X110 */
                .matches = {
                        DMI_MATCH(DMI_BOARD_NAME, "X110"),
                        DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."),
                },
        },
        {
-               .ident = "Acer Aspire One 150",
+               /* Acer Aspire One 150 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
                },
        },
        {
-               .ident = "Advent 4211",
+               /* Advent 4211 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"),
                },
        },
        {
-               .ident = "Medion Akoya Mini E1210",
+               /* Medion Akoya Mini E1210 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "E1210"),
                },
        },
        {
-               .ident = "Mivvy M310",
+               /* Mivvy M310 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "N10"),
                },
        },
        {
-               .ident = "Dell Vostro 1320",
+               /* Dell Vostro 1320 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"),
                },
        },
        {
-               .ident = "Dell Vostro 1520",
+               /* Dell Vostro 1520 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"),
                },
        },
        {
-               .ident = "Dell Vostro 1720",
+               /* Dell Vostro 1720 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
@@ -472,16 +465,16 @@ static struct dmi_system_id __initdata i8042_dmi_reset_table[] = {
 };
 
 #ifdef CONFIG_PNP
-static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = {
        {
-               .ident = "Intel MBO Desktop D845PESV",
+               /* Intel MBO Desktop D845PESV */
                .matches = {
                        DMI_MATCH(DMI_BOARD_NAME, "D845PESV"),
                        DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
                },
        },
        {
-               .ident = "MSI Wind U-100",
+               /* MSI Wind U-100 */
                .matches = {
                        DMI_MATCH(DMI_BOARD_NAME, "U-100"),
                        DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
@@ -490,27 +483,23 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = {
        { }
 };
 
-static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
        {
-               .ident = "Portable",
                .matches = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
                },
        },
        {
-               .ident = "Laptop",
                .matches = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
                },
        },
        {
-               .ident = "Notebook",
                .matches = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
                },
        },
        {
-               .ident = "Sub-Notebook",
                .matches = {
                        DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
                },
@@ -525,58 +514,58 @@ static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = {
  * Originally, this was just confined to older laptops, but a few Acer laptops
  * have turned up in 2007 that also need this again.
  */
-static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = {
+static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
        {
-               .ident = "Acer Aspire 5630",
+               /* Acer Aspire 5630 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
                },
        },
        {
-               .ident = "Acer Aspire 5650",
+               /* Acer Aspire 5650 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
                },
        },
        {
-               .ident = "Acer Aspire 5680",
+               /* Acer Aspire 5680 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
                },
        },
        {
-               .ident = "Acer Aspire 5720",
+               /* Acer Aspire 5720 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
                },
        },
        {
-               .ident = "Acer Aspire 9110",
+               /* Acer Aspire 9110 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
                },
        },
        {
-               .ident = "Acer TravelMate 660",
+               /* Acer TravelMate 660 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"),
                },
        },
        {
-               .ident = "Acer TravelMate 2490",
+               /* Acer TravelMate 2490 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
                },
        },
        {
-               .ident = "Acer TravelMate 4280",
+               /* Acer TravelMate 4280 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"),
diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
new file mode 100644 (file)
index 0000000..fbd3987
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Generic support for sparse keymaps
+ *
+ * Copyright (c) 2009 Dmitry Torokhov
+ *
+ * Derived from wistron button driver:
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+
+MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
+MODULE_DESCRIPTION("Generic support for sparse keymaps");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
+
+/**
+ * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
+                                                   unsigned int code)
+{
+       struct key_entry *key;
+
+       for (key = dev->keycode; key->type != KE_END; key++)
+               if (code == key->code)
+                       return key;
+
+       return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_scancode);
+
+/**
+ * sparse_keymap_entry_from_keycode - perform sparse keymap lookup
+ * @dev: Input device using sparse keymap
+ * @keycode: Key code
+ *
+ * This function is used to perform &struct key_entry lookup in an
+ * input device using sparse keymap.
+ */
+struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
+                                                  unsigned int keycode)
+{
+       struct key_entry *key;
+
+       for (key = dev->keycode; key->type != KE_END; key++)
+               if (key->type == KE_KEY && keycode == key->keycode)
+                       return key;
+
+       return NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
+
+static int sparse_keymap_getkeycode(struct input_dev *dev,
+                                   int scancode, int *keycode)
+{
+       const struct key_entry *key =
+                       sparse_keymap_entry_from_scancode(dev, scancode);
+
+       if (key && key->type == KE_KEY) {
+               *keycode = key->keycode;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int sparse_keymap_setkeycode(struct input_dev *dev,
+                                   int scancode, int keycode)
+{
+       struct key_entry *key;
+       int old_keycode;
+
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+
+       key = sparse_keymap_entry_from_scancode(dev, scancode);
+       if (key && key->type == KE_KEY) {
+               old_keycode = key->keycode;
+               key->keycode = keycode;
+               set_bit(keycode, dev->keybit);
+               if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
+                       clear_bit(old_keycode, dev->keybit);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * sparse_keymap_setup - set up sparse keymap for an input device
+ * @dev: Input device
+ * @keymap: Keymap in form of array of &key_entry structures ending
+ *     with %KE_END type entry
+ * @setup: Function that can be used to adjust keymap entries
+ *     depending on device's deeds, may be %NULL
+ *
+ * The function calculates size and allocates copy of the original
+ * keymap after which sets up input device event bits appropriately.
+ * Before destroying input device allocated keymap should be freed
+ * with a call to sparse_keymap_free().
+ */
+int sparse_keymap_setup(struct input_dev *dev,
+                       const struct key_entry *keymap,
+                       int (*setup)(struct input_dev *, struct key_entry *))
+{
+       size_t map_size = 1; /* to account for the last KE_END entry */
+       const struct key_entry *e;
+       struct key_entry *map, *entry;
+       int i;
+       int error;
+
+       for (e = keymap; e->type != KE_END; e++)
+               map_size++;
+
+       map = kcalloc(map_size, sizeof (struct key_entry), GFP_KERNEL);
+       if (!map)
+               return -ENOMEM;
+
+       memcpy(map, keymap, map_size * sizeof (struct key_entry));
+
+       for (i = 0; i < map_size; i++) {
+               entry = &map[i];
+
+               if (setup) {
+                       error = setup(dev, entry);
+                       if (error)
+                               goto err_out;
+               }
+
+               switch (entry->type) {
+               case KE_KEY:
+                       __set_bit(EV_KEY, dev->evbit);
+                       __set_bit(entry->keycode, dev->keybit);
+                       break;
+
+               case KE_SW:
+                       __set_bit(EV_SW, dev->evbit);
+                       __set_bit(entry->sw.code, dev->swbit);
+                       break;
+               }
+       }
+
+       dev->keycode = map;
+       dev->keycodemax = map_size;
+       dev->getkeycode = sparse_keymap_getkeycode;
+       dev->setkeycode = sparse_keymap_setkeycode;
+
+       return 0;
+
+ err_out:
+       kfree(keymap);
+       return error;
+
+}
+EXPORT_SYMBOL(sparse_keymap_setup);
+
+/**
+ * sparse_keymap_free - free memory allocated for sparse keymap
+ * @dev: Input device using sparse keymap
+ *
+ * This function is used to free memory allocated by sparse keymap
+ * in an input device that was set up by sparse_keymap_setup().
+ */
+void sparse_keymap_free(struct input_dev *dev)
+{
+       kfree(dev->keycode);
+       dev->keycode = NULL;
+       dev->keycodemax = 0;
+       dev->getkeycode = NULL;
+       dev->setkeycode = NULL;
+}
+EXPORT_SYMBOL(sparse_keymap_free);
+
+/**
+ * sparse_keymap_report_entry - report event corresponding to given key entry
+ * @dev: Input device for which event should be reported
+ * @ke: key entry describing event
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ *     entries right after reporting press event, ignored by all other
+ *     entries
+ *
+ * This function is used to report input event described by given
+ * &struct key_entry.
+ */
+void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
+                               unsigned int value, bool autorelease)
+{
+       switch (ke->type) {
+       case KE_KEY:
+               input_report_key(dev, ke->keycode, value);
+               input_sync(dev);
+               if (value && autorelease) {
+                       input_report_key(dev, ke->keycode, 0);
+                       input_sync(dev);
+               }
+               break;
+
+       case KE_SW:
+               value = ke->sw.value;
+               /* fall through */
+
+       case KE_VSW:
+               input_report_switch(dev, ke->sw.code, value);
+               break;
+       }
+}
+EXPORT_SYMBOL(sparse_keymap_report_entry);
+
+/**
+ * sparse_keymap_report_event - report event corresponding to given scancode
+ * @dev: Input device using sparse keymap
+ * @code: Scan code
+ * @value: Value that should be reported (ignored by %KE_SW entries)
+ * @autorelease: Signals whether release event should be emitted for %KE_KEY
+ *     entries right after reporting press event, ignored by all other
+ *     entries
+ *
+ * This function is used to perform lookup in an input device using sparse
+ * keymap and report corresponding event. Returns %true if lookup was
+ * successful and %false otherwise.
+ */
+bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
+                               unsigned int value, bool autorelease)
+{
+       const struct key_entry *ke =
+               sparse_keymap_entry_from_scancode(dev, code);
+
+       if (ke) {
+               sparse_keymap_report_entry(dev, ke, value, autorelease);
+               return true;
+       }
+
+       return false;
+}
+EXPORT_SYMBOL(sparse_keymap_report_event);
+
index 8cc453c..32fc8ba 100644 (file)
@@ -111,6 +111,18 @@ config TOUCHSCREEN_DA9034
          Say Y here to enable the support for the touchscreen found
          on Dialog Semiconductor DA9034 PMIC.
 
+config TOUCHSCREEN_DYNAPRO
+       tristate "Dynapro serial touchscreen"
+       select SERIO
+       help
+         Say Y here if you have a Dynapro serial touchscreen connected to
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called dynapro.
+
 config TOUCHSCREEN_EETI
        tristate "EETI touchscreen panel support"
        depends on I2C
@@ -133,6 +145,18 @@ config TOUCHSCREEN_FUJITSU
          To compile this driver as a module, choose M here: the
          module will be called fujitsu-ts.
 
+config TOUCHSCREEN_S3C2410
+       tristate "Samsung S3C2410 touchscreen input driver"
+       depends on ARCH_S3C2410
+       select S3C24XX_ADC
+       help
+         Say Y here if you have the s3c2410 touchscreen.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called s3c2410_ts.
+
 config TOUCHSCREEN_GUNZE
        tristate "Gunze AHL-51S touchscreen"
        select SERIO
@@ -297,7 +321,7 @@ config TOUCHSCREEN_TOUCHWIN
 
 config TOUCHSCREEN_ATMEL_TSADCC
        tristate "Atmel Touchscreen Interface"
-       depends on ARCH_AT91SAM9RL
+       depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
        help
          Say Y here if you have a 4-wire touchscreen connected to the
           ADC Controller on your Atmel SoC (such as the AT91SAM9RL).
@@ -418,6 +442,7 @@ config TOUCHSCREEN_USB_COMPOSITE
          - IdealTEK URTC1000
          - GoTop Super_Q2/GogoPen/PenPower tablets
          - JASTEC USB Touch Controller/DigiTech DTR-02U
+         - Zytronic controllers
 
          Have a look at <http://linux.chapter7.ch/touchkit/> for
          a usage description and the required user-space stuff.
@@ -490,6 +515,16 @@ config TOUCHSCREEN_USB_E2I
        bool "e2i Touchscreen controller (e.g. from Mimo 740)"
        depends on TOUCHSCREEN_USB_COMPOSITE
 
+config TOUCHSCREEN_USB_ZYTRONIC
+       default y
+       bool "Zytronic controller" if EMBEDDED
+       depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_ETT_TC5UH
+       default y
+       bool "ET&T TC5UH touchscreen controler support" if EMBEDDED
+       depends on TOUCHSCREEN_USB_COMPOSITE
+
 config TOUCHSCREEN_TOUCHIT213
        tristate "Sahara TouchIT-213 touchscreen"
        select SERIO
index 15fa62c..f1f59c9 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846)     += ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)                += h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_CORGI)                += corgi_ts.o
+obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)      += dynapro.o
 obj-$(CONFIG_TOUCHSCREEN_GUNZE)                += gunze.o
 obj-$(CONFIG_TOUCHSCREEN_EETI)         += eeti_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)          += elo.o
@@ -25,7 +26,9 @@ obj-$(CONFIG_TOUCHSCREEN_HP600)               += hp680_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_HP7XX)                += jornada720_ts.o
 obj-$(CONFIG_TOUCHSCREEN_HTCPEN)       += htcpen.o
 obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)        += usbtouchscreen.o
+obj-$(CONFIG_TOUCHSCREEN_PCAP)         += pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)     += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_S3C2410)      += s3c2410_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
@@ -41,4 +44,3 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL)        += atmel-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)     += mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)      += w90p910_ts.o
-obj-$(CONFIG_TOUCHSCREEN_PCAP)         += pcap_ts.o
index 09c8109..52d2ca1 100644 (file)
 #include <linux/spi/ads7846.h>
 #include <asm/irq.h>
 
-
 /*
  * This code has been heavily tested on a Nokia 770, and lightly
- * tested on other ads7846 devices (OSK/Mistral, Lubbock).
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock, Spitz).
  * TSC2046 is just newer ads7846 silicon.
  * Support for ads7843 tested on Atmel at91sam926x-EK.
  * Support for ads7845 has only been stubbed in.
@@ -43,7 +42,7 @@
  * have to maintain our own SW IRQ disabled status. This should be
  * removed as soon as the affected platform's IRQ handling is fixed.
  *
- * app note sbaa036 talks in more detail about accurate sampling...
+ * App note sbaa036 talks in more detail about accurate sampling...
  * that ought to help in situations like LCDs inducing noise (which
  * can also be helped by using synch signals) and more generally.
  * This driver tries to utilize the measures described in the app
@@ -566,10 +565,8 @@ static void ads7846_rx(void *ads)
         * once more the measurement
         */
        if (packet->tc.ignore || Rt > ts->pressure_max) {
-#ifdef VERBOSE
-               pr_debug("%s: ignored %d pressure %d\n",
-                       dev_name(&ts->spi->dev), packet->tc.ignore, Rt);
-#endif
+               dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
+                        packet->tc.ignore, Rt);
                hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
                              HRTIMER_MODE_REL);
                return;
@@ -598,9 +595,7 @@ static void ads7846_rx(void *ads)
                if (!ts->pendown) {
                        input_report_key(input, BTN_TOUCH, 1);
                        ts->pendown = 1;
-#ifdef VERBOSE
-                       dev_dbg(&ts->spi->dev, "DOWN\n");
-#endif
+                       dev_vdbg(&ts->spi->dev, "DOWN\n");
                }
 
                if (ts->swap_xy)
@@ -608,12 +603,10 @@ static void ads7846_rx(void *ads)
 
                input_report_abs(input, ABS_X, x);
                input_report_abs(input, ABS_Y, y);
-               input_report_abs(input, ABS_PRESSURE, Rt);
+               input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
 
                input_sync(input);
-#ifdef VERBOSE
-               dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
-#endif
+               dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
        }
 
        hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
@@ -723,9 +716,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
                        input_sync(input);
 
                        ts->pendown = 0;
-#ifdef VERBOSE
-                       dev_dbg(&ts->spi->dev, "UP\n");
-#endif
+                       dev_vdbg(&ts->spi->dev, "UP\n");
                }
 
                /* measurement cycle ended */
index 35377f5..a12242f 100644 (file)
@@ -59,7 +59,7 @@
 #define ATMEL_WM97XX_AC97C_IRQ         (29)
 #define ATMEL_WM97XX_GPIO_DEFAULT      (32+16) /* Pin 16 on port B. */
 #else
-#error Unkown CPU, this driver only supports AT32AP700X CPUs.
+#error Unknown CPU, this driver only supports AT32AP700X CPUs.
 #endif
 
 struct continuous {
index 9c7fce4..3d9b516 100644 (file)
@@ -22,6 +22,8 @@
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
 
 /* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */
 
@@ -36,7 +38,9 @@
 #define          ATMEL_TSADCC_LOWRES   (1    <<  4)    /* Resolution selection */
 #define          ATMEL_TSADCC_SLEEP    (1    <<  5)    /* Sleep mode */
 #define          ATMEL_TSADCC_PENDET   (1    <<  6)    /* Pen Detect selection */
+#define          ATMEL_TSADCC_PRES     (1    <<  7)    /* Pressure Measurement Selection */
 #define          ATMEL_TSADCC_PRESCAL  (0x3f <<  8)    /* Prescalar Rate Selection */
+#define          ATMEL_TSADCC_EPRESCAL (0xff <<  8)    /* Prescalar Rate Selection (Extended) */
 #define          ATMEL_TSADCC_STARTUP  (0x7f << 16)    /* Start Up time */
 #define          ATMEL_TSADCC_SHTIM    (0xf  << 24)    /* Sample & Hold time */
 #define          ATMEL_TSADCC_PENDBC   (0xf  << 28)    /* Pen Detect debouncing time */
 #define ATMEL_TSADCC_CDR4      0x40    /* Channel Data 4 */
 #define ATMEL_TSADCC_CDR5      0x44    /* Channel Data 5 */
 
-#define ADC_CLOCK      1000000
+#define ATMEL_TSADCC_XPOS      0x50
+#define ATMEL_TSADCC_Z1DAT     0x54
+#define ATMEL_TSADCC_Z2DAT     0x58
+
+#define PRESCALER_VAL(x)       ((x) >> 8)
+
+#define ADC_DEFAULT_CLOCK      100000
 
 struct atmel_tsadcc {
        struct input_dev        *input;
@@ -172,6 +182,7 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
        struct atmel_tsadcc     *ts_dev;
        struct input_dev        *input_dev;
        struct resource         *res;
+       struct at91_tsadcc_data *pdata = pdev->dev.platform_data;
        int             err = 0;
        unsigned int    prsc;
        unsigned int    reg;
@@ -242,31 +253,49 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
        input_dev->phys = ts_dev->phys;
        input_dev->dev.parent = &pdev->dev;
 
-       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-       input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
+       __set_bit(EV_ABS, input_dev->evbit);
        input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0);
        input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0);
 
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
        /* clk_enable() always returns 0, no need to check it */
        clk_enable(ts_dev->clk);
 
        prsc = clk_get_rate(ts_dev->clk);
        dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc);
 
-       prsc = prsc / ADC_CLOCK / 2 - 1;
+       if (!pdata)
+               goto err_fail;
+
+       if (!pdata->adc_clock)
+               pdata->adc_clock = ADC_DEFAULT_CLOCK;
+
+       prsc = (prsc / (2 * pdata->adc_clock)) - 1;
+
+       /* saturate if this value is too high */
+       if (cpu_is_at91sam9rl()) {
+               if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL))
+                       prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL);
+       } else {
+               if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL))
+                       prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL);
+       }
+
+       dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc);
 
        reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE          |
                ((0x00 << 5) & ATMEL_TSADCC_SLEEP)      |       /* Normal Mode */
                ((0x01 << 6) & ATMEL_TSADCC_PENDET)     |       /* Enable Pen Detect */
-               ((prsc << 8) & ATMEL_TSADCC_PRESCAL)    |       /* PRESCAL */
-               ((0x13 << 16) & ATMEL_TSADCC_STARTUP)   |       /* STARTUP */
-               ((0x0F << 28) & ATMEL_TSADCC_PENDBC);           /* PENDBC */
+               (prsc << 8)                             |
+               ((0x26 << 16) & ATMEL_TSADCC_STARTUP)   |
+               ((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC);
 
        atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST);
        atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
        atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
-       atmel_tsadcc_write(ATMEL_TSADCC_TSR, (0x3 << 24) & ATMEL_TSADCC_TSSHTIM);
+       atmel_tsadcc_write(ATMEL_TSADCC_TSR,
+               (pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM);
 
        atmel_tsadcc_read(ATMEL_TSADCC_SR);
        atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
diff --git a/drivers/input/touchscreen/dynapro.c b/drivers/input/touchscreen/dynapro.c
new file mode 100644 (file)
index 0000000..4553539
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Dynapro serial touchscreen driver
+ *
+ * Copyright (c) 2009 Tias Guns
+ * Based on the inexio driver (c) Vojtech Pavlik and Dan Streetman and
+ * Richard Lemon
+ *
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2009/09/19 Tias Guns <tias@ulyssis.org>
+ *   Copied inexio.c and edited for Dynapro protocol (from retired Xorg module)
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "Dynapro serial touchscreen driver"
+
+MODULE_AUTHOR("Tias Guns <tias@ulyssis.org>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define DYNAPRO_FORMAT_TOUCH_BIT 0x40
+#define DYNAPRO_FORMAT_LENGTH 3
+#define DYNAPRO_RESPONSE_BEGIN_BYTE 0x80
+
+#define DYNAPRO_MIN_XC 0
+#define DYNAPRO_MAX_XC 0x3ff
+#define DYNAPRO_MIN_YC 0
+#define DYNAPRO_MAX_YC 0x3ff
+
+#define DYNAPRO_GET_XC(data) (data[1] | ((data[0] & 0x38) << 4))
+#define DYNAPRO_GET_YC(data) (data[2] | ((data[0] & 0x07) << 7))
+#define DYNAPRO_GET_TOUCHED(data) (DYNAPRO_FORMAT_TOUCH_BIT & data[0])
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct dynapro {
+       struct input_dev *dev;
+       struct serio *serio;
+       int idx;
+       unsigned char data[DYNAPRO_FORMAT_LENGTH];
+       char phys[32];
+};
+
+static void dynapro_process_data(struct dynapro *pdynapro)
+{
+       struct input_dev *dev = pdynapro->dev;
+
+       if (DYNAPRO_FORMAT_LENGTH == ++pdynapro->idx) {
+               input_report_abs(dev, ABS_X, DYNAPRO_GET_XC(pdynapro->data));
+               input_report_abs(dev, ABS_Y, DYNAPRO_GET_YC(pdynapro->data));
+               input_report_key(dev, BTN_TOUCH,
+                                DYNAPRO_GET_TOUCHED(pdynapro->data));
+               input_sync(dev);
+
+               pdynapro->idx = 0;
+       }
+}
+
+static irqreturn_t dynapro_interrupt(struct serio *serio,
+               unsigned char data, unsigned int flags)
+{
+       struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+       pdynapro->data[pdynapro->idx] = data;
+
+       if (DYNAPRO_RESPONSE_BEGIN_BYTE & pdynapro->data[0])
+               dynapro_process_data(pdynapro);
+       else
+               dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+                       pdynapro->data[0]);
+
+       return IRQ_HANDLED;
+}
+
+static void dynapro_disconnect(struct serio *serio)
+{
+       struct dynapro *pdynapro = serio_get_drvdata(serio);
+
+       input_get_device(pdynapro->dev);
+       input_unregister_device(pdynapro->dev);
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_put_device(pdynapro->dev);
+       kfree(pdynapro);
+}
+
+/*
+ * dynapro_connect() is the routine that is called when someone adds a
+ * new serio device that supports dynapro protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+
+static int dynapro_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct dynapro *pdynapro;
+       struct input_dev *input_dev;
+       int err;
+
+       pdynapro = kzalloc(sizeof(struct dynapro), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!pdynapro || !input_dev) {
+               err = -ENOMEM;
+               goto fail1;
+       }
+
+       pdynapro->serio = serio;
+       pdynapro->dev = input_dev;
+       snprintf(pdynapro->phys, sizeof(pdynapro->phys),
+                "%s/input0", serio->phys);
+
+       input_dev->name = "Dynapro Serial TouchScreen";
+       input_dev->phys = pdynapro->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_DYNAPRO;
+       input_dev->id.product = 0;
+       input_dev->id.version = 0x0001;
+       input_dev->dev.parent = &serio->dev;
+       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+       input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+       input_set_abs_params(pdynapro->dev, ABS_X,
+                            DYNAPRO_MIN_XC, DYNAPRO_MAX_XC, 0, 0);
+       input_set_abs_params(pdynapro->dev, ABS_Y,
+                            DYNAPRO_MIN_YC, DYNAPRO_MAX_YC, 0, 0);
+
+       serio_set_drvdata(serio, pdynapro);
+
+       err = serio_open(serio, drv);
+       if (err)
+               goto fail2;
+
+       err = input_register_device(pdynapro->dev);
+       if (err)
+               goto fail3;
+
+       return 0;
+
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(pdynapro);
+       return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id dynapro_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_DYNAPRO,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, dynapro_serio_ids);
+
+static struct serio_driver dynapro_drv = {
+       .driver         = {
+               .name   = "dynapro",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = dynapro_serio_ids,
+       .interrupt      = dynapro_interrupt,
+       .connect        = dynapro_connect,
+       .disconnect     = dynapro_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init dynapro_init(void)
+{
+       return serio_register_driver(&dynapro_drv);
+}
+
+static void __exit dynapro_exit(void)
+{
+       serio_unregister_driver(&dynapro_drv);
+}
+
+module_init(dynapro_init);
+module_exit(dynapro_exit);
index 8fc3b08..6cdcf2a 100644 (file)
@@ -14,7 +14,7 @@
  *
  * Notes:
  *     This is a wm97xx extended touch driver to capture touch
- *     data in a continuous manner on the Intel XScale archictecture
+ *     data in a continuous manner on the Intel XScale architecture
  *
  *  Features:
  *       - codecs supported:- WM9705, WM9712, WM9713
@@ -131,7 +131,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
        /* When the AC97 queue has been drained we need to allow time
         * to buffer up samples otherwise we end up spinning polling
         * for samples.  The controller can't have a suitably low
-        * threashold set to use the notifications it gives.
+        * threshold set to use the notifications it gives.
         */
        schedule_timeout_uninterruptible(1);
 
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
new file mode 100644 (file)
index 0000000..6386b44
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * Samsung S3C24XX touchscreen driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the term of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright 2004 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright 2008 Ben Dooks <ben-linux@fluff.org>
+ * Copyright 2009 Simtec Electronics <linux@simtec.co.uk>
+ *
+ * Additional work by Herbert Pötzl <herbert@13thfloor.at> and
+ * Harald Welte <laforge@openmoko.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <plat/adc.h>
+#include <plat/regs-adc.h>
+
+#include <mach/regs-gpio.h>
+#include <mach/ts.h>
+
+#define TSC_SLEEP  (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
+
+#define INT_DOWN       (0)
+#define INT_UP         (1 << 8)
+
+#define WAIT4INT       (S3C2410_ADCTSC_YM_SEN | \
+                        S3C2410_ADCTSC_YP_SEN | \
+                        S3C2410_ADCTSC_XP_SEN | \
+                        S3C2410_ADCTSC_XY_PST(3))
+
+#define AUTOPST                (S3C2410_ADCTSC_YM_SEN | \
+                        S3C2410_ADCTSC_YP_SEN | \
+                        S3C2410_ADCTSC_XP_SEN | \
+                        S3C2410_ADCTSC_AUTO_PST | \
+                        S3C2410_ADCTSC_XY_PST(0))
+
+/* Per-touchscreen data. */
+
+/**
+ * struct s3c2410ts - driver touchscreen state.
+ * @client: The ADC client we registered with the core driver.
+ * @dev: The device we are bound to.
+ * @input: The input device we registered with the input subsystem.
+ * @clock: The clock for the adc.
+ * @io: Pointer to the IO base.
+ * @xp: The accumulated X position data.
+ * @yp: The accumulated Y position data.
+ * @irq_tc: The interrupt number for pen up/down interrupt
+ * @count: The number of samples collected.
+ * @shift: The log2 of the maximum count to read in one go.
+ */
+struct s3c2410ts {
+       struct s3c_adc_client *client;
+       struct device *dev;
+       struct input_dev *input;
+       struct clk *clock;
+       void __iomem *io;
+       unsigned long xp;
+       unsigned long yp;
+       int irq_tc;
+       int count;
+       int shift;
+};
+
+static struct s3c2410ts ts;
+
+/**
+ * s3c2410_ts_connect - configure gpio for s3c2410 systems
+ *
+ * Configure the GPIO for the S3C2410 system, where we have external FETs
+ * connected to the device (later systems such as the S3C2440 integrate
+ * these into the device).
+*/
+static inline void s3c2410_ts_connect(void)
+{
+       s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON);
+       s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON);
+       s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON);
+       s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON);
+}
+
+/**
+ * get_down - return the down state of the pen
+ * @data0: The data read from ADCDAT0 register.
+ * @data1: The data read from ADCDAT1 register.
+ *
+ * Return non-zero if both readings show that the pen is down.
+ */
+static inline bool get_down(unsigned long data0, unsigned long data1)
+{
+       /* returns true if both data values show stylus down */
+       return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
+               !(data1 & S3C2410_ADCDAT0_UPDOWN));
+}
+
+static void touch_timer_fire(unsigned long data)
+{
+       unsigned long data0;
+       unsigned long data1;
+       bool down;
+
+       data0 = readl(ts.io + S3C2410_ADCDAT0);
+       data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+       down = get_down(data0, data1);
+
+       if (ts.count == (1 << ts.shift)) {
+               ts.xp >>= ts.shift;
+               ts.yp >>= ts.shift;
+
+               dev_dbg(ts.dev, "%s: X=%lu, Y=%lu, count=%d\n",
+                       __func__, ts.xp, ts.yp, ts.count);
+
+               input_report_abs(ts.input, ABS_X, ts.xp);
+               input_report_abs(ts.input, ABS_Y, ts.yp);
+
+               input_report_key(ts.input, BTN_TOUCH, 1);
+               input_sync(ts.input);
+
+               ts.xp = 0;
+               ts.yp = 0;
+               ts.count = 0;
+       }
+
+       if (down) {
+               s3c_adc_start(ts.client, 0, 1 << ts.shift);
+       } else {
+               ts.count = 0;
+
+               input_report_key(ts.input, BTN_TOUCH, 0);
+               input_sync(ts.input);
+
+               writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+       }
+}
+
+static DEFINE_TIMER(touch_timer, touch_timer_fire, 0, 0);
+
+/**
+ * stylus_irq - touchscreen stylus event interrupt
+ * @irq: The interrupt number
+ * @dev_id: The device ID.
+ *
+ * Called when the IRQ_TC is fired for a pen up or down event.
+ */
+static irqreturn_t stylus_irq(int irq, void *dev_id)
+{
+       unsigned long data0;
+       unsigned long data1;
+       bool down;
+
+       data0 = readl(ts.io + S3C2410_ADCDAT0);
+       data1 = readl(ts.io + S3C2410_ADCDAT1);
+
+       down = get_down(data0, data1);
+
+       /* TODO we should never get an interrupt with down set while
+        * the timer is running, but maybe we ought to verify that the
+        * timer isn't running anyways. */
+
+       if (down)
+               s3c_adc_start(ts.client, 0, 1 << ts.shift);
+       else
+               dev_info(ts.dev, "%s: count=%d\n", __func__, ts.count);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * s3c24xx_ts_conversion - ADC conversion callback
+ * @client: The client that was registered with the ADC core.
+ * @data0: The reading from ADCDAT0.
+ * @data1: The reading from ADCDAT1.
+ * @left: The number of samples left.
+ *
+ * Called when a conversion has finished.
+ */
+static void s3c24xx_ts_conversion(struct s3c_adc_client *client,
+                                 unsigned data0, unsigned data1,
+                                 unsigned *left)
+{
+       dev_dbg(ts.dev, "%s: %d,%d\n", __func__, data0, data1);
+
+       ts.xp += data0;
+       ts.yp += data1;
+
+       ts.count++;
+
+       /* From tests, it seems that it is unlikely to get a pen-up
+        * event during the conversion process which means we can
+        * ignore any pen-up events with less than the requisite
+        * count done.
+        *
+        * In several thousand conversions, no pen-ups where detected
+        * before count completed.
+        */
+}
+
+/**
+ * s3c24xx_ts_select - ADC selection callback.
+ * @client: The client that was registered with the ADC core.
+ * @select: The reason for select.
+ *
+ * Called when the ADC core selects (or deslects) us as a client.
+ */
+static void s3c24xx_ts_select(struct s3c_adc_client *client, unsigned select)
+{
+       if (select) {
+               writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
+                      ts.io + S3C2410_ADCTSC);
+       } else {
+               mod_timer(&touch_timer, jiffies+1);
+               writel(WAIT4INT | INT_UP, ts.io + S3C2410_ADCTSC);
+       }
+}
+
+/**
+ * s3c2410ts_probe - device core probe entry point
+ * @pdev: The device we are being bound to.
+ *
+ * Initialise, find and allocate any resources we need to run and then
+ * register with the ADC and input systems.
+ */
+static int __devinit s3c2410ts_probe(struct platform_device *pdev)
+{
+       struct s3c2410_ts_mach_info *info;
+       struct device *dev = &pdev->dev;
+       struct input_dev *input_dev;
+       struct resource *res;
+       int ret = -EINVAL;
+
+       /* Initialise input stuff */
+       memset(&ts, 0, sizeof(struct s3c2410ts));
+
+       ts.dev = dev;
+
+       info = pdev->dev.platform_data;
+       if (!info) {
+               dev_err(dev, "no platform data, cannot attach\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(dev, "initialising touchscreen\n");
+
+       ts.clock = clk_get(dev, "adc");
+       if (IS_ERR(ts.clock)) {
+               dev_err(dev, "cannot get adc clock source\n");
+               return -ENOENT;
+       }
+
+       clk_enable(ts.clock);
+       dev_dbg(dev, "got and enabled clocks\n");
+
+       ts.irq_tc = ret = platform_get_irq(pdev, 0);
+       if (ret < 0) {
+               dev_err(dev, "no resource for interrupt\n");
+               goto err_clk;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "no resource for registers\n");
+               ret = -ENOENT;
+               goto err_clk;
+       }
+
+       ts.io = ioremap(res->start, resource_size(res));
+       if (ts.io == NULL) {
+               dev_err(dev, "cannot map registers\n");
+               ret = -ENOMEM;
+               goto err_clk;
+       }
+
+       /* Configure the touchscreen external FETs on the S3C2410 */
+       if (!platform_get_device_id(pdev)->driver_data)
+               s3c2410_ts_connect();
+
+       ts.client = s3c_adc_register(pdev, s3c24xx_ts_select,
+                                    s3c24xx_ts_conversion, 1);
+       if (IS_ERR(ts.client)) {
+               dev_err(dev, "failed to register adc client\n");
+               ret = PTR_ERR(ts.client);
+               goto err_iomap;
+       }
+
+       /* Initialise registers */
+       if ((info->delay & 0xffff) > 0)
+               writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+       writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(dev, "Unable to allocate the input device !!\n");
+               ret = -ENOMEM;
+               goto err_iomap;
+       }
+
+       ts.input = input_dev;
+       ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+       ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+       input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
+       input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
+
+       ts.input->name = "S3C24XX TouchScreen";
+       ts.input->id.bustype = BUS_HOST;
+       ts.input->id.vendor = 0xDEAD;
+       ts.input->id.product = 0xBEEF;
+       ts.input->id.version = 0x0102;
+
+       ts.shift = info->oversampling_shift;
+
+       ret = request_irq(ts.irq_tc, stylus_irq, IRQF_DISABLED,
+                         "s3c2410_ts_pen", ts.input);
+       if (ret) {
+               dev_err(dev, "cannot get TC interrupt\n");
+               goto err_inputdev;
+       }
+
+       dev_info(dev, "driver attached, registering input device\n");
+
+       /* All went ok, so register to the input system */
+       ret = input_register_device(ts.input);
+       if (ret < 0) {
+               dev_err(dev, "failed to register input device\n");
+               ret = -EIO;
+               goto err_tcirq;
+       }
+
+       return 0;
+
+ err_tcirq:
+       free_irq(ts.irq_tc, ts.input);
+ err_inputdev:
+       input_unregister_device(ts.input);
+ err_iomap:
+       iounmap(ts.io);
+ err_clk:
+       del_timer_sync(&touch_timer);
+       clk_put(ts.clock);
+       return ret;
+}
+
+/**
+ * s3c2410ts_remove - device core removal entry point
+ * @pdev: The device we are being removed from.
+ *
+ * Free up our state ready to be removed.
+ */
+static int __devexit s3c2410ts_remove(struct platform_device *pdev)
+{
+       free_irq(ts.irq_tc, ts.input);
+       del_timer_sync(&touch_timer);
+
+       clk_disable(ts.clock);
+       clk_put(ts.clock);
+
+       input_unregister_device(ts.input);
+       iounmap(ts.io);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c2410ts_suspend(struct device *dev)
+{
+       writel(TSC_SLEEP, ts.io + S3C2410_ADCTSC);
+       disable_irq(ts.irq_tc);
+       clk_disable(ts.clock);
+
+       return 0;
+}
+
+static int s3c2410ts_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct s3c2410_ts_mach_info *info = pdev->dev.platform_data;
+
+       clk_enable(ts.clock);
+
+       /* Initialise registers */
+       if ((info->delay & 0xffff) > 0)
+               writel(info->delay & 0xffff, ts.io + S3C2410_ADCDLY);
+
+       writel(WAIT4INT | INT_DOWN, ts.io + S3C2410_ADCTSC);
+
+       return 0;
+}
+
+static struct dev_pm_ops s3c_ts_pmops = {
+       .suspend        = s3c2410ts_suspend,
+       .resume         = s3c2410ts_resume,
+};
+#endif
+
+static struct platform_device_id s3cts_driver_ids[] = {
+       { "s3c2410-ts", 0 },
+       { "s3c2440-ts", 1 },
+       { }
+};
+MODULE_DEVICE_TABLE(platform, s3cts_driver_ids);
+
+static struct platform_driver s3c_ts_driver = {
+       .driver         = {
+               .name   = "s3c24xx-ts",
+               .owner  = THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm     = &s3c_ts_pmops,
+#endif
+       },
+       .id_table       = s3cts_driver_ids,
+       .probe          = s3c2410ts_probe,
+       .remove         = __devexit_p(s3c2410ts_remove),
+};
+
+static int __init s3c2410ts_init(void)
+{
+       return platform_driver_register(&s3c_ts_driver);
+}
+
+static void __exit s3c2410ts_exit(void)
+{
+       platform_driver_unregister(&s3c_ts_driver);
+}
+
+module_init(s3c2410ts_init);
+module_exit(s3c2410ts_exit);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>, "
+             "Ben Dooks <ben@simtec.co.uk>, "
+             "Simtec Electronics <linux@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C24XX Touchscreen driver");
+MODULE_LICENSE("GPL v2");
index 095f84b..89dcbe7 100644 (file)
@@ -355,10 +355,13 @@ static int ucb1400_ts_probe(struct platform_device *dev)
                goto err;
        }
 
-       error = ucb1400_ts_detect_irq(ucb);
-       if (error) {
-               printk(KERN_ERR "UCB1400: IRQ probe failed\n");
-               goto err_free_devs;
+       /* Only in case the IRQ line wasn't supplied, try detecting it */
+       if (ucb->irq < 0) {
+               error = ucb1400_ts_detect_irq(ucb);
+               if (error) {
+                       printk(KERN_ERR "UCB1400: IRQ probe failed\n");
+                       goto err_free_devs;
+               }
        }
 
        init_waitqueue_head(&ucb->ts_wait);
index 68ece58..09a5e73 100644 (file)
@@ -14,6 +14,7 @@
  *  - General Touch
  *  - GoTop Super_Q2/GogoPen/PenPower tablets
  *  - JASTEC USB touch controller/DigiTech DTR-02U
+ *  - Zytronic capacitive touchscreen
  *
  * Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
  * Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -73,6 +74,15 @@ struct usbtouch_device_info {
        int min_press, max_press;
        int rept_size;
 
+       /*
+        * Always service the USB devices irq not just when the input device is
+        * open. This is useful when devices have a watchdog which prevents us
+        * from periodically polling the device. Leave this unset unless your
+        * touchscreen device requires it, as it does consume more of the USB
+        * bandwidth.
+        */
+       bool irq_always;
+
        void (*process_pkt) (struct usbtouch_usb *usbtouch, unsigned char *pkt, int len);
 
        /*
@@ -121,6 +131,8 @@ enum {
        DEVTYPE_GOTOP,
        DEVTYPE_JASTEC,
        DEVTYPE_E2I,
+       DEVTYPE_ZYTRONIC,
+       DEVTYPE_TC5UH,
 };
 
 #define USB_DEVICE_HID_CLASS(vend, prod) \
@@ -201,6 +213,15 @@ static struct usb_device_id usbtouch_devices[] = {
 #ifdef CONFIG_TOUCHSCREEN_USB_E2I
        {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I},
 #endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+       {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+       {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH},
+#endif
+
        {}
 };
 
@@ -538,6 +559,19 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 }
 #endif
 
+/*****************************************************************************
+ * ET&T TC5UH part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+       dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
+       dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
+       dev->touch = pkt[0] & 0x01;
+
+       return 1;
+}
+#endif
 
 /*****************************************************************************
  * IdealTEK URTC1000 Part
@@ -621,6 +655,39 @@ static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 }
 #endif
 
+/*****************************************************************************
+ * Zytronic Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+static int zytronic_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+       switch (pkt[0]) {
+       case 0x3A: /* command response */
+               dbg("%s: Command response %d", __func__, pkt[1]);
+               break;
+
+       case 0xC0: /* down */
+               dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+               dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+               dev->touch = 1;
+               dbg("%s: down %d,%d", __func__, dev->x, dev->y);
+               return 1;
+
+       case 0x80: /* up */
+               dev->x = (pkt[1] & 0x7f) | ((pkt[2] & 0x07) << 7);
+               dev->y = (pkt[3] & 0x7f) | ((pkt[4] & 0x07) << 7);
+               dev->touch = 0;
+               dbg("%s: up %d,%d", __func__, dev->x, dev->y);
+               return 1;
+
+       default:
+               dbg("%s: Unknown return %d", __func__, pkt[0]);
+               break;
+       }
+
+       return 0;
+}
+#endif
 
 /*****************************************************************************
  * the different device descriptors
@@ -783,6 +850,29 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
                .read_data      = e2i_read_data,
        },
 #endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ZYTRONIC
+       [DEVTYPE_ZYTRONIC] = {
+               .min_xc         = 0x0,
+               .max_xc         = 0x03ff,
+               .min_yc         = 0x0,
+               .max_yc         = 0x03ff,
+               .rept_size      = 5,
+               .read_data      = zytronic_read_data,
+               .irq_always     = true,
+       },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
+       [DEVTYPE_TC5UH] = {
+               .min_xc         = 0x0,
+               .max_xc         = 0x0fff,
+               .min_yc         = 0x0,
+               .max_yc         = 0x0fff,
+               .rept_size      = 5,
+               .read_data      = tc5uh_read_data,
+       },
+#endif
 };
 
 
@@ -933,8 +1023,10 @@ static int usbtouch_open(struct input_dev *input)
 
        usbtouch->irq->dev = usbtouch->udev;
 
-       if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
-               return -EIO;
+       if (!usbtouch->type->irq_always) {
+               if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
+                 return -EIO;
+       }
 
        return 0;
 }
@@ -943,7 +1035,8 @@ static void usbtouch_close(struct input_dev *input)
 {
        struct usbtouch_usb *usbtouch = input_get_drvdata(input);
 
-       usb_kill_urb(usbtouch->irq);
+       if (!usbtouch->type->irq_always)
+               usb_kill_urb(usbtouch->irq);
 }
 
 
@@ -1066,6 +1159,9 @@ static int usbtouch_probe(struct usb_interface *intf,
 
        usb_set_intfdata(intf, usbtouch);
 
+       if (usbtouch->type->irq_always)
+               usb_submit_urb(usbtouch->irq, GFP_KERNEL);
+
        return 0;
 
 out_free_buffers:
@@ -1087,7 +1183,7 @@ static void usbtouch_disconnect(struct usb_interface *intf)
 
        dbg("%s - usbtouch is initialized, cleaning up", __func__);
        usb_set_intfdata(intf, NULL);
-       usb_kill_urb(usbtouch->irq);
+       /* this will stop IO via close */
        input_unregister_device(usbtouch->input);
        usb_free_urb(usbtouch->irq);
        usbtouch_free_buffers(interface_to_usbdev(intf), usbtouch);
index 41e4359..eca54db 100644 (file)
@@ -96,7 +96,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
        /* When the AC97 queue has been drained we need to allow time
         * to buffer up samples otherwise we end up spinning polling
         * for samples.  The controller can't have a suitably low
-        * threashold set to use the notifications it gives.
+        * threshold set to use the notifications it gives.
         */
        msleep(1);
 
index 3fc1a54..fa2673f 100644 (file)
@@ -1,6 +1,3 @@
-#
-# Config.in for IBM Active 2000 ISDN driver
-#
 config ISDN_DRV_ACT2000
        tristate "IBM Active 2000 support"
        depends on ISA
@@ -10,4 +7,3 @@ config ISDN_DRV_ACT2000
          into the card using a utility which is part of the latest
          isdn4k-utils package. Please read the file
          <file:Documentation/isdn/README.act2000> for more information.
-
index e1afd60..b2a0475 100644 (file)
@@ -1,6 +1,3 @@
-#
-# Config.in for the CAPI subsystem
-#
 config ISDN_DRV_AVMB1_VERBOSE_REASON
        bool "Verbose reason code reporting"
        default y
@@ -59,4 +56,3 @@ config ISDN_CAPI_CAPIDRV
          the legacy isdn4linux link layer.  If you have a card which is
          supported by a CAPI driver, but still want to use old features like
          ippp interfaces or ttyI emulation, say Y/M here.
-
index 3e6d17f..66b7d7a 100644 (file)
@@ -830,7 +830,7 @@ static void handle_controller(_cmsg * cmsg)
                      case 0: break;
                      case 1: s = "unknown class"; break;
                      case 2: s = "unknown function"; break;
-                     default: s = "unkown error"; break;
+                     default: s = "unknown error"; break;
                   }
                   if (s)
                   printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
index b029d13..cb14ae3 100644 (file)
@@ -806,7 +806,7 @@ static void xdi_xlog_request (byte Adapter, byte Id,
           DELIVERY - indication entered isdn_rc function
           RNR=...  - application had returned RNR=... after the
                      look ahead callback
-          RNum=0   - aplication had not returned any buffer to copy
+          RNum=0   - application had not returned any buffer to copy
                      this indication and will copy it self
           COMPLETE - XDI had copied the data to the buffers provided
                      bu the application and is about to issue the
index 41c26e7..534978b 100644 (file)
@@ -385,7 +385,7 @@ static int SuperTraceMessageInput (void* hLib) {
                   }
                   break;
                 default:
-                  diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind (DMA mode): %02x", Ind);
+                  diva_mnt_internal_dprintf (0, DLI_ERR, "Unknown IDI Ind (DMA mode): %02x", Ind);
               }
               p += (this_ind_length+1);
               total_length -= (4 + this_ind_length);
@@ -420,7 +420,7 @@ static int SuperTraceMessageInput (void* hLib) {
             }
             break;
           default:
-            diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind: %02x", Ind);
+            diva_mnt_internal_dprintf (0, DLI_ERR, "Unknown IDI Ind: %02x", Ind);
         }
       }
     }
index fc46a26..a64bb6c 100644 (file)
@@ -721,7 +721,7 @@ hfcsusb_setup_bch(struct bchannel *bch, int protocol)
        switch (protocol) {
        case (-1):      /* used for init */
                bch->state = -1;
-               /* fall trough */
+               /* fall through */
        case (ISDN_P_NONE):
                if (bch->state == ISDN_P_NONE)
                        return 0; /* already in idle state */
index 43efe73..369196a 100644 (file)
@@ -150,7 +150,7 @@ symbolic(struct hfcusb_symbolic_list list[], const int num)
        for (i = 0; list[i].name != NULL; i++)
                if (list[i].num == num)
                        return list[i].name;
-       return "<unkown USB Error>";
+       return "<unknown USB Error>";
 }
 
 /* USB descriptor need to contain one of the following EndPoint combination: */
index de352a1..09095c7 100644 (file)
@@ -860,7 +860,7 @@ isar_pump_statev_modem(struct isar_ch *ch, u8 devt) {
                pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name);
                break;
        default:
-               pr_info("u%s: nknown pump stev %x\n", ch->is->name, devt);
+               pr_info("u%s: unknown pump stev %x\n", ch->is->name, devt);
                break;
        }
 }
index a420b64..aaaeaaf 100644 (file)
@@ -1086,7 +1086,7 @@ hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg)
                        break;
                default:
                        DBG(HFCUSB_DBG_STATES,
-                              "HFC_USB: hfc_usb_d_l2l1: unkown state : %#x", pr);
+                              "HFC_USB: hfc_usb_d_l2l1: unknown state : %#x", pr);
                        break;
        }
 }
index c9e4231..e86bc65 100644 (file)
@@ -1,6 +1,3 @@
-#
-# Config.in for HYSDN ISDN driver
-#
 config HYSDN
        tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
        depends on m && PROC_FS && PCI
@@ -15,4 +12,3 @@ config HYSDN_CAPI
        depends on HYSDN && ISDN_CAPI
        help
          Say Y here if you like to use Hypercope's CAPI 2.0 interface.
-
index 90b56ed..507e13d 100644 (file)
@@ -1563,7 +1563,7 @@ isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
        *(__be32 *)(p +  4) = cpu_to_be32(CISCO_SLARP_KEEPALIVE);
        *(__be32 *)(p +  8) = cpu_to_be32(lp->cisco_myseq);
        *(__be32 *)(p + 12) = cpu_to_be32(lp->cisco_yourseq);
-       *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliablity, always 0xffff
+       *(__be16 *)(p + 16) = cpu_to_be16(0xffff); // reliability, always 0xffff
        p += 18;
 
        isdn_net_write_super(lp, skb);
index 642d5aa..45df667 100644 (file)
@@ -836,7 +836,7 @@ isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
                        unsigned short hl;
                        struct sk_buff *skb;
                        /*
-                        * we need to reserve enought space in front of
+                        * we need to reserve enough space in front of
                         * sk_buff. old call to dev_alloc_skb only reserved
                         * 16 bytes, now we are looking what the driver want
                         */
@@ -1326,7 +1326,7 @@ isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
                struct sk_buff *new_skb;
                unsigned short hl;
                /*
-                * we need to reserve enought space in front of
+                * we need to reserve enough space in front of
                 * sk_buff. old call to dev_alloc_skb only reserved
                 * 16 bytes, now we are looking what the driver want.
                 */
@@ -1674,7 +1674,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
         * - insert new fragment into the proper sequence slot (once that's done
         *   newfrag will be set to NULL)
         * - reassemble any complete fragment sequence (non-null 'start'
-        *   indicates there is a continguous sequence present)
+        *   indicates there is a contiguous sequence present)
         * - discard any incomplete sequences that are below minseq -- due
         *   to the fact that sender always increment sequence number, if there
         *   is an incomplete sequence below minseq, no new fragments would
index 78f7660..4c41f19 100644 (file)
@@ -470,7 +470,7 @@ isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
                }
                return 0;
        }
-       /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
+       /* BADMUL=value - dummy 0=disable errorchk disabled (threshold multiplier) */
        if (!strncmp(p[0], "BADMUL", 6)) {
                p[0] += 6;
                switch (*p[0]) {
index 89d15ee..4534f21 100644 (file)
@@ -1,6 +1,3 @@
-#
-# Config.in for ICN ISDN driver
-#
 config ISDN_DRV_ICN
        tristate "ICN 2B and 4B support"
        depends on ISA
@@ -13,4 +10,3 @@ config ISDN_DRV_ICN
          separately.  See <file:Documentation/isdn/README> and
          <file:Documentation/isdn/README.icn> for more
          information.
-
index 77ee286..43ff4d3 100644 (file)
  * crossconnections and conferences via software if not possible through
  * hardware. If hardware capability is available, hardware is used.
  *
- * Echo: Is generated by CMX and is used to check performane of hard and
+ * Echo: Is generated by CMX and is used to check performance of hard and
  * software CMX.
  *
  * The CMX has special functions for conferences with one, two and more
index e04bad6..6d4da60 100644 (file)
@@ -725,7 +725,7 @@ tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg)
        if (tm->rcnt == 1) {
                if (*debug & DEBUG_L2_TEI)
                        tm->tei_m.printdebug(fi,
-                           "check req for tei %d sucessful\n", tm->l2->tei);
+                           "check req for tei %d successful\n", tm->l2->tei);
                mISDN_FsmChangeState(fi, ST_TEI_NOP);
        } else if (tm->rcnt > 1) {
                /* duplicate assignment; remove */
index ffba6ec..e9b2dd8 100644 (file)
@@ -1,6 +1,3 @@
-#
-# Config.in for PCBIT ISDN driver
-#
 config ISDN_DRV_PCBIT
        tristate "PCBIT-D support"
        depends on ISA && (BROKEN || X86)
@@ -11,4 +8,3 @@ config ISDN_DRV_PCBIT
          the card using a utility which is distributed separately.  See
          <file:Documentation/isdn/README> and
          <file:Documentation/isdn/README.pcbit> for more information.
-
index e6510ca..7469863 100644 (file)
@@ -1,6 +1,3 @@
-#
-# Config.in for Spellcaster ISDN driver
-#
 config ISDN_DRV_SC
        tristate "Spellcaster support"
        depends on ISA
@@ -9,4 +6,3 @@ config ISDN_DRV_SC
          driver currently builds only in a modularized version.
          To build it, choose M here: the module will be called sc.
          See <file:Documentation/isdn/README.sc> for more information.
-
index 8b93644..3fbe41b 100644 (file)
@@ -15,7 +15,7 @@
  *
  *     WARNING: This driver has only been testen on Apple's
  *     1.25 MHz Dual G4 (March 03). It is tuned for a CPU
- *     temperatur around 57 C.
+ *     temperature around 57 C.
  *
  *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
  *
index ed10381..e412980 100644 (file)
@@ -988,7 +988,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                goto bad_cipher;
        }
 
-       /* Compatiblity mode for old dm-crypt cipher strings */
+       /* Compatibility mode for old dm-crypt cipher strings */
        if (!chainmode || (strcmp(chainmode, "plain") == 0 && !ivmode)) {
                chainmode = "cbc";
                ivmode = "plain";
index 32d0b87..dce971d 100644 (file)
@@ -1116,8 +1116,9 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
        return limit_reached;
 }
 
-static void pg_init_done(struct dm_path *path, int errors)
+static void pg_init_done(void *data, int errors)
 {
+       struct dm_path *path = data;
        struct pgpath *pgpath = path_to_pgpath(path);
        struct priority_group *pg = pgpath->pg;
        struct multipath *m = pg->m;
@@ -1183,12 +1184,11 @@ static void pg_init_done(struct dm_path *path, int errors)
 
 static void activate_path(struct work_struct *work)
 {
-       int ret;
        struct pgpath *pgpath =
                container_of(work, struct pgpath, activate_path);
 
-       ret = scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev));
-       pg_init_done(&pgpath->path, ret);
+       scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
+                               pg_init_done, &pgpath->path);
 }
 
 /*
index 351b98b..169b337 100644 (file)
@@ -1,6 +1,6 @@
 saa7146-objs    := saa7146_i2c.o saa7146_core.o
 saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
-ir-common-objs  := ir-functions.o ir-keymaps.o
+ir-common-objs  := ir-functions.o ir-keymaps.o ir-keytable.o
 
 obj-y += tuners/
 obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
index abd4791..e616f62 100644 (file)
@@ -34,22 +34,19 @@ static int repeat = 1;
 module_param(repeat, int, 0444);
 MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)");
 
-static int debug;    /* debug level (0,1,2) */
-module_param(debug, int, 0644);
-
-#define dprintk(level, fmt, arg...)    if (debug >= level) \
-       printk(KERN_DEBUG fmt , ## arg)
+int media_ir_debug;    /* media_ir_debug level (0,1,2) */
+module_param_named(debug, media_ir_debug, int, 0644);
 
 /* -------------------------------------------------------------------------- */
 
 static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
 {
        if (KEY_RESERVED == ir->keycode) {
-               printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n",
-                      dev->name,ir->ir_key,ir->ir_raw,ir->keypressed);
+               printk(KERN_INFO "%s: unknown key: key=0x%02x down=%d\n",
+                      dev->name, ir->ir_key, ir->keypressed);
                return;
        }
-       dprintk(1,"%s: key event code=%d down=%d\n",
+       IR_dprintk(1,"%s: key event code=%d down=%d\n",
                dev->name,ir->keycode,ir->keypressed);
        input_report_key(dev,ir->keycode,ir->keypressed);
        input_sync(dev);
@@ -57,39 +54,34 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
 
 /* -------------------------------------------------------------------------- */
 
-void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
+int ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
                   int ir_type, struct ir_scancode_table *ir_codes)
 {
-       int i;
-
        ir->ir_type = ir_type;
 
-       memset(ir->ir_codes, 0, sizeof(ir->ir_codes));
+       ir->keytable.size = ir_roundup_tablesize(ir_codes->size);
+       ir->keytable.scan = kzalloc(ir->keytable.size *
+                                   sizeof(struct ir_scancode), GFP_KERNEL);
+       if (!ir->keytable.scan)
+               return -ENOMEM;
 
-       /*
-        * FIXME: This is a temporary workaround to use the new IR tables
-        * with the old approach. Later patches will replace this to a
-        * proper method
-        */
+       IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n",
+               ir->keytable.size,
+               ir->keytable.size * sizeof(ir->keytable.scan));
 
-       if (ir_codes)
-               for (i = 0; i < ir_codes->size; i++)
-                       if (ir_codes->scan[i].scancode < IR_KEYTAB_SIZE)
-                               ir->ir_codes[ir_codes->scan[i].scancode] = ir_codes->scan[i].keycode;
+       ir_copy_table(&ir->keytable, ir_codes);
+       ir_set_keycode_table(dev, &ir->keytable);
 
-       dev->keycode     = ir->ir_codes;
-       dev->keycodesize = sizeof(IR_KEYTAB_TYPE);
-       dev->keycodemax  = IR_KEYTAB_SIZE;
-       for (i = 0; i < IR_KEYTAB_SIZE; i++)
-               set_bit(ir->ir_codes[i], dev->keybit);
        clear_bit(0, dev->keybit);
-
        set_bit(EV_KEY, dev->evbit);
        if (repeat)
                set_bit(EV_REP, dev->evbit);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(ir_input_init);
 
+
 void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
 {
        if (ir->keypressed) {
@@ -100,9 +92,9 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
 EXPORT_SYMBOL_GPL(ir_input_nokey);
 
 void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
-                     u32 ir_key, u32 ir_raw)
+                     u32 ir_key)
 {
-       u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key);
+       u32 keycode = ir_g_keycode_from_table(dev, ir_key);
 
        if (ir->keypressed && ir->keycode != keycode) {
                ir->keypressed = 0;
@@ -110,7 +102,6 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
        }
        if (!ir->keypressed) {
                ir->ir_key  = ir_key;
-               ir->ir_raw  = ir_raw;
                ir->keycode = keycode;
                ir->keypressed = 1;
                ir_input_key_event(dev,ir);
@@ -275,7 +266,7 @@ EXPORT_SYMBOL_GPL(ir_decode_biphase);
  * saa7134 */
 
 /* decode raw bit pattern to RC5 code */
-static u32 ir_rc5_decode(unsigned int code)
+u32 ir_rc5_decode(unsigned int code)
 {
        unsigned int org_code = code;
        unsigned int pair;
@@ -295,15 +286,16 @@ static u32 ir_rc5_decode(unsigned int code)
                        rc5 |= 1;
                        break;
                case 3:
-                       dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);
+                       IR_dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code);
                        return 0;
                }
        }
-       dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
+       IR_dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, "
                "instr=%x\n", rc5, org_code, RC5_START(rc5),
                RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5));
        return rc5;
 }
+EXPORT_SYMBOL_GPL(ir_rc5_decode);
 
 void ir_rc5_timer_end(unsigned long data)
 {
@@ -330,20 +322,20 @@ void ir_rc5_timer_end(unsigned long data)
 
        /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */
        if (gap < 28000) {
-               dprintk(1, "ir-common: spurious timer_end\n");
+               IR_dprintk(1, "ir-common: spurious timer_end\n");
                return;
        }
 
        if (ir->last_bit < 20) {
                /* ignore spurious codes (caused by light/other remotes) */
-               dprintk(1, "ir-common: short code: %x\n", ir->code);
+               IR_dprintk(1, "ir-common: short code: %x\n", ir->code);
        } else {
                ir->code = (ir->code << ir->shift_by) | 1;
                rc5 = ir_rc5_decode(ir->code);
 
                /* two start bits? */
                if (RC5_START(rc5) != ir->start) {
-                       dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5));
+                       IR_dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5));
 
                        /* right address? */
                } else if (RC5_ADDR(rc5) == ir->addr) {
@@ -353,11 +345,10 @@ void ir_rc5_timer_end(unsigned long data)
                        /* Good code, decide if repeat/repress */
                        if (toggle != RC5_TOGGLE(ir->last_rc5) ||
                            instr != RC5_INSTR(ir->last_rc5)) {
-                               dprintk(1, "ir-common: instruction %x, toggle %x\n", instr,
+                               IR_dprintk(1, "ir-common: instruction %x, toggle %x\n", instr,
                                        toggle);
                                ir_input_nokey(ir->dev, &ir->ir);
-                               ir_input_keydown(ir->dev, &ir->ir, instr,
-                                                instr);
+                               ir_input_keydown(ir->dev, &ir->ir, instr);
                        }
 
                        /* Set/reset key-up timer */
@@ -376,7 +367,7 @@ void ir_rc5_timer_keyup(unsigned long data)
 {
        struct card_ir *ir = (struct card_ir *)data;
 
-       dprintk(1, "ir-common: key released\n");
+       IR_dprintk(1, "ir-common: key released\n");
        ir_input_nokey(ir->dev, &ir->ir);
 }
 EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup);
index f679017..328c973 100644 (file)
@@ -1705,6 +1705,7 @@ static struct ir_scancode ir_codes_winfast[] = {
        { 0x37, KEY_RADIO },            /* FM */
        { 0x38, KEY_DVD },
 
+       { 0x1a, KEY_MODE},              /* change to MCE mode on Y04G0051 */
        { 0x3e, KEY_F21 },              /* MCE +VOL, on Y04G0033 */
        { 0x3a, KEY_F22 },              /* MCE -VOL, on Y04G0033 */
        { 0x3b, KEY_F23 },              /* MCE +CH,  on Y04G0033 */
@@ -1846,6 +1847,76 @@ struct ir_scancode_table ir_codes_hauppauge_new_table = {
 };
 EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table);
 
+/*
+ * Hauppauge:the newer, gray remotes (seems there are multiple
+ * slightly different versions), shipped with cx88+ivtv cards.
+ *
+ * This table contains the complete RC5 code, instead of just the data part
+ */
+static struct ir_scancode ir_codes_rc5_hauppauge_new[] = {
+       /* Keys 0 to 9 */
+       { 0x1e00, KEY_0 },
+       { 0x1e01, KEY_1 },
+       { 0x1e02, KEY_2 },
+       { 0x1e03, KEY_3 },
+       { 0x1e04, KEY_4 },
+       { 0x1e05, KEY_5 },
+       { 0x1e06, KEY_6 },
+       { 0x1e07, KEY_7 },
+       { 0x1e08, KEY_8 },
+       { 0x1e09, KEY_9 },
+
+       { 0x1e0a, KEY_TEXT },           /* keypad asterisk as well */
+       { 0x1e0b, KEY_RED },            /* red button */
+       { 0x1e0c, KEY_RADIO },
+       { 0x1e0d, KEY_MENU },
+       { 0x1e0e, KEY_SUBTITLE },               /* also the # key */
+       { 0x1e0f, KEY_MUTE },
+       { 0x1e10, KEY_VOLUMEUP },
+       { 0x1e11, KEY_VOLUMEDOWN },
+       { 0x1e12, KEY_PREVIOUS },               /* previous channel */
+       { 0x1e14, KEY_UP },
+       { 0x1e15, KEY_DOWN },
+       { 0x1e16, KEY_LEFT },
+       { 0x1e17, KEY_RIGHT },
+       { 0x1e18, KEY_VIDEO },          /* Videos */
+       { 0x1e19, KEY_AUDIO },          /* Music */
+       /* 0x1e1a: Pictures - presume this means
+          "Multimedia Home Platform" -
+          no "PICTURES" key in input.h
+        */
+       { 0x1e1a, KEY_MHP },
+
+       { 0x1e1b, KEY_EPG },            /* Guide */
+       { 0x1e1c, KEY_TV },
+       { 0x1e1e, KEY_NEXTSONG },               /* skip >| */
+       { 0x1e1f, KEY_EXIT },           /* back/exit */
+       { 0x1e20, KEY_CHANNELUP },      /* channel / program + */
+       { 0x1e21, KEY_CHANNELDOWN },    /* channel / program - */
+       { 0x1e22, KEY_CHANNEL },                /* source (old black remote) */
+       { 0x1e24, KEY_PREVIOUSSONG },   /* replay |< */
+       { 0x1e25, KEY_ENTER },          /* OK */
+       { 0x1e26, KEY_SLEEP },          /* minimize (old black remote) */
+       { 0x1e29, KEY_BLUE },           /* blue key */
+       { 0x1e2e, KEY_GREEN },          /* green button */
+       { 0x1e30, KEY_PAUSE },          /* pause */
+       { 0x1e32, KEY_REWIND },         /* backward << */
+       { 0x1e34, KEY_FASTFORWARD },    /* forward >> */
+       { 0x1e35, KEY_PLAY },
+       { 0x1e36, KEY_STOP },
+       { 0x1e37, KEY_RECORD },         /* recording */
+       { 0x1e38, KEY_YELLOW },         /* yellow key */
+       { 0x1e3b, KEY_SELECT },         /* top right button */
+       { 0x1e3c, KEY_ZOOM },           /* full */
+       { 0x1e3d, KEY_POWER },          /* system power (green button) */
+};
+
+struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = {
+       .scan = ir_codes_rc5_hauppauge_new,
+       .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new),
+};
+EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table);
+
 static struct ir_scancode ir_codes_npgtech[] = {
        { 0x1d, KEY_SWITCHVIDEOMODE },  /* switch inputs */
        { 0x2a, KEY_FRONT },
@@ -2964,6 +3035,101 @@ struct ir_scancode_table ir_codes_dm1105_nec_table = {
 };
 EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table);
 
+static struct ir_scancode ir_codes_tevii_nec[] = {
+       { 0x0a, KEY_POWER2},
+       { 0x0c, KEY_MUTE},
+       { 0x11, KEY_1},
+       { 0x12, KEY_2},
+       { 0x13, KEY_3},
+       { 0x14, KEY_4},
+       { 0x15, KEY_5},
+       { 0x16, KEY_6},
+       { 0x17, KEY_7},
+       { 0x18, KEY_8},
+       { 0x19, KEY_9},
+       { 0x10, KEY_0},
+       { 0x1c, KEY_MENU},
+       { 0x0f, KEY_VOLUMEDOWN},
+       { 0x1a, KEY_LAST},
+       { 0x0e, KEY_OPEN},
+       { 0x04, KEY_RECORD},
+       { 0x09, KEY_VOLUMEUP},
+       { 0x08, KEY_CHANNELUP},
+       { 0x07, KEY_PVR},
+       { 0x0b, KEY_TIME},
+       { 0x02, KEY_RIGHT},
+       { 0x03, KEY_LEFT},
+       { 0x00, KEY_UP},
+       { 0x1f, KEY_OK},
+       { 0x01, KEY_DOWN},
+       { 0x05, KEY_TUNER},
+       { 0x06, KEY_CHANNELDOWN},
+       { 0x40, KEY_PLAYPAUSE},
+       { 0x1e, KEY_REWIND},
+       { 0x1b, KEY_FAVORITES},
+       { 0x1d, KEY_BACK},
+       { 0x4d, KEY_FASTFORWARD},
+       { 0x44, KEY_EPG},
+       { 0x4c, KEY_INFO},
+       { 0x41, KEY_AB},
+       { 0x43, KEY_AUDIO},
+       { 0x45, KEY_SUBTITLE},
+       { 0x4a, KEY_LIST},
+       { 0x46, KEY_F1},
+       { 0x47, KEY_F2},
+       { 0x5e, KEY_F3},
+       { 0x5c, KEY_F4},
+       { 0x52, KEY_F5},
+       { 0x5a, KEY_F6},
+       { 0x56, KEY_MODE},
+       { 0x58, KEY_SWITCHVIDEOMODE},
+};
+struct ir_scancode_table ir_codes_tevii_nec_table = {
+       .scan = ir_codes_tevii_nec,
+       .size = ARRAY_SIZE(ir_codes_tevii_nec),
+};
+EXPORT_SYMBOL_GPL(ir_codes_tevii_nec_table);
+
+static struct ir_scancode ir_codes_tbs_nec[] = {
+       { 0x04, KEY_POWER2},    /*power*/
+       { 0x14, KEY_MUTE},      /*mute*/
+       { 0x07, KEY_1},
+       { 0x06, KEY_2},
+       { 0x05, KEY_3},
+       { 0x0b, KEY_4},
+       { 0x0a, KEY_5},
+       { 0x09, KEY_6},
+       { 0x0f, KEY_7},
+       { 0x0e, KEY_8},
+       { 0x0d, KEY_9},
+       { 0x12, KEY_0},
+       { 0x16, KEY_CHANNELUP}, /*ch+*/
+       { 0x11, KEY_CHANNELDOWN},/*ch-*/
+       { 0x13, KEY_VOLUMEUP},  /*vol+*/
+       { 0x0c, KEY_VOLUMEDOWN},/*vol-*/
+       { 0x03, KEY_RECORD},    /*rec*/
+       { 0x18, KEY_PAUSE},     /*pause*/
+       { 0x19, KEY_OK},        /*ok*/
+       { 0x1a, KEY_CAMERA},    /* snapshot */
+       { 0x01, KEY_UP},
+       { 0x10, KEY_LEFT},
+       { 0x02, KEY_RIGHT},
+       { 0x08, KEY_DOWN},
+       { 0x15, KEY_FAVORITES},
+       { 0x17, KEY_SUBTITLE},
+       { 0x1d, KEY_ZOOM},
+       { 0x1f, KEY_EXIT},
+       { 0x1e, KEY_MENU},
+       { 0x1c, KEY_EPG},
+       { 0x00, KEY_PREVIOUS},
+       { 0x1b, KEY_MODE},
+};
+struct ir_scancode_table ir_codes_tbs_nec_table = {
+       .scan = ir_codes_tbs_nec,
+       .size = ARRAY_SIZE(ir_codes_tbs_nec),
+};
+EXPORT_SYMBOL_GPL(ir_codes_tbs_nec_table);
+
 /* Terratec Cinergy Hybrid T USB XS
    Devin Heitmueller <dheitmueller@linuxtv.org>
  */
@@ -3147,3 +3313,4 @@ struct ir_scancode_table ir_codes_gadmei_rm008z_table = {
        .size = ARRAY_SIZE(ir_codes_gadmei_rm008z),
 };
 EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table);
+
diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c
new file mode 100644 (file)
index 0000000..26ce5bc
--- /dev/null
@@ -0,0 +1,429 @@
+/* ir-register.c - handle IR scancode->keycode tables
+ *
+ * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ */
+
+#include <linux/usb/input.h>
+
+#include <media/ir-common.h>
+
+#define IR_TAB_MIN_SIZE        32
+#define IR_TAB_MAX_SIZE        1024
+
+/**
+ * ir_seek_table() - returns the element order on the table
+ * @rc_tab:    the ir_scancode_table with the keymap to be used
+ * @scancode:  the scancode that we're seeking
+ *
+ * This routine is used by the input routines when a key is pressed at the
+ * IR. The scancode is received and needs to be converted into a keycode.
+ * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the
+ * corresponding keycode from the table.
+ */
+static int ir_seek_table(struct ir_scancode_table *rc_tab, u32 scancode)
+{
+       int rc;
+       unsigned long flags;
+       struct ir_scancode *keymap = rc_tab->scan;
+
+       spin_lock_irqsave(&rc_tab->lock, flags);
+
+       /* FIXME: replace it by a binary search */
+
+       for (rc = 0; rc < rc_tab->size; rc++)
+               if (keymap[rc].scancode == scancode)
+                       goto exit;
+
+       /* Not found */
+       rc = -EINVAL;
+
+exit:
+       spin_unlock_irqrestore(&rc_tab->lock, flags);
+       return rc;
+}
+
+/**
+ * ir_roundup_tablesize() - gets an optimum value for the table size
+ * @n_elems:           minimum number of entries to store keycodes
+ *
+ * This routine is used to choose the keycode table size.
+ *
+ * In order to have some empty space for new keycodes,
+ * and knowing in advance that kmalloc allocates only power of two
+ * segments, it optimizes the allocated space to have some spare space
+ * for those new keycodes by using the maximum number of entries that
+ * will be effectively be allocated by kmalloc.
+ * In order to reduce the quantity of table resizes, it has a minimum
+ * table size of IR_TAB_MIN_SIZE.
+ */
+int ir_roundup_tablesize(int n_elems)
+{
+       size_t size;
+
+       if (n_elems < IR_TAB_MIN_SIZE)
+               n_elems = IR_TAB_MIN_SIZE;
+
+       /*
+        * As kmalloc only allocates sizes of power of two, get as
+        * much entries as possible for the allocated memory segment
+        */
+       size = roundup_pow_of_two(n_elems * sizeof(struct ir_scancode));
+       n_elems = size / sizeof(struct ir_scancode);
+
+       return n_elems;
+}
+
+/**
+ * ir_copy_table() - copies a keytable, discarding the unused entries
+ * @destin:    destin table
+ * @origin:    origin table
+ *
+ * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED
+ */
+
+int ir_copy_table(struct ir_scancode_table *destin,
+                const struct ir_scancode_table *origin)
+{
+       int i, j = 0;
+
+       for (i = 0; i < origin->size; i++) {
+               if (origin->scan[i].keycode == KEY_UNKNOWN ||
+                  origin->scan[i].keycode == KEY_RESERVED)
+                       continue;
+
+               memcpy(&destin->scan[j], &origin->scan[i], sizeof(struct ir_scancode));
+               j++;
+       }
+       destin->size = j;
+
+       IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size);
+
+       return 0;
+}
+
+/**
+ * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table
+ * @dev:       the struct input_dev device descriptor
+ * @scancode:  the desired scancode
+ * @keycode:   the keycode to be retorned.
+ *
+ * This routine is used to handle evdev EVIOCGKEY ioctl.
+ * If the key is not found, returns -EINVAL, otherwise, returns 0.
+ */
+static int ir_getkeycode(struct input_dev *dev,
+                        int scancode, int *keycode)
+{
+       int elem;
+       struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+
+       elem = ir_seek_table(rc_tab, scancode);
+       if (elem >= 0) {
+               *keycode = rc_tab->scan[elem].keycode;
+               return 0;
+       }
+
+       /*
+        * Scancode not found and table can't be expanded
+        */
+       if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE)
+               return -EINVAL;
+
+       /*
+        * If is there extra space, returns KEY_RESERVED,
+        * otherwise, input core won't let ir_setkeycode to work
+        */
+       *keycode = KEY_RESERVED;
+       return 0;
+}
+
+
+/**
+ * ir_is_resize_needed() - Check if the table needs rezise
+ * @table:             keycode table that may need to resize
+ * @n_elems:           minimum number of entries to store keycodes
+ *
+ * Considering that kmalloc uses power of two storage areas, this
+ * routine detects if the real alloced size will change. If not, it
+ * just returns without doing nothing. Otherwise, it will extend or
+ * reduce the table size to meet the new needs.
+ *
+ * It returns 0 if no resize is needed, 1 otherwise.
+ */
+static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems)
+{
+       int cur_size = ir_roundup_tablesize(table->size);
+       int new_size = ir_roundup_tablesize(n_elems);
+
+       if (cur_size == new_size)
+               return 0;
+
+       /* Resize is needed */
+       return 1;
+}
+
+/**
+ * ir_delete_key() - remove a keycode from the table
+ * @rc_tab:            keycode table
+ * @elem:              element to be removed
+ *
+ */
+static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem)
+{
+       unsigned long flags = 0;
+       int newsize = rc_tab->size - 1;
+       int resize = ir_is_resize_needed(rc_tab, newsize);
+       struct ir_scancode *oldkeymap = rc_tab->scan;
+       struct ir_scancode *newkeymap;
+
+       if (resize) {
+               newkeymap = kzalloc(ir_roundup_tablesize(newsize) *
+                                   sizeof(*newkeymap), GFP_ATOMIC);
+
+               /* There's no memory for resize. Keep the old table */
+               if (!newkeymap)
+                       resize = 0;
+       }
+
+       if (!resize) {
+               newkeymap = oldkeymap;
+
+               /* We'll modify the live table. Lock it */
+               spin_lock_irqsave(&rc_tab->lock, flags);
+       }
+
+       /*
+        * Copy the elements before the one that will be deleted
+        * if (!resize), both oldkeymap and newkeymap points
+        * to the same place, so, there's no need to copy
+        */
+       if (resize && elem > 0)
+               memcpy(newkeymap, oldkeymap,
+                      elem * sizeof(*newkeymap));
+
+       /*
+        * Copy the other elements overwriting the element to be removed
+        * This operation applies to both resize and non-resize case
+        */
+       if (elem < newsize)
+               memcpy(&newkeymap[elem], &oldkeymap[elem + 1],
+                      (newsize - elem) * sizeof(*newkeymap));
+
+       if (resize) {
+               /*
+                * As the copy happened to a temporary table, only here
+                * it needs to lock while replacing the table pointers
+                * to use the new table
+                */
+               spin_lock_irqsave(&rc_tab->lock, flags);
+               rc_tab->size = newsize;
+               rc_tab->scan = newkeymap;
+               spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+               /* Frees the old keytable */
+               kfree(oldkeymap);
+       } else {
+               rc_tab->size = newsize;
+               spin_unlock_irqrestore(&rc_tab->lock, flags);
+       }
+}
+
+/**
+ * ir_insert_key() - insert a keycode at the table
+ * @rc_tab:            keycode table
+ * @scancode:  the desired scancode
+ * @keycode:   the keycode to be retorned.
+ *
+ */
+static int ir_insert_key(struct ir_scancode_table *rc_tab,
+                         int scancode, int keycode)
+{
+       unsigned long flags;
+       int elem = rc_tab->size;
+       int newsize = rc_tab->size + 1;
+       int resize = ir_is_resize_needed(rc_tab, newsize);
+       struct ir_scancode *oldkeymap = rc_tab->scan;
+       struct ir_scancode *newkeymap;
+
+       if (resize) {
+               newkeymap = kzalloc(ir_roundup_tablesize(newsize) *
+                                   sizeof(*newkeymap), GFP_ATOMIC);
+               if (!newkeymap)
+                       return -ENOMEM;
+
+               memcpy(newkeymap, oldkeymap,
+                      rc_tab->size * sizeof(*newkeymap));
+       } else
+               newkeymap  = oldkeymap;
+
+       /* Stores the new code at the table */
+       IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n",
+                  rc_tab->size, scancode, keycode);
+
+       spin_lock_irqsave(&rc_tab->lock, flags);
+       rc_tab->size = newsize;
+       if (resize) {
+               rc_tab->scan = newkeymap;
+               kfree(oldkeymap);
+       }
+       newkeymap[elem].scancode = scancode;
+       newkeymap[elem].keycode  = keycode;
+       spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+       return 0;
+}
+
+/**
+ * ir_setkeycode() - set a keycode at the evdev scancode ->keycode table
+ * @dev:       the struct input_dev device descriptor
+ * @scancode:  the desired scancode
+ * @keycode:   the keycode to be retorned.
+ *
+ * This routine is used to handle evdev EVIOCSKEY ioctl.
+ * There's one caveat here: how can we increase the size of the table?
+ * If the key is not found, returns -EINVAL, otherwise, returns 0.
+ */
+static int ir_setkeycode(struct input_dev *dev,
+                        int scancode, int keycode)
+{
+       int rc = 0;
+       struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+       struct ir_scancode *keymap = rc_tab->scan;
+       unsigned long flags;
+
+       /*
+        * Handle keycode table deletions
+        *
+        * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED,
+        * deal as a trial to remove an existing scancode attribution
+        * if table become too big, reduce it to save space
+        */
+       if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) {
+               rc = ir_seek_table(rc_tab, scancode);
+               if (rc < 0)
+                       return 0;
+
+               IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode);
+               clear_bit(keymap[rc].keycode, dev->keybit);
+               ir_delete_key(rc_tab, rc);
+
+               return 0;
+       }
+
+       /*
+        * Handle keycode replacements
+        *
+        * If the scancode exists, just replace by the new value
+        */
+       rc = ir_seek_table(rc_tab, scancode);
+       if (rc >= 0) {
+               IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n",
+                       rc, scancode, keycode);
+
+               clear_bit(keymap[rc].keycode, dev->keybit);
+
+               spin_lock_irqsave(&rc_tab->lock, flags);
+               keymap[rc].keycode = keycode;
+               spin_unlock_irqrestore(&rc_tab->lock, flags);
+
+               set_bit(keycode, dev->keybit);
+
+               return 0;
+       }
+
+       /*
+        * Handle new scancode inserts
+        *
+        * reallocate table if needed and insert a new keycode
+        */
+
+       /* Avoid growing the table indefinitely */
+       if (rc_tab->size + 1 > IR_TAB_MAX_SIZE)
+               return -EINVAL;
+
+       rc = ir_insert_key(rc_tab, scancode, keycode);
+       if (rc < 0)
+               return rc;
+       set_bit(keycode, dev->keybit);
+
+       return 0;
+}
+
+/**
+ * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode
+ * @input_dev: the struct input_dev descriptor of the device
+ * @scancode:  the scancode that we're seeking
+ *
+ * This routine is used by the input routines when a key is pressed at the
+ * IR. The scancode is received and needs to be converted into a keycode.
+ * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the
+ * corresponding keycode from the table.
+ */
+u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode)
+{
+       struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+       struct ir_scancode *keymap = rc_tab->scan;
+       int elem;
+
+       elem = ir_seek_table(rc_tab, scancode);
+       if (elem >= 0) {
+               IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
+                          dev->name, scancode, keymap[elem].keycode);
+
+               return rc_tab->scan[elem].keycode;
+       }
+
+       printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n",
+              dev->name, scancode);
+
+       /* Reports userspace that an unknown keycode were got */
+       return KEY_RESERVED;
+}
+
+/**
+ * ir_set_keycode_table() - sets the IR keycode table and add the handlers
+ *                         for keymap table get/set
+ * @input_dev: the struct input_dev descriptor of the device
+ * @rc_tab:    the struct ir_scancode_table table of scancode/keymap
+ *
+ * This routine is used to initialize the input infrastructure to work with
+ * an IR.
+ * It should be called before registering the IR device.
+ */
+int ir_set_keycode_table(struct input_dev *input_dev,
+                        struct ir_scancode_table *rc_tab)
+{
+       struct ir_scancode *keymap = rc_tab->scan;
+       int i;
+
+       spin_lock_init(&rc_tab->lock);
+
+       if (rc_tab->scan == NULL || !rc_tab->size)
+               return -EINVAL;
+
+       /* set the bits for the keys */
+       IR_dprintk(1, "key map size: %d\n", rc_tab->size);
+       for (i = 0; i < rc_tab->size; i++) {
+               IR_dprintk(1, "#%d: setting bit for keycode 0x%04x\n",
+                       i, keymap[i].keycode);
+               set_bit(keymap[i].keycode, input_dev->keybit);
+       }
+
+       input_dev->getkeycode = ir_getkeycode;
+       input_dev->setkeycode = ir_setkeycode;
+       input_set_drvdata(input_dev, rc_tab);
+
+       return 0;
+}
+
+void ir_input_free(struct input_dev *dev)
+{
+       struct ir_scancode_table *rc_tab = input_get_drvdata(dev);
+
+       IR_dprintk(1, "Freed keycode table\n");
+
+       rc_tab->size = 0;
+       kfree(rc_tab->scan);
+       rc_tab->scan = NULL;
+}
+EXPORT_SYMBOL_GPL(ir_input_free);
+
index 7e8f568..48cb154 100644 (file)
@@ -98,7 +98,7 @@ static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op)
 
                op_count++;
 
-               /* loop throgh all bytes of message i */
+               /* loop through all bytes of message i */
                for(j = 0; j < m[i].len; j++) {
                        /* write back all bytes that could have been read */
                        m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8));
index 552dab4..becbaad 100644 (file)
@@ -1205,6 +1205,13 @@ static int buffer_activate (struct saa7146_dev *dev,
        return 0;
 }
 
+static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+       saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+       saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+       saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
 static int buffer_prepare(struct videobuf_queue *q,
                          struct videobuf_buffer *vb, enum v4l2_field field)
 {
@@ -1257,16 +1264,12 @@ static int buffer_prepare(struct videobuf_queue *q,
 
                sfmt = format_by_fourcc(dev,buf->fmt->pixelformat);
 
+               release_all_pagetables(dev, buf);
                if( 0 != IS_PLANAR(sfmt->trans)) {
-                       saa7146_pgtable_free(dev->pci, &buf->pt[0]);
-                       saa7146_pgtable_free(dev->pci, &buf->pt[1]);
-                       saa7146_pgtable_free(dev->pci, &buf->pt[2]);
-
                        saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
                        saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
                        saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
                } else {
-                       saa7146_pgtable_free(dev->pci, &buf->pt[0]);
                        saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
                }
 
@@ -1329,6 +1332,9 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
        struct saa7146_buf *buf = (struct saa7146_buf *)vb;
 
        DEB_CAP(("vbuf:%p\n",vb));
+
+       release_all_pagetables(dev, buf);
+
        saa7146_dma_free(dev,q,buf);
 }
 
index 607d319..409a426 100644 (file)
@@ -172,4 +172,11 @@ config MEDIA_TUNER_MC44S803
        help
          Say Y here to support the Freescale MC44S803 based tuners
 
+config MEDIA_TUNER_MAX2165
+       tristate "Maxim MAX2165 silicon tuner"
+       depends on VIDEO_MEDIA && I2C
+       default m if MEDIA_TUNER_CUSTOMISE
+       help
+         A driver for the silicon tuner MAX2165 from Maxim.
+
 endif # MEDIA_TUNER_CUSTOMISE
index 4132b2b..a543852 100644 (file)
@@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
 obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
 obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
 obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
+obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
 
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c
new file mode 100644 (file)
index 0000000..3d03640
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ *  Driver for Maxim MAX2165 silicon tuner
+ *
+ *  Copyright (c) 2009 David T. L. Wong <davidtlwong@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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "max2165.h"
+#include "max2165_priv.h"
+#include "tuner-i2c.h"
+
+#define dprintk(args...) \
+       do { \
+               if (debug) \
+                       printk(KERN_DEBUG "max2165: " args); \
+       } while (0)
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data)
+{
+       int ret;
+       u8 buf[] = { reg, data };
+       struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
+
+       msg.addr = priv->config->i2c_address;
+
+       if (debug >= 2)
+               printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
+                       __func__, reg, data);
+
+       ret = i2c_transfer(priv->i2c, &msg, 1);
+
+       if (ret != 1)
+               dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n",
+                       __func__, reg, data, ret);
+
+       return (ret != 1) ? -EIO : 0;
+}
+
+static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data)
+{
+       int ret;
+       u8 dev_addr = priv->config->i2c_address;
+
+       u8 b0[] = { reg };
+       u8 b1[] = { 0 };
+       struct i2c_msg msg[] = {
+               { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 },
+               { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 },
+       };
+
+       ret = i2c_transfer(priv->i2c, msg, 2);
+       if (ret != 2) {
+               dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n",
+                       __func__, reg, ret);
+               return -EIO;
+       }
+
+       *p_data = b1[0];
+       if (debug >= 2)
+               printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n",
+                       __func__, reg, b1[0]);
+       return 0;
+}
+
+static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg,
+       u8 mask, u8 data)
+{
+       int ret;
+       u8 v;
+
+       data &= mask;
+       ret = max2165_read_reg(priv, reg, &v);
+       if (ret != 0)
+               return ret;
+       v &= ~mask;
+       v |= data;
+       ret = max2165_write_reg(priv, reg, v);
+
+       return ret;
+}
+
+static int max2165_read_rom_table(struct max2165_priv *priv)
+{
+       u8 dat[3];
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1);
+               max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]);
+       }
+
+       priv->tf_ntch_low_cfg = dat[0] >> 4;
+       priv->tf_ntch_hi_cfg = dat[0] & 0x0F;
+       priv->tf_balun_low_ref = dat[1] & 0x0F;
+       priv->tf_balun_hi_ref = dat[1] >> 4;
+       priv->bb_filter_7mhz_cfg = dat[2] & 0x0F;
+       priv->bb_filter_8mhz_cfg = dat[2] >> 4;
+
+       dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg);
+       dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg);
+       dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref);
+       dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref);
+       dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg);
+       dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg);
+
+       return 0;
+}
+
+static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/)
+{
+       u8 v;
+
+       v = (osc / 2);
+       if (v == 2)
+               v = 0x7;
+       else
+               v -= 8;
+
+       max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v);
+
+       return 0;
+}
+
+static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw)
+{
+       u8 val;
+
+       if (bw == BANDWIDTH_8_MHZ)
+               val = priv->bb_filter_8mhz_cfg;
+       else
+               val = priv->bb_filter_7mhz_cfg;
+
+       max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4);
+
+       return 0;
+}
+
+int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
+{
+       u32 remainder;
+       u32 q, f = 0;
+       int i;
+
+       if (0 == divisor)
+               return -1;
+
+       q = dividend / divisor;
+       remainder = dividend - q * divisor;
+
+       for (i = 0; i < 31; i++) {
+               remainder <<= 1;
+               if (remainder >= divisor) {
+                       f += 1;
+                       remainder -= divisor;
+               }
+               f <<= 1;
+       }
+
+       *quotient = q;
+       *fraction = f;
+
+       return 0;
+}
+
+static int max2165_set_rf(struct max2165_priv *priv, u32 freq)
+{
+       u8 tf;
+       u8 tf_ntch;
+       u32 t;
+       u32 quotient, fraction;
+
+       /* Set PLL divider according to RF frequency */
+       fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
+               &quotient, &fraction);
+
+       /* 20-bit fraction */
+       fraction >>= 12;
+
+       max2165_write_reg(priv, REG_NDIV_INT, quotient);
+       max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16);
+       max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8);
+       max2165_write_reg(priv, REG_NDIV_FRAC0, fraction);
+
+       /* Norch Filter */
+       tf_ntch = (freq < 725000000) ?
+               priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg;
+
+       /* Tracking filter balun */
+       t = priv->tf_balun_low_ref;
+       t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref)
+               * (freq / 1000 - 470000) / (780000 - 470000);
+
+       tf = t;
+       dprintk("tf = %X\n", tf);
+       tf |= tf_ntch << 4;
+
+       max2165_write_reg(priv, REG_TRACK_FILTER, tf);
+
+       return 0;
+}
+
+static void max2165_debug_status(struct max2165_priv *priv)
+{
+       u8 status, autotune;
+       u8 auto_vco_success, auto_vco_active;
+       u8 pll_locked;
+       u8 dc_offset_low, dc_offset_hi;
+       u8 signal_lv_over_threshold;
+       u8 vco, vco_sub_band, adc;
+
+       max2165_read_reg(priv, REG_STATUS, &status);
+       max2165_read_reg(priv, REG_AUTOTUNE, &autotune);
+
+       auto_vco_success = (status >> 6) & 0x01;
+       auto_vco_active = (status >> 5) & 0x01;
+       pll_locked = (status >> 4) & 0x01;
+       dc_offset_low = (status >> 3) & 0x01;
+       dc_offset_hi = (status >> 2) & 0x01;
+       signal_lv_over_threshold = status & 0x01;
+
+       vco = autotune >> 6;
+       vco_sub_band = (autotune >> 3) & 0x7;
+       adc = autotune & 0x7;
+
+       dprintk("auto VCO active: %d, auto VCO success: %d\n",
+               auto_vco_active, auto_vco_success);
+       dprintk("PLL locked: %d\n", pll_locked);
+       dprintk("DC offset low: %d, DC offset high: %d\n",
+               dc_offset_low, dc_offset_hi);
+       dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold);
+       dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc);
+}
+
+static int max2165_set_params(struct dvb_frontend *fe,
+       struct dvb_frontend_parameters *params)
+{
+       struct max2165_priv *priv = fe->tuner_priv;
+       int ret;
+
+       dprintk("%s() frequency=%d (Hz)\n", __func__, params->frequency);
+       if (fe->ops.info.type == FE_ATSC) {
+                       return -EINVAL;
+       } else if (fe->ops.info.type == FE_OFDM) {
+               dprintk("%s() OFDM\n", __func__);
+               switch (params->u.ofdm.bandwidth) {
+               case BANDWIDTH_6_MHZ:
+                       return -EINVAL;
+               case BANDWIDTH_7_MHZ:
+               case BANDWIDTH_8_MHZ:
+                       priv->frequency = params->frequency;
+                       priv->bandwidth = params->u.ofdm.bandwidth;
+                       break;
+               default:
+                       printk(KERN_ERR "MAX2165 bandwidth not set!\n");
+                       return -EINVAL;
+               }
+       } else {
+               printk(KERN_ERR "MAX2165 modulation type not supported!\n");
+               return -EINVAL;
+       }
+
+       dprintk("%s() frequency=%d\n", __func__, priv->frequency);
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+       max2165_set_bandwidth(priv, priv->bandwidth);
+       ret = max2165_set_rf(priv, priv->frequency);
+       mdelay(50);
+       max2165_debug_status(priv);
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 0);
+
+       if (ret != 0)
+               return -EREMOTEIO;
+
+       return 0;
+}
+
+static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+       struct max2165_priv *priv = fe->tuner_priv;
+       dprintk("%s()\n", __func__);
+       *freq = priv->frequency;
+       return 0;
+}
+
+static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+       struct max2165_priv *priv = fe->tuner_priv;
+       dprintk("%s()\n", __func__);
+
+       *bw = priv->bandwidth;
+       return 0;
+}
+
+static int max2165_get_status(struct dvb_frontend *fe, u32 *status)
+{
+       struct max2165_priv *priv = fe->tuner_priv;
+       u16 lock_status = 0;
+
+       dprintk("%s()\n", __func__);
+
+       if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 1);
+
+       max2165_debug_status(priv);
+       *status = lock_status;
+
+       if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 0);
+
+       return 0;
+}
+
+static int max2165_sleep(struct dvb_frontend *fe)
+{
+       dprintk("%s()\n", __func__);
+       return 0;
+}
+
+static int max2165_init(struct dvb_frontend *fe)
+{
+       struct max2165_priv *priv = fe->tuner_priv;
+       dprintk("%s()\n", __func__);
+
+       if (fe->ops.i2c_gate_ctrl)
+               fe->ops.i2c_gate_ctrl(fe, 1);
+
+       /* Setup initial values */
+       /* Fractional Mode on */
+       max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18);
+       /* LNA on */
+       max2165_write_reg(priv, REG_LNA, 0x01);
+       max2165_write_reg(priv, REG_PLL_CFG, 0x7A);
+       max2165_write_reg(priv, REG_TEST, 0x08);
+       max2165_write_reg(priv, REG_SHUTDOWN, 0x40);
+       max2165_write_reg(priv, REG_VCO_CTRL, 0x84);
+       max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3);
+       max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75);
+       max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00);
+       max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00);
+
+       max2165_set_osc(priv, priv->config->osc_clk);
+
+       max2165_read_rom_table(priv);
+
+       max2165_set_bandwidth(priv, BANDWIDTH_8_MHZ);
+
+       if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 0);
+
+       return 0;
+}
+
+static int max2165_release(struct dvb_frontend *fe)
+{
+       struct max2165_priv *priv = fe->tuner_priv;
+       dprintk("%s()\n", __func__);
+
+       kfree(priv);
+       fe->tuner_priv = NULL;
+
+       return 0;
+}
+
+static const struct dvb_tuner_ops max2165_tuner_ops = {
+       .info = {
+               .name           = "Maxim MAX2165",
+               .frequency_min  = 470000000,
+               .frequency_max  = 780000000,
+               .frequency_step =     50000,
+       },
+
+       .release           = max2165_release,
+       .init              = max2165_init,
+       .sleep             = max2165_sleep,
+
+       .set_params        = max2165_set_params,
+       .set_analog_params = NULL,
+       .get_frequency     = max2165_get_frequency,
+       .get_bandwidth     = max2165_get_bandwidth,
+       .get_status        = max2165_get_status
+};
+
+struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
+                                  struct i2c_adapter *i2c,
+                                  struct max2165_config *cfg)
+{
+       struct max2165_priv *priv = NULL;
+
+       dprintk("%s(%d-%04x)\n", __func__,
+               i2c ? i2c_adapter_id(i2c) : -1,
+               cfg ? cfg->i2c_address : -1);
+
+       priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+
+       memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops,
+               sizeof(struct dvb_tuner_ops));
+
+       priv->config = cfg;
+       priv->i2c = i2c;
+       fe->tuner_priv = priv;
+
+       max2165_init(fe);
+       max2165_debug_status(priv);
+
+       return fe;
+}
+EXPORT_SYMBOL(max2165_attach);
+
+MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
+MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h
new file mode 100644 (file)
index 0000000..c063c36
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  Driver for Maxim MAX2165 silicon tuner
+ *
+ *  Copyright (c) 2009 David T. L. Wong <davidtlwong@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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MAX2165_H__
+#define __MAX2165_H__
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct max2165_config {
+       u8 i2c_address;
+       u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */
+};
+
+#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \
+    (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE))
+extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
+       struct i2c_adapter *i2c,
+       struct max2165_config *cfg);
+#else
+static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe,
+       struct i2c_adapter *i2c,
+       struct max2165_config *cfg)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h
new file mode 100644 (file)
index 0000000..91bbe02
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  Driver for Maxim MAX2165 silicon tuner
+ *
+ *  Copyright (c) 2009 David T. L. Wong <davidtlwong@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.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MAX2165_PRIV_H__
+#define __MAX2165_PRIV_H__
+
+#define REG_NDIV_INT 0x00
+#define REG_NDIV_FRAC2 0x01
+#define REG_NDIV_FRAC1 0x02
+#define REG_NDIV_FRAC0 0x03
+#define REG_TRACK_FILTER 0x04
+#define REG_LNA 0x05
+#define REG_PLL_CFG 0x06
+#define REG_TEST 0x07
+#define REG_SHUTDOWN 0x08
+#define REG_VCO_CTRL 0x09
+#define REG_BASEBAND_CTRL 0x0A
+#define REG_DC_OFFSET_CTRL 0x0B
+#define REG_DC_OFFSET_DAC 0x0C
+#define REG_ROM_TABLE_ADDR 0x0D
+
+/* Read Only Registers */
+#define REG_ROM_TABLE_DATA 0x10
+#define REG_STATUS 0x11
+#define REG_AUTOTUNE 0x12
+
+struct max2165_priv {
+       struct max2165_config *config;
+       struct i2c_adapter *i2c;
+
+       u32 frequency;
+       u32 bandwidth;
+
+       u8 tf_ntch_low_cfg;
+       u8 tf_ntch_hi_cfg;
+       u8 tf_balun_low_ref;
+       u8 tf_balun_hi_ref;
+       u8 bb_filter_7mhz_cfg;
+       u8 bb_filter_8mhz_cfg;
+};
+
+#endif
index 0803dab..605e28b 100644 (file)
@@ -2789,7 +2789,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
 
        /* add for 2.6.5 Special setting for QAM */
        if (state->Mod_Type == MXL_QAM) {
-               if (state->RF_IN < 680000000)
+               if (state->config->qam_gain != 0)
+                       status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN,
+                                                  state->config->qam_gain);
+               else if (state->RF_IN < 680000000)
                        status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
                else
                        status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
index 7ac6815..fc8a1ff 100644 (file)
@@ -108,6 +108,10 @@ struct mxl5005s_config {
 #define MXL_LOW_IF  1
        u8 if_mode;
 
+       /* Some boards need to override the built-in logic for determining
+          the gain when in QAM mode (the HVR-1600 is one such case) */
+       u8 qam_gain;
+
        /* Stuff I don't know what to do with */
        u8 AgcMasterByte;
 };
index 2d02698..7eb1bf7 100644 (file)
@@ -196,7 +196,7 @@ static void copy_reg_bits(struct reg_pair_t *reg_pair1,
        i = j = 0;
 
        while (reg_pair1[i].reg || reg_pair1[i].val) {
-               while (reg_pair2[j].reg || reg_pair2[j].reg) {
+               while (reg_pair2[j].reg || reg_pair2[j].val) {
                        if (reg_pair1[i].reg != reg_pair2[j].reg) {
                                j++;
                                continue;
index 155c93e..e1f6782 100644 (file)
@@ -326,12 +326,24 @@ int tda18271_init_regs(struct dvb_frontend *fe)
        regs[R_EB22] = 0x48;
        regs[R_EB23] = 0xb0;
 
-       if (priv->small_i2c) {
+       switch (priv->small_i2c) {
+       case TDA18271_08_BYTE_CHUNK_INIT:
+               tda18271_write_regs(fe, 0x00, 0x08);
+               tda18271_write_regs(fe, 0x08, 0x08);
+               tda18271_write_regs(fe, 0x10, 0x08);
+               tda18271_write_regs(fe, 0x18, 0x08);
+               tda18271_write_regs(fe, 0x20, 0x07);
+               break;
+       case TDA18271_16_BYTE_CHUNK_INIT:
                tda18271_write_regs(fe, 0x00, 0x10);
                tda18271_write_regs(fe, 0x10, 0x10);
                tda18271_write_regs(fe, 0x20, 0x07);
-       } else
+               break;
+       case TDA18271_39_BYTE_CHUNK_INIT:
+       default:
                tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
+               break;
+       }
 
        /* setup agc1 gain */
        regs[R_EB17] = 0x00;
index 3a50ce9..b2e1545 100644 (file)
@@ -256,8 +256,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
        struct tda18271_priv *priv = fe->tuner_priv;
        struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
        unsigned char *regs = priv->tda18271_regs;
-       int tm_current, rfcal_comp, approx, i, ret;
-       u8 dc_over_dt, rf_tab;
+       int i, ret;
+       u8 tm_current, dc_over_dt, rf_tab;
+       s32 rfcal_comp, approx;
 
        /* power up */
        ret = tda18271_set_standby_mode(fe, 0, 0, 0);
@@ -277,11 +278,11 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
                return i;
 
        if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
-               approx = map[i].rf_a1 *
-                       (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab;
+               approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) +
+                       map[i].rf_b1 + rf_tab;
        } else {
-               approx = map[i].rf_a2 *
-                       (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab;
+               approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) +
+                       map[i].rf_b2 + rf_tab;
        }
 
        if (approx < 0)
@@ -292,9 +293,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
        tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
 
        /* calculate temperature compensation */
-       rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000;
+       rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000;
 
-       regs[R_EB14] = approx + rfcal_comp;
+       regs[R_EB14] = (unsigned char)(approx + rfcal_comp);
        ret = tda18271_write_regs(fe, R_EB14, 1);
 fail:
        return ret;
@@ -572,6 +573,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
        struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
        unsigned char *regs = priv->tda18271_regs;
        int bcal, rf, i;
+       s32 divisor, dividend;
 #define RF1 0
 #define RF2 1
 #define RF3 2
@@ -610,20 +612,22 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
                switch (rf) {
                case RF1:
                        map[i].rf_a1 = 0;
-                       map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1];
+                       map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]);
                        map[i].rf1   = rf_freq[RF1] / 1000;
                        break;
                case RF2:
-                       map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] -
-                                       prog_cal[RF1] + prog_tab[RF1]) /
-                               (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000);
+                       dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) -
+                                  (s32)(prog_cal[RF1] + prog_tab[RF1]);
+                       divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
+                       map[i].rf_a1 = (dividend / divisor);
                        map[i].rf2   = rf_freq[RF2] / 1000;
                        break;
                case RF3:
-                       map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] -
-                                       prog_cal[RF2] + prog_tab[RF2]) /
-                               (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000);
-                       map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2];
+                       dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) -
+                                  (s32)(prog_cal[RF2] + prog_tab[RF2]);
+                       divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
+                       map[i].rf_a2 = (dividend / divisor);
+                       map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]);
                        map[i].rf3   = rf_freq[RF3] / 1000;
                        break;
                default:
@@ -1181,6 +1185,48 @@ static int tda18271_get_id(struct dvb_frontend *fe)
        return ret;
 }
 
+static int tda18271_setup_configuration(struct dvb_frontend *fe,
+                                       struct tda18271_config *cfg)
+{
+       struct tda18271_priv *priv = fe->tuner_priv;
+
+       priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
+       priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
+       priv->config = (cfg) ? cfg->config : 0;
+       priv->small_i2c = (cfg) ?
+               cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT;
+       priv->output_opt = (cfg) ?
+               cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
+
+       return 0;
+}
+
+static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg)
+{
+       /* tda18271_cal_on_startup == -1 when cal module option is unset */
+       return ((tda18271_cal_on_startup == -1) ?
+               /* honor configuration setting */
+               ((cfg) && (cfg->rf_cal_on_startup)) :
+               /* module option overrides configuration setting */
+               (tda18271_cal_on_startup)) ? 1 : 0;
+}
+
+static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+       struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg;
+
+       tda18271_setup_configuration(fe, cfg);
+
+       if (tda18271_need_cal_on_startup(cfg))
+               tda18271_init(fe);
+
+       /* override default std map with values in config struct */
+       if ((cfg) && (cfg->std_map))
+               tda18271_update_std_map(fe, cfg->std_map);
+
+       return 0;
+}
+
 static struct dvb_tuner_ops tda18271_tuner_ops = {
        .info = {
                .name = "NXP TDA18271HD",
@@ -1193,6 +1239,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = {
        .set_params        = tda18271_set_params,
        .set_analog_params = tda18271_set_analog_params,
        .release           = tda18271_release,
+       .set_config        = tda18271_set_config,
        .get_frequency     = tda18271_get_frequency,
        .get_bandwidth     = tda18271_get_bandwidth,
 };
@@ -1213,33 +1260,14 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
        case 0:
                goto fail;
        case 1:
-       {
                /* new tuner instance */
-               int rf_cal_on_startup;
-
-               priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
-               priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
-               priv->config = (cfg) ? cfg->config : 0;
-               priv->small_i2c = (cfg) ? cfg->small_i2c : 0;
-               priv->output_opt = (cfg) ?
-                       cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
-
-               /* tda18271_cal_on_startup == -1 when cal
-                * module option is unset */
-               if (tda18271_cal_on_startup == -1) {
-                       /* honor attach-time configuration */
-                       rf_cal_on_startup =
-                               ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0;
-               } else {
-                       /* module option overrides attach configuration */
-                       rf_cal_on_startup = tda18271_cal_on_startup;
-               }
+               fe->tuner_priv = priv;
+
+               tda18271_setup_configuration(fe, cfg);
 
                priv->cal_initialized = false;
                mutex_init(&priv->lock);
 
-               fe->tuner_priv = priv;
-
                if (tda_fail(tda18271_get_id(fe)))
                        goto fail;
 
@@ -1249,12 +1277,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
                mutex_lock(&priv->lock);
                tda18271_init_regs(fe);
 
-               if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2))
+               if ((tda18271_need_cal_on_startup(cfg)) &&
+                   (priv->id == TDA18271HDC2))
                        tda18271c2_rf_cal_init(fe);
 
                mutex_unlock(&priv->lock);
                break;
-       }
        default:
                /* existing tuner instance */
                fe->tuner_priv = priv;
@@ -1271,7 +1299,11 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
                                priv->small_i2c = cfg->small_i2c;
                        if (cfg->output_opt)
                                priv->output_opt = cfg->output_opt;
+                       if (cfg->std_map)
+                               tda18271_update_std_map(fe, cfg->std_map);
                }
+               if (tda18271_need_cal_on_startup(cfg))
+                       tda18271_init(fe);
                break;
        }
 
@@ -1298,7 +1330,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach);
 MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
 MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
 MODULE_LICENSE("GPL");
-MODULE_VERSION("0.3");
+MODULE_VERSION("0.4");
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
index e21fdef..e7f84c7 100644 (file)
@@ -978,6 +978,7 @@ static struct tda18271_cid_target_map tda18271_cid_target[] = {
 int tda18271_lookup_cid_target(struct dvb_frontend *fe,
                               u32 *freq, u8 *cid_target, u16 *count_limit)
 {
+       struct tda18271_priv *priv = fe->tuner_priv;
        int i = 0;
 
        while ((tda18271_cid_target[i].rfmax * 1000) < *freq) {
index 2bee229..9589ab0 100644 (file)
@@ -80,10 +80,10 @@ struct tda18271_rf_tracking_filter_cal {
        u32 rf1;
        u32 rf2;
        u32 rf3;
-       int rf_a1;
-       int rf_b1;
-       int rf_a2;
-       int rf_b2;
+       s32 rf_a1;
+       s32 rf_b1;
+       s32 rf_a2;
+       s32 rf_b2;
 };
 
 enum tda18271_pll {
@@ -109,11 +109,12 @@ struct tda18271_priv {
        enum tda18271_i2c_gate gate;
        enum tda18271_ver id;
        enum tda18271_output_options output_opt;
+       enum tda18271_small_i2c small_i2c;
 
        unsigned int config; /* interface to saa713x / tda829x */
-       unsigned int tm_rfcal;
        unsigned int cal_initialized:1;
-       unsigned int small_i2c:1;
+
+       u8 tm_rfcal;
 
        struct tda18271_map_layout *maps;
        struct tda18271_std_map std;
@@ -135,27 +136,37 @@ extern int tda18271_debug;
 #define DBG_ADV  8
 #define DBG_CAL  16
 
-#define tda_printk(kern, fmt, arg...) \
-       printk(kern "%s: " fmt, __func__, ##arg)
-
-#define tda_dprintk(lvl, fmt, arg...) do {\
+#define tda_printk(st, kern, fmt, arg...) do {\
+       if (st) { \
+               struct tda18271_priv *state = st; \
+               printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \
+                       i2c_adapter_id(state->i2c_props.adap), \
+                       state->i2c_props.addr, \
+                       (state->role == TDA18271_MASTER) \
+                       ? "M" : "S", ##arg); \
+       } else \
+               printk(kern "%s: " fmt, __func__, ##arg); \
+} while (0)
+
+#define tda_dprintk(st, lvl, fmt, arg...) do {\
        if (tda18271_debug & lvl) \
-               tda_printk(KERN_DEBUG, fmt, ##arg); } while (0)
+               tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0)
 
 #define tda_info(fmt, arg...)     printk(KERN_INFO     fmt, ##arg)
-#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg)
-#define tda_err(fmt, arg...)  tda_printk(KERN_ERR,     fmt, ##arg)
-#define tda_dbg(fmt, arg...)  tda_dprintk(DBG_INFO,    fmt, ##arg)
-#define tda_map(fmt, arg...)  tda_dprintk(DBG_MAP,     fmt, ##arg)
-#define tda_reg(fmt, arg...)  tda_dprintk(DBG_REG,     fmt, ##arg)
-#define tda_cal(fmt, arg...)  tda_dprintk(DBG_CAL,     fmt, ##arg)
+#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg)
+#define tda_err(fmt, arg...)  tda_printk(priv, KERN_ERR,     fmt, ##arg)
+#define tda_dbg(fmt, arg...)  tda_dprintk(priv, DBG_INFO,    fmt, ##arg)
+#define tda_map(fmt, arg...)  tda_dprintk(priv, DBG_MAP,     fmt, ##arg)
+#define tda_reg(fmt, arg...)  tda_dprintk(priv, DBG_REG,     fmt, ##arg)
+#define tda_cal(fmt, arg...)  tda_dprintk(priv, DBG_CAL,     fmt, ##arg)
 
 #define tda_fail(ret)                                                       \
 ({                                                                          \
        int __ret;                                                           \
        __ret = (ret < 0);                                                   \
        if (__ret)                                                           \
-               tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\
+               tda_printk(priv, KERN_ERR,                                   \
+                          "error %d on line %d\n", ret, __LINE__);          \
        __ret;                                                               \
 })
 
index 323f291..d7fcc36 100644 (file)
@@ -78,6 +78,12 @@ enum tda18271_output_options {
        TDA18271_OUTPUT_XT_OFF = 2,
 };
 
+enum tda18271_small_i2c {
+       TDA18271_39_BYTE_CHUNK_INIT = 0,
+       TDA18271_16_BYTE_CHUNK_INIT = 1,
+       TDA18271_08_BYTE_CHUNK_INIT = 2,
+};
+
 struct tda18271_config {
        /* override default if freq / std settings (optional) */
        struct tda18271_std_map *std_map;
@@ -91,12 +97,12 @@ struct tda18271_config {
        /* output options that can be disabled */
        enum tda18271_output_options output_opt;
 
+       /* some i2c providers cant write all 39 registers at once */
+       enum tda18271_small_i2c small_i2c;
+
        /* force rf tracking filter calibration on startup */
        unsigned int rf_cal_on_startup:1;
 
-       /* some i2c providers cant write all 39 registers at once */
-       unsigned int small_i2c:1;
-
        /* interface to saa713x / tda829x */
        unsigned int config;
 };
index 064d14c..c190b0d 100644 (file)
@@ -33,6 +33,7 @@ module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "enable verbose debug messages");
 
 static int deemphasis_50;
+module_param(deemphasis_50, int, 0644);
 MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis");
 
 /* ---------------------------------------------------------------------- */
index 544cdbe..a71c100 100644 (file)
@@ -463,7 +463,7 @@ static int tda9887_set_insmod(struct dvb_frontend *fe)
                        buf[1] &= ~cQSS;
        }
 
-       if (adjust >= 0x00 && adjust < 0x20) {
+       if (adjust < 0x20) {
                buf[2] &= ~cTopMask;
                buf[2] |= adjust;
        }
index f4ffcdc..432003d 100644 (file)
@@ -61,6 +61,7 @@ struct xc5000_priv {
        u32 bandwidth;
        u8  video_standard;
        u8  rf_mode;
+       u8  radio_input;
 };
 
 /* Misc Defines */
@@ -632,8 +633,12 @@ static int xc5000_set_params(struct dvb_frontend *fe,
        struct xc5000_priv *priv = fe->tuner_priv;
        int ret;
 
-       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS)
-               xc_load_fw_and_init_tuner(fe);
+       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+               if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+                       dprintk(1, "Unable to load firmware and init tuner\n");
+                       return -EINVAL;
+               }
+       }
 
        dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
 
@@ -739,15 +744,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
        return ret;
 }
 
-static int xc5000_set_analog_params(struct dvb_frontend *fe,
+static int xc5000_set_tv_freq(struct dvb_frontend *fe,
        struct analog_parameters *params)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
        int ret;
 
-       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS)
-               xc_load_fw_and_init_tuner(fe);
-
        dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
                __func__, params->frequency);
 
@@ -827,6 +829,86 @@ tune_channel:
        return 0;
 }
 
+static int xc5000_set_radio_freq(struct dvb_frontend *fe,
+       struct analog_parameters *params)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret = -EINVAL;
+       u8 radio_input;
+
+       dprintk(1, "%s() frequency=%d (in units of khz)\n",
+               __func__, params->frequency);
+
+       if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) {
+               dprintk(1, "%s() radio input not configured\n", __func__);
+               return -EINVAL;
+       }
+
+       if (priv->radio_input == XC5000_RADIO_FM1)
+               radio_input = FM_Radio_INPUT1;
+       else if  (priv->radio_input == XC5000_RADIO_FM2)
+               radio_input = FM_Radio_INPUT2;
+       else {
+               dprintk(1, "%s() unknown radio input %d\n", __func__,
+                       priv->radio_input);
+               return -EINVAL;
+       }
+
+       priv->freq_hz = params->frequency * 125 / 2;
+
+       priv->rf_mode = XC_RF_MODE_AIR;
+
+       ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
+                              XC5000_Standard[radio_input].AudioMode);
+
+       if (ret != XC_RESULT_SUCCESS) {
+               printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+               return -EREMOTEIO;
+       }
+
+       ret = xc_SetSignalSource(priv, priv->rf_mode);
+       if (ret != XC_RESULT_SUCCESS) {
+               printk(KERN_ERR
+                       "xc5000: xc_SetSignalSource(%d) failed\n",
+                       priv->rf_mode);
+               return -EREMOTEIO;
+       }
+
+       xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
+
+       return 0;
+}
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+                            struct analog_parameters *params)
+{
+       struct xc5000_priv *priv = fe->tuner_priv;
+       int ret = -EINVAL;
+
+       if (priv->i2c_props.adap == NULL)
+               return -EINVAL;
+
+       if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+               if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
+                       dprintk(1, "Unable to load firmware and init tuner\n");
+                       return -EINVAL;
+               }
+       }
+
+       switch (params->mode) {
+       case V4L2_TUNER_RADIO:
+               ret = xc5000_set_radio_freq(fe, params);
+               break;
+       case V4L2_TUNER_ANALOG_TV:
+       case V4L2_TUNER_DIGITAL_TV:
+               ret = xc5000_set_tv_freq(fe, params);
+               break;
+       }
+
+       return ret;
+}
+
+
 static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
 {
        struct xc5000_priv *priv = fe->tuner_priv;
@@ -1000,6 +1082,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
                priv->if_khz = cfg->if_khz;
        }
 
+       if (priv->radio_input == 0)
+               priv->radio_input = cfg->radio_input;
+
        /* Check if firmware has been loaded. It is possible that another
           instance of the driver has loaded the firmware.
         */
index f4c1466..e6d7236 100644 (file)
@@ -30,11 +30,17 @@ struct i2c_adapter;
 struct xc5000_config {
        u8   i2c_address;
        u32  if_khz;
+       u8   radio_input;
 };
 
 /* xc5000 callback command */
 #define XC5000_TUNER_RESET             0
 
+/* Possible Radio inputs */
+#define XC5000_RADIO_NOT_CONFIGURED            0
+#define XC5000_RADIO_FM1                       1
+#define XC5000_RADIO_FM2                       2
+
 /* For each bridge framework, when it attaches either analog or digital,
  * it has to store a reference back to its _core equivalent structure,
  * so that it can service the hardware by steering gpio's etc.
index b1857c1..78fc469 100644 (file)
@@ -215,7 +215,7 @@ static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend
                freq = 2150000; /* satellite IF is 950..2150MHz */
 
        /* decide which VCO to use for the input frequency */
-       for(i = 1; (i < ARRAY_SIZE(osci)) && (osci[i] < freq); i++);
+       for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++);
        printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq);
        band=bandsel[i];
        /* the gain values must be set by SetSymbolrate */
index 2d099e2..53e3f2a 100644 (file)
@@ -510,7 +510,7 @@ static void dm1105_emit_key(struct work_struct *work)
 
        data = (ircom >> 8) & 0x7f;
 
-       ir_input_keydown(ir->input_dev, &ir->ir, data, data);
+       ir_input_keydown(ir->input_dev, &ir->ir, data);
        ir_input_nokey(ir->input_dev, &ir->ir);
 }
 
@@ -589,7 +589,12 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
        snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
                "pci-%s/ir0", pci_name(dm1105->pdev));
 
-       ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes);
+       err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes);
+       if (err < 0) {
+               input_free_device(input_dev);
+               return err;
+       }
+
        input_dev->name = "DVB on-card IR receiver";
        input_dev->phys = dm1105->ir.input_phys;
        input_dev->id.bustype = BUS_PCI;
@@ -608,6 +613,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
 
        err = input_register_device(input_dev);
        if (err) {
+               ir_input_free(input_dev);
                input_free_device(input_dev);
                return err;
        }
@@ -617,8 +623,8 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
 
 void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
 {
+       ir_input_free(dm1105->ir.input_dev);
        input_unregister_device(dm1105->ir.input_dev);
-
 }
 
 static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb)
index 91c537b..b78cfb7 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/string.h>
 #include <linux/crc32.h>
 #include <asm/uaccess.h>
+#include <asm/div64.h>
 
 #include "dvb_demux.h"
 
@@ -44,6 +45,11 @@ module_param(dvb_demux_tscheck, int, 0644);
 MODULE_PARM_DESC(dvb_demux_tscheck,
                "enable transport stream continuity and TEI check");
 
+static int dvb_demux_speedcheck;
+module_param(dvb_demux_speedcheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_speedcheck,
+               "enable transport stream speed check");
+
 #define dprintk_tscheck(x...) do {                              \
                if (dvb_demux_tscheck && printk_ratelimit())    \
                        printk(x);                              \
@@ -387,6 +393,39 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
        u16 pid = ts_pid(buf);
        int dvr_done = 0;
 
+       if (dvb_demux_speedcheck) {
+               struct timespec cur_time, delta_time;
+               u64 speed_bytes, speed_timedelta;
+
+               demux->speed_pkts_cnt++;
+
+               /* show speed every SPEED_PKTS_INTERVAL packets */
+               if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
+                       cur_time = current_kernel_time();
+
+                       if (demux->speed_last_time.tv_sec != 0 &&
+                                       demux->speed_last_time.tv_nsec != 0) {
+                               delta_time = timespec_sub(cur_time,
+                                               demux->speed_last_time);
+                               speed_bytes = (u64)demux->speed_pkts_cnt
+                                       * 188 * 8;
+                               /* convert to 1024 basis */
+                               speed_bytes = 1000 * div64_u64(speed_bytes,
+                                               1024);
+                               speed_timedelta =
+                                       (u64)timespec_to_ns(&delta_time);
+                               speed_timedelta = div64_u64(speed_timedelta,
+                                               1000000); /* nsec -> usec */
+                               printk(KERN_INFO "TS speed %llu Kbits/sec \n",
+                                               div64_u64(speed_bytes,
+                                                       speed_timedelta));
+                       };
+
+                       demux->speed_last_time = cur_time;
+                       demux->speed_pkts_cnt = 0;
+               };
+       };
+
        if (dvb_demux_tscheck) {
                if (!demux->cnt_storage)
                        demux->cnt_storage = vmalloc(MAX_PID + 1);
index 2fe05d0..a7d876f 100644 (file)
@@ -44,6 +44,8 @@
 
 #define MAX_PID 0x1fff
 
+#define SPEED_PKTS_INTERVAL 50000
+
 struct dvb_demux_filter {
        struct dmx_section_filter filter;
        u8 maskandmode[DMX_MAX_FILTER_SIZE];
@@ -131,6 +133,9 @@ struct dvb_demux {
        spinlock_t lock;
 
        uint8_t *cnt_storage; /* for TS continuity check */
+
+       struct timespec speed_last_time; /* for TS speed check */
+       uint32_t speed_pkts_cnt; /* for TS speed check */
 };
 
 int dvb_dmx_init(struct dvb_demux *dvbdemux);
index 9808241..0746122 100644 (file)
@@ -895,104 +895,27 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
 }
 
 static struct dtv_cmds_h dtv_cmds[] = {
-       [DTV_TUNE] = {
-               .name   = "DTV_TUNE",
-               .cmd    = DTV_TUNE,
-               .set    = 1,
-       },
-       [DTV_CLEAR] = {
-               .name   = "DTV_CLEAR",
-               .cmd    = DTV_CLEAR,
-               .set    = 1,
-       },
+       _DTV_CMD(DTV_TUNE, 1, 0),
+       _DTV_CMD(DTV_CLEAR, 1, 0),
 
        /* Set */
-       [DTV_FREQUENCY] = {
-               .name   = "DTV_FREQUENCY",
-               .cmd    = DTV_FREQUENCY,
-               .set    = 1,
-       },
-       [DTV_BANDWIDTH_HZ] = {
-               .name   = "DTV_BANDWIDTH_HZ",
-               .cmd    = DTV_BANDWIDTH_HZ,
-               .set    = 1,
-       },
-       [DTV_MODULATION] = {
-               .name   = "DTV_MODULATION",
-               .cmd    = DTV_MODULATION,
-               .set    = 1,
-       },
-       [DTV_INVERSION] = {
-               .name   = "DTV_INVERSION",
-               .cmd    = DTV_INVERSION,
-               .set    = 1,
-       },
-       [DTV_DISEQC_MASTER] = {
-               .name   = "DTV_DISEQC_MASTER",
-               .cmd    = DTV_DISEQC_MASTER,
-               .set    = 1,
-               .buffer = 1,
-       },
-       [DTV_SYMBOL_RATE] = {
-               .name   = "DTV_SYMBOL_RATE",
-               .cmd    = DTV_SYMBOL_RATE,
-               .set    = 1,
-       },
-       [DTV_INNER_FEC] = {
-               .name   = "DTV_INNER_FEC",
-               .cmd    = DTV_INNER_FEC,
-               .set    = 1,
-       },
-       [DTV_VOLTAGE] = {
-               .name   = "DTV_VOLTAGE",
-               .cmd    = DTV_VOLTAGE,
-               .set    = 1,
-       },
-       [DTV_TONE] = {
-               .name   = "DTV_TONE",
-               .cmd    = DTV_TONE,
-               .set    = 1,
-       },
-       [DTV_PILOT] = {
-               .name   = "DTV_PILOT",
-               .cmd    = DTV_PILOT,
-               .set    = 1,
-       },
-       [DTV_ROLLOFF] = {
-               .name   = "DTV_ROLLOFF",
-               .cmd    = DTV_ROLLOFF,
-               .set    = 1,
-       },
-       [DTV_DELIVERY_SYSTEM] = {
-               .name   = "DTV_DELIVERY_SYSTEM",
-               .cmd    = DTV_DELIVERY_SYSTEM,
-               .set    = 1,
-       },
-       [DTV_HIERARCHY] = {
-               .name   = "DTV_HIERARCHY",
-               .cmd    = DTV_HIERARCHY,
-               .set    = 1,
-       },
-       [DTV_CODE_RATE_HP] = {
-               .name   = "DTV_CODE_RATE_HP",
-               .cmd    = DTV_CODE_RATE_HP,
-               .set    = 1,
-       },
-       [DTV_CODE_RATE_LP] = {
-               .name   = "DTV_CODE_RATE_LP",
-               .cmd    = DTV_CODE_RATE_LP,
-               .set    = 1,
-       },
-       [DTV_GUARD_INTERVAL] = {
-               .name   = "DTV_GUARD_INTERVAL",
-               .cmd    = DTV_GUARD_INTERVAL,
-               .set    = 1,
-       },
-       [DTV_TRANSMISSION_MODE] = {
-               .name   = "DTV_TRANSMISSION_MODE",
-               .cmd    = DTV_TRANSMISSION_MODE,
-               .set    = 1,
-       },
+       _DTV_CMD(DTV_FREQUENCY, 1, 0),
+       _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
+       _DTV_CMD(DTV_MODULATION, 1, 0),
+       _DTV_CMD(DTV_INVERSION, 1, 0),
+       _DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
+       _DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
+       _DTV_CMD(DTV_INNER_FEC, 1, 0),
+       _DTV_CMD(DTV_VOLTAGE, 1, 0),
+       _DTV_CMD(DTV_TONE, 1, 0),
+       _DTV_CMD(DTV_PILOT, 1, 0),
+       _DTV_CMD(DTV_ROLLOFF, 1, 0),
+       _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
+       _DTV_CMD(DTV_HIERARCHY, 1, 0),
+       _DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
+       _DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
+       _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
+       _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
 
        _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
        _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
@@ -1035,43 +958,13 @@ static struct dtv_cmds_h dtv_cmds[] = {
        _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
 
        /* Get */
-       [DTV_DISEQC_SLAVE_REPLY] = {
-               .name   = "DTV_DISEQC_SLAVE_REPLY",
-               .cmd    = DTV_DISEQC_SLAVE_REPLY,
-               .set    = 0,
-               .buffer = 1,
-       },
-
-       [DTV_API_VERSION] = {
-               .name   = "DTV_API_VERSION",
-               .cmd    = DTV_API_VERSION,
-               .set    = 0,
-       },
-       [DTV_CODE_RATE_HP] = {
-               .name   = "DTV_CODE_RATE_HP",
-               .cmd    = DTV_CODE_RATE_HP,
-               .set    = 0,
-       },
-       [DTV_CODE_RATE_LP] = {
-               .name   = "DTV_CODE_RATE_LP",
-               .cmd    = DTV_CODE_RATE_LP,
-               .set    = 0,
-       },
-       [DTV_GUARD_INTERVAL] = {
-               .name   = "DTV_GUARD_INTERVAL",
-               .cmd    = DTV_GUARD_INTERVAL,
-               .set    = 0,
-       },
-       [DTV_TRANSMISSION_MODE] = {
-               .name   = "DTV_TRANSMISSION_MODE",
-               .cmd    = DTV_TRANSMISSION_MODE,
-               .set    = 0,
-       },
-       [DTV_HIERARCHY] = {
-               .name   = "DTV_HIERARCHY",
-               .cmd    = DTV_HIERARCHY,
-               .set    = 0,
-       },
+       _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
+       _DTV_CMD(DTV_API_VERSION, 0, 0),
+       _DTV_CMD(DTV_CODE_RATE_HP, 0, 0),
+       _DTV_CMD(DTV_CODE_RATE_LP, 0, 0),
+       _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
+       _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
+       _DTV_CMD(DTV_HIERARCHY, 0, 0),
 };
 
 static void dtv_property_dump(struct dtv_property *tvp)
@@ -1712,7 +1605,18 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
        struct dvb_device *dvbdev = file->private_data;
        struct dvb_frontend *fe = dvbdev->priv;
        struct dvb_frontend_private *fepriv = fe->frontend_priv;
-       int err = -EOPNOTSUPP;
+       int cb_err, err = -EOPNOTSUPP;
+
+       if (fe->dvb->fe_ioctl_override) {
+               cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
+                                                   DVB_FE_IOCTL_PRE);
+               if (cb_err < 0)
+                       return cb_err;
+               if (cb_err > 0)
+                       return 0;
+               /* fe_ioctl_override returning 0 allows
+                * dvb-core to continue handling the ioctl */
+       }
 
        switch (cmd) {
        case FE_GET_INFO: {
@@ -1978,6 +1882,13 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file,
                break;
        };
 
+       if (fe->dvb->fe_ioctl_override) {
+               cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
+                                                   DVB_FE_IOCTL_POST);
+               if (cb_err < 0)
+                       return cb_err;
+       }
+
        return err;
 }
 
index 810f07d..52e4ce4 100644 (file)
@@ -160,7 +160,7 @@ struct tuner_state {
  * search callback possible return status
  *
  * DVBFE_ALGO_SEARCH_SUCCESS
- * The frontend search algorithm completed and returned succesfully
+ * The frontend search algorithm completed and returned successfully
  *
  * DVBFE_ALGO_SEARCH_ASLEEP
  * The frontend search algorithm is sleeping
index 0241a7c..8b8558f 100644 (file)
@@ -1396,7 +1396,7 @@ static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
                return ret;
        }
 
-       /* binary compatiblity cruft */
+       /* binary compatibility cruft */
        case __NET_ADD_IF_OLD:
        {
                struct __dvb_net_if_old *dvbnetif = parg;
index 01fc704..f7b499d 100644 (file)
@@ -54,6 +54,8 @@
        module_param_array(adapter_nr, short, NULL, 0444); \
        MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")
 
+struct dvb_frontend;
+
 struct dvb_adapter {
        int num;
        struct list_head list_head;
@@ -69,6 +71,32 @@ struct dvb_adapter {
        int mfe_shared;                 /* indicates mutually exclusive frontends */
        struct dvb_device *mfe_dvbdev;  /* frontend device in use */
        struct mutex mfe_lock;          /* access lock for thread creation */
+
+       /* Allow the adapter/bridge driver to perform an action before and/or
+        * after the core handles an ioctl:
+        *
+        * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled.
+        * DVB_FE_IOCTL_POST indicates that the ioctl has been handled.
+        *
+        * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg:
+        *
+        * return 0 to allow dvb-core to handle the ioctl.
+        * return a positive int to prevent dvb-core from handling the ioctl,
+        *      and exit without error.
+        * return a negative int to prevent dvb-core from handling the ioctl,
+        *      and return that value as an error.
+        *
+        * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg:
+        *
+        * return 0 to allow the dvb_frontend ioctl handler to exit normally.
+        * return a negative int to cause the dvb_frontend ioctl handler to
+        *      return that value as an error.
+        */
+#define DVB_FE_IOCTL_PRE 0
+#define DVB_FE_IOCTL_POST 1
+       int (*fe_ioctl_override)(struct dvb_frontend *fe,
+                                unsigned int cmd, void *parg,
+                                unsigned int stage);
 };
 
 
index 0e4b97f..2dee1bf 100644 (file)
@@ -322,3 +322,11 @@ config DVB_USB_FRIIO
        depends on DVB_USB
        help
          Say Y here to support the Japanese DTV receiver Friio.
+
+config DVB_USB_EC168
+       tristate "E3C EC168 DVB-T USB2.0 support"
+       depends on DVB_USB && EXPERIMENTAL
+       select DVB_EC100
+       select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+       help
+         Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
index 85b83a4..72c92cb 100644 (file)
@@ -82,6 +82,9 @@ obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
 dvb-usb-friio-objs = friio.o friio-fe.o
 obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
 
+dvb-usb-ec168-objs = ec168.o
+obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
index cf042b3..8b60a60 100644 (file)
@@ -730,7 +730,7 @@ static int af9015_read_config(struct usb_device *udev)
                goto error;
        deb_info("%s: IR mode:%d\n", __func__, val);
        for (i = 0; i < af9015_properties_count; i++) {
-               if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+               if (val == AF9015_IR_MODE_DISABLED) {
                        af9015_properties[i].rc_key_map = NULL;
                        af9015_properties[i].rc_key_map_size  = 0;
                } else if (dvb_usb_af9015_remote) {
@@ -868,6 +868,16 @@ static int af9015_read_config(struct usb_device *udev)
                                af9015_config.ir_table_size =
                                  ARRAY_SIZE(af9015_ir_table_avermedia);
                                break;
+                       case USB_VID_MSI_2:
+                               af9015_properties[i].rc_key_map =
+                                 af9015_rc_keys_msi_digivox_iii;
+                               af9015_properties[i].rc_key_map_size =
+                                 ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii);
+                               af9015_config.ir_table =
+                                 af9015_ir_table_msi_digivox_iii;
+                               af9015_config.ir_table_size =
+                                 ARRAY_SIZE(af9015_ir_table_msi_digivox_iii);
+                               break;
                        }
                }
        }
@@ -1283,6 +1293,8 @@ static struct usb_device_id af9015_usb_table[] = {
        {USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_MC810)},
        {USB_DEVICE(USB_VID_KYE,       USB_PID_GENIUS_TVGO_DVB_T03)},
 /* 25 */{USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_399U_2)},
+       {USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_PC160_T)},
+       {USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_SVEON_STV20)},
        {0},
 };
 MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1296,7 +1308,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
                .firmware = "dvb-usb-af9015.fw",
                .no_reconnect = 1,
 
-               .size_of_priv = sizeof(struct af9015_state), \
+               .size_of_priv = sizeof(struct af9015_state),
 
                .num_adapters = 2,
                .adapter = {
@@ -1402,7 +1414,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
                .firmware = "dvb-usb-af9015.fw",
                .no_reconnect = 1,
 
-               .size_of_priv = sizeof(struct af9015_state), \
+               .size_of_priv = sizeof(struct af9015_state),
 
                .num_adapters = 2,
                .adapter = {
@@ -1508,7 +1520,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
                .firmware = "dvb-usb-af9015.fw",
                .no_reconnect = 1,
 
-               .size_of_priv = sizeof(struct af9015_state), \
+               .size_of_priv = sizeof(struct af9015_state),
 
                .num_adapters = 2,
                .adapter = {
@@ -1554,7 +1566,7 @@ static struct dvb_usb_device_properties af9015_properties[] = {
 
                .i2c_algo = &af9015_i2c_algo,
 
-               .num_device_descs = 4, /* max 9 */
+               .num_device_descs = 6, /* max 9 */
                .devices = {
                        {
                                .name = "AverMedia AVerTV Volar GPS 805 (A805)",
@@ -1577,6 +1589,17 @@ static struct dvb_usb_device_properties af9015_properties[] = {
                                .cold_ids = {&af9015_usb_table[24], NULL},
                                .warm_ids = {NULL},
                        },
+                       {
+                               .name = "KWorld PlusTV DVB-T PCI Pro Card " \
+                                       "(DVB-T PC160-T)",
+                               .cold_ids = {&af9015_usb_table[26], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "Sveon STV20 Tuner USB DVB-T HDTV",
+                               .cold_ids = {&af9015_usb_table[27], NULL},
+                               .warm_ids = {NULL},
+                       },
                }
        },
 };
index c41f30e..931c851 100644 (file)
@@ -95,6 +95,7 @@ enum af9015_ir_mode {
        AF9015_IR_MODE_HID,
        AF9015_IR_MODE_RLC,
        AF9015_IR_MODE_RC6,
+       AF9015_IR_MODE_POLLING, /* just guess */
 };
 
 struct af9015_state {
@@ -119,6 +120,7 @@ enum af9015_remote {
 /* 5 */        AF9015_REMOTE_AVERMEDIA_KS,
 };
 
+/* LeadTek - Y04G0051 */
 /* Leadtek WinFast DTV Dongle Gold */
 static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
        { 0x001e, KEY_1 },
@@ -131,64 +133,96 @@ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
        { 0x0025, KEY_8 },
        { 0x0026, KEY_9 },
        { 0x0027, KEY_0 },
-       { 0x0028, KEY_ENTER },
-       { 0x004f, KEY_VOLUMEUP },
-       { 0x0050, KEY_VOLUMEDOWN },
-       { 0x0051, KEY_CHANNELDOWN },
-       { 0x0052, KEY_CHANNELUP },
+       { 0x0028, KEY_OK },
+       { 0x004f, KEY_RIGHT },
+       { 0x0050, KEY_LEFT },
+       { 0x0051, KEY_DOWN },
+       { 0x0052, KEY_UP },
+       { 0x011a, KEY_POWER2 },
+       { 0x04b4, KEY_TV },
+       { 0x04b3, KEY_RED },
+       { 0x04b2, KEY_GREEN },
+       { 0x04b1, KEY_YELLOW },
+       { 0x04b0, KEY_BLUE },
+       { 0x003d, KEY_TEXT },
+       { 0x0113, KEY_SLEEP },
+       { 0x0010, KEY_MUTE },
+       { 0x0105, KEY_ESC },
+       { 0x0009, KEY_SCREEN },
+       { 0x010f, KEY_MENU },
+       { 0x003f, KEY_CHANNEL },
+       { 0x0013, KEY_REWIND },
+       { 0x0012, KEY_PLAY },
+       { 0x0011, KEY_FASTFORWARD },
+       { 0x0005, KEY_PREVIOUS },
+       { 0x0029, KEY_STOP },
+       { 0x002b, KEY_NEXT },
+       { 0x0041, KEY_EPG },
+       { 0x0019, KEY_VIDEO },
+       { 0x0016, KEY_AUDIO },
+       { 0x0037, KEY_DOT },
+       { 0x002a, KEY_AGAIN },
+       { 0x002c, KEY_CAMERA },
+       { 0x003c, KEY_NEW },
+       { 0x0115, KEY_RECORD },
+       { 0x010b, KEY_TIME },
+       { 0x0043, KEY_VOLUMEUP },
+       { 0x0042, KEY_VOLUMEDOWN },
+       { 0x004b, KEY_CHANNELUP },
+       { 0x004e, KEY_CHANNELDOWN },
 };
 
 static u8 af9015_ir_table_leadtek[] = {
-       0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
-       0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
-       0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
-       0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
-       0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
-       0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
-       0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
-       0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
-       0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
-       0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
-       0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
-       0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
-       0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
-       0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
-       0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
-       0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
-       0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
-       0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
-       0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
-       0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
-       0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
-       0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
-       0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
-       0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
-       0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
-       0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
-       0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
-       0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
-       0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
-       0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
-       0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
-       0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
-       0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
-       0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
-       0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
-       0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
-       0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
-       0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
-       0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
-       0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
-       0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
-       0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
-       0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
-       0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
-       0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
-       0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
-       0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
-       0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
-       0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
-       0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+       0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, /* KEY_POWER2 */
+       0x03, 0xfc, 0x56, 0xa9, 0xb4, 0x04, 0x00, /* KEY_TV */
+       0x03, 0xfc, 0x4b, 0xb4, 0xb3, 0x04, 0x00, /* KEY_RED */
+       0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, /* KEY_GREEN */
+       0x03, 0xfc, 0x4d, 0xb2, 0xb1, 0x04, 0x00, /* KEY_YELLOW */
+       0x03, 0xfc, 0x4e, 0xb1, 0xb0, 0x04, 0x00, /* KEY_BLUE */
+       0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, /* KEY_TEXT */
+       0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, /* KEY_SLEEP */
+       0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, /* KEY_MUTE */
+       0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, /* KEY_ESC */
+       0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, /* KEY_STOP (1)*/
+       0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, /* KEY_UP */
+       0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, /* KEY_SCREEN */
+       0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, /* KEY_LEFT */
+       0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, /* KEY_OK (1) */
+       0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, /* KEY_RIGHT */
+       0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, /* KEY_MENU */
+       0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, /* KEY_DOWN */
+       0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, /* KEY_CHANNEL */
+       0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, /* KEY_REWIND */
+       0x03, 0xfc, 0x43, 0xbc, 0x12, 0x00, 0x00, /* KEY_PLAY */
+       0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, /* KEY_FASTFORWARD */
+       0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, /* KEY_VIDEO (1) */
+       0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, /* KEY_PREVIOUS */
+       0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, /* KEY_STOP (2) */
+       0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, /* KEY_NEXT */
+       0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, /* KEY_EPG */
+       0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, /* KEY_1 */
+       0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, /* KEY_2 */
+       0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, /* KEY_3 */
+       0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, /* KEY_VIDEO (2) */
+       0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, /* KEY_4 */
+       0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, /* KEY_5 */
+       0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, /* KEY_6 */
+       0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, /* KEY_AUDIO */
+       0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, /* KEY_7 */
+       0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, /* KEY_8 */
+       0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, /* KEY_9 */
+       0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* KEY_OK (2) */
+       0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, /* KEY_DOT */
+       0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, /* KEY_0 */
+       0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, /* KEY_AGAIN */
+       0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, /* KEY_CAMERA */
+       0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, /* KEY_NEW */
+       0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, /* KEY_RECORD */
+       0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, /* KEY_TIME */
+       0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, /* KEY_VOLUMEUP */
+       0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, /* KEY_VOLUMEDOWN */
+       0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, /* KEY_CHANNELUP */
+       0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, /* KEY_CHANNELDOWN */
 };
 
 /* TwinHan AzureWave AD-TU700(704J) */
@@ -746,4 +780,75 @@ static u8 af9015_ir_table_trekstor[] = {
        0x00, 0xff, 0x84, 0x7b, 0x27, 0x07, 0x00,
 };
 
+/* MSI DIGIVOX mini III */
+static struct dvb_usb_rc_key af9015_rc_keys_msi_digivox_iii[] = {
+       { 0x0713, KEY_POWER },       /* [red power button] */
+       { 0x073b, KEY_VIDEO },       /* Source */
+       { 0x073e, KEY_ZOOM },        /* Zoom */
+       { 0x070b, KEY_POWER2 },      /* ShutDown */
+       { 0x071e, KEY_1 },
+       { 0x071f, KEY_2 },
+       { 0x0720, KEY_3 },
+       { 0x0721, KEY_4 },
+       { 0x0722, KEY_5 },
+       { 0x0723, KEY_6 },
+       { 0x0724, KEY_7 },
+       { 0x0725, KEY_8 },
+       { 0x0726, KEY_9 },
+       { 0x0727, KEY_0 },
+       { 0x0752, KEY_CHANNELUP },   /* CH+ */
+       { 0x0751, KEY_CHANNELDOWN }, /* CH- */
+       { 0x0750, KEY_VOLUMEUP },    /* Vol+ */
+       { 0x074f, KEY_VOLUMEDOWN },  /* Vol- */
+       { 0x0705, KEY_ESC },         /* [back up arrow] */
+       { 0x0708, KEY_OK },          /* [enter arrow] */
+       { 0x073f, KEY_RECORD },      /* Rec */
+       { 0x0716, KEY_STOP },        /* Stop */
+       { 0x072a, KEY_PLAY },        /* Play */
+       { 0x073c, KEY_MUTE },        /* Mute */
+       { 0x0718, KEY_UP },
+       { 0x0707, KEY_DOWN },
+       { 0x070f, KEY_LEFT },
+       { 0x0715, KEY_RIGHT },
+       { 0x0736, KEY_RED },
+       { 0x0737, KEY_GREEN },
+       { 0x072d, KEY_YELLOW },
+       { 0x072e, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_msi_digivox_iii[] = {
+       0x61, 0xd6, 0x43, 0xbc, 0x13, 0x07, 0x00, /* KEY_POWER */
+       0x61, 0xd6, 0x01, 0xfe, 0x3b, 0x07, 0x00, /* KEY_VIDEO */
+       0x61, 0xd6, 0x0b, 0xf4, 0x3e, 0x07, 0x00, /* KEY_ZOOM */
+       0x61, 0xd6, 0x03, 0xfc, 0x0b, 0x07, 0x00, /* KEY_POWER2 */
+       0x61, 0xd6, 0x04, 0xfb, 0x1e, 0x07, 0x00, /* KEY_1 */
+       0x61, 0xd6, 0x08, 0xf7, 0x1f, 0x07, 0x00, /* KEY_2 */
+       0x61, 0xd6, 0x02, 0xfd, 0x20, 0x07, 0x00, /* KEY_3 */
+       0x61, 0xd6, 0x0f, 0xf0, 0x21, 0x07, 0x00, /* KEY_4 */
+       0x61, 0xd6, 0x05, 0xfa, 0x22, 0x07, 0x00, /* KEY_5 */
+       0x61, 0xd6, 0x06, 0xf9, 0x23, 0x07, 0x00, /* KEY_6 */
+       0x61, 0xd6, 0x0c, 0xf3, 0x24, 0x07, 0x00, /* KEY_7 */
+       0x61, 0xd6, 0x0d, 0xf2, 0x25, 0x07, 0x00, /* KEY_8 */
+       0x61, 0xd6, 0x0a, 0xf5, 0x26, 0x07, 0x00, /* KEY_9 */
+       0x61, 0xd6, 0x11, 0xee, 0x27, 0x07, 0x00, /* KEY_0 */
+       0x61, 0xd6, 0x09, 0xf6, 0x52, 0x07, 0x00, /* KEY_CHANNELUP */
+       0x61, 0xd6, 0x07, 0xf8, 0x51, 0x07, 0x00, /* KEY_CHANNELDOWN */
+       0x61, 0xd6, 0x0e, 0xf1, 0x50, 0x07, 0x00, /* KEY_VOLUMEUP */
+       0x61, 0xd6, 0x13, 0xec, 0x4f, 0x07, 0x00, /* KEY_VOLUMEDOWN */
+       0x61, 0xd6, 0x10, 0xef, 0x05, 0x07, 0x00, /* KEY_ESC */
+       0x61, 0xd6, 0x12, 0xed, 0x08, 0x07, 0x00, /* KEY_OK */
+       0x61, 0xd6, 0x14, 0xeb, 0x3f, 0x07, 0x00, /* KEY_RECORD */
+       0x61, 0xd6, 0x15, 0xea, 0x16, 0x07, 0x00, /* KEY_STOP */
+       0x61, 0xd6, 0x16, 0xe9, 0x2a, 0x07, 0x00, /* KEY_PLAY */
+       0x61, 0xd6, 0x17, 0xe8, 0x3c, 0x07, 0x00, /* KEY_MUTE */
+       0x61, 0xd6, 0x18, 0xe7, 0x18, 0x07, 0x00, /* KEY_UP */
+       0x61, 0xd6, 0x19, 0xe6, 0x07, 0x07, 0x00, /* KEY_DOWN */
+       0x61, 0xd6, 0x1a, 0xe5, 0x0f, 0x07, 0x00, /* KEY_LEFT */
+       0x61, 0xd6, 0x1b, 0xe4, 0x15, 0x07, 0x00, /* KEY_RIGHT */
+       0x61, 0xd6, 0x1c, 0xe3, 0x36, 0x07, 0x00, /* KEY_RED */
+       0x61, 0xd6, 0x1d, 0xe2, 0x37, 0x07, 0x00, /* KEY_GREEN */
+       0x61, 0xd6, 0x1e, 0xe1, 0x2d, 0x07, 0x00, /* KEY_YELLOW */
+       0x61, 0xd6, 0x1f, 0xe0, 0x2e, 0x07, 0x00, /* KEY_BLUE */
+};
+
 #endif
index 2ae7f64..bb69f37 100644 (file)
@@ -344,7 +344,7 @@ static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
        if (ret)
                return ret;
 
-       err("Unkown Anysee version: %02x %02x %02x. "\
+       err("Unknown Anysee version: %02x %02x %02x. "\
            "Please report the <linux-dvb@linuxtv.org>.",
            hw_info[0], hw_info[1], hw_info[2]);
 
index 2a53dd0..05fb28e 100644 (file)
 #include "tuner-xc2028.h"
 #include "tuner-simple.h"
 #include "mxl5005s.h"
+#include "max2165.h"
 #include "dib7000p.h"
 #include "dib0070.h"
 #include "lgs8gxx.h"
+#include "atbm8830.h"
 
 /* debug */
 static int dvb_usb_cxusb_debug;
@@ -714,6 +716,11 @@ static struct mxl5005s_config d680_dmb_tuner = {
        .AgcMasterByte   = 0x00,
 };
 
+static struct max2165_config mygica_d689_max2165_cfg = {
+       .i2c_address = 0x60,
+       .osc_clk = 20
+};
+
 /* Callbacks for DVB USB */
 static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap)
 {
@@ -813,6 +820,14 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap)
        return (fe == NULL) ? -EIO : 0;
 }
 
+static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       struct dvb_frontend *fe;
+       fe = dvb_attach(max2165_attach, adap->fe,
+                       &adap->dev->i2c_adap, &mygica_d689_max2165_cfg);
+       return (fe == NULL) ? -EIO : 0;
+}
+
 static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap)
 {
        u8 b;
@@ -1160,6 +1175,55 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
        return 0;
 }
 
+static struct atbm8830_config mygica_d689_atbm8830_cfg = {
+       .prod = ATBM8830_PROD_8830,
+       .demod_address = 0x40,
+       .serial_ts = 0,
+       .ts_sampling_edge = 1,
+       .ts_clk_gated = 0,
+       .osc_clk_freq = 30400, /* in kHz */
+       .if_freq = 0, /* zero IF */
+       .zif_swap_iq = 1,
+};
+
+static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       struct dvb_usb_device *d = adap->dev;
+
+       /* Select required USB configuration */
+       if (usb_set_interface(d->udev, 0, 0) < 0)
+               err("set interface failed");
+
+       /* Unblock all USB pipes */
+       usb_clear_halt(d->udev,
+               usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+       usb_clear_halt(d->udev,
+               usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint));
+       usb_clear_halt(d->udev,
+               usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint));
+
+
+       /* Reset the tuner */
+       if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) {
+               err("clear tuner gpio failed");
+               return -EIO;
+       }
+       msleep(100);
+       if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) {
+               err("set tuner gpio failed");
+               return -EIO;
+       }
+       msleep(100);
+
+       /* Attach frontend */
+       adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg,
+               &d->i2c_adap);
+       if (adap->fe == NULL)
+               return -EIO;
+
+       return 0;
+}
+
 /*
  * DViCO has shipped two devices with the same USB ID, but only one of them
  * needs a firmware download.  Check the device class details to see if they
@@ -1240,6 +1304,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties;
 static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties;
 static struct dvb_usb_device_properties cxusb_aver_a868r_properties;
 static struct dvb_usb_device_properties cxusb_d680_dmb_properties;
+static struct dvb_usb_device_properties cxusb_mygica_d689_properties;
 
 static int cxusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
@@ -1268,6 +1333,8 @@ static int cxusb_probe(struct usb_interface *intf,
                                     THIS_MODULE, NULL, adapter_nr) ||
            0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties,
                                     THIS_MODULE, NULL, adapter_nr) ||
+           0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties,
+                                    THIS_MODULE, NULL, adapter_nr) ||
            0)
                return 0;
 
@@ -1294,6 +1361,7 @@ static struct usb_device_id cxusb_table [] = {
        { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) },
        { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) },
        { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) },
+       { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) },
        {}              /* Terminating entry */
 };
 MODULE_DEVICE_TABLE (usb, cxusb_table);
@@ -1837,6 +1905,55 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = {
        }
 };
 
+static struct dvb_usb_device_properties cxusb_mygica_d689_properties = {
+       .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+       .usb_ctrl         = CYPRESS_FX2,
+
+       .size_of_priv     = sizeof(struct cxusb_state),
+
+       .num_adapters = 1,
+       .adapter = {
+               {
+                       .streaming_ctrl   = cxusb_d680_dmb_streaming_ctrl,
+                       .frontend_attach  = cxusb_mygica_d689_frontend_attach,
+                       .tuner_attach     = cxusb_mygica_d689_tuner_attach,
+
+                       /* parameter for the MPEG2-data transfer */
+                       .stream = {
+                               .type = USB_BULK,
+                               .count = 5,
+                               .endpoint = 0x02,
+                               .u = {
+                                       .bulk = {
+                                               .buffersize = 8192,
+                                       }
+                               }
+                       },
+               },
+       },
+
+       .power_ctrl       = cxusb_d680_dmb_power_ctrl,
+
+       .i2c_algo         = &cxusb_i2c_algo,
+
+       .generic_bulk_ctrl_endpoint = 0x01,
+
+       .rc_interval      = 100,
+       .rc_key_map       = d680_dmb_rc_keys,
+       .rc_key_map_size  = ARRAY_SIZE(d680_dmb_rc_keys),
+       .rc_query         = cxusb_d680_dmb_rc_query,
+
+       .num_device_descs = 1,
+       .devices = {
+               {
+                       "Mygica D689 DMB-TH",
+                       { NULL },
+                       { &cxusb_table[19], NULL },
+               },
+       }
+};
+
 static struct usb_driver cxusb_driver = {
        .name           = "dvb_usb_cxusb",
        .probe          = cxusb_probe,
index 6bd8951..684146f 100644 (file)
@@ -558,8 +558,7 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event,
 struct dib0700_rc_response {
        u8 report_id;
        u8 data_state;
-       u8 system_msb;
-       u8 system_lsb;
+       u16 system;
        u8 data;
        u8 not_data;
 };
@@ -589,37 +588,50 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
                return 0;
        }
 
-       if (actlen != sizeof(buf)) {
-               /* We didn't get back the 6 byte message we expected */
-               err("Unexpected RC response size [%d]", actlen);
-               return -1;
-       }
 
-       poll_reply.report_id  = buf[0];
-       poll_reply.data_state = buf[1];
-       poll_reply.system_msb = buf[2];
-       poll_reply.system_lsb = buf[3];
-       poll_reply.data       = buf[4];
-       poll_reply.not_data   = buf[5];
+       switch (dvb_usb_dib0700_ir_proto) {
+       case 0:
+               poll_reply.report_id  = 0;
+               poll_reply.data_state = 1;
+               poll_reply.system     = buf[2];
+               poll_reply.data       = buf[4];
+               poll_reply.not_data   = buf[5];
+
+               /* NEC protocol sends repeat code as 0 0 0 FF */
+               if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00)
+                   && (poll_reply.not_data == 0xff)) {
+                       poll_reply.data_state = 2;
+                       break;
+               }
+               break;
+       default:
+               if (actlen != sizeof(buf)) {
+                       /* We didn't get back the 6 byte message we expected */
+                       err("Unexpected RC response size [%d]", actlen);
+                       return -1;
+               }
+
+               poll_reply.report_id  = buf[0];
+               poll_reply.data_state = buf[1];
+               poll_reply.system     = (buf[2] << 8) | buf[3];
+               poll_reply.data       = buf[4];
+               poll_reply.not_data   = buf[5];
 
-       /*
-       info("rid=%02x ds=%02x sm=%02x sl=%02x d=%02x nd=%02x\n",
-            poll_reply.report_id, poll_reply.data_state,
-            poll_reply.system_msb, poll_reply.system_lsb,
-            poll_reply.data, poll_reply.not_data);
-       */
+               break;
+       }
 
        if ((poll_reply.data + poll_reply.not_data) != 0xff) {
                /* Key failed integrity check */
-               err("key failed integrity check: %02x %02x %02x %02x",
-                   poll_reply.system_msb, poll_reply.system_lsb,
+               err("key failed integrity check: %04x %02x %02x",
+                   poll_reply.system,
                    poll_reply.data, poll_reply.not_data);
                return -1;
        }
 
+
        /* Find the key in the map */
        for (i = 0; i < d->props.rc_key_map_size; i++) {
-               if (rc5_custom(&keymap[i]) == poll_reply.system_lsb &&
+               if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) &&
                    rc5_data(&keymap[i]) == poll_reply.data) {
                        *event = keymap[i].event;
                        found = 1;
@@ -628,8 +640,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event,
        }
 
        if (found == 0) {
-               err("Unknown remote controller key: %02x %02x %02x %02x",
-                   poll_reply.system_msb, poll_reply.system_lsb,
+               err("Unknown remote controller key: %04x %02x %02x",
+                   poll_reply.system,
                    poll_reply.data, poll_reply.not_data);
                d->last_event = 0;
                return 0;
@@ -874,6 +886,49 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = {
        { 0x1d37, KEY_RECORD },
        { 0x1d3b, KEY_GOTO },
        { 0x1d3d, KEY_POWER },
+
+       /* Key codes for the Pixelview SBTVD remote (proto NEC) */
+       { 0x8613, KEY_MUTE },
+       { 0x8612, KEY_POWER },
+       { 0x8601, KEY_1 },
+       { 0x8602, KEY_2 },
+       { 0x8603, KEY_3 },
+       { 0x8604, KEY_4 },
+       { 0x8605, KEY_5 },
+       { 0x8606, KEY_6 },
+       { 0x8607, KEY_7 },
+       { 0x8608, KEY_8 },
+       { 0x8609, KEY_9 },
+       { 0x8600, KEY_0 },
+       { 0x860d, KEY_CHANNELUP },
+       { 0x8619, KEY_CHANNELDOWN },
+       { 0x8610, KEY_VOLUMEUP },
+       { 0x860c, KEY_VOLUMEDOWN },
+
+       { 0x860a, KEY_CAMERA },
+       { 0x860b, KEY_ZOOM },
+       { 0x861b, KEY_BACKSPACE },
+       { 0x8615, KEY_ENTER },
+
+       { 0x861d, KEY_UP },
+       { 0x861e, KEY_DOWN },
+       { 0x860e, KEY_LEFT },
+       { 0x860f, KEY_RIGHT },
+
+       { 0x8618, KEY_RECORD },
+       { 0x861a, KEY_STOP },
+
+       /* Key codes for the EvolutePC TVWay+ remote (proto NEC) */
+       { 0x7a00, KEY_MENU },
+       { 0x7a01, KEY_RECORD },
+       { 0x7a02, KEY_PLAY },
+       { 0x7a03, KEY_STOP },
+       { 0x7a10, KEY_CHANNELUP },
+       { 0x7a11, KEY_CHANNELDOWN },
+       { 0x7a12, KEY_VOLUMEUP },
+       { 0x7a13, KEY_VOLUMEDOWN },
+       { 0x7a40, KEY_POWER },
+       { 0x7a41, KEY_MUTE },
 };
 
 /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */
@@ -1133,6 +1188,7 @@ static struct dib0070_config dib7770p_dib0070_config = {
         .clock_khz = 12000,
         .clock_pad_drive = 0,
         .flip_chip = 1,
+        .charge_pump = 2,
 };
 
 static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep)
@@ -1209,6 +1265,16 @@ static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap)
        return 0;
 }
 
+static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
+{
+    return dib7000p_pid_filter(adapter->fe, index, pid, onoff);
+}
+
+static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
+{
+    return dib7000p_pid_filter_ctrl(adapter->fe, onoff);
+}
+
 static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = {
        60000, 15000, // internal, sampling
        1, 20, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass
@@ -1500,6 +1566,15 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap)
        return 0;
 }
 
+static int stk807x_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff)
+{
+    return dib8000_pid_filter(adapter->fe, index, pid, onoff);
+}
+
+static int stk807x_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff)
+{
+    return dib8000_pid_filter_ctrl(adapter->fe, onoff);
+}
 
 /* STK807x */
 static int stk807x_frontend_attach(struct dvb_usb_adapter *adap)
@@ -1861,6 +1936,7 @@ struct usb_device_id dib0700_usb_id_table[] = {
        { USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_STK807XPVR) },
        { USB_DEVICE(USB_VID_DIBCOM,    USB_PID_DIBCOM_STK807XP) },
        { USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) },
+       { USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) },
        { 0 }           /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -1895,6 +1971,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7700p_frontend_attach,
                                .tuner_attach     = stk7700p_tuner_attach,
 
@@ -1976,11 +2056,19 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 2,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7700d_frontend_attach,
                                .tuner_attach     = stk7700d_tuner_attach,
 
                                DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
                        }, {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7700d_frontend_attach,
                                .tuner_attach     = stk7700d_tuner_attach,
 
@@ -2023,6 +2111,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7700P2_frontend_attach,
                                .tuner_attach     = stk7700d_tuner_attach,
 
@@ -2055,6 +2147,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7070p_frontend_attach,
                                .tuner_attach     = dib7070p_tuner_attach,
 
@@ -2122,6 +2218,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7070p_frontend_attach,
                                .tuner_attach     = dib7070p_tuner_attach,
 
@@ -2157,6 +2257,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 2,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7070pd_frontend_attach0,
                                .tuner_attach     = dib7070p_tuner_attach,
 
@@ -2164,6 +2268,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
 
                                .size_of_priv     = sizeof(struct dib0700_adapter_state),
                        }, {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7070pd_frontend_attach1,
                                .tuner_attach     = dib7070p_tuner_attach,
 
@@ -2210,6 +2318,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7700ph_frontend_attach,
                                .tuner_attach     = stk7700ph_tuner_attach,
 
@@ -2322,6 +2434,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter       = stk70x0p_pid_filter,
+                               .pid_filter_ctrl  = stk70x0p_pid_filter_ctrl,
                                .frontend_attach  = stk7070p_frontend_attach,
                                .tuner_attach     = dib7770p_tuner_attach,
 
@@ -2353,6 +2469,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 1,
                .adapter = {
                        {
+                               .caps  = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter = stk807x_pid_filter,
+                               .pid_filter_ctrl = stk807x_pid_filter_ctrl,
                                .frontend_attach  = stk807x_frontend_attach,
                                .tuner_attach     = dib807x_tuner_attach,
 
@@ -2363,7 +2483,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                        },
                },
 
-               .num_device_descs = 2,
+               .num_device_descs = 3,
                .devices = {
                        {   "DiBcom STK807xP reference design",
                                { &dib0700_usb_id_table[62], NULL },
@@ -2373,6 +2493,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                                { &dib0700_usb_id_table[63], NULL },
                                { NULL },
                        },
+                       {   "EvolutePC TVWay+",
+                               { &dib0700_usb_id_table[64], NULL },
+                               { NULL },
+                       },
                },
 
                .rc_interval      = DEFAULT_RC_INTERVAL,
@@ -2384,6 +2508,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                .num_adapters = 2,
                .adapter = {
                        {
+                               .caps  = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter = stk807x_pid_filter,
+                               .pid_filter_ctrl = stk807x_pid_filter_ctrl,
                                .frontend_attach  = stk807xpvr_frontend_attach0,
                                .tuner_attach     = dib807x_tuner_attach,
 
@@ -2393,6 +2521,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
                                        sizeof(struct dib0700_adapter_state),
                        },
                        {
+                               .caps  = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+                               .pid_filter_count = 32,
+                               .pid_filter = stk807x_pid_filter,
+                               .pid_filter_ctrl = stk807x_pid_filter_ctrl,
                                .frontend_attach  = stk807xpvr_frontend_attach1,
                                .tuner_attach     = dib807x_tuner_attach,
 
index eeef50b..5c0126d 100644 (file)
@@ -242,7 +242,7 @@ static struct dvb_usb_device_properties dibusb1_1_properties = {
                        { &dibusb_dib3000mb_table[9],  &dibusb_dib3000mb_table[11], NULL },
                        { &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL },
                },
-               {       "Unkown USB1.1 DVB-T device ???? please report the name to the author",
+               {       "Unknown USB1.1 DVB-T device ???? please report the name to the author",
                        { &dibusb_dib3000mb_table[13], NULL },
                        { &dibusb_dib3000mb_table[14], NULL },
                },
index 8a7d87b..df1ec3e 100644 (file)
@@ -88,6 +88,7 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
                goto err;
        }
        adap->dvb_adap.priv = adap;
+       adap->dvb_adap.fe_ioctl_override = adap->props.fe_ioctl_override;
 
        if (adap->dev->props.read_mac_address) {
                if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
index a548c14..f1602d4 100644 (file)
@@ -27,6 +27,7 @@
 #define USB_VID_DIBCOM                         0x10b8
 #define USB_VID_DPOSH                          0x1498
 #define USB_VID_DVICO                          0x0fe9
+#define USB_VID_E3C                            0x18b4
 #define USB_VID_ELGATO                         0x0fd9
 #define USB_VID_EMPIA                          0xeb1a
 #define USB_VID_GENPIX                         0x09c0
@@ -61,6 +62,7 @@
 #define USB_VID_XTENSIONS                      0x1ae7
 #define USB_VID_HUMAX_COEX                     0x10b9
 #define USB_VID_774                            0x7a69
+#define USB_VID_EVOLUTEPC                      0x1e59
 
 /* Product IDs */
 #define USB_PID_ADSTECH_USB2_COLD                      0xa333
 #define USB_PID_DIBCOM_STK7770P                                0x1e80
 #define USB_PID_DPOSH_M9206_COLD                       0x9206
 #define USB_PID_DPOSH_M9206_WARM                       0xa090
+#define USB_PID_E3C_EC168                              0x1689
+#define USB_PID_E3C_EC168_2                            0xfffa
+#define USB_PID_E3C_EC168_3                            0xfffb
+#define USB_PID_E3C_EC168_4                            0x1001
+#define USB_PID_E3C_EC168_5                            0x1002
 #define USB_PID_UNIWILL_STK7700P                       0x6003
 #define USB_PID_GENIUS_TVGO_DVB_T03                    0x4012
 #define USB_PID_GRANDTEC_DVBT_USB_COLD                 0x0fa0
 #define USB_PID_KWORLD_395U_3                          0xe395
 #define USB_PID_KWORLD_MC810                           0xc810
 #define USB_PID_KWORLD_PC160_2T                                0xc160
+#define USB_PID_KWORLD_PC160_T                         0xc161
 #define USB_PID_KWORLD_VSTREAM_COLD                    0x17de
 #define USB_PID_KWORLD_VSTREAM_WARM                    0x17df
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE              0x0055
 #define USB_PID_TELESTAR_STARSTICK_2                   0x8000
 #define USB_PID_MSI_DIGI_VOX_MINI_III                   0x8807
 #define USB_PID_SONY_PLAYTV                            0x0003
+#define USB_PID_MYGICA_D689                            0xd811
 #define USB_PID_ELGATO_EYETV_DTT                       0x0021
 #define USB_PID_ELGATO_EYETV_DTT_Dlx                   0x0020
 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD                0x5000
 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM                0x5001
 #define USB_PID_FRIIO_WHITE                            0x0001
+#define USB_PID_TVWAY_PLUS                             0x0002
+#define USB_PID_SVEON_STV20                            0xe39d
 
 #endif
index edde87c..6b5ded9 100644 (file)
@@ -259,7 +259,7 @@ int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d,
                        *state = REMOTE_KEY_REPEAT;
                        break;
                default:
-                       deb_err("unkown type of remote status: %d\n",keybuf[0]);
+                       deb_err("unknown type of remote status: %d\n",keybuf[0]);
                        break;
        }
        return 0;
index fe2b87e..0143aef 100644 (file)
@@ -162,6 +162,9 @@ struct dvb_usb_adapter_properties {
        struct usb_data_stream_properties stream;
 
        int size_of_priv;
+
+       int (*fe_ioctl_override) (struct dvb_frontend *,
+                                 unsigned int, void *, unsigned int);
 };
 
 /**
diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c
new file mode 100644 (file)
index 0000000..52f5d4f
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * E3C EC168 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "ec168.h"
+#include "ec100.h"
+#include "mxl5005s.h"
+
+/* debug */
+static int dvb_usb_ec168_debug;
+module_param_named(debug, dvb_usb_ec168_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static struct ec100_config ec168_ec100_config;
+
+static int ec168_rw_udev(struct usb_device *udev, struct ec168_req *req)
+{
+       int ret;
+       unsigned int pipe;
+       u8 request, requesttype;
+       u8 buf[req->size];
+
+       switch (req->cmd) {
+       case DOWNLOAD_FIRMWARE:
+       case GPIO:
+       case WRITE_I2C:
+       case STREAMING_CTRL:
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+               request = req->cmd;
+               break;
+       case READ_I2C:
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+               request = req->cmd;
+               break;
+       case GET_CONFIG:
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+               request = CONFIG;
+               break;
+       case SET_CONFIG:
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+               request = CONFIG;
+               break;
+       case WRITE_DEMOD:
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+               request = DEMOD_RW;
+               break;
+       case READ_DEMOD:
+               requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+               request = DEMOD_RW;
+               break;
+       default:
+               err("unknown command:%02x", req->cmd);
+               ret = -EPERM;
+               goto error;
+       }
+
+       if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
+               /* write */
+               memcpy(buf, req->data, req->size);
+               pipe = usb_sndctrlpipe(udev, 0);
+       } else {
+               /* read */
+               pipe = usb_rcvctrlpipe(udev, 0);
+       }
+
+       msleep(1); /* avoid I2C errors */
+
+       ret = usb_control_msg(udev, pipe, request, requesttype, req->value,
+               req->index, buf, sizeof(buf), EC168_USB_TIMEOUT);
+
+       ec168_debug_dump(request, requesttype, req->value, req->index, buf,
+               req->size, deb_xfer);
+
+       if (ret < 0)
+               goto error;
+       else
+               ret = 0;
+
+       /* read request, copy returned data to return buf */
+       if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+               memcpy(req->data, buf, req->size);
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req)
+{
+       return ec168_rw_udev(d->udev, req);
+}
+
+/* I2C */
+static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+       int num)
+{
+       struct dvb_usb_device *d = i2c_get_adapdata(adap);
+       struct ec168_req req;
+       int i = 0;
+       int ret;
+
+       if (num > 2)
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+               return -EAGAIN;
+
+       while (i < num) {
+               if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+                       if (msg[i].addr == ec168_ec100_config.demod_address) {
+                               req.cmd = READ_DEMOD;
+                               req.value = 0;
+                               req.index = 0xff00 + msg[i].buf[0]; /* reg */
+                               req.size = msg[i+1].len; /* bytes to read */
+                               req.data = &msg[i+1].buf[0];
+                               ret = ec168_ctrl_msg(d, &req);
+                               i += 2;
+                       } else {
+                               err("I2C read not implemented");
+                               ret = -ENOSYS;
+                               i += 2;
+                       }
+               } else {
+                       if (msg[i].addr == ec168_ec100_config.demod_address) {
+                               req.cmd = WRITE_DEMOD;
+                               req.value = msg[i].buf[1]; /* val */
+                               req.index = 0xff00 + msg[i].buf[0]; /* reg */
+                               req.size = 0;
+                               req.data = NULL;
+                               ret = ec168_ctrl_msg(d, &req);
+                               i += 1;
+                       } else {
+                               req.cmd = WRITE_I2C;
+                               req.value = msg[i].buf[0]; /* val */
+                               req.index = 0x0100 + msg[i].addr; /* I2C addr */
+                               req.size = msg[i].len-1;
+                               req.data = &msg[i].buf[1];
+                               ret = ec168_ctrl_msg(d, &req);
+                               i += 1;
+                       }
+               }
+               if (ret)
+                       goto error;
+
+       }
+       ret = i;
+
+error:
+       mutex_unlock(&d->i2c_mutex);
+       return i;
+}
+
+
+static u32 ec168_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ec168_i2c_algo = {
+       .master_xfer   = ec168_i2c_xfer,
+       .functionality = ec168_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static struct ec100_config ec168_ec100_config = {
+       .demod_address = 0xff, /* not real address, demod is integrated */
+};
+
+static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       deb_info("%s:\n", __func__);
+       adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config,
+               &adap->dev->i2c_adap);
+       if (adap->fe == NULL)
+               return -ENODEV;
+
+       return 0;
+}
+
+static struct mxl5005s_config ec168_mxl5003s_config = {
+       .i2c_address     = 0xc6,
+       .if_freq         = IF_FREQ_4570000HZ,
+       .xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+       .agc_mode        = MXL_SINGLE_AGC,
+       .tracking_filter = MXL_TF_OFF,
+       .rssi_enable     = MXL_RSSI_ENABLE,
+       .cap_select      = MXL_CAP_SEL_ENABLE,
+       .div_out         = MXL_DIV_OUT_4,
+       .clock_out       = MXL_CLOCK_OUT_DISABLE,
+       .output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+       .top             = MXL5005S_TOP_25P2,
+       .mod_mode        = MXL_DIGITAL_MODE,
+       .if_mode         = MXL_ZERO_IF,
+       .AgcMasterByte   = 0x00,
+};
+
+static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       deb_info("%s:\n", __func__);
+       return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
+               &ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
+}
+
+static int ec168_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+       struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL};
+       deb_info("%s: onoff:%d\n", __func__, onoff);
+       if (onoff)
+               req.index = 0x0102;
+       return ec168_ctrl_msg(adap->dev, &req);
+}
+
+static int ec168_download_firmware(struct usb_device *udev,
+       const struct firmware *fw)
+{
+       int i, len, packets, remainder, ret;
+       u16 addr = 0x0000; /* firmware start address */
+       struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL};
+       deb_info("%s:\n", __func__);
+
+       #define FW_PACKET_MAX_DATA  2048
+       packets = fw->size / FW_PACKET_MAX_DATA;
+       remainder = fw->size % FW_PACKET_MAX_DATA;
+       len = FW_PACKET_MAX_DATA;
+       for (i = 0; i <= packets; i++) {
+               if (i == packets)  /* set size of the last packet */
+                       len = remainder;
+
+               req.size = len;
+               req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
+               req.index = addr;
+               addr += FW_PACKET_MAX_DATA;
+
+               ret = ec168_rw_udev(udev, &req);
+               if (ret) {
+                       err("firmware download failed:%d packet:%d", ret, i);
+                       goto error;
+               }
+       }
+       req.size = 0;
+
+       /* set "warm"? */
+       req.cmd = SET_CONFIG;
+       req.value = 0;
+       req.index = 0x0001;
+       ret = ec168_rw_udev(udev, &req);
+       if (ret)
+               goto error;
+
+       /* really needed - no idea what does */
+       req.cmd = GPIO;
+       req.value = 0;
+       req.index = 0x0206;
+       ret = ec168_rw_udev(udev, &req);
+       if (ret)
+               goto error;
+
+       /* activate tuner I2C? */
+       req.cmd = WRITE_I2C;
+       req.value = 0;
+       req.index = 0x00c6;
+       ret = ec168_rw_udev(udev, &req);
+       if (ret)
+               goto error;
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+static int ec168_identify_state(struct usb_device *udev,
+       struct dvb_usb_device_properties *props,
+       struct dvb_usb_device_description **desc, int *cold)
+{
+       int ret;
+       u8 reply;
+       struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply};
+       deb_info("%s:\n", __func__);
+
+       ret = ec168_rw_udev(udev, &req);
+       if (ret)
+               goto error;
+
+       deb_info("%s: reply:%02x\n", __func__, reply);
+
+       if (reply == 0x01)
+               *cold = 0;
+       else
+               *cold = 1;
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties ec168_properties;
+
+static int ec168_probe(struct usb_interface *intf,
+       const struct usb_device_id *id)
+{
+       int ret;
+       deb_info("%s: interface:%d\n", __func__,
+               intf->cur_altsetting->desc.bInterfaceNumber);
+
+       ret = dvb_usb_device_init(intf, &ec168_properties, THIS_MODULE, NULL,
+               adapter_nr);
+       if (ret)
+               goto error;
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+#define E3C_EC168_1689                          0
+#define E3C_EC168_FFFA                          1
+#define E3C_EC168_FFFB                          2
+#define E3C_EC168_1001                          3
+#define E3C_EC168_1002                          4
+
+static struct usb_device_id ec168_id[] = {
+       [E3C_EC168_1689] =
+               {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168)},
+       [E3C_EC168_FFFA] =
+               {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2)},
+       [E3C_EC168_FFFB] =
+               {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3)},
+       [E3C_EC168_1001] =
+               {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4)},
+       [E3C_EC168_1002] =
+               {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5)},
+       {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ec168_id);
+
+static struct dvb_usb_device_properties ec168_properties = {
+       .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+       .usb_ctrl = DEVICE_SPECIFIC,
+       .download_firmware = ec168_download_firmware,
+       .firmware = "dvb-usb-ec168.fw",
+       .no_reconnect = 1,
+
+       .size_of_priv = 0,
+
+       .num_adapters = 1,
+       .adapter = {
+               {
+                       .streaming_ctrl   = ec168_streaming_ctrl,
+                       .frontend_attach  = ec168_ec100_frontend_attach,
+                       .tuner_attach     = ec168_mxl5003s_tuner_attach,
+                       .stream = {
+                               .type = USB_BULK,
+                               .count = 6,
+                               .endpoint = 0x82,
+                               .u = {
+                                       .bulk = {
+                                               .buffersize = (32*512),
+                                       }
+                               }
+                       },
+               }
+       },
+
+       .identify_state = ec168_identify_state,
+
+       .i2c_algo = &ec168_i2c_algo,
+
+       .num_device_descs = 1,
+       .devices = {
+               {
+                       .name = "E3C EC168 DVB-T USB2.0 reference design",
+                       .cold_ids = {
+                               &ec168_id[E3C_EC168_1689],
+                               &ec168_id[E3C_EC168_FFFA],
+                               &ec168_id[E3C_EC168_FFFB],
+                               &ec168_id[E3C_EC168_1001],
+                               &ec168_id[E3C_EC168_1002],
+                               NULL},
+                       .warm_ids = {NULL},
+               },
+       }
+};
+
+static struct usb_driver ec168_driver = {
+       .name       = "dvb_usb_ec168",
+       .probe      = ec168_probe,
+       .disconnect = dvb_usb_device_exit,
+       .id_table   = ec168_id,
+};
+
+/* module stuff */
+static int __init ec168_module_init(void)
+{
+       int ret;
+       deb_info("%s:\n", __func__);
+       ret = usb_register(&ec168_driver);
+       if (ret)
+               err("module init failed:%d", ret);
+
+       return ret;
+}
+
+static void __exit ec168_module_exit(void)
+{
+       deb_info("%s:\n", __func__);
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&ec168_driver);
+}
+
+module_init(ec168_module_init);
+module_exit(ec168_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("E3C EC168 DVB-T USB2.0 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ec168.h b/drivers/media/dvb/dvb-usb/ec168.h
new file mode 100644 (file)
index 0000000..e7e0b83
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * E3C EC168 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EC168_H
+#define EC168_H
+
+#define DVB_USB_LOG_PREFIX "ec168"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_ec168_debug, 0x01, args)
+#define deb_rc(args...)   dprintk(dvb_usb_ec168_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_ec168_debug, 0x04, args)
+#define deb_reg(args...)  dprintk(dvb_usb_ec168_debug, 0x08, args)
+#define deb_i2c(args...)  dprintk(dvb_usb_ec168_debug, 0x10, args)
+#define deb_fw(args...)   dprintk(dvb_usb_ec168_debug, 0x20, args)
+
+#define ec168_debug_dump(r, t, v, i, b, l, func) { \
+       int loop_; \
+       func("%02x %02x %02x %02x %02x %02x %02x %02x", \
+               t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
+       if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
+               func(" >>> "); \
+       else \
+               func(" <<< "); \
+       for (loop_ = 0; loop_ < l; loop_++) \
+               func("%02x ", b[loop_]); \
+       func("\n");\
+}
+
+#define EC168_USB_TIMEOUT 1000
+
+struct ec168_req {
+       u8  cmd;       /* [1] */
+       u16 value;     /* [2|3] */
+       u16 index;     /* [4|5] */
+       u16 size;      /* [6|7] */
+       u8  *data;
+};
+
+enum ec168_cmd {
+       DOWNLOAD_FIRMWARE    = 0x00,
+       CONFIG               = 0x01,
+       DEMOD_RW             = 0x03,
+       GPIO                 = 0x04,
+       STREAMING_CTRL       = 0x10,
+       READ_I2C             = 0x20,
+       WRITE_I2C            = 0x21,
+       HID_DOWNLOAD         = 0x30,
+       GET_CONFIG,
+       SET_CONFIG,
+       READ_DEMOD,
+       WRITE_DEMOD,
+};
+
+#endif
index c4dfe25..9cbbe42 100644 (file)
@@ -232,12 +232,6 @@ static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state)
        return 0;
 }
 
-static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
-       *ber = 0;
-       return 0;
-}
-
 static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe,
                                           u16 *strength)
 {
@@ -264,26 +258,26 @@ static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe,
        return 0;
 }
 
-static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
-       *snr = 0x0101;
-       return 0;
-}
-
-static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
-       *ucblocks = 0;
-       return 0;
-}
 
-static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe,
-                                       struct dvb_frontend_tune_settings *fs)
+/* filter out un-supported properties to notify users */
+static int jdvbt90502_set_property(struct dvb_frontend *fe,
+                                  struct dtv_property *tvp)
 {
-       fs->min_delay_ms = 500;
-       fs->step_size = 0;
-       fs->max_drift = 0;
-
-       return 0;
+       int r = 0;
+
+       switch (tvp->cmd) {
+       case DTV_DELIVERY_SYSTEM:
+               if (tvp->u.data != SYS_ISDBT)
+                       r = -EINVAL;
+               break;
+       case DTV_CLEAR:
+       case DTV_TUNE:
+       case DTV_FREQUENCY:
+               break;
+       default:
+               r = -EINVAL;
+       }
+       return r;
 }
 
 static int jdvbt90502_get_frontend(struct dvb_frontend *fe,
@@ -314,6 +308,9 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe,
 
        deb_fe("%s: Freq:%d\n", __func__, p->frequency);
 
+       /* for recovery from DTV_CLEAN */
+       fe->dtv_property_cache.delivery_system = SYS_ISDBT;
+
        ret = jdvbt90502_pll_set_freq(state, p->frequency);
        if (ret) {
                deb_fe("%s:ret == %d\n", __func__, ret);
@@ -323,12 +320,6 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe,
        return 0;
 }
 
-static int jdvbt90502_sleep(struct dvb_frontend *fe)
-{
-       deb_fe("%s called.\n", __func__);
-       return 0;
-}
-
 
 /**
  * (reg, val) commad list to initialize this module.
@@ -394,6 +385,7 @@ static int jdvbt90502_init(struct dvb_frontend *fe)
                if (ret != 1)
                        goto error;
        }
+       fe->dtv_property_cache.delivery_system = SYS_ISDBT;
        msleep(100);
 
        return 0;
@@ -468,16 +460,13 @@ static struct dvb_frontend_ops jdvbt90502_ops = {
        .release = jdvbt90502_release,
 
        .init = jdvbt90502_init,
-       .sleep = jdvbt90502_sleep,
        .write = _jdvbt90502_write,
 
+       .set_property = jdvbt90502_set_property,
+
        .set_frontend = jdvbt90502_set_frontend,
        .get_frontend = jdvbt90502_get_frontend,
-       .get_tune_settings = jdvbt90502_get_tune_settings,
 
        .read_status = jdvbt90502_read_status,
-       .read_ber = jdvbt90502_read_ber,
        .read_signal_strength = jdvbt90502_read_signal_strength,
-       .read_snr = jdvbt90502_read_snr,
-       .read_ucblocks = jdvbt90502_read_ucblocks,
 };
index 9da2cc9..f9702e3 100644 (file)
@@ -56,7 +56,7 @@ static void usb_urb_complete(struct urb *urb)
                                stream->complete(stream, b, urb->actual_length);
                        break;
                default:
-                       err("unkown endpoint type in completition handler.");
+                       err("unknown endpoint type in completition handler.");
                        return;
        }
        usb_submit_urb(urb,GFP_ATOMIC);
@@ -228,7 +228,7 @@ int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properti
                case USB_ISOC:
                        return usb_isoc_urb_init(stream);
                default:
-                       err("unkown URB-type for data transfer.");
+                       err("unknown URB-type for data transfer.");
                        return -EINVAL;
        }
 }
index 6902825..4afa292 100644 (file)
@@ -1,6 +1,6 @@
 config DVB_FIREDTV
        tristate "FireDTV and FloppyDTV"
-       depends on DVB_CORE && IEEE1394
+       depends on DVB_CORE && (FIREWIRE || IEEE1394)
        help
          Support for DVB receivers from Digital Everywhere
          which are connected via IEEE 1394 (FireWire).
@@ -13,8 +13,11 @@ config DVB_FIREDTV
 
 if DVB_FIREDTV
 
+config DVB_FIREDTV_FIREWIRE
+       def_bool FIREWIRE = y || (FIREWIRE = m && DVB_FIREDTV = m)
+
 config DVB_FIREDTV_IEEE1394
-       def_bool IEEE1394
+       def_bool IEEE1394 = y || (IEEE1394 = m && DVB_FIREDTV = m)
 
 config DVB_FIREDTV_INPUT
        def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m)
index 2034695..da84203 100644 (file)
@@ -1,6 +1,7 @@
 obj-$(CONFIG_DVB_FIREDTV) += firedtv.o
 
 firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o
+firedtv-$(CONFIG_DVB_FIREDTV_FIREWIRE) += firedtv-fw.o
 firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o
 firedtv-$(CONFIG_DVB_FIREDTV_INPUT)    += firedtv-rc.o
 
index 2b6eeea..7c5459c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * FireDTV driver (formerly known as FireSAT)
+ * FireDTV driver -- ieee1394 I/O backend
  *
  * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
  * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
 #include <iso.h>
 #include <nodemgr.h>
 
+#include <dvb_demux.h>
+
 #include "firedtv.h"
 
 static LIST_HEAD(node_list);
 static DEFINE_SPINLOCK(node_list_lock);
 
-#define FIREWIRE_HEADER_SIZE   4
-#define CIP_HEADER_SIZE                8
+#define CIP_HEADER_SIZE                        8
+#define MPEG2_TS_HEADER_SIZE           4
+#define MPEG2_TS_SOURCE_PACKET_SIZE    (4 + 188)
 
 static void rawiso_activity_cb(struct hpsb_iso *iso)
 {
@@ -62,20 +65,20 @@ static void rawiso_activity_cb(struct hpsb_iso *iso)
                buf = dma_region_i(&iso->data_buf, unsigned char,
                        iso->infos[packet].offset + CIP_HEADER_SIZE);
                count = (iso->infos[packet].len - CIP_HEADER_SIZE) /
-                       (188 + FIREWIRE_HEADER_SIZE);
+                       MPEG2_TS_SOURCE_PACKET_SIZE;
 
                /* ignore empty packet */
                if (iso->infos[packet].len <= CIP_HEADER_SIZE)
                        continue;
 
                while (count--) {
-                       if (buf[FIREWIRE_HEADER_SIZE] == 0x47)
+                       if (buf[MPEG2_TS_HEADER_SIZE] == 0x47)
                                dvb_dmx_swfilter_packets(&fdtv->demux,
-                                               &buf[FIREWIRE_HEADER_SIZE], 1);
+                                               &buf[MPEG2_TS_HEADER_SIZE], 1);
                        else
                                dev_err(fdtv->device,
                                        "skipping invalid packet\n");
-                       buf += 188 + FIREWIRE_HEADER_SIZE;
+                       buf += MPEG2_TS_SOURCE_PACKET_SIZE;
                }
        }
 out:
@@ -87,15 +90,20 @@ static inline struct node_entry *node_of(struct firedtv *fdtv)
        return container_of(fdtv->device, struct unit_directory, device)->ne;
 }
 
-static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg)
+static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
 {
-       return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data,
-                             (__force quadlet_t)arg);
+       int ret;
+
+       ret = hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP,
+               (__force quadlet_t *)&data[1], (__force quadlet_t)data[0]);
+       data[0] = data[1];
+
+       return ret;
 }
 
-static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len)
+static int node_read(struct firedtv *fdtv, u64 addr, void *data)
 {
-       return hpsb_node_read(node_of(fdtv), addr, data, len);
+       return hpsb_node_read(node_of(fdtv), addr, data, 4);
 }
 
 static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
@@ -212,6 +220,7 @@ static int node_probe(struct device *dev)
                goto fail;
 
        avc_register_remote_control(fdtv);
+
        return 0;
 fail:
        spin_lock_irq(&node_list_lock);
@@ -220,6 +229,7 @@ fail:
        fdtv_unregister_rc(fdtv);
 fail_free:
        kfree(fdtv);
+
        return err;
 }
 
@@ -233,10 +243,9 @@ static int node_remove(struct device *dev)
        list_del(&fdtv->list);
        spin_unlock_irq(&node_list_lock);
 
-       cancel_work_sync(&fdtv->remote_ctrl_work);
        fdtv_unregister_rc(fdtv);
-
        kfree(fdtv);
+
        return 0;
 }
 
@@ -252,6 +261,7 @@ static int node_update(struct unit_directory *ud)
 
 static struct hpsb_protocol_driver fdtv_driver = {
        .name           = "firedtv",
+       .id_table       = fdtv_id_table,
        .update         = node_update,
        .driver         = {
                .probe  = node_probe,
@@ -264,12 +274,11 @@ static struct hpsb_highlevel fdtv_highlevel = {
        .fcp_request    = fcp_request,
 };
 
-int __init fdtv_1394_init(struct ieee1394_device_id id_table[])
+int __init fdtv_1394_init(void)
 {
        int ret;
 
        hpsb_register_highlevel(&fdtv_highlevel);
-       fdtv_driver.id_table = id_table;
        ret = hpsb_register_protocol(&fdtv_driver);
        if (ret) {
                printk(KERN_ERR "firedtv: failed to register protocol\n");
index 485d061..50c42a4 100644 (file)
@@ -1236,14 +1236,14 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len)
 
 #define CMP_OUTPUT_PLUG_CONTROL_REG_0  0xfffff0000904ULL
 
-static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len)
+static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data)
 {
        int ret;
 
        if (mutex_lock_interruptible(&fdtv->avc_mutex))
                return -EINTR;
 
-       ret = fdtv->backend->read(fdtv, addr, buf, len);
+       ret = fdtv->backend->read(fdtv, addr, data);
        if (ret < 0)
                dev_err(fdtv->device, "CMP: read I/O error\n");
 
@@ -1251,14 +1251,14 @@ static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len)
        return ret;
 }
 
-static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg)
+static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
 {
        int ret;
 
        if (mutex_lock_interruptible(&fdtv->avc_mutex))
                return -EINTR;
 
-       ret = fdtv->backend->lock(fdtv, addr, data, arg);
+       ret = fdtv->backend->lock(fdtv, addr, data);
        if (ret < 0)
                dev_err(fdtv->device, "CMP: lock I/O error\n");
 
@@ -1288,25 +1288,25 @@ static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift)
 
 int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel)
 {
-       __be32 old_opcr, opcr;
+       __be32 old_opcr, opcr[2];
        u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);
        int attempts = 0;
        int ret;
 
-       ret = cmp_read(fdtv, &opcr, opcr_address, 4);
+       ret = cmp_read(fdtv, opcr_address, opcr);
        if (ret < 0)
                return ret;
 
 repeat:
-       if (!get_opcr_online(opcr)) {
+       if (!get_opcr_online(*opcr)) {
                dev_err(fdtv->device, "CMP: output offline\n");
                return -EBUSY;
        }
 
-       old_opcr = opcr;
+       old_opcr = *opcr;
 
-       if (get_opcr_p2p_connections(opcr)) {
-               if (get_opcr_channel(opcr) != channel) {
+       if (get_opcr_p2p_connections(*opcr)) {
+               if (get_opcr_channel(*opcr) != channel) {
                        dev_err(fdtv->device, "CMP: cannot change channel\n");
                        return -EBUSY;
                }
@@ -1314,11 +1314,11 @@ repeat:
 
                /* We don't allocate isochronous resources. */
        } else {
-               set_opcr_channel(&opcr, channel);
-               set_opcr_data_rate(&opcr, 2); /* S400 */
+               set_opcr_channel(opcr, channel);
+               set_opcr_data_rate(opcr, 2); /* S400 */
 
                /* FIXME: this is for the worst case - optimize */
-               set_opcr_overhead_id(&opcr, 0);
+               set_opcr_overhead_id(opcr, 0);
 
                /*
                 * FIXME: allocate isochronous channel and bandwidth at IRM
@@ -1326,13 +1326,16 @@ repeat:
                 */
        }
 
-       set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1);
+       set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1);
 
-       ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr);
+       opcr[1] = *opcr;
+       opcr[0] = old_opcr;
+
+       ret = cmp_lock(fdtv, opcr_address, opcr);
        if (ret < 0)
                return ret;
 
-       if (old_opcr != opcr) {
+       if (old_opcr != *opcr) {
                /*
                 * FIXME: if old_opcr.P2P_Connections > 0,
                 * deallocate isochronous channel and bandwidth at IRM
@@ -1350,27 +1353,30 @@ repeat:
 
 void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel)
 {
-       __be32 old_opcr, opcr;
+       __be32 old_opcr, opcr[2];
        u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2);
        int attempts = 0;
 
-       if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0)
+       if (cmp_read(fdtv, opcr_address, opcr) < 0)
                return;
 
 repeat:
-       if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) ||
-           get_opcr_channel(opcr) != channel) {
+       if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) ||
+           get_opcr_channel(*opcr) != channel) {
                dev_err(fdtv->device, "CMP: no connection to break\n");
                return;
        }
 
-       old_opcr = opcr;
-       set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1);
+       old_opcr = *opcr;
+       set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1);
+
+       opcr[1] = *opcr;
+       opcr[0] = old_opcr;
 
-       if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0)
+       if (cmp_lock(fdtv, opcr_address, opcr) < 0)
                return;
 
-       if (old_opcr != opcr) {
+       if (old_opcr != *opcr) {
                /*
                 * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last
                 * owner, deallocate isochronous channel and bandwidth at IRM
index 5742fde..fc9996c 100644 (file)
@@ -297,7 +297,7 @@ struct firedtv *fdtv_alloc(struct device *dev,
 #define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d
 #define AVC_SW_VERSION_ENTRY   0x010001
 
-static struct ieee1394_device_id fdtv_id_table[] = {
+const struct ieee1394_device_id fdtv_id_table[] = {
        {
                /* FloppyDTV S/CI and FloppyDTV S2 */
                .match_flags    = MATCH_FLAGS,
@@ -346,12 +346,23 @@ MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table);
 
 static int __init fdtv_init(void)
 {
-       return fdtv_1394_init(fdtv_id_table);
+       int ret;
+
+       ret = fdtv_fw_init();
+       if (ret < 0)
+               return ret;
+
+       ret = fdtv_1394_init();
+       if (ret < 0)
+               fdtv_fw_exit();
+
+       return ret;
 }
 
 static void __exit fdtv_exit(void)
 {
        fdtv_1394_exit();
+       fdtv_fw_exit();
 }
 
 module_init(fdtv_init);
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
new file mode 100644 (file)
index 0000000..fe44789
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * FireDTV driver -- firewire I/O backend
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/page.h>
+
+#include <dvb_demux.h>
+
+#include "firedtv.h"
+
+static LIST_HEAD(node_list);
+static DEFINE_SPINLOCK(node_list_lock);
+
+static inline struct fw_device *device_of(struct firedtv *fdtv)
+{
+       return fw_device(fdtv->device->parent);
+}
+
+static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len,
+                   int tcode)
+{
+       struct fw_device *device = device_of(fdtv);
+       int rcode, generation = device->generation;
+
+       smp_rmb(); /* node_id vs. generation */
+
+       rcode = fw_run_transaction(device->card, tcode, device->node_id,
+                       generation, device->max_speed, addr, data, len);
+
+       return rcode != RCODE_COMPLETE ? -EIO : 0;
+}
+
+static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
+{
+       return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP);
+}
+
+static int node_read(struct firedtv *fdtv, u64 addr, void *data)
+{
+       return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST);
+}
+
+static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
+{
+       return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST);
+}
+
+#define ISO_HEADER_SIZE                        4
+#define CIP_HEADER_SIZE                        8
+#define MPEG2_TS_HEADER_SIZE           4
+#define MPEG2_TS_SOURCE_PACKET_SIZE    (4 + 188)
+
+#define MAX_PACKET_SIZE                1024  /* 776, rounded up to 2^n */
+#define PACKETS_PER_PAGE       (PAGE_SIZE / MAX_PACKET_SIZE)
+#define N_PACKETS              64    /* buffer size */
+#define N_PAGES                        DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE)
+#define IRQ_INTERVAL           16
+
+struct firedtv_receive_context {
+       struct fw_iso_context *context;
+       struct fw_iso_buffer buffer;
+       int interrupt_packet;
+       int current_packet;
+       char *pages[N_PAGES];
+};
+
+static int queue_iso(struct firedtv_receive_context *ctx, int index)
+{
+       struct fw_iso_packet p;
+
+       p.payload_length = MAX_PACKET_SIZE;
+       p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1));
+       p.skip = 0;
+       p.header_length = ISO_HEADER_SIZE;
+
+       return fw_iso_context_queue(ctx->context, &p, &ctx->buffer,
+                                   index * MAX_PACKET_SIZE);
+}
+
+static void handle_iso(struct fw_iso_context *context, u32 cycle,
+                      size_t header_length, void *header, void *data)
+{
+       struct firedtv *fdtv = data;
+       struct firedtv_receive_context *ctx = fdtv->backend_data;
+       __be32 *h, *h_end;
+       int length, err, i = ctx->current_packet;
+       char *p, *p_end;
+
+       for (h = header, h_end = h + header_length / 4; h < h_end; h++) {
+               length = be32_to_cpup(h) >> 16;
+               if (unlikely(length > MAX_PACKET_SIZE)) {
+                       dev_err(fdtv->device, "length = %d\n", length);
+                       length = MAX_PACKET_SIZE;
+               }
+
+               p = ctx->pages[i / PACKETS_PER_PAGE]
+                               + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE;
+               p_end = p + length;
+
+               for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end;
+                    p += MPEG2_TS_SOURCE_PACKET_SIZE)
+                       dvb_dmx_swfilter_packets(&fdtv->demux, p, 1);
+
+               err = queue_iso(ctx, i);
+               if (unlikely(err))
+                       dev_err(fdtv->device, "requeue failed\n");
+
+               i = (i + 1) & (N_PACKETS - 1);
+       }
+       ctx->current_packet = i;
+}
+
+static int start_iso(struct firedtv *fdtv)
+{
+       struct firedtv_receive_context *ctx;
+       struct fw_device *device = device_of(fdtv);
+       int i, err;
+
+       ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->context = fw_iso_context_create(device->card,
+                       FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel,
+                       device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv);
+       if (IS_ERR(ctx->context)) {
+               err = PTR_ERR(ctx->context);
+               goto fail_free;
+       }
+
+       err = fw_iso_buffer_init(&ctx->buffer, device->card,
+                                N_PAGES, DMA_FROM_DEVICE);
+       if (err)
+               goto fail_context_destroy;
+
+       ctx->interrupt_packet = 0;
+       ctx->current_packet = 0;
+
+       for (i = 0; i < N_PAGES; i++)
+               ctx->pages[i] = page_address(ctx->buffer.pages[i]);
+
+       for (i = 0; i < N_PACKETS; i++) {
+               err = queue_iso(ctx, i);
+               if (err)
+                       goto fail;
+       }
+
+       err = fw_iso_context_start(ctx->context, -1, 0,
+                                  FW_ISO_CONTEXT_MATCH_ALL_TAGS);
+       if (err)
+               goto fail;
+
+       fdtv->backend_data = ctx;
+
+       return 0;
+fail:
+       fw_iso_buffer_destroy(&ctx->buffer, device->card);
+fail_context_destroy:
+       fw_iso_context_destroy(ctx->context);
+fail_free:
+       kfree(ctx);
+
+       return err;
+}
+
+static void stop_iso(struct firedtv *fdtv)
+{
+       struct firedtv_receive_context *ctx = fdtv->backend_data;
+
+       fw_iso_context_stop(ctx->context);
+       fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card);
+       fw_iso_context_destroy(ctx->context);
+       kfree(ctx);
+}
+
+static const struct firedtv_backend backend = {
+       .lock           = node_lock,
+       .read           = node_read,
+       .write          = node_write,
+       .start_iso      = start_iso,
+       .stop_iso       = stop_iso,
+};
+
+static void handle_fcp(struct fw_card *card, struct fw_request *request,
+                      int tcode, int destination, int source, int generation,
+                      int speed, unsigned long long offset,
+                      void *payload, size_t length, void *callback_data)
+{
+       struct firedtv *f, *fdtv = NULL;
+       struct fw_device *device;
+       unsigned long flags;
+       int su;
+
+       if ((tcode != TCODE_WRITE_QUADLET_REQUEST &&
+            tcode != TCODE_WRITE_BLOCK_REQUEST) ||
+           offset != CSR_REGISTER_BASE + CSR_FCP_RESPONSE ||
+           length == 0 ||
+           (((u8 *)payload)[0] & 0xf0) != 0) {
+               fw_send_response(card, request, RCODE_TYPE_ERROR);
+               return;
+       }
+
+       su = ((u8 *)payload)[1] & 0x7;
+
+       spin_lock_irqsave(&node_list_lock, flags);
+       list_for_each_entry(f, &node_list, list) {
+               device = device_of(f);
+               if (device->generation != generation)
+                       continue;
+
+               smp_rmb(); /* node_id vs. generation */
+
+               if (device->card == card &&
+                   device->node_id == source &&
+                   (f->subunit == su || (f->subunit == 0 && su == 0x7))) {
+                       fdtv = f;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&node_list_lock, flags);
+
+       if (fdtv) {
+               avc_recv(fdtv, payload, length);
+               fw_send_response(card, request, RCODE_COMPLETE);
+       }
+}
+
+static struct fw_address_handler fcp_handler = {
+       .length           = CSR_FCP_END - CSR_FCP_RESPONSE,
+       .address_callback = handle_fcp,
+};
+
+static const struct fw_address_region fcp_region = {
+       .start  = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
+       .end    = CSR_REGISTER_BASE + CSR_FCP_END,
+};
+
+/* Adjust the template string if models with longer names appear. */
+#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4))
+
+static size_t model_name(u32 *directory, __be32 *buffer)
+{
+       struct fw_csr_iterator ci;
+       int i, length, key, value, last_key = 0;
+       u32 *block = NULL;
+
+       fw_csr_iterator_init(&ci, directory);
+       while (fw_csr_iterator_next(&ci, &key, &value)) {
+               if (last_key == CSR_MODEL &&
+                   key == (CSR_DESCRIPTOR | CSR_LEAF))
+                       block = ci.p - 1 + value;
+               last_key = key;
+       }
+
+       if (block == NULL)
+               return 0;
+
+       length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN);
+       if (length <= 0)
+               return 0;
+
+       /* fast-forward to text string */
+       block += 3;
+
+       for (i = 0; i < length; i++)
+               buffer[i] = cpu_to_be32(block[i]);
+
+       return length * 4;
+}
+
+static int node_probe(struct device *dev)
+{
+       struct firedtv *fdtv;
+       __be32 name[MAX_MODEL_NAME_LEN];
+       int name_len, err;
+
+       name_len = model_name(fw_unit(dev)->directory, name);
+
+       fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len);
+       if (!fdtv)
+               return -ENOMEM;
+
+       err = fdtv_register_rc(fdtv, dev);
+       if (err)
+               goto fail_free;
+
+       spin_lock_irq(&node_list_lock);
+       list_add_tail(&fdtv->list, &node_list);
+       spin_unlock_irq(&node_list_lock);
+
+       err = avc_identify_subunit(fdtv);
+       if (err)
+               goto fail;
+
+       err = fdtv_dvb_register(fdtv);
+       if (err)
+               goto fail;
+
+       avc_register_remote_control(fdtv);
+
+       return 0;
+fail:
+       spin_lock_irq(&node_list_lock);
+       list_del(&fdtv->list);
+       spin_unlock_irq(&node_list_lock);
+       fdtv_unregister_rc(fdtv);
+fail_free:
+       kfree(fdtv);
+
+       return err;
+}
+
+static int node_remove(struct device *dev)
+{
+       struct firedtv *fdtv = dev_get_drvdata(dev);
+
+       fdtv_dvb_unregister(fdtv);
+
+       spin_lock_irq(&node_list_lock);
+       list_del(&fdtv->list);
+       spin_unlock_irq(&node_list_lock);
+
+       fdtv_unregister_rc(fdtv);
+
+       kfree(fdtv);
+       return 0;
+}
+
+static void node_update(struct fw_unit *unit)
+{
+       struct firedtv *fdtv = dev_get_drvdata(&unit->device);
+
+       if (fdtv->isochannel >= 0)
+               cmp_establish_pp_connection(fdtv, fdtv->subunit,
+                                           fdtv->isochannel);
+}
+
+static struct fw_driver fdtv_driver = {
+       .driver   = {
+               .owner  = THIS_MODULE,
+               .name   = "firedtv",
+               .bus    = &fw_bus_type,
+               .probe  = node_probe,
+               .remove = node_remove,
+       },
+       .update   = node_update,
+       .id_table = fdtv_id_table,
+};
+
+int __init fdtv_fw_init(void)
+{
+       int ret;
+
+       ret = fw_core_add_address_handler(&fcp_handler, &fcp_region);
+       if (ret < 0)
+               return ret;
+
+       return driver_register(&fdtv_driver.driver);
+}
+
+void fdtv_fw_exit(void)
+{
+       driver_unregister(&fdtv_driver.driver);
+       fw_core_remove_address_handler(&fcp_handler);
+}
index 27bca2e..599d66e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/workqueue.h>
 
 #include "firedtv.h"
 
@@ -163,6 +164,7 @@ fail:
 
 void fdtv_unregister_rc(struct firedtv *fdtv)
 {
+       cancel_work_sync(&fdtv->remote_ctrl_work);
        kfree(fdtv->remote_ctrl_dev->keycode);
        input_unregister_device(fdtv->remote_ctrl_dev);
 }
index d48530b..35080db 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/dvb/dmx.h>
 #include <linux/dvb/frontend.h>
 #include <linux/list.h>
+#include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
 #include <linux/spinlock_types.h>
 #include <linux/types.h>
@@ -72,8 +73,8 @@ struct input_dev;
 struct firedtv;
 
 struct firedtv_backend {
-       int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg);
-       int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len);
+       int (*lock)(struct firedtv *fdtv, u64 addr, __be32 data[]);
+       int (*read)(struct firedtv *fdtv, u64 addr, void *data);
        int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len);
        int (*start_iso)(struct firedtv *fdtv);
        void (*stop_iso)(struct firedtv *fdtv);
@@ -119,10 +120,10 @@ struct firedtv {
 
 /* firedtv-1394.c */
 #ifdef CONFIG_DVB_FIREDTV_IEEE1394
-int fdtv_1394_init(struct ieee1394_device_id id_table[]);
+int fdtv_1394_init(void);
 void fdtv_1394_exit(void);
 #else
-static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; }
+static inline int fdtv_1394_init(void) { return 0; }
 static inline void fdtv_1394_exit(void) {}
 #endif
 
@@ -163,10 +164,20 @@ struct firedtv *fdtv_alloc(struct device *dev,
                           const struct firedtv_backend *backend,
                           const char *name, size_t name_len);
 extern const char *fdtv_model_names[];
+extern const struct ieee1394_device_id fdtv_id_table[];
 
 /* firedtv-fe.c */
 void fdtv_frontend_init(struct firedtv *fdtv);
 
+/* firedtv-fw.c */
+#ifdef CONFIG_DVB_FIREDTV_FIREWIRE
+int fdtv_fw_init(void);
+void fdtv_fw_exit(void);
+#else
+static inline int fdtv_fw_init(void) { return 0; }
+static inline void fdtv_fw_exit(void) {}
+#endif
+
 /* firedtv-rc.c */
 #ifdef CONFIG_DVB_FIREDTV_INPUT
 int fdtv_register_rc(struct firedtv *fdtv, struct device *dev);
index d7c4837..58aac01 100644 (file)
@@ -201,6 +201,13 @@ config DVB_SI21XX
        help
          A DVB-S tuner module. Say Y when you want to support this frontend.
 
+config DVB_DS3000
+       tristate "Montage Tehnology DS3000 based"
+       depends on DVB_CORE && I2C
+       default m if DVB_FE_CUSTOMISE
+       help
+         A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
 comment "DVB-T (terrestrial) frontends"
        depends on DVB_CORE
 
@@ -342,6 +349,13 @@ config DVB_AF9013
        help
          Say Y when you want to support this frontend.
 
+config DVB_EC100
+       tristate "E3C EC100"
+       depends on DVB_CORE && I2C
+       default m if DVB_FE_CUSTOMISE
+       help
+         Say Y when you want to support this frontend.
+
 comment "DVB-C (cable) frontends"
        depends on DVB_CORE
 
@@ -557,6 +571,13 @@ config DVB_LGS8GXX
        help
          A DMB-TH tuner module. Say Y when you want to support this frontend.
 
+config DVB_ATBM8830
+       tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator"
+       depends on DVB_CORE && I2C
+       default m if DVB_FE_CUSTOMISE
+       help
+         A DMB-TH tuner module. Say Y when you want to support this frontend.
+
 comment "Tools to develop new frontends"
 
 config DVB_DUMMY_FE
index 3523767..8234825 100644 (file)
@@ -64,6 +64,7 @@ obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
 obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
 obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
 obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o
+obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o
 obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
 obj-$(CONFIG_DVB_AF9013) += af9013.o
 obj-$(CONFIG_DVB_CX24116) += cx24116.o
@@ -76,3 +77,5 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o
 obj-$(CONFIG_DVB_STV090x) += stv090x.o
 obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
 obj-$(CONFIG_DVB_ISL6423) += isl6423.o
+obj-$(CONFIG_DVB_EC100) += ec100.o
+obj-$(CONFIG_DVB_DS3000) += ds3000.o
diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c
new file mode 100644 (file)
index 0000000..59881a5
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ *    Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ *    ATBM8830, ATBM8831
+ *
+ *    Copyright (C) 2009 David T.L. Wong <davidtlwong@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.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/div64.h>
+#include "dvb_frontend.h"
+
+#include "atbm8830.h"
+#include "atbm8830_priv.h"
+
+#define dprintk(args...) \
+       do { \
+               if (debug) \
+                       printk(KERN_DEBUG "atbm8830: " args); \
+       } while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data)
+{
+       int ret = 0;
+       u8 dev_addr;
+       u8 buf1[] = { reg >> 8, reg & 0xFF };
+       u8 buf2[] = { data };
+       struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
+       struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 };
+
+       dev_addr = priv->config->demod_address;
+       msg1.addr = dev_addr;
+       msg2.addr = dev_addr;
+
+       if (debug >= 2)
+               printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n",
+                       __func__, reg, data);
+
+       ret = i2c_transfer(priv->i2c, &msg1, 1);
+       if (ret != 1)
+               return -EIO;
+
+       ret = i2c_transfer(priv->i2c, &msg2, 1);
+       return (ret != 1) ? -EIO : 0;
+}
+
+static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data)
+{
+       int ret;
+       u8 dev_addr;
+
+       u8 buf1[] = { reg >> 8, reg & 0xFF };
+       u8 buf2[] = { 0 };
+       struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
+       struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 };
+
+       dev_addr = priv->config->demod_address;
+       msg1.addr = dev_addr;
+       msg2.addr = dev_addr;
+
+       ret = i2c_transfer(priv->i2c, &msg1, 1);
+       if (ret != 1) {
+               dprintk(KERN_DEBUG "%s: error reg=0x%04x, ret=%i\n",
+                       __func__, reg, ret);
+               return -EIO;
+       }
+
+       ret = i2c_transfer(priv->i2c, &msg2, 1);
+       if (ret != 1)
+               return -EIO;
+
+       *p_data = buf2[0];
+       if (debug >= 2)
+               printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n",
+                       __func__, reg, buf2[0]);
+
+       return 0;
+}
+
+/* Lock register latch so that multi-register read is atomic */
+static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock)
+{
+       return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0);
+}
+
+static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
+{
+       u32 val;
+       u64 t;
+
+       /* 0x100000 * freq / 30.4MHz */
+       t = (u64)0x100000 * freq;
+       do_div(t, 30400);
+       val = t;
+
+       atbm8830_write_reg(priv, REG_OSC_CLK, val);
+       atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8);
+       atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16);
+
+       return 0;
+}
+
+static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
+{
+
+       u32 fs = priv->config->osc_clk_freq;
+       u64 t;
+       u32 val;
+       u8 dat;
+
+       if (freq != 0) {
+               /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */
+               t = (u64) 2 * 31416 * (freq - fs);
+               t <<= 22;
+               do_div(t, fs);
+               do_div(t, 1000);
+               val = t;
+
+               atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1);
+               atbm8830_write_reg(priv, REG_IF_FREQ, val);
+               atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8);
+               atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16);
+
+               atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
+               dat &= 0xFC;
+               atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
+       } else {
+               /* Zero IF */
+               atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0);
+
+               atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
+               dat &= 0xFC;
+               dat |= 0x02;
+               atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
+
+               if (priv->config->zif_swap_iq)
+                       atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03);
+               else
+                       atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01);
+       }
+
+       return 0;
+}
+
+static int is_locked(struct atbm_state *priv, u8 *locked)
+{
+       u8 status;
+
+       atbm8830_read_reg(priv, REG_LOCK_STATUS, &status);
+
+       if (locked != NULL)
+               *locked = (status == 1);
+       return 0;
+}
+
+
+static int set_static_channel_mode(struct atbm_state *priv)
+{
+       int i;
+
+       for (i = 0; i < 5; i++)
+               atbm8830_write_reg(priv, 0x099B + i, 0x08);
+
+       atbm8830_write_reg(priv, 0x095B, 0x7F);
+       atbm8830_write_reg(priv, 0x09CB, 0x01);
+       atbm8830_write_reg(priv, 0x09CC, 0x7F);
+       atbm8830_write_reg(priv, 0x09CD, 0x7F);
+       atbm8830_write_reg(priv, 0x0E01, 0x20);
+
+       /* For single carrier */
+       atbm8830_write_reg(priv, 0x0B03, 0x0A);
+       atbm8830_write_reg(priv, 0x0935, 0x10);
+       atbm8830_write_reg(priv, 0x0936, 0x08);
+       atbm8830_write_reg(priv, 0x093E, 0x08);
+       atbm8830_write_reg(priv, 0x096E, 0x06);
+
+       /* frame_count_max0 */
+       atbm8830_write_reg(priv, 0x0B09, 0x00);
+       /* frame_count_max1 */
+       atbm8830_write_reg(priv, 0x0B0A, 0x08);
+
+       return 0;
+}
+
+static int set_ts_config(struct atbm_state *priv)
+{
+       const struct atbm8830_config *cfg = priv->config;
+
+       /*Set parallel/serial ts mode*/
+       atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0);
+       atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0);
+       /*Set ts sampling edge*/
+       atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE,
+               cfg->ts_sampling_edge ? 1 : 0);
+       /*Set ts clock freerun*/
+       atbm8830_write_reg(priv, REG_TS_CLK_FREERUN,
+               cfg->ts_clk_gated ? 0 : 1);
+
+       return 0;
+}
+
+static int atbm8830_init(struct dvb_frontend *fe)
+{
+       struct atbm_state *priv = fe->demodulator_priv;
+       const struct atbm8830_config *cfg = priv->config;
+
+       /*Set oscillator frequency*/
+       set_osc_freq(priv, cfg->osc_clk_freq);
+
+       /*Set IF frequency*/
+       set_if_freq(priv, cfg->if_freq);
+
+
+       /*Set static channel mode*/
+       set_static_channel_mode(priv);
+
+       set_ts_config(priv);
+       /*Turn off DSP reset*/
+       atbm8830_write_reg(priv, 0x000A, 0);
+
+       /*SW version test*/
+       atbm8830_write_reg(priv, 0x020C, 11);
+
+       /* Run */
+       atbm8830_write_reg(priv, REG_DEMOD_RUN, 1);
+
+       return 0;
+}
+
+
+static void atbm8830_release(struct dvb_frontend *fe)
+{
+       struct atbm_state *state = fe->demodulator_priv;
+       dprintk("%s\n", __func__);
+
+       kfree(state);
+}
+
+static int atbm8830_set_fe(struct dvb_frontend *fe,
+                         struct dvb_frontend_parameters *fe_params)
+{
+       struct atbm_state *priv = fe->demodulator_priv;
+       int i;
+       u8 locked = 0;
+       dprintk("%s\n", __func__);
+
+       /* set frequency */
+       if (fe->ops.tuner_ops.set_params) {
+               if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 1);
+               fe->ops.tuner_ops.set_params(fe, fe_params);
+               if (fe->ops.i2c_gate_ctrl)
+                       fe->ops.i2c_gate_ctrl(fe, 0);
+       }
+
+       /* start auto lock */
+       for (i = 0; i < 10; i++) {
+               mdelay(100);
+               dprintk("Try %d\n", i);
+               is_locked(priv, &locked);
+               if (locked != 0) {
+                       dprintk("ATBM8830 locked!\n");
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int atbm8830_get_fe(struct dvb_frontend *fe,
+                         struct dvb_frontend_parameters *fe_params)
+{
+       dprintk("%s\n", __func__);
+
+       /* TODO: get real readings from device */
+       /* inversion status */
+       fe_params->inversion = INVERSION_OFF;
+
+       /* bandwidth */
+       fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+
+       fe_params->u.ofdm.code_rate_HP = FEC_AUTO;
+       fe_params->u.ofdm.code_rate_LP = FEC_AUTO;
+
+       fe_params->u.ofdm.constellation = QAM_AUTO;
+
+       /* transmission mode */
+       fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO;
+
+       /* guard interval */
+       fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO;
+
+       /* hierarchy */
+       fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+
+       return 0;
+}
+
+static int atbm8830_get_tune_settings(struct dvb_frontend *fe,
+       struct dvb_frontend_tune_settings *fesettings)
+{
+       fesettings->min_delay_ms = 0;
+       fesettings->step_size = 0;
+       fesettings->max_drift = 0;
+       return 0;
+}
+
+static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
+{
+       struct atbm_state *priv = fe->demodulator_priv;
+       u8 locked = 0;
+       u8 agc_locked = 0;
+
+       dprintk("%s\n", __func__);
+       *fe_status = 0;
+
+       is_locked(priv, &locked);
+       if (locked) {
+               *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                       FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+       }
+       dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
+
+       atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked);
+       dprintk("AGC Lock: %d\n", agc_locked);
+
+       return 0;
+}
+
+static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       struct atbm_state *priv = fe->demodulator_priv;
+       u32 frame_err;
+       u8 t;
+
+       dprintk("%s\n", __func__);
+
+       atbm8830_reglatch_lock(priv, 1);
+
+       atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t);
+       frame_err = t & 0x7F;
+       frame_err <<= 8;
+       atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t);
+       frame_err |= t;
+
+       atbm8830_reglatch_lock(priv, 0);
+
+       *ber = frame_err * 100 / 32767;
+
+       dprintk("%s: ber=0x%x\n", __func__, *ber);
+       return 0;
+}
+
+static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
+{
+       struct atbm_state *priv = fe->demodulator_priv;
+       u32 pwm;
+       u8 t;
+
+       dprintk("%s\n", __func__);
+       atbm8830_reglatch_lock(priv, 1);
+
+       atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t);
+       pwm = t & 0x03;
+       pwm <<= 8;
+       atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t);
+       pwm |= t;
+
+       atbm8830_reglatch_lock(priv, 0);
+
+       dprintk("AGC PWM = 0x%02X\n", pwm);
+       pwm = 0x400 - pwm;
+
+       *signal = pwm * 0x10000 / 0x400;
+
+       return 0;
+}
+
+static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       dprintk("%s\n", __func__);
+       *snr = 0;
+       return 0;
+}
+
+static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       dprintk("%s\n", __func__);
+       *ucblocks = 0;
+       return 0;
+}
+
+static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+       struct atbm_state *priv = fe->demodulator_priv;
+
+       return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0);
+}
+
+static struct dvb_frontend_ops atbm8830_ops = {
+       .info = {
+               .name = "AltoBeam ATBM8830/8831 DMB-TH",
+               .type = FE_OFDM,
+               .frequency_min = 474000000,
+               .frequency_max = 858000000,
+               .frequency_stepsize = 10000,
+               .caps =
+                       FE_CAN_FEC_AUTO |
+                       FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO
+       },
+
+       .release = atbm8830_release,
+
+       .init = atbm8830_init,
+       .sleep = NULL,
+       .write = NULL,
+       .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl,
+
+       .set_frontend = atbm8830_set_fe,
+       .get_frontend = atbm8830_get_fe,
+       .get_tune_settings = atbm8830_get_tune_settings,
+
+       .read_status = atbm8830_read_status,
+       .read_ber = atbm8830_read_ber,
+       .read_signal_strength = atbm8830_read_signal_strength,
+       .read_snr = atbm8830_read_snr,
+       .read_ucblocks = atbm8830_read_ucblocks,
+};
+
+struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+       struct i2c_adapter *i2c)
+{
+       struct atbm_state *priv = NULL;
+       u8 data = 0;
+
+       dprintk("%s()\n", __func__);
+
+       if (config == NULL || i2c == NULL)
+               return NULL;
+
+       priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL);
+       if (priv == NULL)
+               goto error_out;
+
+       priv->config = config;
+       priv->i2c = i2c;
+
+       /* check if the demod is there */
+       if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) {
+               dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n",
+                       __func__, priv->config->demod_address);
+               goto error_out;
+       }
+       dprintk("atbm8830 chip id: 0x%02X\n", data);
+
+       memcpy(&priv->frontend.ops, &atbm8830_ops,
+              sizeof(struct dvb_frontend_ops));
+       priv->frontend.demodulator_priv = priv;
+
+       atbm8830_init(&priv->frontend);
+
+       atbm8830_i2c_gate_ctrl(&priv->frontend, 1);
+
+       return &priv->frontend;
+
+error_out:
+       dprintk("%s() error_out\n", __func__);
+       kfree(priv);
+       return NULL;
+
+}
+EXPORT_SYMBOL(atbm8830_attach);
+
+MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver");
+MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb/frontends/atbm8830.h
new file mode 100644 (file)
index 0000000..e8149f3
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *    Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ *    ATBM8830, ATBM8831
+ *
+ *    Copyright (C) 2009 David T.L. Wong <davidtlwong@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.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ATBM8830_H__
+#define __ATBM8830_H__
+
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#define ATBM8830_PROD_8830 0
+#define ATBM8830_PROD_8831 1
+
+struct atbm8830_config {
+
+       /* product type */
+       u8 prod;
+
+       /* the demodulator's i2c address */
+       u8 demod_address;
+
+       /* parallel or serial transport stream */
+       u8 serial_ts;
+
+       /* transport stream clock output only when receving valid stream */
+       u8 ts_clk_gated;
+
+       /* Decoder sample TS data at rising edge of clock */
+       u8 ts_sampling_edge;
+
+       /* Oscillator clock frequency */
+       u32 osc_clk_freq; /* in kHz */
+
+       /* IF frequency */
+       u32 if_freq; /* in kHz */
+
+       /* Swap I/Q for zero IF */
+       u8 zif_swap_iq;
+
+       /* Tuner AGC settings */
+       u8 agc_min;
+       u8 agc_max;
+       u8 agc_hold_loop;
+};
+
+#if defined(CONFIG_DVB_ATBM8830) || \
+       (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE))
+extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+               struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+               struct i2c_adapter *i2c) {
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif /* CONFIG_DVB_ATBM8830 */
+
+#endif /* __ATBM8830_H__ */
diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h
new file mode 100644 (file)
index 0000000..ce960f7
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *    Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ *    ATBM8830, ATBM8831
+ *
+ *    Copyright (C) 2009 David T.L. Wong <davidtlwong@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.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef __ATBM8830_PRIV_H
+#define __ATBM8830_PRIV_H
+
+struct atbm_state {
+       struct i2c_adapter *i2c;
+       /* configuration settings */
+       const struct atbm8830_config *config;
+       struct dvb_frontend frontend;
+};
+
+#define REG_CHIP_ID    0x0000
+#define REG_TUNER_BASEBAND     0x0001
+#define REG_DEMOD_RUN  0x0004
+#define REG_DSP_RESET  0x0005
+#define REG_RAM_RESET  0x0006
+#define REG_ADC_RESET  0x0007
+#define REG_TSPORT_RESET       0x0008
+#define REG_BLKERR_POL 0x000C
+#define REG_I2C_GATE   0x0103
+#define REG_TS_SAMPLE_EDGE     0x0301
+#define REG_TS_PKT_LEN_204     0x0302
+#define REG_TS_PKT_LEN_AUTO    0x0303
+#define REG_TS_SERIAL  0x0305
+#define REG_TS_CLK_FREERUN     0x0306
+#define REG_TS_VALID_MODE      0x0307
+#define REG_TS_CLK_MODE        0x030B /* 1 for serial, 0 for parallel */
+
+#define REG_TS_ERRBIT_USE      0x030C
+#define REG_LOCK_STATUS        0x030D
+#define REG_ADC_CONFIG 0x0602
+#define REG_CARRIER_OFFSET     0x0827 /* 0x0827-0x0829 little endian */
+#define REG_DETECTED_PN_MODE   0x082D
+#define REG_READ_LATCH 0x084D
+#define REG_IF_FREQ    0x0A00 /* 0x0A00-0x0A02 little endian */
+#define REG_OSC_CLK    0x0A03 /* 0x0A03-0x0A05 little endian */
+#define REG_BYPASS_CCI 0x0A06
+#define REG_ANALOG_LUMA_DETECTED       0x0A25
+#define REG_ANALOG_AUDIO_DETECTED      0x0A26
+#define REG_ANALOG_CHROMA_DETECTED     0x0A39
+#define REG_FRAME_ERR_CNT      0x0B04
+#define REG_USE_EXT_ADC        0x0C00
+#define REG_SWAP_I_Q   0x0C01
+#define REG_TPS_MANUAL 0x0D01
+#define REG_TPS_CONFIG 0x0D02
+#define REG_BYPASS_DEINTERLEAVER       0x0E00
+#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */
+#define REG_AGC_MIN    0x1020
+#define REG_AGC_MAX    0x1023
+#define REG_AGC_LOCK   0x1027
+#define REG_AGC_PWM_VAL        0x1028 /* 0x1028-0x1029 little endian */
+#define REG_AGC_HOLD_LOOP      0x1031
+
+#endif
+
index 74981ee..2dc2723 100644 (file)
@@ -23,7 +23,6 @@
 /* Developer notes:
  *
  * VBI support is not yet working
- * Saturation and hue setting are not yet working
  * Enough is implemented here for CVBS and S-Video inputs, but the actual
  *  analog demodulator code isn't implemented (not needed for xc5000 since it
  *  has its own demodulator and outputs CVBS)
@@ -236,8 +235,10 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
        state->contrast = 0x79;
        au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);
        au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);
+       state->saturation = 0x80;
        au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);
        au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);
+       state->hue = 0x00;
 
        /* Other decoder registers */
        au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);
@@ -315,7 +316,7 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
        if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
            input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
                /* Despite what the table says, for the HVR-950q we still need
-                  to be in CVBS mode for the S-Video input (reason uknown). */
+                  to be in CVBS mode for the S-Video input (reason unknown). */
                /* filter_coef_type = 3; */
                filter_coef_type = 5;
        } else {
@@ -504,7 +505,19 @@ static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
                                ctrl->value);
                break;
        case V4L2_CID_SATURATION:
+               state->saturation = ctrl->value;
+               au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH,
+                               ctrl->value);
+               au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH,
+                               ctrl->value);
+               break;
        case V4L2_CID_HUE:
+               state->hue = ctrl->value;
+               au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH,
+                               ctrl->value >> 8);
+               au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH,
+                               ctrl->value & 0xFF);
+               break;
        case V4L2_CID_AUDIO_VOLUME:
        case V4L2_CID_AUDIO_BASS:
        case V4L2_CID_AUDIO_TREBLE:
@@ -534,7 +547,11 @@ static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
                ctrl->value = state->contrast;
                break;
        case V4L2_CID_SATURATION:
+               ctrl->value = state->saturation;
+               break;
        case V4L2_CID_HUE:
+               ctrl->value = state->hue;
+               break;
        case V4L2_CID_AUDIO_VOLUME:
        case V4L2_CID_AUDIO_BASS:
        case V4L2_CID_AUDIO_TREBLE:
@@ -632,8 +649,9 @@ static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
        case V4L2_CID_BRIGHTNESS:
                return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
        case V4L2_CID_SATURATION:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
        case V4L2_CID_HUE:
-               /* Not yet implemented */
+               return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0);
        default:
                break;
        }
index f328f2b..c74c4e7 100644 (file)
@@ -62,6 +62,8 @@ struct au8522_state {
        u32 rev;
        u8 brightness;
        u8 contrast;
+       u8 saturation;
+       s16 hue;
 };
 
 /* These are routines shared by both the VSB/QAM demodulator and the analog
index ffbcfab..00a4e8f 100644 (file)
@@ -1,4 +1,4 @@
-       /*
+/*
     cx24110 - Single Chip Satellite Channel Receiver driver module
 
     Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
@@ -96,7 +96,7 @@ static struct {u8 reg; u8 data;} cx24110_regdata[]=
         {0x42,0x00}, /* @ middle bytes " */
         {0x43,0x00}, /* @ LSB          " */
                      /* leave the carrier tracking loop parameters on default */
-                     /* leave the bit timing loop parameters at gefault */
+                     /* leave the bit timing loop parameters at default */
         {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */
                      /* the cx24108 data sheet for symbol rates above 15MS/s */
         {0x57,0x00}, /* @ Filter sigma delta enabled, positive */
index 075b2b5..e9ee555 100644 (file)
@@ -589,7 +589,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
                info("detected CX24113 variant\n");
                break;
        case REV_CX24113:
-               info("sucessfully detected\n");
+               info("successfully detected\n");
                break;
        default:
                err("unsupported device id: %x\n", state->rev);
index 136b9d2..ad4c8cf 100644 (file)
@@ -154,7 +154,7 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe,
                        case BANDWIDTH_AUTO:
                                return -EOPNOTSUPP;
                        default:
-                               err("unkown bandwidth value.");
+                               err("unknown bandwidth value.");
                                return -EINVAL;
                }
        }
index 0781f94..750ae61 100644 (file)
@@ -108,7 +108,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode)
 
        outreg = 0;
        fifo_threshold = 1792;
-       smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1);
+       smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1);
 
        dprintk( "setting output mode for demod %p to %d",
                        &state->demod, mode);
@@ -162,18 +162,19 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff)
        if (state->div_force_off) {
                dprintk( "diversity combination deactivated - forced by COFDM parameters");
                onoff = 0;
-       }
+               dib7000p_write_word(state, 207, 0);
+       } else
+               dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
+
        state->div_state = (u8)onoff;
 
        if (onoff) {
                dib7000p_write_word(state, 204, 6);
                dib7000p_write_word(state, 205, 16);
                /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */
-               dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0));
        } else {
                dib7000p_write_word(state, 204, 1);
                dib7000p_write_word(state, 205, 0);
-               dib7000p_write_word(state, 207, 0);
        }
 
        return 0;
@@ -1188,7 +1189,7 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat)
                *stat |= FE_HAS_VITERBI;
        if (lock & 0x0010)
                *stat |= FE_HAS_SYNC;
-       if (lock & 0x0008)
+    if ((lock & 0x0038) == 0x38)
                *stat |= FE_HAS_LOCK;
 
        return 0;
@@ -1302,6 +1303,24 @@ struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum di
 }
 EXPORT_SYMBOL(dib7000p_get_i2c_master);
 
+int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+    struct dib7000p_state *state = fe->demodulator_priv;
+    u16 val = dib7000p_read_word(state, 235) & 0xffef;
+    val |= (onoff & 0x1) << 4;
+    dprintk("PID filter enabled %d", onoff);
+    return dib7000p_write_word(state, 235, val);
+}
+EXPORT_SYMBOL(dib7000p_pid_filter_ctrl);
+
+int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+    struct dib7000p_state *state = fe->demodulator_priv;
+    dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff);
+    return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0);
+}
+EXPORT_SYMBOL(dib7000p_pid_filter);
+
 int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[])
 {
        struct dib7000p_state st = { .i2c_adap = i2c };
@@ -1314,8 +1333,10 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau
                /* designated i2c address */
                new_addr          = (0x40 + k) << 1;
                st.i2c_addr = new_addr;
+               dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */
                if (dib7000p_identify(&st) != 0) {
                        st.i2c_addr = default_addr;
+                       dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */
                        if (dib7000p_identify(&st) != 0) {
                                dprintk("DiB7000P #%d: not identified\n", k);
                                return -EIO;
@@ -1372,6 +1393,8 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
        demod->demodulator_priv = st;
        memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops));
 
+    dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */
+
        if (dib7000p_identify(st) != 0)
                goto error;
 
index 02a4c82..805dd13 100644 (file)
@@ -51,6 +51,8 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
 extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
 extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
 extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
+extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff);
+extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff);
 #else
 static inline
 struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
@@ -95,6 +97,17 @@ static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap)
        printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
        return -ENODEV;
 }
+static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+    printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+    return -ENODEV;
+}
+
+static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff)
+{
+    printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+    return -ENODEV;
+}
 #endif
 
 #endif
index 852c790..898400d 100644 (file)
@@ -954,7 +954,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear
        u8 guard, crate, constellation, timeI;
        u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 };
        u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff;        // All 13 segments enabled
-       const s16 *ncoeff, *ana_fe;
+       const s16 *ncoeff = NULL, *ana_fe;
        u16 tmcc_pow = 0;
        u16 coff_pow = 0x2800;
        u16 init_prbs = 0xfff;
@@ -2121,7 +2121,7 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr)
        else
                result -= intlog10(2) * 10 * noise_exp - 100;
 
-       *snr = result / (1 << 24);
+       *snr = result / ((1 << 24) / 10);
        return 0;
 }
 
@@ -2195,6 +2195,25 @@ struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000
 
 EXPORT_SYMBOL(dib8000_get_i2c_master);
 
+int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+       struct dib8000_state *st = fe->demodulator_priv;
+    u16 val = dib8000_read_word(st, 299) & 0xffef;
+    val |= (onoff & 0x1) << 4;
+
+    dprintk("pid filter enabled %d", onoff);
+    return dib8000_write_word(st, 299, val);
+}
+EXPORT_SYMBOL(dib8000_pid_filter_ctrl);
+
+int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+       struct dib8000_state *st = fe->demodulator_priv;
+    dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
+    return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0);
+}
+EXPORT_SYMBOL(dib8000_pid_filter);
+
 static const struct dvb_frontend_ops dib8000_ops = {
        .info = {
                 .name = "DiBcom 8000 ISDB-T",
index a86de34..8c89482 100644 (file)
@@ -44,6 +44,8 @@ extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u
 
 extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
 extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value);
+extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff);
+extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff);
 #else
 static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg)
 {
@@ -74,6 +76,18 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value)
        printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
        return -ENODEV;
 }
+
+int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return -ENODEV;
+}
+
+int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return -ENODEV;
+}
 #endif
 
 #endif
index 0100755..868b78b 100644 (file)
@@ -81,7 +81,7 @@ static struct {
 #include "drx397xD_fw.h"
 };
 
-/* use only with writer lock aquired */
+/* use only with writer lock acquired */
 static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix)
 {
        memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c
new file mode 100644 (file)
index 0000000..cff3535
--- /dev/null
@@ -0,0 +1,1367 @@
+/*
+    Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver
+    Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+
+    Copyright (C) 2009 TurboSight.com
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+#include "ds3000.h"
+
+static int debug;
+
+#define dprintk(args...) \
+       do { \
+               if (debug) \
+                       printk(args); \
+       } while (0)
+
+/* as of March 2009 current DS3000 firmware version is 1.78 */
+/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */
+#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw"
+
+#define DS3000_SAMPLE_RATE 96000 /* in kHz */
+#define DS3000_XTAL_FREQ   27000 /* in kHz */
+
+/* Register values to initialise the demod in DVB-S mode */
+static u8 ds3000_dvbs_init_tab[] = {
+       0x23, 0x05,
+       0x08, 0x03,
+       0x0c, 0x00,
+       0x21, 0x54,
+       0x25, 0x82,
+       0x27, 0x31,
+       0x30, 0x08,
+       0x31, 0x40,
+       0x32, 0x32,
+       0x33, 0x35,
+       0x35, 0xff,
+       0x3a, 0x00,
+       0x37, 0x10,
+       0x38, 0x10,
+       0x39, 0x02,
+       0x42, 0x60,
+       0x4a, 0x40,
+       0x4b, 0x04,
+       0x4d, 0x91,
+       0x5d, 0xc8,
+       0x50, 0x77,
+       0x51, 0x77,
+       0x52, 0x36,
+       0x53, 0x36,
+       0x56, 0x01,
+       0x63, 0x43,
+       0x64, 0x30,
+       0x65, 0x40,
+       0x68, 0x26,
+       0x69, 0x4c,
+       0x70, 0x20,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x40,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x60,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x80,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0xa0,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x1f,
+       0x76, 0x00,
+       0x77, 0xd1,
+       0x78, 0x0c,
+       0x79, 0x80,
+       0x7f, 0x04,
+       0x7c, 0x00,
+       0x80, 0x86,
+       0x81, 0xa6,
+       0x85, 0x04,
+       0xcd, 0xf4,
+       0x90, 0x33,
+       0xa0, 0x44,
+       0xc0, 0x18,
+       0xc3, 0x10,
+       0xc4, 0x08,
+       0xc5, 0x80,
+       0xc6, 0x80,
+       0xc7, 0x0a,
+       0xc8, 0x1a,
+       0xc9, 0x80,
+       0xfe, 0x92,
+       0xe0, 0xf8,
+       0xe6, 0x8b,
+       0xd0, 0x40,
+       0xf8, 0x20,
+       0xfa, 0x0f,
+       0xfd, 0x20,
+       0xad, 0x20,
+       0xae, 0x07,
+       0xb8, 0x00,
+};
+
+/* Register values to initialise the demod in DVB-S2 mode */
+static u8 ds3000_dvbs2_init_tab[] = {
+       0x23, 0x0f,
+       0x08, 0x07,
+       0x0c, 0x00,
+       0x21, 0x54,
+       0x25, 0x82,
+       0x27, 0x31,
+       0x30, 0x08,
+       0x31, 0x32,
+       0x32, 0x32,
+       0x33, 0x35,
+       0x35, 0xff,
+       0x3a, 0x00,
+       0x37, 0x10,
+       0x38, 0x10,
+       0x39, 0x02,
+       0x42, 0x60,
+       0x4a, 0x80,
+       0x4b, 0x04,
+       0x4d, 0x81,
+       0x5d, 0x88,
+       0x50, 0x36,
+       0x51, 0x36,
+       0x52, 0x36,
+       0x53, 0x36,
+       0x63, 0x60,
+       0x64, 0x10,
+       0x65, 0x10,
+       0x68, 0x04,
+       0x69, 0x29,
+       0x70, 0x20,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x40,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x60,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x80,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0xa0,
+       0x71, 0x70,
+       0x72, 0x04,
+       0x73, 0x00,
+       0x70, 0x1f,
+       0xa0, 0x44,
+       0xc0, 0x08,
+       0xc1, 0x10,
+       0xc2, 0x08,
+       0xc3, 0x10,
+       0xc4, 0x08,
+       0xc5, 0xf0,
+       0xc6, 0xf0,
+       0xc7, 0x0a,
+       0xc8, 0x1a,
+       0xc9, 0x80,
+       0xca, 0x23,
+       0xcb, 0x24,
+       0xce, 0x74,
+       0x90, 0x03,
+       0x76, 0x80,
+       0x77, 0x42,
+       0x78, 0x0a,
+       0x79, 0x80,
+       0xad, 0x40,
+       0xae, 0x07,
+       0x7f, 0xd4,
+       0x7c, 0x00,
+       0x80, 0xa8,
+       0x81, 0xda,
+       0x7c, 0x01,
+       0x80, 0xda,
+       0x81, 0xec,
+       0x7c, 0x02,
+       0x80, 0xca,
+       0x81, 0xeb,
+       0x7c, 0x03,
+       0x80, 0xba,
+       0x81, 0xdb,
+       0x85, 0x08,
+       0x86, 0x00,
+       0x87, 0x02,
+       0x89, 0x80,
+       0x8b, 0x44,
+       0x8c, 0xaa,
+       0x8a, 0x10,
+       0xba, 0x00,
+       0xf5, 0x04,
+       0xfe, 0x44,
+       0xd2, 0x32,
+       0xb8, 0x00,
+};
+
+/* DS3000 doesn't need some parameters as input and auto-detects them */
+/* save input from the application of those parameters */
+struct ds3000_tuning {
+       u32 frequency;
+       u32 symbol_rate;
+       fe_spectral_inversion_t inversion;
+       enum fe_code_rate fec;
+
+       /* input values */
+       u8 inversion_val;
+       fe_modulation_t delivery;
+       u8 rolloff;
+};
+
+struct ds3000_state {
+       struct i2c_adapter *i2c;
+       const struct ds3000_config *config;
+
+       struct dvb_frontend frontend;
+
+       struct ds3000_tuning dcur;
+       struct ds3000_tuning dnxt;
+
+       u8 skip_fw_load;
+
+       /* previous uncorrected block counter for DVB-S2 */
+       u16 prevUCBS2;
+};
+
+static int ds3000_writereg(struct ds3000_state *state, int reg, int data)
+{
+       u8 buf[] = { reg, data };
+       struct i2c_msg msg = { .addr = state->config->demod_address,
+               .flags = 0, .buf = buf, .len = 2 };
+       int err;
+
+       dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
+
+       err = i2c_transfer(state->i2c, &msg, 1);
+       if (err != 1) {
+               printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x,"
+                        " value == 0x%02x)\n", __func__, err, reg, data);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data)
+{
+       u8 buf[] = { reg, data };
+       struct i2c_msg msg = { .addr = 0x60,
+               .flags = 0, .buf = buf, .len = 2 };
+       int err;
+
+       dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data);
+
+       ds3000_writereg(state, 0x03, 0x11);
+       err = i2c_transfer(state->i2c, &msg, 1);
+       if (err != 1) {
+               printk("%s: writereg error(err == %i, reg == 0x%02x,"
+                        " value == 0x%02x)\n", __func__, err, reg, data);
+               return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+/* I2C write for 8k firmware load */
+static int ds3000_writeFW(struct ds3000_state *state, int reg,
+                               const u8 *data, u16 len)
+{
+       int i, ret = -EREMOTEIO;
+       struct i2c_msg msg;
+       u8 *buf;
+
+       buf = kmalloc(3, GFP_KERNEL);
+       if (buf == NULL) {
+               printk(KERN_ERR "Unable to kmalloc\n");
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       *(buf) = reg;
+
+       msg.addr = state->config->demod_address;
+       msg.flags = 0;
+       msg.buf = buf;
+       msg.len = 3;
+
+       for (i = 0; i < len; i += 2) {
+               memcpy(buf + 1, data + i, 2);
+
+               dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len);
+
+               ret = i2c_transfer(state->i2c, &msg, 1);
+               if (ret != 1) {
+                       printk(KERN_ERR "%s: write error(err == %i, "
+                               "reg == 0x%02x\n", __func__, ret, reg);
+                       ret = -EREMOTEIO;
+               }
+       }
+
+error:
+       kfree(buf);
+
+       return ret;
+}
+
+static int ds3000_readreg(struct ds3000_state *state, u8 reg)
+{
+       int ret;
+       u8 b0[] = { reg };
+       u8 b1[] = { 0 };
+       struct i2c_msg msg[] = {
+               {
+                       .addr = state->config->demod_address,
+                       .flags = 0,
+                       .buf = b0,
+                       .len = 1
+               }, {
+                       .addr = state->config->demod_address,
+                       .flags = I2C_M_RD,
+                       .buf = b1,
+                       .len = 1
+               }
+       };
+
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2) {
+               printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+               return ret;
+       }
+
+       dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
+
+       return b1[0];
+}
+
+static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg)
+{
+       int ret;
+       u8 b0[] = { reg };
+       u8 b1[] = { 0 };
+       struct i2c_msg msg[] = {
+               {
+                       .addr = 0x60,
+                       .flags = 0,
+                       .buf = b0,
+                       .len = 1
+               }, {
+                       .addr = 0x60,
+                       .flags = I2C_M_RD,
+                       .buf = b1,
+                       .len = 1
+               }
+       };
+
+       ds3000_writereg(state, 0x03, 0x12);
+       ret = i2c_transfer(state->i2c, msg, 2);
+
+       if (ret != 2) {
+               printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret);
+               return ret;
+       }
+
+       dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]);
+
+       return b1[0];
+}
+
+static int ds3000_set_inversion(struct ds3000_state *state,
+                                       fe_spectral_inversion_t inversion)
+{
+       dprintk("%s(%d)\n", __func__, inversion);
+
+       switch (inversion) {
+       case INVERSION_OFF:
+       case INVERSION_ON:
+       case INVERSION_AUTO:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       state->dnxt.inversion = inversion;
+
+       return 0;
+}
+
+static int ds3000_set_symbolrate(struct ds3000_state *state, u32 rate)
+{
+       int ret = 0;
+
+       dprintk("%s()\n", __func__);
+
+       dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate);
+
+       /*  check if symbol rate is within limits */
+       if ((state->dnxt.symbol_rate >
+                               state->frontend.ops.info.symbol_rate_max) ||
+           (state->dnxt.symbol_rate <
+                               state->frontend.ops.info.symbol_rate_min))
+               ret = -EOPNOTSUPP;
+
+       state->dnxt.symbol_rate = rate;
+
+       return ret;
+}
+
+static int ds3000_load_firmware(struct dvb_frontend *fe,
+                                       const struct firmware *fw);
+
+static int ds3000_firmware_ondemand(struct dvb_frontend *fe)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       const struct firmware *fw;
+       int ret = 0;
+
+       dprintk("%s()\n", __func__);
+
+       if (ds3000_readreg(state, 0xb2) <= 0)
+               return ret;
+
+       if (state->skip_fw_load)
+               return 0;
+       /* Load firmware */
+       /* request the firmware, this will block until someone uploads it */
+       printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__,
+                               DS3000_DEFAULT_FIRMWARE);
+       ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE,
+                               state->i2c->dev.parent);
+       printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__);
+       if (ret) {
+               printk(KERN_ERR "%s: No firmware uploaded (timeout or file not "
+                               "found?)\n", __func__);
+               return ret;
+       }
+
+       /* Make sure we don't recurse back through here during loading */
+       state->skip_fw_load = 1;
+
+       ret = ds3000_load_firmware(fe, fw);
+       if (ret)
+               printk("%s: Writing firmware to device failed\n", __func__);
+
+       release_firmware(fw);
+
+       dprintk("%s: Firmware upload %s\n", __func__,
+                       ret == 0 ? "complete" : "failed");
+
+       /* Ensure firmware is always loaded if required */
+       state->skip_fw_load = 0;
+
+       return ret;
+}
+
+static int ds3000_load_firmware(struct dvb_frontend *fe,
+                                       const struct firmware *fw)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+
+       dprintk("%s\n", __func__);
+       dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n",
+                       fw->size,
+                       fw->data[0],
+                       fw->data[1],
+                       fw->data[fw->size - 2],
+                       fw->data[fw->size - 1]);
+
+       /* Begin the firmware load process */
+       ds3000_writereg(state, 0xb2, 0x01);
+       /* write the entire firmware */
+       ds3000_writeFW(state, 0xb0, fw->data, fw->size);
+       ds3000_writereg(state, 0xb2, 0x00);
+
+       return 0;
+}
+
+static void ds3000_dump_registers(struct dvb_frontend *fe)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       int x, y, reg = 0, val;
+
+       for (y = 0; y < 16; y++) {
+               dprintk("%s: %02x: ", __func__, y);
+               for (x = 0; x < 16; x++) {
+                       reg = (y << 4) + x;
+                       val = ds3000_readreg(state, reg);
+                       if (x != 15)
+                               dprintk("%02x ",  val);
+                       else
+                               dprintk("%02x\n", val);
+               }
+       }
+       dprintk("%s: -- DS3000 DUMP DONE --\n", __func__);
+}
+
+static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       int lock;
+
+       *status = 0;
+
+       switch (c->delivery_system) {
+       case SYS_DVBS:
+               lock = ds3000_readreg(state, 0xd1);
+               if ((lock & 0x07) == 0x07)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                               FE_HAS_VITERBI | FE_HAS_SYNC |
+                               FE_HAS_LOCK;
+
+               break;
+       case SYS_DVBS2:
+               lock = ds3000_readreg(state, 0x0d);
+               if ((lock & 0x8f) == 0x8f)
+                       *status = FE_HAS_SIGNAL | FE_HAS_CARRIER |
+                               FE_HAS_VITERBI | FE_HAS_SYNC |
+                               FE_HAS_LOCK;
+
+               break;
+       default:
+               return 1;
+       }
+
+       dprintk("%s: status = 0x%02x\n", __func__, lock);
+
+       return 0;
+}
+
+#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK)
+static int ds3000_is_tuned(struct dvb_frontend *fe)
+{
+       fe_status_t tunerstat;
+
+       ds3000_read_status(fe, &tunerstat);
+
+       return ((tunerstat & FE_IS_TUNED) == FE_IS_TUNED);
+}
+
+/* read DS3000 BER value */
+static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       u8 data;
+       u32 ber_reading, lpdc_frames;
+
+       dprintk("%s()\n", __func__);
+
+       switch (c->delivery_system) {
+       case SYS_DVBS:
+               /* set the number of bytes checked during
+               BER estimation */
+               ds3000_writereg(state, 0xf9, 0x04);
+               /* read BER estimation status */
+               data = ds3000_readreg(state, 0xf8);
+               /* check if BER estimation is ready */
+               if ((data & 0x10) == 0) {
+                       /* this is the number of error bits,
+                       to calculate the bit error rate
+                       divide to 8388608 */
+                       *ber = (ds3000_readreg(state, 0xf7) << 8) |
+                               ds3000_readreg(state, 0xf6);
+                       /* start counting error bits */
+                       /* need to be set twice
+                       otherwise it fails sometimes */
+                       data |= 0x10;
+                       ds3000_writereg(state, 0xf8, data);
+                       ds3000_writereg(state, 0xf8, data);
+               } else
+                       /* used to indicate that BER estimation
+                       is not ready, i.e. BER is unknown */
+                       *ber = 0xffffffff;
+               break;
+       case SYS_DVBS2:
+               /* read the number of LPDC decoded frames */
+               lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) |
+                               (ds3000_readreg(state, 0xd6) << 8) |
+                               ds3000_readreg(state, 0xd5);
+               /* read the number of packets with bad CRC */
+               ber_reading = (ds3000_readreg(state, 0xf8) << 8) |
+                               ds3000_readreg(state, 0xf7);
+               if (lpdc_frames > 750) {
+                       /* clear LPDC frame counters */
+                       ds3000_writereg(state, 0xd1, 0x01);
+                       /* clear bad packets counter */
+                       ds3000_writereg(state, 0xf9, 0x01);
+                       /* enable bad packets counter */
+                       ds3000_writereg(state, 0xf9, 0x00);
+                       /* enable LPDC frame counters */
+                       ds3000_writereg(state, 0xd1, 0x00);
+                       *ber = ber_reading;
+               } else
+                       /* used to indicate that BER estimation is not ready,
+                       i.e. BER is unknown */
+                       *ber = 0xffffffff;
+               break;
+       default:
+               return 1;
+       }
+
+       return 0;
+}
+
+/* read TS2020 signal strength */
+static int ds3000_read_signal_strength(struct dvb_frontend *fe,
+                                               u16 *signal_strength)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       u16 sig_reading, sig_strength;
+       u8 rfgain, bbgain;
+
+       dprintk("%s()\n", __func__);
+
+       rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f;
+       bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f;
+
+       if (rfgain > 15)
+               rfgain = 15;
+       if (bbgain > 13)
+               bbgain = 13;
+
+       sig_reading = rfgain * 2 + bbgain * 3;
+
+       sig_strength = 40 + (64 - sig_reading) * 50 / 64 ;
+
+       /* cook the value to be suitable for szap-s2 human readable output */
+       *signal_strength = sig_strength * 1000;
+
+       dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__,
+                       sig_reading, *signal_strength);
+
+       return 0;
+}
+
+/* calculate DS3000 snr value in dB */
+static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       u8 snr_reading, snr_value;
+       u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp;
+       static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */
+               0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03,
+               0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717,
+               0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505
+       };
+       static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */
+               0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103,
+               0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5,
+               0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6,
+               0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888,
+               0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51,
+               0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68,
+               0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206,
+               0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a,
+               0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649,
+               0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813,
+               0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1,
+               0x49e9, 0x4a20, 0x4a57
+       };
+
+       dprintk("%s()\n", __func__);
+
+       switch (c->delivery_system) {
+       case SYS_DVBS:
+               snr_reading = ds3000_readreg(state, 0xff);
+               snr_reading /= 8;
+               if (snr_reading == 0)
+                       *snr = 0x0000;
+               else {
+                       if (snr_reading > 20)
+                               snr_reading = 20;
+                       snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026;
+                       /* cook the value to be suitable for szap-s2
+                       human readable output */
+                       *snr = snr_value * 8 * 655;
+               }
+               dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+                               snr_reading, *snr);
+               break;
+       case SYS_DVBS2:
+               dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) +
+                               (ds3000_readreg(state, 0x8d) << 4);
+               dvbs2_signal_reading = ds3000_readreg(state, 0x8e);
+               tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1;
+               if (dvbs2_signal_reading == 0) {
+                       *snr = 0x0000;
+                       return 0;
+               }
+               if (dvbs2_noise_reading == 0) {
+                       snr_value = 0x0013;
+                       /* cook the value to be suitable for szap-s2
+                       human readable output */
+                       *snr = 0xffff;
+                       return 0;
+               }
+               if (tmp > dvbs2_noise_reading) {
+                       snr_reading = tmp / dvbs2_noise_reading;
+                       if (snr_reading > 80)
+                               snr_reading = 80;
+                       snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000;
+                       /* cook the value to be suitable for szap-s2
+                       human readable output */
+                       *snr = snr_value * 5 * 655;
+               } else {
+                       snr_reading = dvbs2_noise_reading / tmp;
+                       if (snr_reading > 80)
+                               snr_reading = 80;
+                       *snr = -(dvbs2_snr_tab[snr_reading] / 1000);
+               }
+               dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__,
+                               snr_reading, *snr);
+               break;
+       default:
+               return 1;
+       }
+
+       return 0;
+}
+
+/* read DS3000 uncorrected blocks */
+static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+       u8 data;
+       u16 _ucblocks;
+
+       dprintk("%s()\n", __func__);
+
+       switch (c->delivery_system) {
+       case SYS_DVBS:
+               *ucblocks = (ds3000_readreg(state, 0xf5) << 8) |
+                               ds3000_readreg(state, 0xf4);
+               data = ds3000_readreg(state, 0xf8);
+               /* clear packet counters */
+               data &= ~0x20;
+               ds3000_writereg(state, 0xf8, data);
+               /* enable packet counters */
+               data |= 0x20;
+               ds3000_writereg(state, 0xf8, data);
+               break;
+       case SYS_DVBS2:
+               _ucblocks = (ds3000_readreg(state, 0xe2) << 8) |
+                               ds3000_readreg(state, 0xe1);
+               if (_ucblocks > state->prevUCBS2)
+                       *ucblocks = _ucblocks - state->prevUCBS2;
+               else
+                       *ucblocks = state->prevUCBS2 - _ucblocks;
+               state->prevUCBS2 = _ucblocks;
+               break;
+       default:
+               return 1;
+       }
+
+       return 0;
+}
+
+/* Overwrite the current tuning params, we are about to tune */
+static void ds3000_clone_params(struct dvb_frontend *fe)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur));
+}
+
+static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       u8 data;
+
+       dprintk("%s(%d)\n", __func__, tone);
+       if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) {
+               printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone);
+               return -EINVAL;
+       }
+
+       data = ds3000_readreg(state, 0xa2);
+       data &= ~0xc0;
+       ds3000_writereg(state, 0xa2, data);
+
+       switch (tone) {
+       case SEC_TONE_ON:
+               dprintk("%s: setting tone on\n", __func__);
+               data = ds3000_readreg(state, 0xa1);
+               data &= ~0x43;
+               data |= 0x04;
+               ds3000_writereg(state, 0xa1, data);
+               break;
+       case SEC_TONE_OFF:
+               dprintk("%s: setting tone off\n", __func__);
+               data = ds3000_readreg(state, 0xa2);
+               data |= 0x80;
+               ds3000_writereg(state, 0xa2, data);
+               break;
+       }
+
+       return 0;
+}
+
+static int ds3000_send_diseqc_msg(struct dvb_frontend *fe,
+                               struct dvb_diseqc_master_cmd *d)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       int i;
+       u8 data;
+
+       /* Dump DiSEqC message */
+       dprintk("%s(", __func__);
+       for (i = 0 ; i < d->msg_len;) {
+               dprintk("0x%02x", d->msg[i]);
+               if (++i < d->msg_len)
+                       dprintk(", ");
+       }
+
+       /* enable DiSEqC message send pin */
+       data = ds3000_readreg(state, 0xa2);
+       data &= ~0xc0;
+       ds3000_writereg(state, 0xa2, data);
+
+       /* DiSEqC message */
+       for (i = 0; i < d->msg_len; i++)
+               ds3000_writereg(state, 0xa3 + i, d->msg[i]);
+
+       data = ds3000_readreg(state, 0xa1);
+       /* clear DiSEqC message length and status,
+       enable DiSEqC message send */
+       data &= ~0xf8;
+       /* set DiSEqC mode, modulation active during 33 pulses,
+       set DiSEqC message length */
+       data |= ((d->msg_len - 1) << 3) | 0x07;
+       ds3000_writereg(state, 0xa1, data);
+
+       /* wait up to 150ms for DiSEqC transmission to complete */
+       for (i = 0; i < 15; i++) {
+               data = ds3000_readreg(state, 0xa1);
+               if ((data & 0x40) == 0)
+                       break;
+               msleep(10);
+       }
+
+       /* DiSEqC timeout after 150ms */
+       if (i == 15) {
+               data = ds3000_readreg(state, 0xa1);
+               data &= ~0x80;
+               data |= 0x40;
+               ds3000_writereg(state, 0xa1, data);
+
+               data = ds3000_readreg(state, 0xa2);
+               data &= ~0xc0;
+               data |= 0x80;
+               ds3000_writereg(state, 0xa2, data);
+
+               return 1;
+       }
+
+       data = ds3000_readreg(state, 0xa2);
+       data &= ~0xc0;
+       data |= 0x80;
+       ds3000_writereg(state, 0xa2, data);
+
+       return 0;
+}
+
+/* Send DiSEqC burst */
+static int ds3000_diseqc_send_burst(struct dvb_frontend *fe,
+                                       fe_sec_mini_cmd_t burst)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       int i;
+       u8 data;
+
+       dprintk("%s()\n", __func__);
+
+       data = ds3000_readreg(state, 0xa2);
+       data &= ~0xc0;
+       ds3000_writereg(state, 0xa2, data);
+
+       /* DiSEqC burst */
+       if (burst == SEC_MINI_A)
+               /* Unmodulated tone burst */
+               ds3000_writereg(state, 0xa1, 0x02);
+       else if (burst == SEC_MINI_B)
+               /* Modulated tone burst */
+               ds3000_writereg(state, 0xa1, 0x01);
+       else
+               return -EINVAL;
+
+       msleep(13);
+       for (i = 0; i < 5; i++) {
+               data = ds3000_readreg(state, 0xa1);
+               if ((data & 0x40) == 0)
+                       break;
+               msleep(1);
+       }
+
+       if (i == 5) {
+               data = ds3000_readreg(state, 0xa1);
+               data &= ~0x80;
+               data |= 0x40;
+               ds3000_writereg(state, 0xa1, data);
+
+               data = ds3000_readreg(state, 0xa2);
+               data &= ~0xc0;
+               data |= 0x80;
+               ds3000_writereg(state, 0xa2, data);
+
+               return 1;
+       }
+
+       data = ds3000_readreg(state, 0xa2);
+       data &= ~0xc0;
+       data |= 0x80;
+       ds3000_writereg(state, 0xa2, data);
+
+       return 0;
+}
+
+static void ds3000_release(struct dvb_frontend *fe)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       dprintk("%s\n", __func__);
+       kfree(state);
+}
+
+static struct dvb_frontend_ops ds3000_ops;
+
+struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+                                   struct i2c_adapter *i2c)
+{
+       struct ds3000_state *state = NULL;
+       int ret;
+
+       dprintk("%s\n", __func__);
+
+       /* allocate memory for the internal state */
+       state = kmalloc(sizeof(struct ds3000_state), GFP_KERNEL);
+       if (state == NULL) {
+               printk(KERN_ERR "Unable to kmalloc\n");
+               goto error2;
+       }
+
+       /* setup the state */
+       memset(state, 0, sizeof(struct ds3000_state));
+
+       state->config = config;
+       state->i2c = i2c;
+       state->prevUCBS2 = 0;
+
+       /* check if the demod is present */
+       ret = ds3000_readreg(state, 0x00) & 0xfe;
+       if (ret != 0xe0) {
+               printk(KERN_ERR "Invalid probe, probably not a DS3000\n");
+               goto error3;
+       }
+
+       printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n",
+                       ds3000_readreg(state, 0x02),
+                       ds3000_readreg(state, 0x01));
+
+       memcpy(&state->frontend.ops, &ds3000_ops,
+                       sizeof(struct dvb_frontend_ops));
+       state->frontend.demodulator_priv = state;
+       return &state->frontend;
+
+error3:
+       kfree(state);
+error2:
+       return NULL;
+}
+EXPORT_SYMBOL(ds3000_attach);
+
+static int ds3000_set_property(struct dvb_frontend *fe,
+       struct dtv_property *tvp)
+{
+       dprintk("%s(..)\n", __func__);
+       return 0;
+}
+
+static int ds3000_get_property(struct dvb_frontend *fe,
+       struct dtv_property *tvp)
+{
+       dprintk("%s(..)\n", __func__);
+       return 0;
+}
+
+static int ds3000_tune(struct dvb_frontend *fe,
+                               struct dvb_frontend_parameters *p)
+{
+       struct ds3000_state *state = fe->demodulator_priv;
+       struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+       int ret = 0, retune, i;
+       u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf;
+       u16 value, ndiv;
+       u32 f3db;
+
+       dprintk("%s() ", __func__);
+
+       /* Load the firmware if required */
+       ret = ds3000_firmware_ondemand(fe);
+       if (ret != 0) {
+               printk(KERN_ERR "%s: Unable initialise the firmware\n",
+                                                               __func__);
+               return ret;
+       }
+
+       state->dnxt.delivery = c->modulation;
+       state->dnxt.frequency = c->frequency;
+       state->dnxt.rolloff = 2; /* fixme */
+       state->dnxt.fec = c->fec_inner;
+
+       ret = ds3000_set_inversion(state, p->inversion);
+       if (ret !=  0)
+               return ret;
+
+       ret = ds3000_set_symbolrate(state, c->symbol_rate);
+       if (ret !=  0)
+               return ret;
+
+       /* discard the 'current' tuning parameters and prepare to tune */
+       ds3000_clone_params(fe);
+
+       retune = 1;     /* try 1 times */
+       dprintk("%s:   retune = %d\n", __func__, retune);
+       dprintk("%s:   frequency   = %d\n", __func__, state->dcur.frequency);
+       dprintk("%s:   symbol_rate = %d\n", __func__, state->dcur.symbol_rate);
+       dprintk("%s:   FEC       = %d \n", __func__,
+               state->dcur.fec);
+       dprintk("%s:   Inversion   = %d\n", __func__, state->dcur.inversion);
+
+       do {
+               /* Reset status register */
+               status = 0;
+               /* Tune */
+               /* TS2020 init */
+               ds3000_tuner_writereg(state, 0x42, 0x73);
+               ds3000_tuner_writereg(state, 0x05, 0x01);
+               ds3000_tuner_writereg(state, 0x62, 0xf5);
+               /* unknown */
+               ds3000_tuner_writereg(state, 0x07, 0x02);
+               ds3000_tuner_writereg(state, 0x10, 0x00);
+               ds3000_tuner_writereg(state, 0x60, 0x79);
+               ds3000_tuner_writereg(state, 0x08, 0x01);
+               ds3000_tuner_writereg(state, 0x00, 0x01);
+               /* calculate and set freq divider */
+               if (state->dcur.frequency < 1146000) {
+                       ds3000_tuner_writereg(state, 0x10, 0x11);
+                       ndiv = ((state->dcur.frequency * (6 + 8) * 4) +
+                                       (DS3000_XTAL_FREQ / 2)) /
+                                       DS3000_XTAL_FREQ - 1024;
+               } else {
+                       ds3000_tuner_writereg(state, 0x10, 0x01);
+                       ndiv = ((state->dcur.frequency * (6 + 8) * 2) +
+                                       (DS3000_XTAL_FREQ / 2)) /
+                                       DS3000_XTAL_FREQ - 1024;
+               }
+
+               ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8);
+               ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff);
+
+               /* set pll */
+               ds3000_tuner_writereg(state, 0x03, 0x06);
+               ds3000_tuner_writereg(state, 0x51, 0x0f);
+               ds3000_tuner_writereg(state, 0x51, 0x1f);
+               ds3000_tuner_writereg(state, 0x50, 0x10);
+               ds3000_tuner_writereg(state, 0x50, 0x00);
+               msleep(5);
+
+               /* unknown */
+               ds3000_tuner_writereg(state, 0x51, 0x17);
+               ds3000_tuner_writereg(state, 0x51, 0x1f);
+               ds3000_tuner_writereg(state, 0x50, 0x08);
+               ds3000_tuner_writereg(state, 0x50, 0x00);
+               msleep(5);
+
+               value = ds3000_tuner_readreg(state, 0x3d);
+               value &= 0x0f;
+               if ((value > 4) && (value < 15)) {
+                       value -= 3;
+                       if (value < 4)
+                               value = 4;
+                       value = ((value << 3) | 0x01) & 0x79;
+               }
+
+               ds3000_tuner_writereg(state, 0x60, value);
+               ds3000_tuner_writereg(state, 0x51, 0x17);
+               ds3000_tuner_writereg(state, 0x51, 0x1f);
+               ds3000_tuner_writereg(state, 0x50, 0x08);
+               ds3000_tuner_writereg(state, 0x50, 0x00);
+
+               /* set low-pass filter period */
+               ds3000_tuner_writereg(state, 0x04, 0x2e);
+               ds3000_tuner_writereg(state, 0x51, 0x1b);
+               ds3000_tuner_writereg(state, 0x51, 0x1f);
+               ds3000_tuner_writereg(state, 0x50, 0x04);
+               ds3000_tuner_writereg(state, 0x50, 0x00);
+               msleep(5);
+
+               f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000;
+               if ((state->dcur.symbol_rate / 1000) < 5000)
+                       f3db += 3000;
+               if (f3db < 7000)
+                       f3db = 7000;
+               if (f3db > 40000)
+                       f3db = 40000;
+
+               /* set low-pass filter baseband */
+               value = ds3000_tuner_readreg(state, 0x26);
+               mlpf = 0x2e * 207 / ((value << 1) + 151);
+               mlpf_max = mlpf * 135 / 100;
+               mlpf_min = mlpf * 78 / 100;
+               if (mlpf_max > 63)
+                       mlpf_max = 63;
+
+               /* rounded to the closest integer */
+               nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2))
+                               / (2766 * DS3000_XTAL_FREQ);
+               if (nlpf > 23)
+                       nlpf = 23;
+               if (nlpf < 1)
+                       nlpf = 1;
+
+               /* rounded to the closest integer */
+               mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
+                               (1000 * f3db / 2)) / (1000 * f3db);
+
+               if (mlpf_new < mlpf_min) {
+                       nlpf++;
+                       mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) +
+                                       (1000 * f3db / 2)) / (1000 * f3db);
+               }
+
+               if (mlpf_new > mlpf_max)
+                       mlpf_new = mlpf_max;
+
+               ds3000_tuner_writereg(state, 0x04, mlpf_new);
+               ds3000_tuner_writereg(state, 0x06, nlpf);
+               ds3000_tuner_writereg(state, 0x51, 0x1b);
+               ds3000_tuner_writereg(state, 0x51, 0x1f);
+               ds3000_tuner_writereg(state, 0x50, 0x04);
+               ds3000_tuner_writereg(state, 0x50, 0x00);
+               msleep(5);
+
+               /* unknown */
+               ds3000_tuner_writereg(state, 0x51, 0x1e);
+               ds3000_tuner_writereg(state, 0x51, 0x1f);
+               ds3000_tuner_writereg(state, 0x50, 0x01);
+               ds3000_tuner_writereg(state, 0x50, 0x00);
+               msleep(60);
+
+               /* ds3000 global reset */
+               ds3000_writereg(state, 0x07, 0x80);
+               ds3000_writereg(state, 0x07, 0x00);
+               /* ds3000 build-in uC reset */
+               ds3000_writereg(state, 0xb2, 0x01);
+               /* ds3000 software reset */
+               ds3000_writereg(state, 0x00, 0x01);
+
+               switch (c->delivery_system) {
+               case SYS_DVBS:
+                       /* initialise the demod in DVB-S mode */
+                       for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2)
+                               ds3000_writereg(state,
+                                       ds3000_dvbs_init_tab[i],
+                                       ds3000_dvbs_init_tab[i + 1]);
+                       value = ds3000_readreg(state, 0xfe);
+                       value &= 0xc0;
+                       value |= 0x1b;
+                       ds3000_writereg(state, 0xfe, value);
+                       break;
+               case SYS_DVBS2:
+                       /* initialise the demod in DVB-S2 mode */
+                       for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2)
+                               ds3000_writereg(state,
+                                       ds3000_dvbs2_init_tab[i],
+                                       ds3000_dvbs2_init_tab[i + 1]);
+                       ds3000_writereg(state, 0xfe, 0x54);
+                       break;
+               default:
+                       return 1;
+               }
+
+               /* enable 27MHz clock output */
+               ds3000_writereg(state, 0x29, 0x80);
+               /* enable ac coupling */
+               ds3000_writereg(state, 0x25, 0x8a);
+
+               /* enhance symbol rate performance */
+               if ((state->dcur.symbol_rate / 1000) <= 5000) {
+                       value = 29777 / (state->dcur.symbol_rate / 1000) + 1;
+                       if (value % 2 != 0)
+                               value++;
+                       ds3000_writereg(state, 0xc3, 0x0d);
+                       ds3000_writereg(state, 0xc8, value);
+                       ds3000_writereg(state, 0xc4, 0x10);
+                       ds3000_writereg(state, 0xc7, 0x0e);
+               } else if ((state->dcur.symbol_rate / 1000) <= 10000) {
+                       value = 92166 / (state->dcur.symbol_rate / 1000) + 1;
+                       if (value % 2 != 0)
+                               value++;
+                       ds3000_writereg(state, 0xc3, 0x07);
+                       ds3000_writereg(state, 0xc8, value);
+                       ds3000_writereg(state, 0xc4, 0x09);
+                       ds3000_writereg(state, 0xc7, 0x12);
+               } else if ((state->dcur.symbol_rate / 1000) <= 20000) {
+                       value = 64516 / (state->dcur.symbol_rate / 1000) + 1;
+                       ds3000_writereg(state, 0xc3, value);
+                       ds3000_writereg(state, 0xc8, 0x0e);
+                       ds3000_writereg(state, 0xc4, 0x07);
+                       ds3000_writereg(state, 0xc7, 0x18);
+               } else {
+                       value = 129032 / (state->dcur.symbol_rate / 1000) + 1;
+                       ds3000_writereg(state, 0xc3, value);
+                       ds3000_writereg(state, 0xc8, 0x0a);
+                       ds3000_writereg(state, 0xc4, 0x05);
+                       ds3000_writereg(state, 0xc7, 0x24);
+               }
+
+               /* normalized symbol rate rounded to the closest integer */
+               value = (((state->dcur.symbol_rate / 1000) << 16) +
+                               (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE;
+               ds3000_writereg(state, 0x61, value & 0x00ff);
+               ds3000_writereg(state, 0x62, (value & 0xff00) >> 8);
+
+               /* co-channel interference cancellation disabled */
+               ds3000_writereg(state, 0x56, 0x00);
+
+               /* equalizer disabled */
+               ds3000_writereg(state, 0x76, 0x00);
+
+               /*ds3000_writereg(state, 0x08, 0x03);
+               ds3000_writereg(state, 0xfd, 0x22);
+               ds3000_writereg(state, 0x08, 0x07);
+               ds3000_writereg(state, 0xfd, 0x42);
+               ds3000_writereg(state, 0x08, 0x07);*/
+
+               /* ds3000 out of software reset */
+               ds3000_writereg(state, 0x00, 0x00);
+               /* start ds3000 build-in uC */
+               ds3000_writereg(state, 0xb2, 0x00);
+
+               /* TODO: calculate and set carrier offset */
+
+               /* wait before retrying */
+               for (i = 0; i < 30 ; i++) {
+                       if (ds3000_is_tuned(fe)) {
+                               dprintk("%s: Tuned\n", __func__);
+                               ds3000_dump_registers(fe);
+                               goto tuned;
+                       }
+                       msleep(1);
+               }
+
+               dprintk("%s: Not tuned\n", __func__);
+               ds3000_dump_registers(fe);
+
+       } while (--retune);
+
+tuned:
+       return ret;
+}
+
+static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe)
+{
+       dprintk("%s()\n", __func__);
+       return DVBFE_ALGO_SW;
+}
+
+/*
+ * Initialise or wake up device
+ *
+ * Power config will reset and load initial firmware if required
+ */
+static int ds3000_initfe(struct dvb_frontend *fe)
+{
+       dprintk("%s()\n", __func__);
+       return 0;
+}
+
+/* Put device to sleep */
+static int ds3000_sleep(struct dvb_frontend *fe)
+{
+       dprintk("%s()\n", __func__);
+       return 0;
+}
+
+static struct dvb_frontend_ops ds3000_ops = {
+
+       .info = {
+               .name = "Montage Technology DS3000/TS2020",
+               .type = FE_QPSK,
+               .frequency_min = 950000,
+               .frequency_max = 2150000,
+               .frequency_stepsize = 1011, /* kHz for QPSK frontends */
+               .frequency_tolerance = 5000,
+               .symbol_rate_min = 1000000,
+               .symbol_rate_max = 45000000,
+               .caps = FE_CAN_INVERSION_AUTO |
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+                       FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_2G_MODULATION |
+                       FE_CAN_QPSK | FE_CAN_RECOVER
+       },
+
+       .release = ds3000_release,
+
+       .init = ds3000_initfe,
+       .sleep = ds3000_sleep,
+       .read_status = ds3000_read_status,
+       .read_ber = ds3000_read_ber,
+       .read_signal_strength = ds3000_read_signal_strength,
+       .read_snr = ds3000_read_snr,
+       .read_ucblocks = ds3000_read_ucblocks,
+       .set_tone = ds3000_set_tone,
+       .diseqc_send_master_cmd = ds3000_send_diseqc_msg,
+       .diseqc_send_burst = ds3000_diseqc_send_burst,
+       .get_frontend_algo = ds3000_get_algo,
+
+       .set_property = ds3000_set_property,
+       .get_property = ds3000_get_property,
+       .set_frontend = ds3000_tune,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)");
+
+MODULE_DESCRIPTION("DVB Frontend module for Montage Technology "
+                       "DS3000/TS2020 hardware");
+MODULE_AUTHOR("Konstantin Dimitrov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h
new file mode 100644 (file)
index 0000000..67f6703
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver
+    Copyright (C) 2009 Konstantin Dimitrov <kosio.dimitrov@gmail.com>
+
+    Copyright (C) 2009 TurboSight.com
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef DS3000_H
+#define DS3000_H
+
+#include <linux/dvb/frontend.h>
+
+struct ds3000_config {
+       /* the demodulator's i2c address */
+       u8 demod_address;
+};
+
+#if defined(CONFIG_DVB_DS3000) || \
+                       (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE))
+extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+                                       struct i2c_adapter *i2c);
+#else
+static inline
+struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
+                                       struct i2c_adapter *i2c)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif /* CONFIG_DVB_DS3000 */
+#endif /* DS3000_H */
diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb/frontends/ec100.c
new file mode 100644 (file)
index 0000000..2414dc6
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dvb_frontend.h"
+#include "ec100_priv.h"
+#include "ec100.h"
+
+int ec100_debug;
+module_param_named(debug, ec100_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+struct ec100_state {
+       struct i2c_adapter *i2c;
+       struct dvb_frontend frontend;
+       struct ec100_config config;
+
+       u16 ber;
+};
+
+/* write single register */
+static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
+{
+       u8 buf[2] = {reg, val};
+       struct i2c_msg msg = {
+               .addr = state->config.demod_address,
+               .flags = 0,
+               .len = 2,
+               .buf = buf};
+
+       if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+               warn("I2C write failed reg:%02x", reg);
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+/* read single register */
+static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
+{
+       struct i2c_msg msg[2] = {
+               {
+                       .addr = state->config.demod_address,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = &reg
+               }, {
+                       .addr = state->config.demod_address,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = val
+               }
+       };
+
+       if (i2c_transfer(state->i2c, msg, 2) != 2) {
+               warn("I2C read failed reg:%02x", reg);
+               return -EREMOTEIO;
+       }
+       return 0;
+}
+
+static int ec100_set_frontend(struct dvb_frontend *fe,
+       struct dvb_frontend_parameters *params)
+{
+       struct ec100_state *state = fe->demodulator_priv;
+       int ret;
+       u8 tmp, tmp2;
+
+       deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency,
+               params->u.ofdm.bandwidth);
+
+       /* program tuner */
+       if (fe->ops.tuner_ops.set_params)
+               fe->ops.tuner_ops.set_params(fe, params);
+
+       ret = ec100_write_reg(state, 0x04, 0x06);
+       if (ret)
+               goto error;
+       ret = ec100_write_reg(state, 0x67, 0x58);
+       if (ret)
+               goto error;
+       ret = ec100_write_reg(state, 0x05, 0x18);
+       if (ret)
+               goto error;
+
+       /* reg/bw |   6  |   7  |   8
+          -------+------+------+------
+          A 0x1b | 0xa1 | 0xe7 | 0x2c
+          A 0x1c | 0x55 | 0x63 | 0x72
+          -------+------+------+------
+          B 0x1b | 0xb7 | 0x00 | 0x49
+          B 0x1c | 0x55 | 0x64 | 0x72 */
+
+       switch (params->u.ofdm.bandwidth) {
+       case BANDWIDTH_6_MHZ:
+               tmp = 0xb7;
+               tmp2 = 0x55;
+               break;
+       case BANDWIDTH_7_MHZ:
+               tmp = 0x00;
+               tmp2 = 0x64;
+               break;
+       case BANDWIDTH_8_MHZ:
+       default:
+               tmp = 0x49;
+               tmp2 = 0x72;
+       }
+
+       ret = ec100_write_reg(state, 0x1b, tmp);
+       if (ret)
+               goto error;
+       ret = ec100_write_reg(state, 0x1c, tmp2);
+       if (ret)
+               goto error;
+
+       ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */
+       if (ret)
+               goto error;
+       ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */
+       if (ret)
+               goto error;
+
+       ret = ec100_write_reg(state, 0x08, 0x24);
+       if (ret)
+               goto error;
+
+       ret = ec100_write_reg(state, 0x00, 0x00); /* go */
+       if (ret)
+               goto error;
+       ret = ec100_write_reg(state, 0x00, 0x20); /* go */
+       if (ret)
+               goto error;
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+static int ec100_get_tune_settings(struct dvb_frontend *fe,
+       struct dvb_frontend_tune_settings *fesettings)
+{
+       fesettings->min_delay_ms = 300;
+       fesettings->step_size = 0;
+       fesettings->max_drift = 0;
+
+       return 0;
+}
+
+static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+       struct ec100_state *state = fe->demodulator_priv;
+       int ret;
+       u8 tmp;
+       *status = 0;
+
+       ret = ec100_read_reg(state, 0x42, &tmp);
+       if (ret)
+               goto error;
+
+       if (tmp & 0x80) {
+               /* bit7 set - have lock */
+               *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+                       FE_HAS_SYNC | FE_HAS_LOCK;
+       } else {
+               ret = ec100_read_reg(state, 0x01, &tmp);
+               if (ret)
+                       goto error;
+
+               if (tmp & 0x10) {
+                       /* bit4 set - have signal */
+                       *status |= FE_HAS_SIGNAL;
+                       if (!(tmp & 0x01)) {
+                               /* bit0 clear - have ~valid signal */
+                               *status |= FE_HAS_CARRIER |  FE_HAS_VITERBI;
+                       }
+               }
+       }
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+       struct ec100_state *state = fe->demodulator_priv;
+       int ret;
+       u8 tmp, tmp2;
+       u16 ber2;
+
+       *ber = 0;
+
+       ret = ec100_read_reg(state, 0x65, &tmp);
+       if (ret)
+               goto error;
+       ret = ec100_read_reg(state, 0x66, &tmp2);
+       if (ret)
+               goto error;
+
+       ber2 = (tmp2 << 8) | tmp;
+
+       /* if counter overflow or clear */
+       if (ber2 < state->ber)
+               *ber = ber2;
+       else
+               *ber = ber2 - state->ber;
+
+       state->ber = ber2;
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+       struct ec100_state *state = fe->demodulator_priv;
+       int ret;
+       u8 tmp;
+
+       ret = ec100_read_reg(state, 0x24, &tmp);
+       if (ret) {
+               *strength = 0;
+               goto error;
+       }
+
+       *strength = ((tmp << 8) | tmp);
+
+       return ret;
+error:
+       deb_info("%s: failed:%d\n", __func__, ret);
+       return ret;
+}
+
+static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       *snr = 0;
+       return 0;
+}
+
+static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+       *ucblocks = 0;
+       return 0;
+}
+
+static void ec100_release(struct dvb_frontend *fe)
+{
+       struct ec100_state *state = fe->demodulator_priv;
+       kfree(state);
+}
+
+static struct dvb_frontend_ops ec100_ops;
+
+struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+       struct i2c_adapter *i2c)
+{
+       int ret;
+       struct ec100_state *state = NULL;
+       u8 tmp;
+
+       /* allocate memory for the internal state */
+       state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL);
+       if (state == NULL)
+               goto error;
+
+       /* setup the state */
+       state->i2c = i2c;
+       memcpy(&state->config, config, sizeof(struct ec100_config));
+
+       /* check if the demod is there */
+       ret = ec100_read_reg(state, 0x33, &tmp);
+       if (ret || tmp != 0x0b)
+               goto error;
+
+       /* create dvb_frontend */
+       memcpy(&state->frontend.ops, &ec100_ops,
+               sizeof(struct dvb_frontend_ops));
+       state->frontend.demodulator_priv = state;
+
+       return &state->frontend;
+error:
+       kfree(state);
+       return NULL;
+}
+EXPORT_SYMBOL(ec100_attach);
+
+static struct dvb_frontend_ops ec100_ops = {
+       .info = {
+               .name = "E3C EC100 DVB-T",
+               .type = FE_OFDM,
+               .caps =
+                       FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+                       FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+                       FE_CAN_QPSK | FE_CAN_QAM_16 |
+                       FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+                       FE_CAN_TRANSMISSION_MODE_AUTO |
+                       FE_CAN_GUARD_INTERVAL_AUTO |
+                       FE_CAN_HIERARCHY_AUTO |
+                       FE_CAN_MUTE_TS
+       },
+
+       .release = ec100_release,
+       .set_frontend = ec100_set_frontend,
+       .get_tune_settings = ec100_get_tune_settings,
+       .read_status = ec100_read_status,
+       .read_ber = ec100_read_ber,
+       .read_signal_strength = ec100_read_signal_strength,
+       .read_snr = ec100_read_snr,
+       .read_ucblocks = ec100_read_ucblocks,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/ec100.h b/drivers/media/dvb/frontends/ec100.h
new file mode 100644 (file)
index 0000000..ee8e524
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EC100_H
+#define EC100_H
+
+#include <linux/dvb/frontend.h>
+
+struct ec100_config {
+       /* demodulator's I2C address */
+       u8 demod_address;
+};
+
+
+#if defined(CONFIG_DVB_EC100) || \
+       (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE))
+extern struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+       struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ec100_attach(
+       const struct ec100_config *config, struct i2c_adapter *i2c)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+#endif
+
+#endif /* EC100_H */
diff --git a/drivers/media/dvb/frontends/ec100_priv.h b/drivers/media/dvb/frontends/ec100_priv.h
new file mode 100644 (file)
index 0000000..5c99014
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef EC100_PRIV
+#define EC100_PRIV
+
+#define LOG_PREFIX "ec100"
+
+#define dprintk(var, level, args...) \
+       do { if ((var & level)) printk(args); } while (0)
+
+#define deb_info(args...) dprintk(ec100_debug, 0x01, args)
+
+#undef err
+#define err(f, arg...)  printk(KERN_ERR     LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO    LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+#endif /* EC100_PRIV */
index 056387b..43971e6 100644 (file)
@@ -479,7 +479,7 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status)
        switch (state->current_modulation) {
        case QAM_256:
        case QAM_64:
-               /* Need to undestand why there are 3 lock levels here */
+               /* Need to understand why there are 3 lock levels here */
                if ((buf[0] & 0x07) == 0x07)
                        *status |= FE_HAS_CARRIER;
                break;
@@ -520,7 +520,7 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status)
        switch (state->current_modulation) {
        case QAM_256:
        case QAM_64:
-               /* Need to undestand why there are 3 lock levels here */
+               /* Need to understand why there are 3 lock levels here */
                if ((buf[0] & 0x07) == 0x07)
                        *status |= FE_HAS_CARRIER;
                else
index fb30115..0e2f61a 100644 (file)
@@ -44,7 +44,15 @@ struct s5h1409_state {
        int if_freq;
 
        u32 is_qam_locked;
-       u32 qam_state;
+
+       /* QAM tuning state goes through the following state transitions */
+#define QAM_STATE_UNTUNED 0
+#define QAM_STATE_TUNING_STARTED 1
+#define QAM_STATE_INTERLEAVE_SET 2
+#define QAM_STATE_QAM_OPTIMIZED_L1 3
+#define QAM_STATE_QAM_OPTIMIZED_L2 4
+#define QAM_STATE_QAM_OPTIMIZED_L3 5
+       u8  qam_state;
 };
 
 static int debug;
@@ -347,7 +355,7 @@ static int s5h1409_softreset(struct dvb_frontend *fe)
        s5h1409_writereg(state, 0xf5, 0);
        s5h1409_writereg(state, 0xf5, 1);
        state->is_qam_locked = 0;
-       state->qam_state = 0;
+       state->qam_state = QAM_STATE_UNTUNED;
        return 0;
 }
 
@@ -474,6 +482,59 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe)
        struct s5h1409_state *state = fe->demodulator_priv;
        u16 reg;
 
+       if (state->qam_state < QAM_STATE_INTERLEAVE_SET) {
+               /* We should not perform amhum optimization until
+                  the interleave mode has been configured */
+               return;
+       }
+
+       if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) {
+               /* We've already reached the maximum optimization level, so
+                  dont bother banging on the status registers */
+               return;
+       }
+
+       /* QAM EQ lock check */
+       reg = s5h1409_readreg(state, 0xf0);
+
+       if ((reg >> 13) & 0x1) {
+               reg &= 0xff;
+
+               s5h1409_writereg(state, 0x96, 0x000c);
+               if (reg < 0x68) {
+                       if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) {
+                               dprintk("%s() setting QAM state to OPT_L3\n",
+                                       __func__);
+                               s5h1409_writereg(state, 0x93, 0x3130);
+                               s5h1409_writereg(state, 0x9e, 0x2836);
+                               state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3;
+                       }
+               } else {
+                       if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) {
+                               dprintk("%s() setting QAM state to OPT_L2\n",
+                                       __func__);
+                               s5h1409_writereg(state, 0x93, 0x3332);
+                               s5h1409_writereg(state, 0x9e, 0x2c37);
+                               state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2;
+                       }
+               }
+
+       } else {
+               if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) {
+                       dprintk("%s() setting QAM state to OPT_L1\n", __func__);
+                       s5h1409_writereg(state, 0x96, 0x0008);
+                       s5h1409_writereg(state, 0x93, 0x3332);
+                       s5h1409_writereg(state, 0x9e, 0x2c37);
+                       state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1;
+               }
+       }
+}
+
+static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe)
+{
+       struct s5h1409_state *state = fe->demodulator_priv;
+       u16 reg;
+
        if (state->is_qam_locked)
                return;
 
@@ -506,6 +567,44 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe)
        struct s5h1409_state *state = fe->demodulator_priv;
        u16 reg, reg1, reg2;
 
+       if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) {
+               /* We've done the optimization already */
+               return;
+       }
+
+       reg = s5h1409_readreg(state, 0xf1);
+
+       /* Master lock */
+       if ((reg >> 15) & 0x1) {
+               if (state->qam_state == QAM_STATE_UNTUNED ||
+                   state->qam_state == QAM_STATE_TUNING_STARTED) {
+                       dprintk("%s() setting QAM state to INTERLEAVE_SET\n",
+                               __func__);
+                       reg1 = s5h1409_readreg(state, 0xb2);
+                       reg2 = s5h1409_readreg(state, 0xad);
+
+                       s5h1409_writereg(state, 0x96, 0x0020);
+                       s5h1409_writereg(state, 0xad,
+                               (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff)));
+                       state->qam_state = QAM_STATE_INTERLEAVE_SET;
+               }
+       } else {
+               if (state->qam_state == QAM_STATE_UNTUNED) {
+                       dprintk("%s() setting QAM state to TUNING_STARTED\n",
+                               __func__);
+                       s5h1409_writereg(state, 0x96, 0x08);
+                       s5h1409_writereg(state, 0xab,
+                               s5h1409_readreg(state, 0xab) | 0x1001);
+                       state->qam_state = QAM_STATE_TUNING_STARTED;
+               }
+       }
+}
+
+static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe)
+{
+       struct s5h1409_state *state = fe->demodulator_priv;
+       u16 reg, reg1, reg2;
+
        reg = s5h1409_readreg(state, 0xf1);
 
        /* Master lock */
@@ -553,16 +652,24 @@ static int s5h1409_set_frontend(struct dvb_frontend *fe,
                        fe->ops.i2c_gate_ctrl(fe, 0);
        }
 
-       /* Optimize the demod for QAM */
-       if (p->u.vsb.modulation != VSB_8) {
-               s5h1409_set_qam_amhum_mode(fe);
-               s5h1409_set_qam_interleave_mode(fe);
-       }
-
        /* Issue a reset to the demod so it knows to resync against the
           newly tuned frequency */
        s5h1409_softreset(fe);
 
+       /* Optimize the demod for QAM */
+       if (state->current_modulation != VSB_8) {
+               /* This almost certainly applies to all boards, but for now
+                  only do it for the HVR-1600.  Once the other boards are
+                  tested, the "legacy" versions can just go away */
+               if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+                       s5h1409_set_qam_interleave_mode(fe);
+                       s5h1409_set_qam_amhum_mode(fe);
+               } else {
+                       s5h1409_set_qam_amhum_mode_legacy(fe);
+                       s5h1409_set_qam_interleave_mode_legacy(fe);
+               }
+       }
+
        return 0;
 }
 
@@ -614,6 +721,21 @@ static int s5h1409_init(struct dvb_frontend *fe)
        /* The datasheet says that after initialisation, VSB is default */
        state->current_modulation = VSB_8;
 
+       /* Optimize for the HVR-1600 if appropriate.  Note that some of these
+          may get folded into the generic case after testing with other
+          devices */
+       if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+               /* VSB AGC REF */
+               s5h1409_writereg(state, 0x09, 0x0050);
+
+               /* Unknown but Windows driver does it... */
+               s5h1409_writereg(state, 0x21, 0x0001);
+               s5h1409_writereg(state, 0x50, 0x030e);
+
+               /* QAM AGC REF */
+               s5h1409_writereg(state, 0x82, 0x0800);
+       }
+
        if (state->config->output_mode == S5H1409_SERIAL_OUTPUT)
                s5h1409_writereg(state, 0xab,
                        s5h1409_readreg(state, 0xab) | 0x100); /* Serial */
@@ -641,6 +763,17 @@ static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status)
 
        *status = 0;
 
+       /* Optimize the demod for QAM */
+       if (state->current_modulation != VSB_8) {
+               /* This almost certainly applies to all boards, but for now
+                  only do it for the HVR-1600.  Once the other boards are
+                  tested, the "legacy" versions can just go away */
+               if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) {
+                       s5h1409_set_qam_interleave_mode(fe);
+                       s5h1409_set_qam_amhum_mode(fe);
+               }
+       }
+
        /* Get the demodulator status */
        reg = s5h1409_readreg(state, 0xf1);
        if (reg & 0x1000)
index 070d974..91f2ebd 100644 (file)
@@ -57,6 +57,13 @@ struct s5h1409_config {
 #define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK    2
 #define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3
        u16 mpeg_timing;
+
+       /* HVR-1600 optimizations (to better work with MXL5005s)
+          Note: some of these are likely to be folded into the generic driver
+          after being regression tested with other boards */
+#define S5H1409_HVR1600_NOOPTIMIZE 0
+#define S5H1409_HVR1600_OPTIMIZE   1
+       u8 hvr1600_opt;
 };
 
 #if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \
index a04c782..1570669 100644 (file)
@@ -1571,7 +1571,7 @@ static enum dvbfe_search stb0899_search(struct dvb_frontend *fe, struct dvb_fron
  * stb0899_track
  * periodically check the signal level against a specified
  * threshold level and perform derotator centering.
- * called once we have a lock from a succesful search
+ * called once we have a lock from a successful search
  * event.
  *
  * Will be called periodically called to maintain the
diff --git a/drivers/media/dvb/frontends/stb6100_proc.h b/drivers/media/dvb/frontends/stb6100_proc.h
new file mode 100644 (file)
index 0000000..112163a
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+       STB6100 Silicon Tuner wrapper
+       Copyright (C)2009 Igor M. Liplianin (liplianin@me.by)
+
+       This program is free software; you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation; either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program; if not, write to the Free Software
+       Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency)
+{
+       struct dvb_frontend_ops *frontend_ops = NULL;
+       struct dvb_tuner_ops    *tuner_ops = NULL;
+       struct tuner_state      state;
+       int err = 0;
+
+       if (&fe->ops)
+               frontend_ops = &fe->ops;
+       if (&frontend_ops->tuner_ops)
+               tuner_ops = &frontend_ops->tuner_ops;
+       if (tuner_ops->get_state) {
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 1);
+
+               err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state);
+               if (err < 0) {
+                       printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+                       return err;
+               }
+
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 0);
+
+               *frequency = state.frequency;
+       }
+
+       return 0;
+}
+
+static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency)
+{
+       struct dvb_frontend_ops *frontend_ops = NULL;
+       struct dvb_tuner_ops    *tuner_ops = NULL;
+       struct tuner_state      state;
+       int err = 0;
+
+       state.frequency = frequency;
+       if (&fe->ops)
+               frontend_ops = &fe->ops;
+       if (&frontend_ops->tuner_ops)
+               tuner_ops = &frontend_ops->tuner_ops;
+       if (tuner_ops->set_state) {
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 1);
+
+               err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state);
+               if (err < 0) {
+                       printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+                       return err;
+               }
+
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 0);
+
+       }
+
+       return 0;
+}
+
+static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth)
+{
+       struct dvb_frontend_ops *frontend_ops = NULL;
+       struct dvb_tuner_ops    *tuner_ops = NULL;
+       struct tuner_state      state;
+       int err = 0;
+
+       if (&fe->ops)
+               frontend_ops = &fe->ops;
+       if (&frontend_ops->tuner_ops)
+               tuner_ops = &frontend_ops->tuner_ops;
+       if (tuner_ops->get_state) {
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 1);
+
+               err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state);
+               if (err < 0) {
+                       printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+                       return err;
+               }
+
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 0);
+
+               *bandwidth = state.bandwidth;
+       }
+
+       return 0;
+}
+
+static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth)
+{
+       struct dvb_frontend_ops *frontend_ops = NULL;
+       struct dvb_tuner_ops    *tuner_ops = NULL;
+       struct tuner_state      state;
+       int err = 0;
+
+       state.bandwidth = bandwidth;
+       if (&fe->ops)
+               frontend_ops = &fe->ops;
+       if (&frontend_ops->tuner_ops)
+               tuner_ops = &frontend_ops->tuner_ops;
+       if (tuner_ops->set_state) {
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 1);
+
+               err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state);
+               if (err < 0) {
+                       printk(KERN_ERR "%s: Invalid parameter\n", __func__);
+                       return err;
+               }
+
+               if (frontend_ops->i2c_gate_ctrl)
+                       frontend_ops->i2c_gate_ctrl(fe, 0);
+
+       }
+
+       return 0;
+}
index bf4e9b6..29c3fa8 100644 (file)
@@ -36,6 +36,7 @@ struct stv0900_reg {
 
 struct stv0900_config {
        u8 demod_address;
+       u8 demod_mode;
        u32 xtal;
        u8 clkmode;/* 0 for CLKI,  2 for XTALI */
 
@@ -48,6 +49,8 @@ struct stv0900_config {
        u8 tun2_maddress;
        u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */
        u8 tun2_adc;
+       /* Set device param to start dma */
+       int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured);
 };
 
 #if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \
index 3bde332..df49ea0 100644 (file)
@@ -34,7 +34,7 @@
 #include "stv0900_priv.h"
 #include "stv0900_init.h"
 
-static int stvdebug = 1;
+int stvdebug = 1;
 module_param_named(debug, stvdebug, int, 0644);
 
 /* internal params node */
@@ -105,7 +105,8 @@ static struct stv0900_inode *append_internal(struct stv0900_internal *internal)
                while (new_node->next_inode != NULL)
                        new_node = new_node->next_inode;
 
-               new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL);
+               new_node->next_inode = kmalloc(sizeof(struct stv0900_inode),
+                                                               GFP_KERNEL);
                if (new_node->next_inode != NULL)
                        new_node = new_node->next_inode;
                else
@@ -128,13 +129,13 @@ s32 ge2comp(s32 a, s32 width)
                return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a;
 }
 
-void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr,
+void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr,
                                                                u8 reg_data)
 {
        u8 data[3];
        int ret;
        struct i2c_msg i2cmsg = {
-               .addr  = i_params->i2c_addr,
+               .addr  = intp->i2c_addr,
                .flags = 0,
                .len   = 3,
                .buf   = data,
@@ -144,33 +145,33 @@ void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr,
        data[1] = LSB(reg_addr);
        data[2] = reg_data;
 
-       ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1);
+       ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1);
        if (ret != 1)
-               dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret);
+               dprintk("%s: i2c error %d\n", __func__, ret);
 }
 
-u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg)
+u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg)
 {
        int ret;
        u8 b0[] = { MSB(reg), LSB(reg) };
        u8 buf = 0;
        struct i2c_msg msg[] = {
                {
-                       .addr   = i_params->i2c_addr,
+                       .addr   = intp->i2c_addr,
                        .flags  = 0,
                        .buf = b0,
                        .len = 2,
                }, {
-                       .addr   = i_params->i2c_addr,
+                       .addr   = intp->i2c_addr,
                        .flags  = I2C_M_RD,
                        .buf = &buf,
                        .len = 1,
                },
        };
 
-       ret = i2c_transfer(i_params->i2c_adap, msg, 2);
+       ret = i2c_transfer(intp->i2c_adap, msg, 2);
        if (ret != 2)
-               dprintk(KERN_ERR "%s: i2c error %d, reg[0x%02x]\n",
+               dprintk("%s: i2c error %d, reg[0x%02x]\n",
                                __func__, ret, reg);
 
        return buf;
@@ -190,169 +191,165 @@ void extract_mask_pos(u32 label, u8 *mask, u8 *pos)
        (*pos) = (i - 1);
 }
 
-void stv0900_write_bits(struct stv0900_internal *i_params, u32 label, u8 val)
+void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val)
 {
        u8 reg, mask, pos;
 
-       reg = stv0900_read_reg(i_params, (label >> 16) & 0xffff);
+       reg = stv0900_read_reg(intp, (label >> 16) & 0xffff);
        extract_mask_pos(label, &mask, &pos);
 
        val = mask & (val << pos);
 
        reg = (reg & (~mask)) | val;
-       stv0900_write_reg(i_params, (label >> 16) & 0xffff, reg);
+       stv0900_write_reg(intp, (label >> 16) & 0xffff, reg);
 
 }
 
-u8 stv0900_get_bits(struct stv0900_internal *i_params, u32 label)
+u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label)
 {
        u8 val = 0xff;
        u8 mask, pos;
 
        extract_mask_pos(label, &mask, &pos);
 
-       val = stv0900_read_reg(i_params, label >> 16);
+       val = stv0900_read_reg(intp, label >> 16);
        val = (val & mask) >> pos;
 
        return val;
 }
 
-enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params)
+enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp)
 {
        s32 i;
-       enum fe_stv0900_error error;
-
-       if (i_params != NULL) {
-               i_params->chip_id = stv0900_read_reg(i_params, R0900_MID);
-               if (i_params->errs == STV0900_NO_ERROR) {
-                       /*Startup sequence*/
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5c);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c);
-                       stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c);
-                       stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f);
-                       stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20);
-                       stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20);
-                       stv0900_write_reg(i_params, R0900_NCOARSE, 0x13);
-                       msleep(3);
-                       stv0900_write_reg(i_params, R0900_I2CCFG, 0x08);
-
-                       switch (i_params->clkmode) {
-                       case 0:
-                       case 2:
-                               stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20
-                                               | i_params->clkmode);
-                               break;
-                       default:
-                               /* preserve SELOSCI bit */
-                               i = 0x02 & stv0900_read_reg(i_params, R0900_SYNTCTRL);
-                               stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 | i);
-                               break;
-                       }
 
-                       msleep(3);
-                       for (i = 0; i < 182; i++)
-                               stv0900_write_reg(i_params, STV0900_InitVal[i][0], STV0900_InitVal[i][1]);
+       if (intp == NULL)
+               return STV0900_INVALID_HANDLE;
 
-                       if (stv0900_read_reg(i_params, R0900_MID) >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_TSGENERAL, 0x0c);
-                               for (i = 0; i < 32; i++)
-                                       stv0900_write_reg(i_params, STV0900_Cut20_AddOnVal[i][0], STV0900_Cut20_AddOnVal[i][1]);
-                       }
+       intp->chip_id = stv0900_read_reg(intp, R0900_MID);
 
-                       stv0900_write_reg(i_params, R0900_P1_FSPYCFG, 0x6c);
-                       stv0900_write_reg(i_params, R0900_P2_FSPYCFG, 0x6c);
-                       stv0900_write_reg(i_params, R0900_TSTRES0, 0x80);
-                       stv0900_write_reg(i_params, R0900_TSTRES0, 0x00);
-               }
-               error = i_params->errs;
-       } else
-               error = STV0900_INVALID_HANDLE;
+       if (intp->errs != STV0900_NO_ERROR)
+               return intp->errs;
 
-       return error;
+       /*Startup sequence*/
+       stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c);
+       stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c);
+       msleep(3);
+       stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c);
+       stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f);
+       stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20);
+       stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20);
+       stv0900_write_reg(intp, R0900_NCOARSE, 0x13);
+       msleep(3);
+       stv0900_write_reg(intp, R0900_I2CCFG, 0x08);
 
+       switch (intp->clkmode) {
+       case 0:
+       case 2:
+               stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20
+                               | intp->clkmode);
+               break;
+       default:
+               /* preserve SELOSCI bit */
+               i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL);
+               stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i);
+               break;
+       }
+
+       msleep(3);
+       for (i = 0; i < 181; i++)
+               stv0900_write_reg(intp, STV0900_InitVal[i][0],
+                               STV0900_InitVal[i][1]);
+
+       if (stv0900_read_reg(intp, R0900_MID) >= 0x20) {
+               stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c);
+               for (i = 0; i < 32; i++)
+                       stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0],
+                                       STV0900_Cut20_AddOnVal[i][1]);
+       }
+
+       stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c);
+       stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c);
+
+       stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01);
+       stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21);
+
+       stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20);
+       stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20);
+
+       stv0900_write_reg(intp, R0900_TSTRES0, 0x80);
+       stv0900_write_reg(intp, R0900_TSTRES0, 0x00);
+
+       return STV0900_NO_ERROR;
 }
 
-u32 stv0900_get_mclk_freq(struct stv0900_internal *i_params, u32 ext_clk)
+u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk)
 {
        u32 mclk = 90000000, div = 0, ad_div = 0;
 
-       div = stv0900_get_bits(i_params, F0900_M_DIV);
-       ad_div = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6);
+       div = stv0900_get_bits(intp, F0900_M_DIV);
+       ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6);
 
        mclk = (div + 1) * ext_clk / ad_div;
 
-       dprintk(KERN_INFO "%s: Calculated Mclk = %d\n", __func__, mclk);
+       dprintk("%s: Calculated Mclk = %d\n", __func__, mclk);
 
        return mclk;
 }
 
-enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *i_params, u32 mclk)
+enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk)
 {
-       enum fe_stv0900_error error = STV0900_NO_ERROR;
        u32 m_div, clk_sel;
 
-       dprintk(KERN_INFO "%s: Mclk set to %d, Quartz = %d\n", __func__, mclk,
-                       i_params->quartz);
+       dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk,
+                       intp->quartz);
 
-       if (i_params == NULL)
-               error = STV0900_INVALID_HANDLE;
-       else {
-               if (i_params->errs)
-                       error = STV0900_I2C_ERROR;
-               else {
-                       clk_sel = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6);
-                       m_div = ((clk_sel * mclk) / i_params->quartz) - 1;
-                       stv0900_write_bits(i_params, F0900_M_DIV, m_div);
-                       i_params->mclk = stv0900_get_mclk_freq(i_params,
-                                                       i_params->quartz);
-
-                       /*Set the DiseqC frequency to 22KHz */
-                       /*
-                               Formula:
-                               DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg)
-                               DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg)
-                       */
-                       m_div = i_params->mclk / 704000;
-                       stv0900_write_reg(i_params, R0900_P1_F22TX, m_div);
-                       stv0900_write_reg(i_params, R0900_P1_F22RX, m_div);
-
-                       stv0900_write_reg(i_params, R0900_P2_F22TX, m_div);
-                       stv0900_write_reg(i_params, R0900_P2_F22RX, m_div);
-
-                       if ((i_params->errs))
-                               error = STV0900_I2C_ERROR;
-               }
-       }
+       if (intp == NULL)
+               return STV0900_INVALID_HANDLE;
 
-       return error;
+       if (intp->errs)
+               return STV0900_I2C_ERROR;
+
+       clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6);
+       m_div = ((clk_sel * mclk) / intp->quartz) - 1;
+       stv0900_write_bits(intp, F0900_M_DIV, m_div);
+       intp->mclk = stv0900_get_mclk_freq(intp,
+                                       intp->quartz);
+
+       /*Set the DiseqC frequency to 22KHz */
+       /*
+               Formula:
+               DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg)
+               DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg)
+       */
+       m_div = intp->mclk / 704000;
+       stv0900_write_reg(intp, R0900_P1_F22TX, m_div);
+       stv0900_write_reg(intp, R0900_P1_F22RX, m_div);
+
+       stv0900_write_reg(intp, R0900_P2_F22TX, m_div);
+       stv0900_write_reg(intp, R0900_P2_F22RX, m_div);
+
+       if ((intp->errs))
+               return STV0900_I2C_ERROR;
+
+       return STV0900_NO_ERROR;
 }
 
-u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr,
+u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr,
                                        enum fe_stv0900_demod_num demod)
 {
        u32 lsb, msb, hsb, err_val;
-       s32 err1field_hsb, err1field_msb, err1field_lsb;
-       s32 err2field_hsb, err2field_msb, err2field_lsb;
-
-       dmd_reg(err1field_hsb, F0900_P1_ERR_CNT12, F0900_P2_ERR_CNT12);
-       dmd_reg(err1field_msb, F0900_P1_ERR_CNT11, F0900_P2_ERR_CNT11);
-       dmd_reg(err1field_lsb, F0900_P1_ERR_CNT10, F0900_P2_ERR_CNT10);
-
-       dmd_reg(err2field_hsb, F0900_P1_ERR_CNT22, F0900_P2_ERR_CNT22);
-       dmd_reg(err2field_msb, F0900_P1_ERR_CNT21, F0900_P2_ERR_CNT21);
-       dmd_reg(err2field_lsb, F0900_P1_ERR_CNT20, F0900_P2_ERR_CNT20);
 
        switch (cntr) {
        case 0:
        default:
-               hsb = stv0900_get_bits(i_params, err1field_hsb);
-               msb = stv0900_get_bits(i_params, err1field_msb);
-               lsb = stv0900_get_bits(i_params, err1field_lsb);
+               hsb = stv0900_get_bits(intp, ERR_CNT12);
+               msb = stv0900_get_bits(intp, ERR_CNT11);
+               lsb = stv0900_get_bits(intp, ERR_CNT10);
                break;
        case 1:
-               hsb = stv0900_get_bits(i_params, err2field_hsb);
-               msb = stv0900_get_bits(i_params, err2field_msb);
-               lsb = stv0900_get_bits(i_params, err2field_lsb);
+               hsb = stv0900_get_bits(intp, ERR_CNT22);
+               msb = stv0900_get_bits(intp, ERR_CNT21);
+               lsb = stv0900_get_bits(intp, ERR_CNT20);
                break;
        }
 
@@ -364,26 +361,22 @@ u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr,
 static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
 
-       u32 fi2c;
-
-       dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON);
-
-       stv0900_write_bits(i_params, fi2c, enable);
+       stv0900_write_bits(intp, I2CT_ON, enable);
 
        return 0;
 }
 
-static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
+static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp,
                                        enum fe_stv0900_clock_type path1_ts,
                                        enum fe_stv0900_clock_type path2_ts)
 {
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       if (i_params->chip_id >= 0x20) {
+       if (intp->chip_id >= 0x20) {
                switch (path1_ts) {
                case STV0900_PARALLEL_PUNCT_CLOCK:
                case STV0900_DVBCI_CLOCK:
@@ -391,20 +384,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
                        case STV0900_SERIAL_PUNCT_CLOCK:
                        case STV0900_SERIAL_CONT_CLOCK:
                        default:
-                               stv0900_write_reg(i_params, R0900_TSGENERAL,
+                               stv0900_write_reg(intp, R0900_TSGENERAL,
                                                        0x00);
                                break;
                        case STV0900_PARALLEL_PUNCT_CLOCK:
                        case STV0900_DVBCI_CLOCK:
-                               stv0900_write_reg(i_params, R0900_TSGENERAL,
+                               stv0900_write_reg(intp, R0900_TSGENERAL,
                                                        0x06);
-                               stv0900_write_bits(i_params,
+                               stv0900_write_bits(intp,
                                                F0900_P1_TSFIFO_MANSPEED, 3);
-                               stv0900_write_bits(i_params,
+                               stv0900_write_bits(intp,
                                                F0900_P2_TSFIFO_MANSPEED, 0);
-                               stv0900_write_reg(i_params,
+                               stv0900_write_reg(intp,
                                                R0900_P1_TSSPEED, 0x14);
-                               stv0900_write_reg(i_params,
+                               stv0900_write_reg(intp,
                                                R0900_P2_TSSPEED, 0x28);
                                break;
                        }
@@ -416,14 +409,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
                        case STV0900_SERIAL_PUNCT_CLOCK:
                        case STV0900_SERIAL_CONT_CLOCK:
                        default:
-                               stv0900_write_reg(i_params,
+                               stv0900_write_reg(intp,
                                                R0900_TSGENERAL, 0x0C);
                                break;
                        case STV0900_PARALLEL_PUNCT_CLOCK:
                        case STV0900_DVBCI_CLOCK:
-                               stv0900_write_reg(i_params,
+                               stv0900_write_reg(intp,
                                                R0900_TSGENERAL, 0x0A);
-                               dprintk(KERN_INFO "%s: 0x0a\n", __func__);
+                               dprintk("%s: 0x0a\n", __func__);
                                break;
                        }
                        break;
@@ -436,20 +429,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
                        case STV0900_SERIAL_PUNCT_CLOCK:
                        case STV0900_SERIAL_CONT_CLOCK:
                        default:
-                               stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+                               stv0900_write_reg(intp, R0900_TSGENERAL1X,
                                                        0x10);
                                break;
                        case STV0900_PARALLEL_PUNCT_CLOCK:
                        case STV0900_DVBCI_CLOCK:
-                               stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+                               stv0900_write_reg(intp, R0900_TSGENERAL1X,
                                                        0x16);
-                               stv0900_write_bits(i_params,
+                               stv0900_write_bits(intp,
                                                F0900_P1_TSFIFO_MANSPEED, 3);
-                               stv0900_write_bits(i_params,
+                               stv0900_write_bits(intp,
                                                F0900_P2_TSFIFO_MANSPEED, 0);
-                               stv0900_write_reg(i_params, R0900_P1_TSSPEED,
+                               stv0900_write_reg(intp, R0900_P1_TSSPEED,
                                                        0x14);
-                               stv0900_write_reg(i_params, R0900_P2_TSSPEED,
+                               stv0900_write_reg(intp, R0900_P2_TSSPEED,
                                                        0x28);
                                break;
                        }
@@ -462,14 +455,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
                        case STV0900_SERIAL_PUNCT_CLOCK:
                        case STV0900_SERIAL_CONT_CLOCK:
                        default:
-                               stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+                               stv0900_write_reg(intp, R0900_TSGENERAL1X,
                                                        0x14);
                                break;
                        case STV0900_PARALLEL_PUNCT_CLOCK:
                        case STV0900_DVBCI_CLOCK:
-                               stv0900_write_reg(i_params, R0900_TSGENERAL1X,
+                               stv0900_write_reg(intp, R0900_TSGENERAL1X,
                                                        0x12);
-                               dprintk(KERN_INFO "%s: 0x12\n", __func__);
+                               dprintk("%s: 0x12\n", __func__);
                                break;
                        }
 
@@ -479,20 +472,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
 
        switch (path1_ts) {
        case STV0900_PARALLEL_PUNCT_CLOCK:
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00);
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00);
                break;
        case STV0900_DVBCI_CLOCK:
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00);
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01);
                break;
        case STV0900_SERIAL_PUNCT_CLOCK:
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01);
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00);
                break;
        case STV0900_SERIAL_CONT_CLOCK:
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01);
-               stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01);
+               stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01);
                break;
        default:
                break;
@@ -500,29 +493,29 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
 
        switch (path2_ts) {
        case STV0900_PARALLEL_PUNCT_CLOCK:
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00);
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00);
                break;
        case STV0900_DVBCI_CLOCK:
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00);
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01);
                break;
        case STV0900_SERIAL_PUNCT_CLOCK:
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01);
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00);
                break;
        case STV0900_SERIAL_CONT_CLOCK:
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01);
-               stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01);
+               stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01);
                break;
        default:
                break;
        }
 
-       stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1);
-       stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 0);
-       stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1);
-       stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 0);
+       stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1);
+       stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0);
+       stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1);
+       stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0);
 }
 
 void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency,
@@ -574,7 +567,7 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
        }
 }
 
-static s32 stv0900_get_rf_level(struct stv0900_internal *i_params,
+static s32 stv0900_get_rf_level(struct stv0900_internal *intp,
                                const struct stv0900_table *lookup,
                                enum fe_stv0900_demod_num demod)
 {
@@ -584,45 +577,41 @@ static s32 stv0900_get_rf_level(struct stv0900_internal *i_params,
                i,
                rf_lvl = 0;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       if ((lookup != NULL) && lookup->size) {
-               switch (demod) {
-               case STV0900_DEMOD_1:
-               default:
-                       agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE1),
-                                               stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE0));
-                       break;
-               case STV0900_DEMOD_2:
-                       agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE1),
-                                               stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE0));
-                       break;
-               }
+       if ((lookup == NULL) || (lookup->size <= 0))
+               return 0;
 
-               imin = 0;
-               imax = lookup->size - 1;
-               if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[imax].regval)) {
-                       while ((imax - imin) > 1) {
-                               i = (imax + imin) >> 1;
+       agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1),
+                               stv0900_get_bits(intp, AGCIQ_VALUE0));
 
-                               if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[i].regval))
-                                       imax = i;
-                               else
-                                       imin = i;
-                       }
+       imin = 0;
+       imax = lookup->size - 1;
+       if (INRANGE(lookup->table[imin].regval, agc_gain,
+                                       lookup->table[imax].regval)) {
+               while ((imax - imin) > 1) {
+                       i = (imax + imin) >> 1;
 
-                       rf_lvl = (((s32)agc_gain - lookup->table[imin].regval)
-                                       * (lookup->table[imax].realval - lookup->table[imin].realval)
-                                       / (lookup->table[imax].regval - lookup->table[imin].regval))
-                                       + lookup->table[imin].realval;
-               } else if (agc_gain > lookup->table[0].regval)
-                       rf_lvl = 5;
-               else if (agc_gain < lookup->table[lookup->size-1].regval)
-                       rf_lvl = -100;
+                       if (INRANGE(lookup->table[imin].regval,
+                                       agc_gain,
+                                       lookup->table[i].regval))
+                               imax = i;
+                       else
+                               imin = i;
+               }
 
-       }
+               rf_lvl = (s32)agc_gain - lookup->table[imin].regval;
+               rf_lvl *= (lookup->table[imax].realval -
+                               lookup->table[imin].realval);
+               rf_lvl /= (lookup->table[imax].regval -
+                               lookup->table[imin].regval);
+               rf_lvl += lookup->table[imin].realval;
+       } else if (agc_gain > lookup->table[0].regval)
+               rf_lvl = 5;
+       else if (agc_gain < lookup->table[lookup->size-1].regval)
+               rf_lvl = -100;
 
-       dprintk(KERN_INFO "%s: RFLevel = %d\n", __func__, rf_lvl);
+       dprintk("%s: RFLevel = %d\n", __func__, rf_lvl);
 
        return rf_lvl;
 }
@@ -634,50 +623,51 @@ static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
        s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf,
                                                                state->demod);
 
-       *strength = (rflevel + 100) * (16383 / 105);
+       rflevel = (rflevel + 100) * (65535 / 70);
+       if (rflevel < 0)
+               rflevel = 0;
+
+       if (rflevel > 65535)
+               rflevel = 65535;
+
+       *strength = rflevel;
 
        return 0;
 }
 
-
 static s32 stv0900_carr_get_quality(struct dvb_frontend *fe,
                                        const struct stv0900_table *lookup)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
 
-       s32 c_n = -100,
-               regval, imin, imax,
+       s32     c_n = -100,
+               regval,
+               imin,
+               imax,
                i,
-               lock_flag_field,
                noise_field1,
                noise_field0;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       dmd_reg(lock_flag_field, F0900_P1_LOCK_DEFINITIF,
-                                       F0900_P2_LOCK_DEFINITIF);
        if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) {
-               dmd_reg(noise_field1, F0900_P1_NOSPLHT_NORMED1,
-                                       F0900_P2_NOSPLHT_NORMED1);
-               dmd_reg(noise_field0, F0900_P1_NOSPLHT_NORMED0,
-                                       F0900_P2_NOSPLHT_NORMED0);
+               noise_field1 = NOSPLHT_NORMED1;
+               noise_field0 = NOSPLHT_NORMED0;
        } else {
-               dmd_reg(noise_field1, F0900_P1_NOSDATAT_NORMED1,
-                                       F0900_P2_NOSDATAT_NORMED1);
-               dmd_reg(noise_field0, F0900_P1_NOSDATAT_NORMED0,
-                                       F0900_P2_NOSDATAT_NORMED0);
+               noise_field1 = NOSDATAT_NORMED1;
+               noise_field0 = NOSDATAT_NORMED0;
        }
 
-       if (stv0900_get_bits(i_params, lock_flag_field)) {
+       if (stv0900_get_bits(intp, LOCK_DEFINITIF)) {
                if ((lookup != NULL) && lookup->size) {
                        regval = 0;
                        msleep(5);
                        for (i = 0; i < 16; i++) {
-                               regval += MAKEWORD(stv0900_get_bits(i_params,
+                               regval += MAKEWORD(stv0900_get_bits(intp,
                                                                noise_field1),
-                                               stv0900_get_bits(i_params,
+                                               stv0900_get_bits(intp,
                                                                noise_field0));
                                msleep(1);
                        }
@@ -715,10 +705,9 @@ static s32 stv0900_carr_get_quality(struct dvb_frontend *fe,
 static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
        u8 err_val1, err_val0;
-       s32 err_field1, err_field0;
        u32 header_err_val = 0;
 
        *ucblocks = 0x0;
@@ -726,24 +715,14 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
                /* DVB-S2 delineator errors count */
 
                /* retreiving number for errnous headers */
-               dmd_reg(err_field0, R0900_P1_BBFCRCKO0,
-                                       R0900_P2_BBFCRCKO0);
-               dmd_reg(err_field1, R0900_P1_BBFCRCKO1,
-                                       R0900_P2_BBFCRCKO1);
-
-               err_val1 = stv0900_read_reg(i_params, err_field1);
-               err_val0 = stv0900_read_reg(i_params, err_field0);
-               header_err_val = (err_val1<<8) | err_val0;
+               err_val1 = stv0900_read_reg(intp, BBFCRCKO1);
+               err_val0 = stv0900_read_reg(intp, BBFCRCKO0);
+               header_err_val = (err_val1 << 8) | err_val0;
 
                /* retreiving number for errnous packets */
-               dmd_reg(err_field0, R0900_P1_UPCRCKO0,
-                                       R0900_P2_UPCRCKO0);
-               dmd_reg(err_field1, R0900_P1_UPCRCKO1,
-                                       R0900_P2_UPCRCKO1);
-
-               err_val1 = stv0900_read_reg(i_params, err_field1);
-               err_val0 = stv0900_read_reg(i_params, err_field0);
-               *ucblocks = (err_val1<<8) | err_val0;
+               err_val1 = stv0900_read_reg(intp, UPCRCKO1);
+               err_val0 = stv0900_read_reg(intp, UPCRCKO0);
+               *ucblocks = (err_val1 << 8) | err_val0;
                *ucblocks += header_err_val;
        }
 
@@ -752,33 +731,27 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks)
 
 static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr)
 {
-       *snr = stv0900_carr_get_quality(fe,
+       s32 snrlcl = stv0900_carr_get_quality(fe,
                        (const struct stv0900_table *)&stv0900_s2_cn);
-       *snr += 30;
-       *snr *= (16383 / 1030);
+       snrlcl = (snrlcl + 30) * 384;
+       if (snrlcl < 0)
+               snrlcl = 0;
+
+       if (snrlcl > 65535)
+               snrlcl = 65535;
+
+       *snr = snrlcl;
 
        return 0;
 }
 
-static u32 stv0900_get_ber(struct stv0900_internal *i_params,
+static u32 stv0900_get_ber(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod)
 {
        u32 ber = 10000000, i;
-       s32 dmd_state_reg;
        s32 demod_state;
-       s32 vstatus_reg;
-       s32 prvit_field;
-       s32 pdel_status_reg;
-       s32 pdel_lock_field;
-
-       dmd_reg(dmd_state_reg, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
-       dmd_reg(vstatus_reg, R0900_P1_VSTATUSVIT, R0900_P2_VSTATUSVIT);
-       dmd_reg(prvit_field, F0900_P1_PRFVIT, F0900_P2_PRFVIT);
-       dmd_reg(pdel_status_reg, R0900_P1_PDELSTATUS1, R0900_P2_PDELSTATUS1);
-       dmd_reg(pdel_lock_field, F0900_P1_PKTDELIN_LOCK,
-                               F0900_P2_PKTDELIN_LOCK);
 
-       demod_state = stv0900_get_bits(i_params, dmd_state_reg);
+       demod_state = stv0900_get_bits(intp, HEADER_MODE);
 
        switch (demod_state) {
        case STV0900_SEARCH:
@@ -790,11 +763,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params,
                ber = 0;
                for (i = 0; i < 5; i++) {
                        msleep(5);
-                       ber += stv0900_get_err_count(i_params, 0, demod);
+                       ber += stv0900_get_err_count(intp, 0, demod);
                }
 
                ber /= 5;
-               if (stv0900_get_bits(i_params, prvit_field)) {
+               if (stv0900_get_bits(intp, PRFVIT)) {
                        ber *= 9766;
                        ber = ber >> 13;
                }
@@ -804,11 +777,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params,
                ber = 0;
                for (i = 0; i < 5; i++) {
                        msleep(5);
-                       ber += stv0900_get_err_count(i_params, 0, demod);
+                       ber += stv0900_get_err_count(intp, 0, demod);
                }
 
                ber /= 5;
-               if (stv0900_get_bits(i_params, pdel_lock_field)) {
+               if (stv0900_get_bits(intp, PKTDELIN_LOCK)) {
                        ber *= 9766;
                        ber = ber >> 13;
                }
@@ -829,20 +802,16 @@ static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber)
        return 0;
 }
 
-int stv0900_get_demod_lock(struct stv0900_internal *i_params,
+int stv0900_get_demod_lock(struct stv0900_internal *intp,
                        enum fe_stv0900_demod_num demod, s32 time_out)
 {
        s32 timer = 0,
-               lock = 0,
-               header_field,
-               lock_field;
+               lock = 0;
 
        enum fe_stv0900_search_state    dmd_state;
 
-       dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
-       dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF);
        while ((timer < time_out) && (lock == 0)) {
-               dmd_state = stv0900_get_bits(i_params, header_field);
+               dmd_state = stv0900_get_bits(intp, HEADER_MODE);
                dprintk("Demod State = %d\n", dmd_state);
                switch (dmd_state) {
                case STV0900_SEARCH:
@@ -852,7 +821,7 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params,
                        break;
                case STV0900_DVBS2_FOUND:
                case STV0900_DVBS_FOUND:
-                       lock = stv0900_get_bits(i_params, lock_field);
+                       lock = stv0900_get_bits(intp, LOCK_DEFINITIF);
                        break;
                }
 
@@ -870,56 +839,40 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params,
        return lock;
 }
 
-void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params,
+void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod)
 {
        s32 regflist,
        i;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       dmd_reg(regflist, R0900_P1_MODCODLST0, R0900_P2_MODCODLST0);
+       regflist = MODCODLST0;
 
        for (i = 0; i < 16; i++)
-               stv0900_write_reg(i_params, regflist + i, 0xff);
+               stv0900_write_reg(intp, regflist + i, 0xff);
 }
 
-void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
+void stv0900_activate_s2_modcod(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod)
 {
        u32 matype,
-       mod_code,
-       fmod,
-       reg_index,
-       field_index;
+               mod_code,
+               fmod,
+               reg_index,
+               field_index;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       if (i_params->chip_id <= 0x11) {
+       if (intp->chip_id <= 0x11) {
                msleep(5);
 
-               switch (demod) {
-               case STV0900_DEMOD_1:
-               default:
-                       mod_code = stv0900_read_reg(i_params,
-                                                       R0900_P1_PLHMODCOD);
-                       matype = mod_code & 0x3;
-                       mod_code = (mod_code & 0x7f) >> 2;
-
-                       reg_index = R0900_P1_MODCODLSTF - mod_code / 2;
-                       field_index = mod_code % 2;
-                       break;
-               case STV0900_DEMOD_2:
-                       mod_code = stv0900_read_reg(i_params,
-                                                       R0900_P2_PLHMODCOD);
-                       matype = mod_code & 0x3;
-                       mod_code = (mod_code & 0x7f) >> 2;
-
-                       reg_index = R0900_P2_MODCODLSTF - mod_code / 2;
-                       field_index = mod_code % 2;
-                       break;
-               }
+               mod_code = stv0900_read_reg(intp, PLHMODCOD);
+               matype = mod_code & 0x3;
+               mod_code = (mod_code & 0x7f) >> 2;
 
+               reg_index = MODCODLSTF - mod_code / 2;
+               field_index = mod_code % 2;
 
                switch (matype) {
                case 0:
@@ -938,70 +891,41 @@ void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
                }
 
                if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910))
-                                                       && (matype <= 1)) {
+                                               && (matype <= 1)) {
                        if (field_index == 0)
-                               stv0900_write_reg(i_params, reg_index,
+                               stv0900_write_reg(intp, reg_index,
                                                        0xf0 | fmod);
                        else
-                               stv0900_write_reg(i_params, reg_index,
+                               stv0900_write_reg(intp, reg_index,
                                                        (fmod << 4) | 0xf);
                }
-       } else if (i_params->chip_id >= 0x12) {
-               switch (demod) {
-               case STV0900_DEMOD_1:
-               default:
-                       for (reg_index = 0; reg_index < 7; reg_index++)
-                               stv0900_write_reg(i_params, R0900_P1_MODCODLST0 + reg_index, 0xff);
 
-                       stv0900_write_reg(i_params, R0900_P1_MODCODLSTE, 0xff);
-                       stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0xcf);
-                       for (reg_index = 0; reg_index < 8; reg_index++)
-                               stv0900_write_reg(i_params, R0900_P1_MODCODLST7 + reg_index, 0xcc);
+       } else if (intp->chip_id >= 0x12) {
+               for (reg_index = 0; reg_index < 7; reg_index++)
+                       stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff);
 
-                       break;
-               case STV0900_DEMOD_2:
-                       for (reg_index = 0; reg_index < 7; reg_index++)
-                               stv0900_write_reg(i_params, R0900_P2_MODCODLST0 + reg_index, 0xff);
+               stv0900_write_reg(intp, MODCODLSTE, 0xff);
+               stv0900_write_reg(intp, MODCODLSTF, 0xcf);
+               for (reg_index = 0; reg_index < 8; reg_index++)
+                       stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc);
 
-                       stv0900_write_reg(i_params, R0900_P2_MODCODLSTE, 0xff);
-                       stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0xcf);
-                       for (reg_index = 0; reg_index < 8; reg_index++)
-                               stv0900_write_reg(i_params, R0900_P2_MODCODLST7 + reg_index, 0xcc);
-
-                       break;
-               }
 
        }
 }
 
-void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params,
+void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
        u32 reg_index;
 
-       dprintk(KERN_INFO "%s\n", __func__);
-
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               stv0900_write_reg(i_params, R0900_P1_MODCODLST0, 0xff);
-               stv0900_write_reg(i_params, R0900_P1_MODCODLST1, 0xf0);
-               stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0x0f);
-               for (reg_index = 0; reg_index < 13; reg_index++)
-                       stv0900_write_reg(i_params,
-                                       R0900_P1_MODCODLST2 + reg_index, 0);
+       dprintk("%s\n", __func__);
 
-               break;
-       case STV0900_DEMOD_2:
-               stv0900_write_reg(i_params, R0900_P2_MODCODLST0, 0xff);
-               stv0900_write_reg(i_params, R0900_P2_MODCODLST1, 0xf0);
-               stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0x0f);
-               for (reg_index = 0; reg_index < 13; reg_index++)
-                       stv0900_write_reg(i_params,
-                                       R0900_P2_MODCODLST2 + reg_index, 0);
+       stv0900_write_reg(intp, MODCODLST0, 0xff);
+       stv0900_write_reg(intp, MODCODLST1, 0xf0);
+       stv0900_write_reg(intp, MODCODLSTF, 0x0f);
+       for (reg_index = 0; reg_index < 13; reg_index++)
+               stv0900_write_reg(intp, MODCODLST2 + reg_index, 0);
 
-               break;
-       }
 }
 
 static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe)
@@ -1012,7 +936,7 @@ static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe)
 static int stb0900_set_property(struct dvb_frontend *fe,
                                struct dtv_property *tvp)
 {
-       dprintk(KERN_INFO "%s(..)\n", __func__);
+       dprintk("%s(..)\n", __func__);
 
        return 0;
 }
@@ -1020,166 +944,123 @@ static int stb0900_set_property(struct dvb_frontend *fe,
 static int stb0900_get_property(struct dvb_frontend *fe,
                                struct dtv_property *tvp)
 {
-       dprintk(KERN_INFO "%s(..)\n", __func__);
+       dprintk("%s(..)\n", __func__);
 
        return 0;
 }
 
-void stv0900_start_search(struct stv0900_internal *i_params,
+void stv0900_start_search(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod)
 {
-
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1f);
-
-               if (i_params->chip_id == 0x10)
-                       stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xaa);
-
-               if (i_params->chip_id < 0x20)
-                       stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55);
-
-               if (i_params->dmd1_symbol_rate <= 5000000) {
-                       stv0900_write_reg(i_params, R0900_P1_CARCFG, 0x44);
-                       stv0900_write_reg(i_params, R0900_P1_CFRUP1, 0x0f);
-                       stv0900_write_reg(i_params, R0900_P1_CFRUP0, 0xff);
-                       stv0900_write_reg(i_params, R0900_P1_CFRLOW1, 0xf0);
-                       stv0900_write_reg(i_params, R0900_P1_CFRLOW0, 0x00);
-                       stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68);
+       u32 freq;
+       s16 freq_s16 ;
+
+       stv0900_write_bits(intp, DEMOD_MODE, 0x1f);
+       if (intp->chip_id == 0x10)
+               stv0900_write_reg(intp, CORRELEXP, 0xaa);
+
+       if (intp->chip_id < 0x20)
+               stv0900_write_reg(intp, CARHDR, 0x55);
+
+       if (intp->chip_id <= 0x20) {
+               if (intp->symbol_rate[0] <= 5000000) {
+                       stv0900_write_reg(intp, CARCFG, 0x44);
+                       stv0900_write_reg(intp, CFRUP1, 0x0f);
+                       stv0900_write_reg(intp, CFRUP0, 0xff);
+                       stv0900_write_reg(intp, CFRLOW1, 0xf0);
+                       stv0900_write_reg(intp, CFRLOW0, 0x00);
+                       stv0900_write_reg(intp, RTCS2, 0x68);
                } else {
-                       stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xc4);
-                       stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44);
-               }
-
-               stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0);
-               stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0);
-
-               if (i_params->chip_id >= 0x20) {
-                       stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41);
-                       stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41);
-
-                       if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) {
-                               stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82);
-                               stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0);
-                       }
+                       stv0900_write_reg(intp, CARCFG, 0xc4);
+                       stv0900_write_reg(intp, RTCS2, 0x44);
                }
 
-               stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xe0);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xc0);
-               stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0);
-               stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
-               stv0900_write_bits(i_params, F0900_P1_S1S2_SEQUENTIAL, 0);
-               stv0900_write_reg(i_params, R0900_P1_RTC, 0x88);
-               if (i_params->chip_id >= 0x20) {
-                       if (i_params->dmd1_symbol_rate < 2000000) {
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x39);
-                               stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x40);
-                       }
-
-                       if (i_params->dmd1_symbol_rate < 10000000) {
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4c);
-                               stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20);
-                       } else {
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4b);
-                               stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20);
-                       }
+       } else { /*cut 3.0 above*/
+               if (intp->symbol_rate[demod] <= 5000000)
+                       stv0900_write_reg(intp, RTCS2, 0x68);
+               else
+                       stv0900_write_reg(intp, RTCS2, 0x44);
 
+               stv0900_write_reg(intp, CARCFG, 0x46);
+               if (intp->srch_algo[demod] == STV0900_WARM_START) {
+                       freq = 1000 << 16;
+                       freq /= (intp->mclk / 1000);
+                       freq_s16 = (s16)freq;
                } else {
-                       if (i_params->dmd1_symbol_rate < 10000000)
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xef);
+                       freq = (intp->srch_range[demod] / 2000);
+                       if (intp->symbol_rate[demod] <= 5000000)
+                               freq += 80;
                        else
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
-               }
+                               freq += 600;
 
-               switch (i_params->dmd1_srch_algo) {
-               case STV0900_WARM_START:
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-                       break;
-               case STV0900_COLD_START:
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
-                       break;
-               default:
-                       break;
-               }
-
-               break;
-       case STV0900_DEMOD_2:
-               stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1f);
-               if (i_params->chip_id == 0x10)
-                       stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xaa);
-
-               if (i_params->chip_id < 0x20)
-                       stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55);
-
-               if (i_params->dmd2_symbol_rate <= 5000000) {
-                       stv0900_write_reg(i_params, R0900_P2_CARCFG, 0x44);
-                       stv0900_write_reg(i_params, R0900_P2_CFRUP1, 0x0f);
-                       stv0900_write_reg(i_params, R0900_P2_CFRUP0, 0xff);
-                       stv0900_write_reg(i_params, R0900_P2_CFRLOW1, 0xf0);
-                       stv0900_write_reg(i_params, R0900_P2_CFRLOW0, 0x00);
-                       stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68);
-               } else {
-                       stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xc4);
-                       stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44);
+                       freq = freq << 16;
+                       freq /= (intp->mclk / 1000);
+                       freq_s16 = (s16)freq;
                }
 
-               stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0);
-               stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0);
+               stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16));
+               stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16));
+               freq_s16 *= (-1);
+               stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16));
+               stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16));
+       }
 
-               if (i_params->chip_id >= 0x20) {
-                       stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41);
-                       stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41);
-                       if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) {
-                               stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82);
-                               stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0);
-                       }
-               }
+       stv0900_write_reg(intp, CFRINIT1, 0);
+       stv0900_write_reg(intp, CFRINIT0, 0);
 
-               stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xe0);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xc0);
-               stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0);
-               stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
-               stv0900_write_bits(i_params, F0900_P2_S1S2_SEQUENTIAL, 0);
-               stv0900_write_reg(i_params, R0900_P2_RTC, 0x88);
-               if (i_params->chip_id >= 0x20) {
-                       if (i_params->dmd2_symbol_rate < 2000000) {
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x39);
-                               stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x40);
-                       }
+       if (intp->chip_id >= 0x20) {
+               stv0900_write_reg(intp, EQUALCFG, 0x41);
+               stv0900_write_reg(intp, FFECFG, 0x41);
 
-                       if (i_params->dmd2_symbol_rate < 10000000) {
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4c);
-                               stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20);
-                       } else {
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4b);
-                               stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20);
-                       }
+               if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) ||
+                       (intp->srch_standard[demod] == STV0900_SEARCH_DSS) ||
+                       (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) {
+                       stv0900_write_reg(intp, VITSCALE,
+                                                               0x82);
+                       stv0900_write_reg(intp, VAVSRVIT, 0x0);
+               }
+       }
 
+       stv0900_write_reg(intp, SFRSTEP, 0x00);
+       stv0900_write_reg(intp, TMGTHRISE, 0xe0);
+       stv0900_write_reg(intp, TMGTHFALL, 0xc0);
+       stv0900_write_bits(intp, SCAN_ENABLE, 0);
+       stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+       stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0);
+       stv0900_write_reg(intp, RTC, 0x88);
+       if (intp->chip_id >= 0x20) {
+               if (intp->symbol_rate[demod] < 2000000) {
+                       if (intp->chip_id <= 0x20)
+                               stv0900_write_reg(intp, CARFREQ, 0x39);
+                       else  /*cut 3.0*/
+                               stv0900_write_reg(intp, CARFREQ, 0x89);
+
+                       stv0900_write_reg(intp, CARHDR, 0x40);
+               } else if (intp->symbol_rate[demod] < 10000000) {
+                       stv0900_write_reg(intp, CARFREQ, 0x4c);
+                       stv0900_write_reg(intp, CARHDR, 0x20);
                } else {
-                       if (i_params->dmd2_symbol_rate < 10000000)
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xef);
-                       else
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
+                       stv0900_write_reg(intp, CARFREQ, 0x4b);
+                       stv0900_write_reg(intp, CARHDR, 0x20);
                }
 
-               switch (i_params->dmd2_srch_algo) {
-               case STV0900_WARM_START:
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-                       break;
-               case STV0900_COLD_START:
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
-                       break;
-               default:
-                       break;
-               }
+       } else {
+               if (intp->symbol_rate[demod] < 10000000)
+                       stv0900_write_reg(intp, CARFREQ, 0xef);
+               else
+                       stv0900_write_reg(intp, CARFREQ, 0xed);
+       }
 
+       switch (intp->srch_algo[demod]) {
+       case STV0900_WARM_START:
+               stv0900_write_reg(intp, DMDISTATE, 0x1f);
+               stv0900_write_reg(intp, DMDISTATE, 0x18);
+               break;
+       case STV0900_COLD_START:
+               stv0900_write_reg(intp, DMDISTATE, 0x1f);
+               stv0900_write_reg(intp, DMDISTATE, 0x15);
+               break;
+       default:
                break;
        }
 }
@@ -1188,33 +1069,40 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode,
                                                        s32 pilot, u8 chip_id)
 {
        u8 aclc_value = 0x29;
-       s32     i;
-       const struct stv0900_car_loop_optim *car_loop_s2;
-
-       dprintk(KERN_INFO "%s\n", __func__);
-
-       if (chip_id <= 0x12)
-               car_loop_s2 = FE_STV0900_S2CarLoop;
-       else if (chip_id == 0x20)
-               car_loop_s2 = FE_STV0900_S2CarLoopCut20;
-       else
-               car_loop_s2 = FE_STV0900_S2CarLoop;
+       s32 i;
+       const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2;
+
+       dprintk("%s\n", __func__);
+
+       if (chip_id <= 0x12) {
+               cls2 = FE_STV0900_S2CarLoop;
+               cllqs2 = FE_STV0900_S2LowQPCarLoopCut30;
+               cllas2 = FE_STV0900_S2APSKCarLoopCut30;
+       } else if (chip_id == 0x20) {
+               cls2 = FE_STV0900_S2CarLoopCut20;
+               cllqs2 = FE_STV0900_S2LowQPCarLoopCut20;
+               cllas2 = FE_STV0900_S2APSKCarLoopCut20;
+       } else {
+               cls2 = FE_STV0900_S2CarLoopCut30;
+               cllqs2 = FE_STV0900_S2LowQPCarLoopCut30;
+               cllas2 = FE_STV0900_S2APSKCarLoopCut30;
+       }
 
        if (modcode < STV0900_QPSK_12) {
                i = 0;
-               while ((i < 3) && (modcode != FE_STV0900_S2LowQPCarLoopCut20[i].modcode))
+               while ((i < 3) && (modcode != cllqs2[i].modcode))
                        i++;
 
                if (i >= 3)
                        i = 2;
        } else {
                i = 0;
-               while ((i < 14) && (modcode != car_loop_s2[i].modcode))
+               while ((i < 14) && (modcode != cls2[i].modcode))
                        i++;
 
                if (i >= 14) {
                        i = 0;
-                       while ((i < 11) && (modcode != FE_STV0900_S2APSKCarLoopCut20[i].modcode))
+                       while ((i < 11) && (modcode != cllas2[i].modcode))
                                i++;
 
                        if (i >= 11)
@@ -1225,76 +1113,82 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode,
        if (modcode <= STV0900_QPSK_25) {
                if (pilot) {
                        if (srate <= 3000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_2;
+                               aclc_value = cllqs2[i].car_loop_pilots_on_2;
                        else if (srate <= 7000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_5;
+                               aclc_value = cllqs2[i].car_loop_pilots_on_5;
                        else if (srate <= 15000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_10;
+                               aclc_value = cllqs2[i].car_loop_pilots_on_10;
                        else if (srate <= 25000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_20;
+                               aclc_value = cllqs2[i].car_loop_pilots_on_20;
                        else
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_30;
+                               aclc_value = cllqs2[i].car_loop_pilots_on_30;
                } else {
                        if (srate <= 3000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_2;
+                               aclc_value = cllqs2[i].car_loop_pilots_off_2;
                        else if (srate <= 7000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_5;
+                               aclc_value = cllqs2[i].car_loop_pilots_off_5;
                        else if (srate <= 15000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_10;
+                               aclc_value = cllqs2[i].car_loop_pilots_off_10;
                        else if (srate <= 25000000)
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_20;
+                               aclc_value = cllqs2[i].car_loop_pilots_off_20;
                        else
-                               aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_30;
+                               aclc_value = cllqs2[i].car_loop_pilots_off_30;
                }
 
        } else if (modcode <= STV0900_8PSK_910) {
                if (pilot) {
                        if (srate <= 3000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_on_2;
+                               aclc_value = cls2[i].car_loop_pilots_on_2;
                        else if (srate <= 7000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_on_5;
+                               aclc_value = cls2[i].car_loop_pilots_on_5;
                        else if (srate <= 15000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_on_10;
+                               aclc_value = cls2[i].car_loop_pilots_on_10;
                        else if (srate <= 25000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_on_20;
+                               aclc_value = cls2[i].car_loop_pilots_on_20;
                        else
-                               aclc_value = car_loop_s2[i].car_loop_pilots_on_30;
+                               aclc_value = cls2[i].car_loop_pilots_on_30;
                } else {
                        if (srate <= 3000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_off_2;
+                               aclc_value = cls2[i].car_loop_pilots_off_2;
                        else if (srate <= 7000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_off_5;
+                               aclc_value = cls2[i].car_loop_pilots_off_5;
                        else if (srate <= 15000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_off_10;
+                               aclc_value = cls2[i].car_loop_pilots_off_10;
                        else if (srate <= 25000000)
-                               aclc_value = car_loop_s2[i].car_loop_pilots_off_20;
+                               aclc_value = cls2[i].car_loop_pilots_off_20;
                        else
-                               aclc_value = car_loop_s2[i].car_loop_pilots_off_30;
+                               aclc_value = cls2[i].car_loop_pilots_off_30;
                }
 
        } else {
                if (srate <= 3000000)
-                       aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_2;
+                       aclc_value = cllas2[i].car_loop_pilots_on_2;
                else if (srate <= 7000000)
-                       aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_5;
+                       aclc_value = cllas2[i].car_loop_pilots_on_5;
                else if (srate <= 15000000)
-                       aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_10;
+                       aclc_value = cllas2[i].car_loop_pilots_on_10;
                else if (srate <= 25000000)
-                       aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_20;
+                       aclc_value = cllas2[i].car_loop_pilots_on_20;
                else
-                       aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_30;
+                       aclc_value = cllas2[i].car_loop_pilots_on_30;
        }
 
        return aclc_value;
 }
 
-u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modulation, u8 chip_id)
+u8 stv0900_get_optim_short_carr_loop(s32 srate,
+                               enum fe_stv0900_modulation modulation,
+                               u8 chip_id)
 {
+       const struct stv0900_short_frames_car_loop_optim *s2scl;
+       const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30;
        s32 mod_index = 0;
-
        u8 aclc_value = 0x0b;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
+
+       s2scl = FE_STV0900_S2ShortCarLoop;
+       s2sclc30 = FE_STV0900_S2ShortCarLoopCut30;
 
        switch (modulation) {
        case STV0900_QPSK:
@@ -1312,75 +1206,116 @@ u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modul
                break;
        }
 
-       switch (chip_id) {
-       case 0x20:
+       if (chip_id >= 0x30) {
                if (srate <= 3000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_2;
+                       aclc_value = s2sclc30[mod_index].car_loop_2;
                else if (srate <= 7000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_5;
+                       aclc_value = s2sclc30[mod_index].car_loop_5;
                else if (srate <= 15000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_10;
+                       aclc_value = s2sclc30[mod_index].car_loop_10;
                else if (srate <= 25000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_20;
+                       aclc_value = s2sclc30[mod_index].car_loop_20;
                else
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_30;
+                       aclc_value = s2sclc30[mod_index].car_loop_30;
 
-               break;
-       case 0x12:
-       default:
+       } else if (chip_id >= 0x20) {
                if (srate <= 3000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_2;
+                       aclc_value = s2scl[mod_index].car_loop_cut20_2;
                else if (srate <= 7000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_5;
+                       aclc_value = s2scl[mod_index].car_loop_cut20_5;
                else if (srate <= 15000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_10;
+                       aclc_value = s2scl[mod_index].car_loop_cut20_10;
                else if (srate <= 25000000)
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_20;
+                       aclc_value = s2scl[mod_index].car_loop_cut20_20;
                else
-                       aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_30;
+                       aclc_value = s2scl[mod_index].car_loop_cut20_30;
+
+       } else {
+               if (srate <= 3000000)
+                       aclc_value = s2scl[mod_index].car_loop_cut12_2;
+               else if (srate <= 7000000)
+                       aclc_value = s2scl[mod_index].car_loop_cut12_5;
+               else if (srate <= 15000000)
+                       aclc_value = s2scl[mod_index].car_loop_cut12_10;
+               else if (srate <= 25000000)
+                       aclc_value = s2scl[mod_index].car_loop_cut12_20;
+               else
+                       aclc_value = s2scl[mod_index].car_loop_cut12_30;
 
-               break;
        }
 
        return aclc_value;
 }
 
-static enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *i_params,
+static
+enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_mode LDPC_Mode,
                                        enum fe_stv0900_demod_num demod)
 {
        enum fe_stv0900_error error = STV0900_NO_ERROR;
+       s32 reg_ind;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
        switch (LDPC_Mode) {
        case STV0900_DUAL:
        default:
-               if ((i_params->demod_mode != STV0900_DUAL)
-                       || (stv0900_get_bits(i_params, F0900_DDEMOD) != 1)) {
-                       stv0900_write_reg(i_params, R0900_GENCFG, 0x1d);
-
-                       i_params->demod_mode = STV0900_DUAL;
-
-                       stv0900_write_bits(i_params, F0900_FRESFEC, 1);
-                       stv0900_write_bits(i_params, F0900_FRESFEC, 0);
+               if ((intp->demod_mode != STV0900_DUAL)
+                       || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) {
+                       stv0900_write_reg(intp, R0900_GENCFG, 0x1d);
+
+                       intp->demod_mode = STV0900_DUAL;
+
+                       stv0900_write_bits(intp, F0900_FRESFEC, 1);
+                       stv0900_write_bits(intp, F0900_FRESFEC, 0);
+
+                       for (reg_ind = 0; reg_ind < 7; reg_ind++)
+                               stv0900_write_reg(intp,
+                                               R0900_P1_MODCODLST0 + reg_ind,
+                                               0xff);
+                       for (reg_ind = 0; reg_ind < 8; reg_ind++)
+                               stv0900_write_reg(intp,
+                                               R0900_P1_MODCODLST7 + reg_ind,
+                                               0xcc);
+
+                       stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff);
+                       stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf);
+
+                       for (reg_ind = 0; reg_ind < 7; reg_ind++)
+                               stv0900_write_reg(intp,
+                                               R0900_P2_MODCODLST0 + reg_ind,
+                                               0xff);
+                       for (reg_ind = 0; reg_ind < 8; reg_ind++)
+                               stv0900_write_reg(intp,
+                                               R0900_P2_MODCODLST7 + reg_ind,
+                                               0xcc);
+
+                       stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff);
+                       stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf);
                }
 
                break;
        case STV0900_SINGLE:
-               if (demod == STV0900_DEMOD_2)
-                       stv0900_write_reg(i_params, R0900_GENCFG, 0x06);
-               else
-                       stv0900_write_reg(i_params, R0900_GENCFG, 0x04);
+               if (demod == STV0900_DEMOD_2) {
+                       stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1);
+                       stv0900_activate_s2_modcod_single(intp,
+                                                       STV0900_DEMOD_2);
+                       stv0900_write_reg(intp, R0900_GENCFG, 0x06);
+               } else {
+                       stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2);
+                       stv0900_activate_s2_modcod_single(intp,
+                                                       STV0900_DEMOD_1);
+                       stv0900_write_reg(intp, R0900_GENCFG, 0x04);
+               }
 
-               i_params->demod_mode = STV0900_SINGLE;
+               intp->demod_mode = STV0900_SINGLE;
 
-               stv0900_write_bits(i_params, F0900_FRESFEC, 1);
-               stv0900_write_bits(i_params, F0900_FRESFEC, 0);
-               stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1);
-               stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0);
-               stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1);
-               stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0);
+               stv0900_write_bits(intp, F0900_FRESFEC, 1);
+               stv0900_write_bits(intp, F0900_FRESFEC, 0);
+               stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1);
+               stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0);
+               stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1);
+               stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0);
                break;
        }
 
@@ -1393,131 +1328,131 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
        struct stv0900_state *state = fe->demodulator_priv;
        enum fe_stv0900_error error = STV0900_NO_ERROR;
        enum fe_stv0900_error demodError = STV0900_NO_ERROR;
+       struct stv0900_internal *intp = NULL;
+
        int selosci, i;
 
        struct stv0900_inode *temp_int = find_inode(state->i2c_adap,
                                                state->config->demod_address);
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       if (temp_int != NULL) {
+       if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) {
                state->internal = temp_int->internal;
                (state->internal->dmds_used)++;
-               dprintk(KERN_INFO "%s: Find Internal Structure!\n", __func__);
+               dprintk("%s: Find Internal Structure!\n", __func__);
                return STV0900_NO_ERROR;
        } else {
-               state->internal = kmalloc(sizeof(struct stv0900_internal), GFP_KERNEL);
+               state->internal = kmalloc(sizeof(struct stv0900_internal),
+                                                               GFP_KERNEL);
                temp_int = append_internal(state->internal);
                state->internal->dmds_used = 1;
                state->internal->i2c_adap = state->i2c_adap;
                state->internal->i2c_addr = state->config->demod_address;
                state->internal->clkmode = state->config->clkmode;
                state->internal->errs = STV0900_NO_ERROR;
-               dprintk(KERN_INFO "%s: Create New Internal Structure!\n", __func__);
+               dprintk("%s: Create New Internal Structure!\n", __func__);
        }
 
-       if (state->internal != NULL) {
-               demodError = stv0900_initialize(state->internal);
-               if (demodError == STV0900_NO_ERROR) {
-                               error = STV0900_NO_ERROR;
-               } else {
-                       if (demodError == STV0900_INVALID_HANDLE)
-                               error = STV0900_INVALID_HANDLE;
-                       else
-                               error = STV0900_I2C_ERROR;
-               }
+       if (state->internal == NULL) {
+               error = STV0900_INVALID_HANDLE;
+               return error;
+       }
 
-               if (state->internal != NULL) {
-                       if (error == STV0900_NO_ERROR) {
-                               state->internal->demod_mode = p_init->demod_mode;
-
-                               stv0900_st_dvbs2_single(state->internal, state->internal->demod_mode, STV0900_DEMOD_1);
-
-                               state->internal->chip_id = stv0900_read_reg(state->internal, R0900_MID);
-                               state->internal->rolloff = p_init->rolloff;
-                               state->internal->quartz = p_init->dmd_ref_clk;
-
-                               stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff);
-                               stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff);
-
-                               state->internal->ts_config = p_init->ts_config;
-                               if (state->internal->ts_config == NULL)
-                                       stv0900_set_ts_parallel_serial(state->internal,
-                                                       p_init->path1_ts_clock,
-                                                       p_init->path2_ts_clock);
-                               else {
-                                       for (i = 0; state->internal->ts_config[i].addr != 0xffff; i++)
-                                               stv0900_write_reg(state->internal,
-                                                               state->internal->ts_config[i].addr,
-                                                               state->internal->ts_config[i].val);
-
-                                       stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 1);
-                                       stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 0);
-                                       stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 1);
-                                       stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 0);
-                               }
+       demodError = stv0900_initialize(state->internal);
+       if (demodError == STV0900_NO_ERROR) {
+                       error = STV0900_NO_ERROR;
+       } else {
+               if (demodError == STV0900_INVALID_HANDLE)
+                       error = STV0900_INVALID_HANDLE;
+               else
+                       error = STV0900_I2C_ERROR;
 
-                               stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress);
-                               switch (p_init->tuner1_adc) {
-                               case 1:
-                                       stv0900_write_reg(state->internal, R0900_TSTTNR1, 0x26);
-                                       break;
-                               default:
-                                       break;
-                               }
+               return error;
+       }
 
-                               stv0900_write_bits(state->internal, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress);
-                               switch (p_init->tuner2_adc) {
-                               case 1:
-                                       stv0900_write_reg(state->internal, R0900_TSTTNR3, 0x26);
-                                       break;
-                               default:
-                                       break;
-                               }
+       if (state->internal == NULL) {
+               error = STV0900_INVALID_HANDLE;
+               return error;
+       }
 
-                               stv0900_write_bits(state->internal, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inversion);
-                               stv0900_write_bits(state->internal, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inversion);
-                               stv0900_set_mclk(state->internal, 135000000);
-                               msleep(3);
-
-                               switch (state->internal->clkmode) {
-                               case 0:
-                               case 2:
-                                       stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | state->internal->clkmode);
-                                       break;
-                               default:
-                                       selosci = 0x02 & stv0900_read_reg(state->internal, R0900_SYNTCTRL);
-                                       stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | selosci);
-                                       break;
-                               }
-                               msleep(3);
+       intp = state->internal;
 
-                               state->internal->mclk = stv0900_get_mclk_freq(state->internal, state->internal->quartz);
-                               if (state->internal->errs)
-                                       error = STV0900_I2C_ERROR;
-                       }
-               } else {
-                       error = STV0900_INVALID_HANDLE;
-               }
+       intp->demod_mode = p_init->demod_mode;
+       stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1);
+       intp->chip_id = stv0900_read_reg(intp, R0900_MID);
+       intp->rolloff = p_init->rolloff;
+       intp->quartz = p_init->dmd_ref_clk;
+
+       stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff);
+       stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff);
+
+       intp->ts_config = p_init->ts_config;
+       if (intp->ts_config == NULL)
+               stv0900_set_ts_parallel_serial(intp,
+                               p_init->path1_ts_clock,
+                               p_init->path2_ts_clock);
+       else {
+               for (i = 0; intp->ts_config[i].addr != 0xffff; i++)
+                       stv0900_write_reg(intp,
+                                       intp->ts_config[i].addr,
+                                       intp->ts_config[i].val);
+
+               stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1);
+               stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0);
+               stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1);
+               stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0);
+       }
+
+       stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress);
+       switch (p_init->tuner1_adc) {
+       case 1:
+               stv0900_write_reg(intp, R0900_TSTTNR1, 0x26);
+               break;
+       default:
+               break;
+       }
+
+       stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress);
+       switch (p_init->tuner2_adc) {
+       case 1:
+               stv0900_write_reg(intp, R0900_TSTTNR3, 0x26);
+               break;
+       default:
+               break;
+       }
+
+       stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv);
+       stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv);
+       stv0900_set_mclk(intp, 135000000);
+       msleep(3);
+
+       switch (intp->clkmode) {
+       case 0:
+       case 2:
+               stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode);
+               break;
+       default:
+               selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL);
+               stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci);
+               break;
        }
+       msleep(3);
+
+       intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz);
+       if (intp->errs)
+               error = STV0900_I2C_ERROR;
 
        return error;
 }
 
-static int stv0900_status(struct stv0900_internal *i_params,
+static int stv0900_status(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
        enum fe_stv0900_search_state demod_state;
-       s32 mode_field, delin_field, lock_field, fifo_field, lockedvit_field;
        int locked = FALSE;
 
-       dmd_reg(mode_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
-       dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF);
-       dmd_reg(delin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK);
-       dmd_reg(fifo_field, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK);
-       dmd_reg(lockedvit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT);
-
-       demod_state = stv0900_get_bits(i_params, mode_field);
+       demod_state = stv0900_get_bits(intp, HEADER_MODE);
        switch (demod_state) {
        case STV0900_SEARCH:
        case STV0900_PLH_DETECTED:
@@ -1525,17 +1460,19 @@ static int stv0900_status(struct stv0900_internal *i_params,
                locked = FALSE;
                break;
        case STV0900_DVBS2_FOUND:
-               locked = stv0900_get_bits(i_params, lock_field) &&
-                               stv0900_get_bits(i_params, delin_field) &&
-                               stv0900_get_bits(i_params, fifo_field);
+               locked = stv0900_get_bits(intp, LOCK_DEFINITIF) &&
+                               stv0900_get_bits(intp, PKTDELIN_LOCK) &&
+                               stv0900_get_bits(intp, TSFIFO_LINEOK);
                break;
        case STV0900_DVBS_FOUND:
-               locked = stv0900_get_bits(i_params, lock_field) &&
-                               stv0900_get_bits(i_params, lockedvit_field) &&
-                               stv0900_get_bits(i_params, fifo_field);
+               locked = stv0900_get_bits(intp, LOCK_DEFINITIF) &&
+                               stv0900_get_bits(intp, LOCKEDVIT) &&
+                               stv0900_get_bits(intp, TSFIFO_LINEOK);
                break;
        }
 
+       dprintk("%s: locked = %d\n", __func__, locked);
+
        return locked;
 }
 
@@ -1543,7 +1480,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
                                        struct dvb_frontend_parameters *params)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
+       enum fe_stv0900_demod_num demod = state->demod;
        struct dtv_frontend_properties *c = &fe->dtv_property_cache;
 
        struct stv0900_search_params p_search;
@@ -1551,10 +1489,16 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
 
        enum fe_stv0900_error error = STV0900_NO_ERROR;
 
-       dprintk(KERN_INFO "%s: ", __func__);
+       dprintk("%s: ", __func__);
+
+       if (!(INRANGE(100000, c->symbol_rate, 70000000)))
+               return DVBFE_ALGO_SEARCH_FAILED;
+
+       if (state->config->set_ts_params)
+               state->config->set_ts_params(fe, 0);
 
        p_result.locked = FALSE;
-       p_search.path = state->demod;
+       p_search.path = demod;
        p_search.frequency = c->frequency;
        p_search.symbol_rate = c->symbol_rate;
        p_search.search_range = 10000000;
@@ -1563,103 +1507,47 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
        p_search.iq_inversion = STV0900_IQ_AUTO;
        p_search.search_algo = STV0900_BLIND_SEARCH;
 
-       if ((INRANGE(100000, p_search.symbol_rate, 70000000)) &&
-                       (INRANGE(100000, p_search.search_range, 50000000))) {
-               switch (p_search.path) {
-               case STV0900_DEMOD_1:
-               default:
-                       i_params->dmd1_srch_standard = p_search.standard;
-                       i_params->dmd1_symbol_rate = p_search.symbol_rate;
-                       i_params->dmd1_srch_range = p_search.search_range;
-                       i_params->tuner1_freq = p_search.frequency;
-                       i_params->dmd1_srch_algo = p_search.search_algo;
-                       i_params->dmd1_srch_iq_inv = p_search.iq_inversion;
-                       i_params->dmd1_fec = p_search.fec;
+       intp->srch_standard[demod] = p_search.standard;
+       intp->symbol_rate[demod] = p_search.symbol_rate;
+       intp->srch_range[demod] = p_search.search_range;
+       intp->freq[demod] = p_search.frequency;
+       intp->srch_algo[demod] = p_search.search_algo;
+       intp->srch_iq_inv[demod] = p_search.iq_inversion;
+       intp->fec[demod] = p_search.fec;
+       if ((stv0900_algo(fe) == STV0900_RANGEOK) &&
+                               (intp->errs == STV0900_NO_ERROR)) {
+               p_result.locked = intp->result[demod].locked;
+               p_result.standard = intp->result[demod].standard;
+               p_result.frequency = intp->result[demod].frequency;
+               p_result.symbol_rate = intp->result[demod].symbol_rate;
+               p_result.fec = intp->result[demod].fec;
+               p_result.modcode = intp->result[demod].modcode;
+               p_result.pilot = intp->result[demod].pilot;
+               p_result.frame_len = intp->result[demod].frame_len;
+               p_result.spectrum = intp->result[demod].spectrum;
+               p_result.rolloff = intp->result[demod].rolloff;
+               p_result.modulation = intp->result[demod].modulation;
+       } else {
+               p_result.locked = FALSE;
+               switch (intp->err[demod]) {
+               case STV0900_I2C_ERROR:
+                       error = STV0900_I2C_ERROR;
                        break;
-
-               case STV0900_DEMOD_2:
-                       i_params->dmd2_srch_stndrd = p_search.standard;
-                       i_params->dmd2_symbol_rate = p_search.symbol_rate;
-                       i_params->dmd2_srch_range = p_search.search_range;
-                       i_params->tuner2_freq = p_search.frequency;
-                       i_params->dmd2_srch_algo = p_search.search_algo;
-                       i_params->dmd2_srch_iq_inv = p_search.iq_inversion;
-                       i_params->dmd2_fec = p_search.fec;
+               case STV0900_NO_ERROR:
+               default:
+                       error = STV0900_SEARCH_FAILED;
                        break;
                }
-
-               if ((stv0900_algo(fe) == STV0900_RANGEOK) &&
-                                       (i_params->errs == STV0900_NO_ERROR)) {
-                       switch (p_search.path) {
-                       case STV0900_DEMOD_1:
-                       default:
-                               p_result.locked = i_params->dmd1_rslts.locked;
-                               p_result.standard = i_params->dmd1_rslts.standard;
-                               p_result.frequency = i_params->dmd1_rslts.frequency;
-                               p_result.symbol_rate = i_params->dmd1_rslts.symbol_rate;
-                               p_result.fec = i_params->dmd1_rslts.fec;
-                               p_result.modcode = i_params->dmd1_rslts.modcode;
-                               p_result.pilot = i_params->dmd1_rslts.pilot;
-                               p_result.frame_length = i_params->dmd1_rslts.frame_length;
-                               p_result.spectrum = i_params->dmd1_rslts.spectrum;
-                               p_result.rolloff = i_params->dmd1_rslts.rolloff;
-                               p_result.modulation = i_params->dmd1_rslts.modulation;
-                               break;
-                       case STV0900_DEMOD_2:
-                               p_result.locked = i_params->dmd2_rslts.locked;
-                               p_result.standard = i_params->dmd2_rslts.standard;
-                               p_result.frequency = i_params->dmd2_rslts.frequency;
-                               p_result.symbol_rate = i_params->dmd2_rslts.symbol_rate;
-                               p_result.fec = i_params->dmd2_rslts.fec;
-                               p_result.modcode = i_params->dmd2_rslts.modcode;
-                               p_result.pilot = i_params->dmd2_rslts.pilot;
-                               p_result.frame_length = i_params->dmd2_rslts.frame_length;
-                               p_result.spectrum = i_params->dmd2_rslts.spectrum;
-                               p_result.rolloff = i_params->dmd2_rslts.rolloff;
-                               p_result.modulation = i_params->dmd2_rslts.modulation;
-                               break;
-                       }
-
-               } else {
-                       p_result.locked = FALSE;
-                       switch (p_search.path) {
-                       case STV0900_DEMOD_1:
-                               switch (i_params->dmd1_err) {
-                               case STV0900_I2C_ERROR:
-                                       error = STV0900_I2C_ERROR;
-                                       break;
-                               case STV0900_NO_ERROR:
-                               default:
-                                       error = STV0900_SEARCH_FAILED;
-                                       break;
-                               }
-                               break;
-                       case STV0900_DEMOD_2:
-                               switch (i_params->dmd2_err) {
-                               case STV0900_I2C_ERROR:
-                                       error = STV0900_I2C_ERROR;
-                                       break;
-                               case STV0900_NO_ERROR:
-                               default:
-                                       error = STV0900_SEARCH_FAILED;
-                                       break;
-                               }
-                               break;
-                       }
-               }
-
-       } else
-               error = STV0900_BAD_PARAMETER;
+       }
 
        if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) {
-               dprintk(KERN_INFO "Search Success\n");
+               dprintk("Search Success\n");
                return DVBFE_ALGO_SEARCH_SUCCESS;
        } else {
-               dprintk(KERN_INFO "Search Fail\n");
+               dprintk("Search Fail\n");
                return DVBFE_ALGO_SEARCH_FAILED;
        }
 
-       return DVBFE_ALGO_SEARCH_ERROR;
 }
 
 static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status)
@@ -1690,16 +1578,13 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts)
 {
 
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       s32 rst_field;
-
-       dmd_reg(rst_field, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE);
 
        if (stop_ts == TRUE)
-               stv0900_write_bits(i_params, rst_field, 1);
+               stv0900_write_bits(intp, RST_HWARE, 1);
        else
-               stv0900_write_bits(i_params, rst_field, 0);
+               stv0900_write_bits(intp, RST_HWARE, 0);
 
        return 0;
 }
@@ -1707,23 +1592,19 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts)
 static int stv0900_diseqc_init(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       s32 mode_field, reset_field;
 
-       dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
-       dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET);
-
-       stv0900_write_bits(i_params, mode_field, state->config->diseqc_mode);
-       stv0900_write_bits(i_params, reset_field, 1);
-       stv0900_write_bits(i_params, reset_field, 0);
+       stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode);
+       stv0900_write_bits(intp, DISEQC_RESET, 1);
+       stv0900_write_bits(intp, DISEQC_RESET, 0);
 
        return 0;
 }
 
 static int stv0900_init(struct dvb_frontend *fe)
 {
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
        stv0900_stop_ts(fe, 1);
        stv0900_diseqc_init(fe);
@@ -1731,48 +1612,24 @@ static int stv0900_init(struct dvb_frontend *fe)
        return 0;
 }
 
-static int stv0900_diseqc_send(struct stv0900_internal *i_params , u8 *Data,
+static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data,
                                u32 NbData, enum fe_stv0900_demod_num demod)
 {
        s32 i = 0;
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 1);
-               while (i < NbData) {
-                       while (stv0900_get_bits(i_params, F0900_P1_FIFO_FULL))
-                               ;/* checkpatch complains */
-                       stv0900_write_reg(i_params, R0900_P1_DISTXDATA, Data[i]);
-                       i++;
-               }
-
-               stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 0);
-               i = 0;
-               while ((stv0900_get_bits(i_params, F0900_P1_TX_IDLE) != 1) && (i < 10)) {
-                       msleep(10);
-                       i++;
-               }
-
-               break;
-       case STV0900_DEMOD_2:
-               stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 1);
-
-               while (i < NbData) {
-                       while (stv0900_get_bits(i_params, F0900_P2_FIFO_FULL))
-                               ;/* checkpatch complains */
-                       stv0900_write_reg(i_params, R0900_P2_DISTXDATA, Data[i]);
-                       i++;
-               }
-
-               stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 0);
-               i = 0;
-               while ((stv0900_get_bits(i_params, F0900_P2_TX_IDLE) != 1) && (i < 10)) {
-                       msleep(10);
-                       i++;
-               }
+       stv0900_write_bits(intp, DIS_PRECHARGE, 1);
+       while (i < NbData) {
+               while (stv0900_get_bits(intp, FIFO_FULL))
+                       ;/* checkpatch complains */
+               stv0900_write_reg(intp, DISTXDATA, data[i]);
+               i++;
+       }
 
-               break;
+       stv0900_write_bits(intp, DIS_PRECHARGE, 0);
+       i = 0;
+       while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) {
+               msleep(10);
+               i++;
        }
 
        return 0;
@@ -1792,22 +1649,21 @@ static int stv0900_send_master_cmd(struct dvb_frontend *fe,
 static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       s32 mode_field;
-       u32 diseqc_fifo;
+       u8 data;
 
-       dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
-       dmd_reg(diseqc_fifo, R0900_P1_DISTXDATA, R0900_P2_DISTXDATA);
 
        switch (burst) {
        case SEC_MINI_A:
-               stv0900_write_bits(i_params, mode_field, 3);/* Unmodulated */
-               stv0900_write_reg(i_params, diseqc_fifo, 0x00);
+               stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */
+               data = 0x00;
+               stv0900_diseqc_send(intp, &data, 1, state->demod);
                break;
        case SEC_MINI_B:
-               stv0900_write_bits(i_params, mode_field, 2);/* Modulated */
-               stv0900_write_reg(i_params, diseqc_fifo, 0xff);
+               stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */
+               data = 0xff;
+               stv0900_diseqc_send(intp, &data, 1, state->demod);
                break;
        }
 
@@ -1818,68 +1674,54 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe,
                                struct dvb_diseqc_slave_reply *reply)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
+       enum fe_stv0900_demod_num demod = state->demod;
        s32 i = 0;
 
-       switch (state->demod) {
-       case STV0900_DEMOD_1:
-       default:
-               reply->msg_len = 0;
-
-               while ((stv0900_get_bits(i_params, F0900_P1_RX_END) != 1) && (i < 10)) {
-                       msleep(10);
-                       i++;
-               }
-
-               if (stv0900_get_bits(i_params, F0900_P1_RX_END)) {
-                       reply->msg_len = stv0900_get_bits(i_params, F0900_P1_FIFO_BYTENBR);
-
-                       for (i = 0; i < reply->msg_len; i++)
-                               reply->msg[i] = stv0900_read_reg(i_params, R0900_P1_DISRXDATA);
-               }
-               break;
-       case STV0900_DEMOD_2:
-               reply->msg_len = 0;
+       reply->msg_len = 0;
 
-               while ((stv0900_get_bits(i_params, F0900_P2_RX_END) != 1) && (i < 10)) {
-                       msleep(10);
-                       i++;
-               }
+       while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) {
+               msleep(10);
+               i++;
+       }
 
-               if (stv0900_get_bits(i_params, F0900_P2_RX_END)) {
-                       reply->msg_len = stv0900_get_bits(i_params, F0900_P2_FIFO_BYTENBR);
+       if (stv0900_get_bits(intp, RX_END)) {
+               reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR);
 
-                       for (i = 0; i < reply->msg_len; i++)
-                               reply->msg[i] = stv0900_read_reg(i_params, R0900_P2_DISRXDATA);
-               }
-               break;
+               for (i = 0; i < reply->msg_len; i++)
+                       reply->msg[i] = stv0900_read_reg(intp, DISRXDATA);
        }
 
        return 0;
 }
 
-static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       s32 mode_field, reset_field;
-
-       dprintk(KERN_INFO "%s: %s\n", __func__, ((tone == 0) ? "Off" : "On"));
 
-       dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
-       dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET);
+       dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off"));
 
-       if (tone) {
-               /*Set the DiseqC mode to 22Khz continues tone*/
-               stv0900_write_bits(i_params, mode_field, 0);
-               stv0900_write_bits(i_params, reset_field, 1);
+       switch (toneoff) {
+       case SEC_TONE_ON:
+               /*Set the DiseqC mode to 22Khz _continues_ tone*/
+               stv0900_write_bits(intp, DISTX_MODE, 0);
+               stv0900_write_bits(intp, DISEQC_RESET, 1);
                /*release DiseqC reset to enable the 22KHz tone*/
-               stv0900_write_bits(i_params, reset_field, 0);
-       } else {
-               stv0900_write_bits(i_params, mode_field, 0);
+               stv0900_write_bits(intp, DISEQC_RESET, 0);
+               break;
+       case SEC_TONE_OFF:
+               /*return diseqc mode to config->diseqc_mode.
+               Usually it's without _continues_ tone */
+               stv0900_write_bits(intp, DISTX_MODE,
+                               state->config->diseqc_mode);
                /*maintain the DiseqC reset to disable the 22KHz tone*/
-               stv0900_write_bits(i_params, reset_field, 1);
+               stv0900_write_bits(intp, DISEQC_RESET, 1);
+               stv0900_write_bits(intp, DISEQC_RESET, 0);
+               break;
+       default:
+               return -EINVAL;
        }
 
        return 0;
@@ -1889,11 +1731,11 @@ static void stv0900_release(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
        if ((--(state->internal->dmds_used)) <= 0) {
 
-               dprintk(KERN_INFO "%s: Actually removing\n", __func__);
+               dprintk("%s: Actually removing\n", __func__);
 
                remove_inode(state->internal);
                kfree(state->internal);
@@ -1963,17 +1805,17 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
        case 0:
        case 1:
                init_params.dmd_ref_clk         = config->xtal;
-               init_params.demod_mode          = STV0900_DUAL;
+               init_params.demod_mode          = config->demod_mode;
                init_params.rolloff             = STV0900_35;
                init_params.path1_ts_clock      = config->path1_mode;
                init_params.tun1_maddress       = config->tun1_maddress;
-               init_params.tun1_iq_inversion   = STV0900_IQ_NORMAL;
+               init_params.tun1_iq_inv         = STV0900_IQ_NORMAL;
                init_params.tuner1_adc          = config->tun1_adc;
                init_params.path2_ts_clock      = config->path2_mode;
                init_params.ts_config           = config->ts_config_regs;
                init_params.tun2_maddress       = config->tun2_maddress;
                init_params.tuner2_adc          = config->tun2_adc;
-               init_params.tun2_iq_inversion   = STV0900_IQ_SWAPPED;
+               init_params.tun2_iq_inv         = STV0900_IQ_SWAPPED;
 
                err_stv0900 = stv0900_init_internal(&state->frontend,
                                                        &init_params);
index ff388b4..b684df9 100644 (file)
@@ -141,85 +141,228 @@ struct stv0900_short_frames_car_loop_optim {
 
 };
 
+struct stv0900_short_frames_car_loop_optim_vs_mod {
+       enum fe_stv0900_modulation modulation;
+       u8 car_loop_2;    /* SR<3msps      */
+       u8 car_loop_5;    /* 3<SR<=7msps   */
+       u8 car_loop_10;   /* 7<SR<=15msps  */
+       u8 car_loop_20;   /* 10<SR<=25msps */
+       u8 car_loop_30;   /* 10<SR<=45msps */
+};
+
 /* Cut 1.x Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */
-static const struct stv0900_car_loop_optim     FE_STV0900_S2CarLoop[14] = {
-       /*Modcod                2MPon   2MPoff  5MPon   5MPoff  10MPon  10MPoff 20MPon  20MPoff 30MPon  30MPoff */
-       { STV0900_QPSK_12,      0x1C,   0x0D,   0x1B,   0x2C,   0x3A,   0x1C,   0x2A,   0x3B,   0x2A,   0x1B },
-       { STV0900_QPSK_35,      0x2C,   0x0D,   0x2B,   0x2C,   0x3A,   0x0C,   0x3A,   0x2B,   0x2A,   0x0B },
-       { STV0900_QPSK_23,      0x2C,   0x0D,   0x2B,   0x2C,   0x0B,   0x0C,   0x3A,   0x1B,   0x2A,   0x3A },
-       { STV0900_QPSK_34,      0x3C,   0x0D,   0x3B,   0x1C,   0x0B,   0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
-       { STV0900_QPSK_45,      0x3C,   0x0D,   0x3B,   0x1C,   0x0B,   0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
-       { STV0900_QPSK_56,      0x0D,   0x0D,   0x3B,   0x1C,   0x0B,   0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
-       { STV0900_QPSK_89,      0x0D,   0x0D,   0x3B,   0x1C,   0x1B,   0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
-       { STV0900_QPSK_910,     0x1D,   0x0D,   0x3B,   0x1C,   0x1B,   0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
-       { STV0900_8PSK_35,      0x29,   0x3B,   0x09,   0x2B,   0x38,   0x0B,   0x18,   0x1A,   0x08,   0x0A },
-       { STV0900_8PSK_23,      0x0A,   0x3B,   0x29,   0x2B,   0x19,   0x0B,   0x38,   0x1A,   0x18,   0x0A },
-       { STV0900_8PSK_34,      0x3A,   0x3B,   0x2A,   0x2B,   0x39,   0x0B,   0x19,   0x1A,   0x38,   0x0A },
-       { STV0900_8PSK_56,      0x1B,   0x3B,   0x0B,   0x2B,   0x1A,   0x0B,   0x39,   0x1A,   0x19,   0x0A },
-       { STV0900_8PSK_89,      0x3B,   0x3B,   0x0B,   0x2B,   0x2A,   0x0B,   0x39,   0x1A,   0x29,   0x39 },
-       { STV0900_8PSK_910,     0x3B,   0x3B,   0x0B,   0x2B,   0x2A,   0x0B,   0x39,   0x1A,   0x29,   0x39 }
+static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = {
+       /*Modcod                2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff */
+       { STV0900_QPSK_12,      0x1C,   0x0D,   0x1B,   0x2C,   0x3A,
+                               0x1C,   0x2A,   0x3B,   0x2A,   0x1B },
+       { STV0900_QPSK_35,      0x2C,   0x0D,   0x2B,   0x2C,   0x3A,
+                               0x0C,   0x3A,   0x2B,   0x2A,   0x0B },
+       { STV0900_QPSK_23,      0x2C,   0x0D,   0x2B,   0x2C,   0x0B,
+                               0x0C,   0x3A,   0x1B,   0x2A,   0x3A },
+       { STV0900_QPSK_34,      0x3C,   0x0D,   0x3B,   0x1C,   0x0B,
+                               0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
+       { STV0900_QPSK_45,      0x3C,   0x0D,   0x3B,   0x1C,   0x0B,
+                               0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
+       { STV0900_QPSK_56,      0x0D,   0x0D,   0x3B,   0x1C,   0x0B,
+                               0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
+       { STV0900_QPSK_89,      0x0D,   0x0D,   0x3B,   0x1C,   0x1B,
+                               0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
+       { STV0900_QPSK_910,     0x1D,   0x0D,   0x3B,   0x1C,   0x1B,
+                               0x3B,   0x3A,   0x0B,   0x2A,   0x3A },
+       { STV0900_8PSK_35,      0x29,   0x3B,   0x09,   0x2B,   0x38,
+                               0x0B,   0x18,   0x1A,   0x08,   0x0A },
+       { STV0900_8PSK_23,      0x0A,   0x3B,   0x29,   0x2B,   0x19,
+                               0x0B,   0x38,   0x1A,   0x18,   0x0A },
+       { STV0900_8PSK_34,      0x3A,   0x3B,   0x2A,   0x2B,   0x39,
+                               0x0B,   0x19,   0x1A,   0x38,   0x0A },
+       { STV0900_8PSK_56,      0x1B,   0x3B,   0x0B,   0x2B,   0x1A,
+                               0x0B,   0x39,   0x1A,   0x19,   0x0A },
+       { STV0900_8PSK_89,      0x3B,   0x3B,   0x0B,   0x2B,   0x2A,
+                               0x0B,   0x39,   0x1A,   0x29,   0x39 },
+       { STV0900_8PSK_910,     0x3B,   0x3B,   0x0B,   0x2B,   0x2A,
+                               0x0B,   0x39,   0x1A,   0x29,   0x39 }
 };
 
 
 /* Cut 2.0 Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */
-static const struct stv0900_car_loop_optim     FE_STV0900_S2CarLoopCut20[14]   = {
-       /* Modcod               2MPon   2MPoff  5MPon   5MPoff  10MPon  10MPoff 20MPon  20MPoff 30MPon  30MPoff */
-       { STV0900_QPSK_12,      0x1F,   0x3F,   0x1E,   0x3F,   0x3D,   0x1F,   0x3D,   0x3E,   0x3D,   0x1E },
-       { STV0900_QPSK_35,      0x2F,   0x3F,   0x2E,   0x2F,   0x3D,   0x0F,   0x0E,   0x2E,   0x3D,   0x0E },
-       { STV0900_QPSK_23,      0x2F,   0x3F,   0x2E,   0x2F,   0x0E,   0x0F,   0x0E,   0x1E,   0x3D,   0x3D },
-       { STV0900_QPSK_34,      0x3F,   0x3F,   0x3E,   0x1F,   0x0E,   0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
-       { STV0900_QPSK_45,      0x3F,   0x3F,   0x3E,   0x1F,   0x0E,   0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
-       { STV0900_QPSK_56,      0x3F,   0x3F,   0x3E,   0x1F,   0x0E,   0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
-       { STV0900_QPSK_89,      0x3F,   0x3F,   0x3E,   0x1F,   0x1E,   0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
-       { STV0900_QPSK_910,     0x3F,   0x3F,   0x3E,   0x1F,   0x1E,   0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
-       { STV0900_8PSK_35,      0x3c,   0x0c,   0x1c,   0x3b,   0x0c,   0x3b,   0x2b,   0x2b,   0x1b,   0x2b },
-       { STV0900_8PSK_23,      0x1d,   0x0c,   0x3c,   0x0c,   0x2c,   0x3b,   0x0c,   0x2b,   0x2b,   0x2b },
-       { STV0900_8PSK_34,      0x0e,   0x1c,   0x3d,   0x0c,   0x0d,   0x3b,   0x2c,   0x3b,   0x0c,   0x2b },
-       { STV0900_8PSK_56,      0x2e,   0x3e,   0x1e,   0x2e,   0x2d,   0x1e,   0x3c,   0x2d,   0x2c,   0x1d },
-       { STV0900_8PSK_89,      0x3e,   0x3e,   0x1e,   0x2e,   0x3d,   0x1e,   0x0d,   0x2d,   0x3c,   0x1d },
-       { STV0900_8PSK_910,     0x3e,   0x3e,   0x1e,   0x2e,   0x3d,   0x1e,   0x1d,   0x2d,   0x0d,   0x1d }
+static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = {
+       /* Modcod               2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff */
+       { STV0900_QPSK_12,      0x1F,   0x3F,   0x1E,   0x3F,   0x3D,
+                               0x1F,   0x3D,   0x3E,   0x3D,   0x1E },
+       { STV0900_QPSK_35,      0x2F,   0x3F,   0x2E,   0x2F,   0x3D,
+                               0x0F,   0x0E,   0x2E,   0x3D,   0x0E },
+       { STV0900_QPSK_23,      0x2F,   0x3F,   0x2E,   0x2F,   0x0E,
+                               0x0F,   0x0E,   0x1E,   0x3D,   0x3D },
+       { STV0900_QPSK_34,      0x3F,   0x3F,   0x3E,   0x1F,   0x0E,
+                               0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
+       { STV0900_QPSK_45,      0x3F,   0x3F,   0x3E,   0x1F,   0x0E,
+                               0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
+       { STV0900_QPSK_56,      0x3F,   0x3F,   0x3E,   0x1F,   0x0E,
+                               0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
+       { STV0900_QPSK_89,      0x3F,   0x3F,   0x3E,   0x1F,   0x1E,
+                               0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
+       { STV0900_QPSK_910,     0x3F,   0x3F,   0x3E,   0x1F,   0x1E,
+                               0x3E,   0x0E,   0x1E,   0x3D,   0x3D },
+       { STV0900_8PSK_35,      0x3c,   0x0c,   0x1c,   0x3b,   0x0c,
+                               0x3b,   0x2b,   0x2b,   0x1b,   0x2b },
+       { STV0900_8PSK_23,      0x1d,   0x0c,   0x3c,   0x0c,   0x2c,
+                               0x3b,   0x0c,   0x2b,   0x2b,   0x2b },
+       { STV0900_8PSK_34,      0x0e,   0x1c,   0x3d,   0x0c,   0x0d,
+                               0x3b,   0x2c,   0x3b,   0x0c,   0x2b },
+       { STV0900_8PSK_56,      0x2e,   0x3e,   0x1e,   0x2e,   0x2d,
+                               0x1e,   0x3c,   0x2d,   0x2c,   0x1d },
+       { STV0900_8PSK_89,      0x3e,   0x3e,   0x1e,   0x2e,   0x3d,
+                               0x1e,   0x0d,   0x2d,   0x3c,   0x1d },
+       { STV0900_8PSK_910,     0x3e,   0x3e,   0x1e,   0x2e,   0x3d,
+                               0x1e,   0x1d,   0x2d,   0x0d,   0x1d },
 };
 
 
 
 /* Cut 2.0 Tracking carrier loop carrier 16APSK 2/3 to 32APSK 9/10 long Frame */
 static const struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut20[11] = {
-       /* Modcod               2MPon   2MPoff  5MPon   5MPoff  10MPon  10MPoff 20MPon  20MPoff 30MPon  30MPoff */
-       { STV0900_16APSK_23,    0x0C,   0x0C,   0x0C,   0x0C,   0x1D,   0x0C,   0x3C,   0x0C,   0x2C,   0x0C },
-       { STV0900_16APSK_34,    0x0C,   0x0C,   0x0C,   0x0C,   0x0E,   0x0C,   0x2D,   0x0C,   0x1D,   0x0C },
-       { STV0900_16APSK_45,    0x0C,   0x0C,   0x0C,   0x0C,   0x1E,   0x0C,   0x3D,   0x0C,   0x2D,   0x0C },
-       { STV0900_16APSK_56,    0x0C,   0x0C,   0x0C,   0x0C,   0x1E,   0x0C,   0x3D,   0x0C,   0x2D,   0x0C },
-       { STV0900_16APSK_89,    0x0C,   0x0C,   0x0C,   0x0C,   0x2E,   0x0C,   0x0E,   0x0C,   0x3D,   0x0C },
-       { STV0900_16APSK_910,   0x0C,   0x0C,   0x0C,   0x0C,   0x2E,   0x0C,   0x0E,   0x0C,   0x3D,   0x0C },
-       { STV0900_32APSK_34,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
-       { STV0900_32APSK_45,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
-       { STV0900_32APSK_56,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
-       { STV0900_32APSK_89,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
-       { STV0900_32APSK_910,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C }
+       /* Modcod               2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff */
+       { STV0900_16APSK_23,    0x0C,   0x0C,   0x0C,   0x0C,   0x1D,
+                               0x0C,   0x3C,   0x0C,   0x2C,   0x0C },
+       { STV0900_16APSK_34,    0x0C,   0x0C,   0x0C,   0x0C,   0x0E,
+                               0x0C,   0x2D,   0x0C,   0x1D,   0x0C },
+       { STV0900_16APSK_45,    0x0C,   0x0C,   0x0C,   0x0C,   0x1E,
+                               0x0C,   0x3D,   0x0C,   0x2D,   0x0C },
+       { STV0900_16APSK_56,    0x0C,   0x0C,   0x0C,   0x0C,   0x1E,
+                               0x0C,   0x3D,   0x0C,   0x2D,   0x0C },
+       { STV0900_16APSK_89,    0x0C,   0x0C,   0x0C,   0x0C,   0x2E,
+                               0x0C,   0x0E,   0x0C,   0x3D,   0x0C },
+       { STV0900_16APSK_910,   0x0C,   0x0C,   0x0C,   0x0C,   0x2E,
+                               0x0C,   0x0E,   0x0C,   0x3D,   0x0C },
+       { STV0900_32APSK_34,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,
+                               0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
+       { STV0900_32APSK_45,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,
+                               0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
+       { STV0900_32APSK_56,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,
+                               0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
+       { STV0900_32APSK_89,    0x0C,   0x0C,   0x0C,   0x0C,   0x0C,
+                               0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
+       { STV0900_32APSK_910,   0x0C,   0x0C,   0x0C,   0x0C,   0x0C,
+                               0x0C,   0x0C,   0x0C,   0x0C,   0x0C },
 };
 
 
 /* Cut 2.0 Tracking carrier loop carrier QPSK 1/4 to QPSK 2/5 long Frame */
 static const struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut20[3] = {
-       /* Modcod               2MPon   2MPoff  5MPon   5MPoff  10MPon  10MPoff 20MPon  20MPoff 30MPon  30MPoff */
-       { STV0900_QPSK_14,      0x0F,   0x3F,   0x0E,   0x3F,   0x2D,   0x2F,   0x2D,   0x1F,   0x3D,   0x3E },
-       { STV0900_QPSK_13,      0x0F,   0x3F,   0x0E,   0x3F,   0x2D,   0x2F,   0x3D,   0x0F,   0x3D,   0x2E },
-       { STV0900_QPSK_25,      0x1F,   0x3F,   0x1E,   0x3F,   0x3D,   0x1F,   0x3D,   0x3E,   0x3D,   0x2E }
+       /* Modcod               2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff */
+       { STV0900_QPSK_14,      0x0F,   0x3F,   0x0E,   0x3F,   0x2D,
+                               0x2F,   0x2D,   0x1F,   0x3D,   0x3E },
+       { STV0900_QPSK_13,      0x0F,   0x3F,   0x0E,   0x3F,   0x2D,
+                               0x2F,   0x3D,   0x0F,   0x3D,   0x2E },
+       { STV0900_QPSK_25,      0x1F,   0x3F,   0x1E,   0x3F,   0x3D,
+                               0x1F,   0x3D,   0x3E,   0x3D,   0x2E }
 };
 
 
 /* Cut 2.0 Tracking carrier loop carrier  short Frame, cut 1.2 and 2.0 */
-static const struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4]   = {
-       /*Mod   2M_cut1.2 2M_cut2.0 5M_cut1.2 5M_cut2.0 10M_cut1.2 10M_cut2.0 20M_cut1.2 20M_cut2.0 30M_cut1.2 30M_cut2.0 */
-       { STV0900_QPSK,         0x3C,   0x2F,   0x2B,   0x2E,   0x0B,   0x0E,   0x3A,   0x0E,   0x2A,   0x3D },
-       { STV0900_8PSK,         0x0B,   0x3E,   0x2A,   0x0E,   0x0A,   0x2D,   0x19,   0x0D,   0x09,   0x3C },
-       { STV0900_16APSK,       0x1B,   0x1E,   0x1B,   0x1E,   0x1B,   0x1E,   0x3A,   0x3D,   0x2A,   0x2D },
-       { STV0900_32APSK,       0x1B,   0x1E,   0x1B,   0x1E,   0x1B,   0x1E,   0x3A,   0x3D,   0x2A,   0x2D }
+static const
+struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = {
+       /*Mod           2Mcut1.2 2Mcut2.0 5Mcut1.2 5Mcut2.0 10Mcut1.2
+                       10Mcut2.0 20Mcut1.2 20M_cut2.0 30Mcut1.2 30Mcut2.0*/
+       { STV0900_QPSK,         0x3C,   0x2F,   0x2B,   0x2E,   0x0B,
+                               0x0E,   0x3A,   0x0E,   0x2A,   0x3D },
+       { STV0900_8PSK,         0x0B,   0x3E,   0x2A,   0x0E,   0x0A,
+                               0x2D,   0x19,   0x0D,   0x09,   0x3C },
+       { STV0900_16APSK,       0x1B,   0x1E,   0x1B,   0x1E,   0x1B,
+                               0x1E,   0x3A,   0x3D,   0x2A,   0x2D },
+       { STV0900_32APSK,       0x1B,   0x1E,   0x1B,   0x1E,   0x1B,
+                               0x1E,   0x3A,   0x3D,   0x2A,   0x2D }
+};
+
+static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut30[14] = {
+       /*Modcod                2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff */
+       { STV0900_QPSK_12,      0x3C,   0x2C,   0x0C,   0x2C,   0x1B,
+                               0x2C,   0x1B,   0x1C,   0x0B,   0x3B },
+       { STV0900_QPSK_35,      0x0D,   0x0D,   0x0C,   0x0D,   0x1B,
+                               0x3C,   0x1B,   0x1C,   0x0B,   0x3B },
+       { STV0900_QPSK_23,      0x1D,   0x0D,   0x0C,   0x1D,   0x2B,
+                               0x3C,   0x1B,   0x1C,   0x0B,   0x3B },
+       { STV0900_QPSK_34,      0x1D,   0x1D,   0x0C,   0x1D,   0x2B,
+                               0x3C,   0x1B,   0x1C,   0x0B,   0x3B },
+       { STV0900_QPSK_45,      0x2D,   0x1D,   0x1C,   0x1D,   0x2B,
+                               0x3C,   0x2B,   0x0C,   0x1B,   0x3B },
+       { STV0900_QPSK_56,      0x2D,   0x1D,   0x1C,   0x1D,   0x2B,
+                               0x3C,   0x2B,   0x0C,   0x1B,   0x3B },
+       { STV0900_QPSK_89,      0x3D,   0x2D,   0x1C,   0x1D,   0x3B,
+                               0x3C,   0x2B,   0x0C,   0x1B,   0x3B },
+       { STV0900_QPSK_910,     0x3D,   0x2D,   0x1C,   0x1D,   0x3B,
+                               0x3C,   0x2B,   0x0C,   0x1B,   0x3B },
+       { STV0900_8PSK_35,      0x39,   0x19,   0x39,   0x19,   0x19,
+                               0x19,   0x19,   0x19,   0x09,   0x19 },
+       { STV0900_8PSK_23,      0x2A,   0x39,   0x1A,   0x0A,   0x39,
+                               0x0A,   0x29,   0x39,   0x29,   0x0A },
+       { STV0900_8PSK_34,      0x0B,   0x3A,   0x0B,   0x0B,   0x3A,
+                               0x1B,   0x1A,   0x0B,   0x1A,   0x3A },
+       { STV0900_8PSK_56,      0x0C,   0x1B,   0x3B,   0x2B,   0x1B,
+                               0x3B,   0x3A,   0x3B,   0x3A,   0x1B },
+       { STV0900_8PSK_89,      0x2C,   0x2C,   0x2C,   0x1C,   0x2B,
+                               0x0C,   0x0B,   0x3B,   0x0B,   0x1B },
+       { STV0900_8PSK_910,     0x2C,   0x3C,   0x2C,   0x1C,   0x3B,
+                               0x1C,   0x0B,   0x3B,   0x0B,   0x1B }
+};
+
+static const
+struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut30[11] = {
+       /*Modcod                2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff */
+       { STV0900_16APSK_23,    0x0A,   0x0A,   0x0A,   0x0A,   0x1A,
+                               0x0A,   0x3A,   0x0A,   0x2A,   0x0A },
+       { STV0900_16APSK_34,    0x0A,   0x0A,   0x0A,   0x0A,   0x0B,
+                               0x0A,   0x3B,   0x0A,   0x1B,   0x0A },
+       { STV0900_16APSK_45,    0x0A,   0x0A,   0x0A,   0x0A,   0x1B,
+                               0x0A,   0x3B,   0x0A,   0x2B,   0x0A },
+       { STV0900_16APSK_56,    0x0A,   0x0A,   0x0A,   0x0A,   0x1B,
+                               0x0A,   0x3B,   0x0A,   0x2B,   0x0A },
+       { STV0900_16APSK_89,    0x0A,   0x0A,   0x0A,   0x0A,   0x2B,
+                               0x0A,   0x0C,   0x0A,   0x3B,   0x0A },
+       { STV0900_16APSK_910,   0x0A,   0x0A,   0x0A,   0x0A,   0x2B,
+                               0x0A,   0x0C,   0x0A,   0x3B,   0x0A },
+       { STV0900_32APSK_34,    0x0A,   0x0A,   0x0A,   0x0A,   0x0A,
+                               0x0A,   0x0A,   0x0A,   0x0A,   0x0A },
+       { STV0900_32APSK_45,    0x0A,   0x0A,   0x0A,   0x0A,   0x0A,
+                               0x0A,   0x0A,   0x0A,   0x0A,   0x0A },
+       { STV0900_32APSK_56,    0x0A,   0x0A,   0x0A,   0x0A,   0x0A,
+                               0x0A,   0x0A,   0x0A,   0x0A,   0x0A },
+       { STV0900_32APSK_89,    0x0A,   0x0A,   0x0A,   0x0A,   0x0A,
+                               0x0A,   0x0A,   0x0A,   0x0A,   0x0A },
+       { STV0900_32APSK_910,   0x0A,   0x0A,   0x0A,   0x0A,   0x0A,
+                               0x0A,   0x0A,   0x0A,   0x0A,   0x0A }
+};
+
+static const
+struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut30[3] = {
+       /*Modcod                2MPon   2MPoff  5MPon   5MPoff  10MPon
+                               10MPoff 20MPon  20MPoff 30MPon  30MPoff*/
+       { STV0900_QPSK_14,      0x0C,   0x3C,   0x0B,   0x3C,   0x2A,
+                               0x2C,   0x2A,   0x1C,   0x3A,   0x3B },
+       { STV0900_QPSK_13,      0x0C,   0x3C,   0x0B,   0x3C,   0x2A,
+                               0x2C,   0x3A,   0x0C,   0x3A,   0x2B },
+       { STV0900_QPSK_25,      0x1C,   0x3C,   0x1B,   0x3C,   0x3A,
+                               0x1C,   0x3A,   0x3B,   0x3A,   0x2B }
+};
+
+static const struct stv0900_short_frames_car_loop_optim_vs_mod
+FE_STV0900_S2ShortCarLoopCut30[4] = {
+       /*Mod           2Mcut3.0 5Mcut3.0 10Mcut3.0 20Mcut3.0 30Mcut3.0*/
+       { STV0900_QPSK,         0x2C,   0x2B,   0x0B,   0x0B,   0x3A },
+       { STV0900_8PSK,         0x3B,   0x0B,   0x2A,   0x0A,   0x39 },
+       { STV0900_16APSK,       0x1B,   0x1B,   0x1B,   0x3A,   0x2A },
+       { STV0900_32APSK,       0x1B,   0x1B,   0x1B,   0x3A,   0x2A },
+
 };
 
-static const u16 STV0900_InitVal[182][2] = {
+static const u16 STV0900_InitVal[181][2] = {
        { R0900_OUTCFG          , 0x00  },
-       { R0900_MODECFG         , 0xff  },
        { R0900_AGCRF1CFG       , 0x11  },
        { R0900_AGCRF2CFG       , 0x13  },
        { R0900_TSGENERAL1X     , 0x14  },
@@ -381,7 +524,7 @@ static const u16 STV0900_InitVal[182][2] = {
        { R0900_GAINLLR_NF15    , 0x1A  },
        { R0900_GAINLLR_NF16    , 0x1F  },
        { R0900_GAINLLR_NF17    , 0x21  },
-       { R0900_RCCFGH          , 0x20  },
+       { R0900_RCCFG2          , 0x20  },
        { R0900_P1_FECM         , 0x01  }, /*disable DSS modes*/
        { R0900_P2_FECM         , 0x01  }, /*disable DSS modes*/
        { R0900_P1_PRVIT        , 0x2F  }, /*disable puncture rate 6/7*/
index 5ed7a14..d8ba8a9 100644 (file)
 #define FALSE (!TRUE)
 #endif
 
-#define        dmd_reg(a, b, c) \
-       do { \
-               a = 0; \
-               switch (demod) { \
-               case STV0900_DEMOD_1: \
-               default: \
-                       a = b; \
-                       break; \
-               case STV0900_DEMOD_2: \
-                       a = c; \
-                       break; \
-               } \
-       } while (0)
-
-static int stvdebug;
-
 #define dprintk(args...) \
        do { \
                if (stvdebug) \
@@ -70,6 +54,8 @@ static int stvdebug;
 
 #define STV0900_MAXLOOKUPSIZE 500
 #define STV0900_BLIND_SEARCH_AGC2_TH 700
+#define STV0900_BLIND_SEARCH_AGC2_TH_CUT30 1400
+#define IQPOWER_THRESHOLD  30
 
 /* One point of the lookup table */
 struct stv000_lookpoint {
@@ -263,14 +249,14 @@ struct stv0900_init_params{
        int     tuner1_adc;
 
        /* IQ from the tuner1 to the demod */
-       enum stv0900_iq_inversion       tun1_iq_inversion;
+       enum stv0900_iq_inversion       tun1_iq_inv;
        enum fe_stv0900_clock_type      path2_ts_clock;
 
        u8      tun2_maddress;
        int     tuner2_adc;
 
        /* IQ from the tuner2 to the demod */
-       enum stv0900_iq_inversion       tun2_iq_inversion;
+       enum stv0900_iq_inversion       tun2_iq_inv;
        struct stv0900_reg              *ts_config;
 };
 
@@ -300,7 +286,7 @@ struct stv0900_signal_info {
        enum fe_stv0900_modcode                 modcode;
        enum fe_stv0900_modulation              modulation;
        enum fe_stv0900_pilot                   pilot;
-       enum fe_stv0900_frame_length            frame_length;
+       enum fe_stv0900_frame_length            frame_len;
        enum stv0900_iq_inversion               spectrum;
        enum fe_stv0900_rolloff                 rolloff;
 
@@ -318,47 +304,25 @@ struct stv0900_internal{
        /* Demodulator use for single demod or for dual demod) */
        enum fe_stv0900_demod_mode      demod_mode;
 
-       /*Demod 1*/
-       s32     tuner1_freq;
-       s32     tuner1_bw;
-       s32     dmd1_symbol_rate;
-       s32     dmd1_srch_range;
+       /*Demod*/
+       s32     freq[2];
+       s32     bw[2];
+       s32     symbol_rate[2];
+       s32     srch_range[2];
 
        /* algorithm for search Blind, Cold or Warm*/
-       enum fe_stv0900_search_algo     dmd1_srch_algo;
+       enum fe_stv0900_search_algo     srch_algo[2];
        /* search standard: Auto, DVBS1/DSS only or DVBS2 only*/
-       enum fe_stv0900_search_standard dmd1_srch_standard;
+       enum fe_stv0900_search_standard srch_standard[2];
        /* inversion search : auto, auto norma first, normal or inverted */
-       enum fe_stv0900_search_iq       dmd1_srch_iq_inv;
-       enum fe_stv0900_modcode         dmd1_modcode;
-       enum fe_stv0900_modulation      dmd1_modulation;
-       enum fe_stv0900_fec             dmd1_fec;
-
-       struct stv0900_signal_info      dmd1_rslts;
-       enum fe_stv0900_signal_type     dmd1_state;
+       enum fe_stv0900_search_iq       srch_iq_inv[2];
+       enum fe_stv0900_modcode         modcode[2];
+       enum fe_stv0900_modulation      modulation[2];
+       enum fe_stv0900_fec             fec[2];
 
-       enum fe_stv0900_error           dmd1_err;
+       struct stv0900_signal_info      result[2];
+       enum fe_stv0900_error           err[2];
 
-       /*Demod 2*/
-       s32     tuner2_freq;
-       s32     tuner2_bw;
-       s32     dmd2_symbol_rate;
-       s32     dmd2_srch_range;
-
-       enum fe_stv0900_search_algo     dmd2_srch_algo;
-       enum fe_stv0900_search_standard dmd2_srch_stndrd;
-       /* inversion search : auto, auto normal first, normal or inverted */
-       enum fe_stv0900_search_iq       dmd2_srch_iq_inv;
-       enum fe_stv0900_modcode         dmd2_modcode;
-       enum fe_stv0900_modulation      dmd2_modulation;
-       enum fe_stv0900_fec             dmd2_fec;
-
-       /* results of the search*/
-       struct stv0900_signal_info      dmd2_rslts;
-       /* current state of the search algorithm */
-       enum fe_stv0900_signal_type     dmd2_state;
-
-       enum fe_stv0900_error           dmd2_err;
 
        struct i2c_adapter      *i2c_adap;
        u8                      i2c_addr;
@@ -379,6 +343,8 @@ struct stv0900_state {
        int demod;
 };
 
+extern int stvdebug;
+
 extern s32 ge2comp(s32 a, s32 width);
 
 extern void stv0900_write_reg(struct stv0900_internal *i_params,
@@ -418,13 +384,14 @@ extern u8 stv0900_get_optim_short_carr_loop(s32 srate,
 extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params,
                                enum fe_stv0900_demod_num demod);
 
-extern void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
+extern void stv0900_activate_s2_modcod(struct stv0900_internal *i_params,
                                enum fe_stv0900_demod_num demod);
 
-extern void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params,
+extern void stv0900_activate_s2_modcod_single(struct stv0900_internal *i_params,
                                enum fe_stv0900_demod_num demod);
 
-extern enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
+extern enum
+fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
                                enum fe_stv0900_demod_num demod);
 
 #endif
index 264f9cf..7b8edf1 100644 (file)
@@ -14,7 +14,7 @@
  *
  * 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  *
  * GNU General Public License for more details.
  *
 #ifndef STV0900_REG_H
 #define STV0900_REG_H
 
+extern s32 shiftx(s32 x, int demod, s32 shift);
+
+#define REGx(x) shiftx(x, demod, 0x200)
+#define FLDx(x) shiftx(x, demod, 0x2000000)
+
 /*MID*/
-#define R0900_MID  0xf100
-#define F0900_MCHIP_IDENT  0xf10000f0
-#define F0900_MRELEASE  0xf100000f
+#define R0900_MID 0xf100
+#define F0900_MCHIP_IDENT 0xf10000f0
+#define F0900_MRELEASE 0xf100000f
 
 /*DACR1*/
-#define R0900_DACR1  0xf113
-#define F0900_DAC_MODE  0xf11300e0
-#define F0900_DAC_VALUE1  0xf113000f
+#define R0900_DACR1 0xf113
+#define F0900_DAC_MODE 0xf11300e0
+#define F0900_DAC_VALUE1 0xf113000f
 
 /*DACR2*/
-#define R0900_DACR2  0xf114
-#define F0900_DAC_VALUE0  0xf11400ff
+#define R0900_DACR2 0xf114
+#define F0900_DAC_VALUE0 0xf11400ff
 
 /*OUTCFG*/
-#define R0900_OUTCFG  0xf11c
-#define F0900_INV_DATA6  0xf11c0080
-#define F0900_OUTSERRS1_HZ  0xf11c0040
-#define F0900_OUTSERRS2_HZ  0xf11c0020
-#define F0900_OUTSERRS3_HZ  0xf11c0010
-#define F0900_OUTPARRS3_HZ  0xf11c0008
-#define F0900_OUTHZ3_CONTROL  0xf11c0007
-
-/*MODECFG*/
-#define R0900_MODECFG  0xf11d
-#define F0900_FECSPY_SEL_2  0xf11d0020
-#define F0900_HWARE_SEL_2  0xf11d0010
-#define F0900_PKTDEL_SEL_2  0xf11d0008
-#define F0900_DISEQC_SEL_2  0xf11d0004
-#define F0900_VIT_SEL_2  0xf11d0002
-#define F0900_DEMOD_SEL_2  0xf11d0001
+#define R0900_OUTCFG 0xf11c
+#define F0900_OUTSERRS1_HZ 0xf11c0040
+#define F0900_OUTSERRS2_HZ 0xf11c0020
+#define F0900_OUTSERRS3_HZ 0xf11c0010
+#define F0900_OUTPARRS3_HZ 0xf11c0008
 
 /*IRQSTATUS3*/
-#define R0900_IRQSTATUS3  0xf120
-#define F0900_SPLL_LOCK  0xf1200020
-#define F0900_SSTREAM_LCK_3  0xf1200010
-#define F0900_SSTREAM_LCK_2  0xf1200008
-#define F0900_SSTREAM_LCK_1  0xf1200004
-#define F0900_SDVBS1_PRF_2  0xf1200002
-#define F0900_SDVBS1_PRF_1  0xf1200001
+#define R0900_IRQSTATUS3 0xf120
+#define F0900_SPLL_LOCK 0xf1200020
+#define F0900_SSTREAM_LCK_3 0xf1200010
+#define F0900_SSTREAM_LCK_2 0xf1200008
+#define F0900_SSTREAM_LCK_1 0xf1200004
+#define F0900_SDVBS1_PRF_2 0xf1200002
+#define F0900_SDVBS1_PRF_1 0xf1200001
 
 /*IRQSTATUS2*/
-#define R0900_IRQSTATUS2  0xf121
-#define F0900_SSPY_ENDSIM_3  0xf1210080
-#define F0900_SSPY_ENDSIM_2  0xf1210040
-#define F0900_SSPY_ENDSIM_1  0xf1210020
-#define F0900_SPKTDEL_ERROR_2  0xf1210010
-#define F0900_SPKTDEL_LOCKB_2  0xf1210008
-#define F0900_SPKTDEL_LOCK_2  0xf1210004
-#define F0900_SPKTDEL_ERROR_1  0xf1210002
-#define F0900_SPKTDEL_LOCKB_1  0xf1210001
+#define R0900_IRQSTATUS2 0xf121
+#define F0900_SSPY_ENDSIM_3 0xf1210080
+#define F0900_SSPY_ENDSIM_2 0xf1210040
+#define F0900_SSPY_ENDSIM_1 0xf1210020
+#define F0900_SPKTDEL_ERROR_2 0xf1210010
+#define F0900_SPKTDEL_LOCKB_2 0xf1210008
+#define F0900_SPKTDEL_LOCK_2 0xf1210004
+#define F0900_SPKTDEL_ERROR_1 0xf1210002
+#define F0900_SPKTDEL_LOCKB_1 0xf1210001
 
 /*IRQSTATUS1*/
-#define R0900_IRQSTATUS1  0xf122
-#define F0900_SPKTDEL_LOCK_1  0xf1220080
-#define F0900_SEXTPINB2  0xf1220040
-#define F0900_SEXTPIN2  0xf1220020
-#define F0900_SEXTPINB1  0xf1220010
-#define F0900_SEXTPIN1  0xf1220008
-#define F0900_SDEMOD_LOCKB_2  0xf1220004
-#define F0900_SDEMOD_LOCK_2  0xf1220002
-#define F0900_SDEMOD_IRQ_2  0xf1220001
+#define R0900_IRQSTATUS1 0xf122
+#define F0900_SPKTDEL_LOCK_1 0xf1220080
+#define F0900_SDEMOD_LOCKB_2 0xf1220004
+#define F0900_SDEMOD_LOCK_2 0xf1220002
+#define F0900_SDEMOD_IRQ_2 0xf1220001
 
 /*IRQSTATUS0*/
-#define R0900_IRQSTATUS0  0xf123
-#define F0900_SDEMOD_LOCKB_1  0xf1230080
-#define F0900_SDEMOD_LOCK_1  0xf1230040
-#define F0900_SDEMOD_IRQ_1  0xf1230020
-#define F0900_SBCH_ERRFLAG  0xf1230010
-#define F0900_SDISEQC2RX_IRQ  0xf1230008
-#define F0900_SDISEQC2TX_IRQ  0xf1230004
-#define F0900_SDISEQC1RX_IRQ  0xf1230002
-#define F0900_SDISEQC1TX_IRQ  0xf1230001
+#define R0900_IRQSTATUS0 0xf123
+#define F0900_SDEMOD_LOCKB_1 0xf1230080
+#define F0900_SDEMOD_LOCK_1 0xf1230040
+#define F0900_SDEMOD_IRQ_1 0xf1230020
+#define F0900_SBCH_ERRFLAG 0xf1230010
+#define F0900_SDISEQC2RX_IRQ 0xf1230008
+#define F0900_SDISEQC2TX_IRQ 0xf1230004
+#define F0900_SDISEQC1RX_IRQ 0xf1230002
+#define F0900_SDISEQC1TX_IRQ 0xf1230001
 
 /*IRQMASK3*/
-#define R0900_IRQMASK3  0xf124
-#define F0900_MPLL_LOCK  0xf1240020
-#define F0900_MSTREAM_LCK_3  0xf1240010
-#define F0900_MSTREAM_LCK_2  0xf1240008
-#define F0900_MSTREAM_LCK_1  0xf1240004
-#define F0900_MDVBS1_PRF_2  0xf1240002
-#define F0900_MDVBS1_PRF_1  0xf1240001
+#define R0900_IRQMASK3 0xf124
+#define F0900_MPLL_LOCK 0xf1240020
+#define F0900_MSTREAM_LCK_3 0xf1240010
+#define F0900_MSTREAM_LCK_2 0xf1240008
+#define F0900_MSTREAM_LCK_1 0xf1240004
+#define F0900_MDVBS1_PRF_2 0xf1240002
+#define F0900_MDVBS1_PRF_1 0xf1240001
 
 /*IRQMASK2*/
-#define R0900_IRQMASK2  0xf125
-#define F0900_MSPY_ENDSIM_3  0xf1250080
-#define F0900_MSPY_ENDSIM_2  0xf1250040
-#define F0900_MSPY_ENDSIM_1  0xf1250020
-#define F0900_MPKTDEL_ERROR_2  0xf1250010
-#define F0900_MPKTDEL_LOCKB_2  0xf1250008
-#define F0900_MPKTDEL_LOCK_2  0xf1250004
-#define F0900_MPKTDEL_ERROR_1  0xf1250002
-#define F0900_MPKTDEL_LOCKB_1  0xf1250001
+#define R0900_IRQMASK2 0xf125
+#define F0900_MSPY_ENDSIM_3 0xf1250080
+#define F0900_MSPY_ENDSIM_2 0xf1250040
+#define F0900_MSPY_ENDSIM_1 0xf1250020
+#define F0900_MPKTDEL_ERROR_2 0xf1250010
+#define F0900_MPKTDEL_LOCKB_2 0xf1250008
+#define F0900_MPKTDEL_LOCK_2 0xf1250004
+#define F0900_MPKTDEL_ERROR_1 0xf1250002
+#define F0900_MPKTDEL_LOCKB_1 0xf1250001
 
 /*IRQMASK1*/
-#define R0900_IRQMASK1  0xf126
-#define F0900_MPKTDEL_LOCK_1  0xf1260080
-#define F0900_MEXTPINB2  0xf1260040
-#define F0900_MEXTPIN2  0xf1260020
-#define F0900_MEXTPINB1  0xf1260010
-#define F0900_MEXTPIN1  0xf1260008
-#define F0900_MDEMOD_LOCKB_2  0xf1260004
-#define F0900_MDEMOD_LOCK_2  0xf1260002
-#define F0900_MDEMOD_IRQ_2  0xf1260001
+#define R0900_IRQMASK1 0xf126
+#define F0900_MPKTDEL_LOCK_1 0xf1260080
+#define F0900_MEXTPINB2 0xf1260040
+#define F0900_MEXTPIN2 0xf1260020
+#define F0900_MEXTPINB1 0xf1260010
+#define F0900_MEXTPIN1 0xf1260008
+#define F0900_MDEMOD_LOCKB_2 0xf1260004
+#define F0900_MDEMOD_LOCK_2 0xf1260002
+#define F0900_MDEMOD_IRQ_2 0xf1260001
 
 /*IRQMASK0*/
-#define R0900_IRQMASK0  0xf127
-#define F0900_MDEMOD_LOCKB_1  0xf1270080
-#define F0900_MDEMOD_LOCK_1  0xf1270040
-#define F0900_MDEMOD_IRQ_1  0xf1270020
-#define F0900_MBCH_ERRFLAG  0xf1270010
-#define F0900_MDISEQC2RX_IRQ  0xf1270008
-#define F0900_MDISEQC2TX_IRQ  0xf1270004
-#define F0900_MDISEQC1RX_IRQ  0xf1270002
-#define F0900_MDISEQC1TX_IRQ  0xf1270001
+#define R0900_IRQMASK0 0xf127
+#define F0900_MDEMOD_LOCKB_1 0xf1270080
+#define F0900_MDEMOD_LOCK_1 0xf1270040
+#define F0900_MDEMOD_IRQ_1 0xf1270020
+#define F0900_MBCH_ERRFLAG 0xf1270010
+#define F0900_MDISEQC2RX_IRQ 0xf1270008
+#define F0900_MDISEQC2TX_IRQ 0xf1270004
+#define F0900_MDISEQC1RX_IRQ 0xf1270002
+#define F0900_MDISEQC1TX_IRQ 0xf1270001
 
 /*I2CCFG*/
-#define R0900_I2CCFG  0xf129
-#define F0900_I2C2_FASTMODE  0xf1290080
-#define F0900_STATUS_WR2  0xf1290040
-#define F0900_I2C2ADDR_INC  0xf1290030
-#define F0900_I2C_FASTMODE  0xf1290008
-#define F0900_STATUS_WR  0xf1290004
-#define F0900_I2CADDR_INC  0xf1290003
+#define R0900_I2CCFG 0xf129
+#define F0900_I2C_FASTMODE 0xf1290008
+#define F0900_I2CADDR_INC 0xf1290003
 
 /*P1_I2CRPT*/
-#define R0900_P1_I2CRPT  0xf12a
-#define F0900_P1_I2CT_ON  0xf12a0080
-#define F0900_P1_ENARPT_LEVEL  0xf12a0070
-#define F0900_P1_SCLT_DELAY  0xf12a0008
-#define F0900_P1_STOP_ENABLE  0xf12a0004
-#define F0900_P1_STOP_SDAT2SDA  0xf12a0002
+#define R0900_P1_I2CRPT 0xf12a
+#define I2CRPT shiftx(R0900_P1_I2CRPT, demod, -1)
+#define F0900_P1_I2CT_ON 0xf12a0080
+#define I2CT_ON shiftx(F0900_P1_I2CT_ON, demod, -0x10000)
+#define F0900_P1_ENARPT_LEVEL 0xf12a0070
+#define F0900_P1_SCLT_DELAY 0xf12a0008
+#define F0900_P1_STOP_ENABLE 0xf12a0004
+#define F0900_P1_STOP_SDAT2SDA 0xf12a0002
 
 /*P2_I2CRPT*/
-#define R0900_P2_I2CRPT  0xf12b
-#define F0900_P2_I2CT_ON  0xf12b0080
-#define F0900_P2_ENARPT_LEVEL  0xf12b0070
-#define F0900_P2_SCLT_DELAY  0xf12b0008
-#define F0900_P2_STOP_ENABLE  0xf12b0004
-#define F0900_P2_STOP_SDAT2SDA  0xf12b0002
+#define R0900_P2_I2CRPT 0xf12b
+#define F0900_P2_I2CT_ON 0xf12b0080
+#define F0900_P2_ENARPT_LEVEL 0xf12b0070
+#define F0900_P2_SCLT_DELAY 0xf12b0008
+#define F0900_P2_STOP_ENABLE 0xf12b0004
+#define F0900_P2_STOP_SDAT2SDA 0xf12b0002
+
+/*IOPVALUE6*/
+#define R0900_IOPVALUE6 0xf138
+#define F0900_VSCL 0xf1380004
+#define F0900_VSDA 0xf1380002
+#define F0900_VDATA3_0 0xf1380001
+
+/*IOPVALUE5*/
+#define R0900_IOPVALUE5 0xf139
+#define F0900_VDATA3_1 0xf1390080
+#define F0900_VDATA3_2 0xf1390040
+#define F0900_VDATA3_3 0xf1390020
+#define F0900_VDATA3_4 0xf1390010
+#define F0900_VDATA3_5 0xf1390008
+#define F0900_VDATA3_6 0xf1390004
+#define F0900_VDATA3_7 0xf1390002
+#define F0900_VCLKOUT3 0xf1390001
+
+/*IOPVALUE4*/
+#define R0900_IOPVALUE4 0xf13a
+#define F0900_VSTROUT3 0xf13a0080
+#define F0900_VDPN3 0xf13a0040
+#define F0900_VERROR3 0xf13a0020
+#define F0900_VDATA2_7 0xf13a0010
+#define F0900_VCLKOUT2 0xf13a0008
+#define F0900_VSTROUT2 0xf13a0004
+#define F0900_VDPN2 0xf13a0002
+#define F0900_VERROR2 0xf13a0001
+
+/*IOPVALUE3*/
+#define R0900_IOPVALUE3 0xf13b
+#define F0900_VDATA1_7 0xf13b0080
+#define F0900_VCLKOUT1 0xf13b0040
+#define F0900_VSTROUT1 0xf13b0020
+#define F0900_VDPN1 0xf13b0010
+#define F0900_VERROR1 0xf13b0008
+#define F0900_VCLKOUT27 0xf13b0004
+#define F0900_VDISEQCOUT2 0xf13b0002
+#define F0900_VSCLT2 0xf13b0001
+
+/*IOPVALUE2*/
+#define R0900_IOPVALUE2 0xf13c
+#define F0900_VSDAT2 0xf13c0080
+#define F0900_VAGCRF2 0xf13c0040
+#define F0900_VDISEQCOUT1 0xf13c0020
+#define F0900_VSCLT1 0xf13c0010
+#define F0900_VSDAT1 0xf13c0008
+#define F0900_VAGCRF1 0xf13c0004
+#define F0900_VDIRCLK 0xf13c0002
+#define F0900_VSTDBY 0xf13c0001
+
+/*IOPVALUE1*/
+#define R0900_IOPVALUE1 0xf13d
+#define F0900_VCS1 0xf13d0080
+#define F0900_VCS0 0xf13d0040
+#define F0900_VGPIO13 0xf13d0020
+#define F0900_VGPIO12 0xf13d0010
+#define F0900_VGPIO11 0xf13d0008
+#define F0900_VGPIO10 0xf13d0004
+#define F0900_VGPIO9 0xf13d0002
+#define F0900_VGPIO8 0xf13d0001
+
+/*IOPVALUE0*/
+#define R0900_IOPVALUE0 0xf13e
+#define F0900_VGPIO7 0xf13e0080
+#define F0900_VGPIO6 0xf13e0040
+#define F0900_VGPIO5 0xf13e0020
+#define F0900_VGPIO4 0xf13e0010
+#define F0900_VGPIO3 0xf13e0008
+#define F0900_VGPIO2 0xf13e0004
+#define F0900_VGPIO1 0xf13e0002
+#define F0900_VCLKI2 0xf13e0001
 
 /*CLKI2CFG*/
-#define R0900_CLKI2CFG  0xf140
-#define F0900_CLKI2_OPD  0xf1400080
-#define F0900_CLKI2_CONFIG  0xf140007e
-#define F0900_CLKI2_XOR  0xf1400001
+#define R0900_CLKI2CFG 0xf140
+#define F0900_CLKI2_OPD 0xf1400080
+#define F0900_CLKI2_CONFIG 0xf140007e
+#define F0900_CLKI2_XOR 0xf1400001
 
 /*GPIO1CFG*/
-#define R0900_GPIO1CFG  0xf141
-#define F0900_GPIO1_OPD  0xf1410080
-#define F0900_GPIO1_CONFIG  0xf141007e
-#define F0900_GPIO1_XOR  0xf1410001
+#define R0900_GPIO1CFG 0xf141
+#define F0900_GPIO1_OPD 0xf1410080
+#define F0900_GPIO1_CONFIG 0xf141007e
+#define F0900_GPIO1_XOR 0xf1410001
 
 /*GPIO2CFG*/
-#define R0900_GPIO2CFG  0xf142
-#define F0900_GPIO2_OPD  0xf1420080
-#define F0900_GPIO2_CONFIG  0xf142007e
-#define F0900_GPIO2_XOR  0xf1420001
+#define R0900_GPIO2CFG 0xf142
+#define F0900_GPIO2_OPD 0xf1420080
+#define F0900_GPIO2_CONFIG 0xf142007e
+#define F0900_GPIO2_XOR 0xf1420001
 
 /*GPIO3CFG*/
-#define R0900_GPIO3CFG  0xf143
-#define F0900_GPIO3_OPD  0xf1430080
-#define F0900_GPIO3_CONFIG  0xf143007e
-#define F0900_GPIO3_XOR  0xf1430001
+#define R0900_GPIO3CFG 0xf143
+#define F0900_GPIO3_OPD 0xf1430080
+#define F0900_GPIO3_CONFIG 0xf143007e
+#define F0900_GPIO3_XOR 0xf1430001
 
 /*GPIO4CFG*/
-#define R0900_GPIO4CFG  0xf144
-#define F0900_GPIO4_OPD  0xf1440080
-#define F0900_GPIO4_CONFIG  0xf144007e
-#define F0900_GPIO4_XOR  0xf1440001
+#define R0900_GPIO4CFG 0xf144
+#define F0900_GPIO4_OPD 0xf1440080
+#define F0900_GPIO4_CONFIG 0xf144007e
+#define F0900_GPIO4_XOR 0xf1440001
 
 /*GPIO5CFG*/
-#define R0900_GPIO5CFG  0xf145
-#define F0900_GPIO5_OPD  0xf1450080
-#define F0900_GPIO5_CONFIG  0xf145007e
-#define F0900_GPIO5_XOR  0xf1450001
+#define R0900_GPIO5CFG 0xf145
+#define F0900_GPIO5_OPD 0xf1450080
+#define F0900_GPIO5_CONFIG 0xf145007e
+#define F0900_GPIO5_XOR 0xf1450001
 
 /*GPIO6CFG*/
-#define R0900_GPIO6CFG  0xf146
-#define F0900_GPIO6_OPD  0xf1460080
-#define F0900_GPIO6_CONFIG  0xf146007e
-#define F0900_GPIO6_XOR  0xf1460001
+#define R0900_GPIO6CFG 0xf146
+#define F0900_GPIO6_OPD 0xf1460080
+#define F0900_GPIO6_CONFIG 0xf146007e
+#define F0900_GPIO6_XOR 0xf1460001
 
 /*GPIO7CFG*/
-#define R0900_GPIO7CFG  0xf147
-#define F0900_GPIO7_OPD  0xf1470080
-#define F0900_GPIO7_CONFIG  0xf147007e
-#define F0900_GPIO7_XOR  0xf1470001
+#define R0900_GPIO7CFG 0xf147
+#define F0900_GPIO7_OPD 0xf1470080
+#define F0900_GPIO7_CONFIG 0xf147007e
+#define F0900_GPIO7_XOR 0xf1470001
 
 /*GPIO8CFG*/
-#define R0900_GPIO8CFG  0xf148
-#define F0900_GPIO8_OPD  0xf1480080
-#define F0900_GPIO8_CONFIG  0xf148007e
-#define F0900_GPIO8_XOR  0xf1480001
+#define R0900_GPIO8CFG 0xf148
+#define F0900_GPIO8_OPD 0xf1480080
+#define F0900_GPIO8_CONFIG 0xf148007e
+#define F0900_GPIO8_XOR 0xf1480001
 
 /*GPIO9CFG*/
-#define R0900_GPIO9CFG  0xf149
-#define F0900_GPIO9_OPD  0xf1490080
-#define F0900_GPIO9_CONFIG  0xf149007e
-#define F0900_GPIO9_XOR  0xf1490001
+#define R0900_GPIO9CFG 0xf149
+#define F0900_GPIO9_OPD 0xf1490080
+#define F0900_GPIO9_CONFIG 0xf149007e
+#define F0900_GPIO9_XOR 0xf1490001
 
 /*GPIO10CFG*/
-#define R0900_GPIO10CFG  0xf14a
-#define F0900_GPIO10_OPD  0xf14a0080
-#define F0900_GPIO10_CONFIG  0xf14a007e
-#define F0900_GPIO10_XOR  0xf14a0001
+#define R0900_GPIO10CFG 0xf14a
+#define F0900_GPIO10_OPD 0xf14a0080
+#define F0900_GPIO10_CONFIG 0xf14a007e
+#define F0900_GPIO10_XOR 0xf14a0001
 
 /*GPIO11CFG*/
-#define R0900_GPIO11CFG  0xf14b
-#define F0900_GPIO11_OPD  0xf14b0080
-#define F0900_GPIO11_CONFIG  0xf14b007e
-#define F0900_GPIO11_XOR  0xf14b0001
+#define R0900_GPIO11CFG 0xf14b
+#define F0900_GPIO11_OPD 0xf14b0080
+#define F0900_GPIO11_CONFIG 0xf14b007e
+#define F0900_GPIO11_XOR 0xf14b0001
 
 /*GPIO12CFG*/
-#define R0900_GPIO12CFG  0xf14c
-#define F0900_GPIO12_OPD  0xf14c0080
-#define F0900_GPIO12_CONFIG  0xf14c007e
-#define F0900_GPIO12_XOR  0xf14c0001
+#define R0900_GPIO12CFG 0xf14c
+#define F0900_GPIO12_OPD 0xf14c0080
+#define F0900_GPIO12_CONFIG 0xf14c007e
+#define F0900_GPIO12_XOR 0xf14c0001
 
 /*GPIO13CFG*/
-#define R0900_GPIO13CFG  0xf14d
-#define F0900_GPIO13_OPD  0xf14d0080
-#define F0900_GPIO13_CONFIG  0xf14d007e
-#define F0900_GPIO13_XOR  0xf14d0001
+#define R0900_GPIO13CFG 0xf14d
+#define F0900_GPIO13_OPD 0xf14d0080
+#define F0900_GPIO13_CONFIG 0xf14d007e
+#define F0900_GPIO13_XOR 0xf14d0001
 
 /*CS0CFG*/
-#define R0900_CS0CFG  0xf14e
-#define F0900_CS0_OPD  0xf14e0080
-#define F0900_CS0_CONFIG  0xf14e007e
-#define F0900_CS0_XOR  0xf14e0001
+#define R0900_CS0CFG 0xf14e
+#define F0900_CS0_OPD 0xf14e0080
+#define F0900_CS0_CONFIG 0xf14e007e
+#define F0900_CS0_XOR 0xf14e0001
 
 /*CS1CFG*/
-#define R0900_CS1CFG  0xf14f
-#define F0900_CS1_OPD  0xf14f0080
-#define F0900_CS1_CONFIG  0xf14f007e
-#define F0900_CS1_XOR  0xf14f0001
+#define R0900_CS1CFG 0xf14f
+#define F0900_CS1_OPD 0xf14f0080
+#define F0900_CS1_CONFIG 0xf14f007e
+#define F0900_CS1_XOR 0xf14f0001
 
 /*STDBYCFG*/
-#define R0900_STDBYCFG  0xf150
-#define F0900_STDBY_OPD  0xf1500080
-#define F0900_STDBY_CONFIG  0xf150007e
-#define F0900_STBDY_XOR  0xf1500001
+#define R0900_STDBYCFG 0xf150
+#define F0900_STDBY_OPD 0xf1500080
+#define F0900_STDBY_CONFIG 0xf150007e
+#define F0900_STBDY_XOR 0xf1500001
 
 /*DIRCLKCFG*/
-#define R0900_DIRCLKCFG  0xf151
-#define F0900_DIRCLK_OPD  0xf1510080
-#define F0900_DIRCLK_CONFIG  0xf151007e
-#define F0900_DIRCLK_XOR  0xf1510001
+#define R0900_DIRCLKCFG 0xf151
+#define F0900_DIRCLK_OPD 0xf1510080
+#define F0900_DIRCLK_CONFIG 0xf151007e
+#define F0900_DIRCLK_XOR 0xf1510001
 
 /*AGCRF1CFG*/
-#define R0900_AGCRF1CFG  0xf152
-#define F0900_AGCRF1_OPD  0xf1520080
-#define F0900_AGCRF1_CONFIG  0xf152007e
-#define F0900_AGCRF1_XOR  0xf1520001
+#define R0900_AGCRF1CFG 0xf152
+#define F0900_AGCRF1_OPD 0xf1520080
+#define F0900_AGCRF1_CONFIG 0xf152007e
+#define F0900_AGCRF1_XOR 0xf1520001
 
 /*SDAT1CFG*/
-#define R0900_SDAT1CFG  0xf153
-#define F0900_SDAT1_OPD  0xf1530080
-#define F0900_SDAT1_CONFIG  0xf153007e
-#define F0900_SDAT1_XOR  0xf1530001
+#define R0900_SDAT1CFG 0xf153
+#define F0900_SDAT1_OPD 0xf1530080
+#define F0900_SDAT1_CONFIG 0xf153007e
+#define F0900_SDAT1_XOR 0xf1530001
 
 /*SCLT1CFG*/
-#define R0900_SCLT1CFG  0xf154
-#define F0900_SCLT1_OPD  0xf1540080
-#define F0900_SCLT1_CONFIG  0xf154007e
-#define F0900_SCLT1_XOR  0xf1540001
+#define R0900_SCLT1CFG 0xf154
+#define F0900_SCLT1_OPD 0xf1540080
+#define F0900_SCLT1_CONFIG 0xf154007e
+#define F0900_SCLT1_XOR 0xf1540001
 
 /*DISEQCO1CFG*/
-#define R0900_DISEQCO1CFG  0xf155
-#define F0900_DISEQCO1_OPD  0xf1550080
-#define F0900_DISEQCO1_CONFIG  0xf155007e
-#define F0900_DISEQC1_XOR  0xf1550001
+#define R0900_DISEQCO1CFG 0xf155
+#define F0900_DISEQCO1_OPD 0xf1550080
+#define F0900_DISEQCO1_CONFIG 0xf155007e
+#define F0900_DISEQC1_XOR 0xf1550001
 
 /*AGCRF2CFG*/
-#define R0900_AGCRF2CFG  0xf156
-#define F0900_AGCRF2_OPD  0xf1560080
-#define F0900_AGCRF2_CONFIG  0xf156007e
-#define F0900_AGCRF2_XOR  0xf1560001
+#define R0900_AGCRF2CFG 0xf156
+#define F0900_AGCRF2_OPD 0xf1560080
+#define F0900_AGCRF2_CONFIG 0xf156007e
+#define F0900_AGCRF2_XOR 0xf1560001
 
 /*SDAT2CFG*/
-#define R0900_SDAT2CFG  0xf157
-#define F0900_SDAT2_OPD  0xf1570080
-#define F0900_SDAT2_CONFIG  0xf157007e
-#define F0900_SDAT2_XOR  0xf1570001
+#define R0900_SDAT2CFG 0xf157
+#define F0900_SDAT2_OPD 0xf1570080
+#define F0900_SDAT2_CONFIG 0xf157007e
+#define F0900_SDAT2_XOR 0xf1570001
 
 /*SCLT2CFG*/
-#define R0900_SCLT2CFG  0xf158
-#define F0900_SCLT2_OPD  0xf1580080
-#define F0900_SCLT2_CONFIG  0xf158007e
-#define F0900_SCLT2_XOR  0xf1580001
+#define R0900_SCLT2CFG 0xf158
+#define F0900_SCLT2_OPD 0xf1580080
+#define F0900_SCLT2_CONFIG 0xf158007e
+#define F0900_SCLT2_XOR 0xf1580001
 
 /*DISEQCO2CFG*/
-#define R0900_DISEQCO2CFG  0xf159
-#define F0900_DISEQCO2_OPD  0xf1590080
-#define F0900_DISEQCO2_CONFIG  0xf159007e
-#define F0900_DISEQC2_XOR  0xf1590001
+#define R0900_DISEQCO2CFG 0xf159
+#define F0900_DISEQCO2_OPD 0xf1590080
+#define F0900_DISEQCO2_CONFIG 0xf159007e
+#define F0900_DISEQC2_XOR 0xf1590001
 
 /*CLKOUT27CFG*/
-#define R0900_CLKOUT27CFG  0xf15a
-#define F0900_CLKOUT27_OPD  0xf15a0080
-#define F0900_CLKOUT27_CONFIG  0xf15a007e
-#define F0900_CLKOUT27_XOR  0xf15a0001
+#define R0900_CLKOUT27CFG 0xf15a
+#define F0900_CLKOUT27_OPD 0xf15a0080
+#define F0900_CLKOUT27_CONFIG 0xf15a007e
+#define F0900_CLKOUT27_XOR 0xf15a0001
 
 /*ERROR1CFG*/
-#define R0900_ERROR1CFG  0xf15b
-#define F0900_ERROR1_OPD  0xf15b0080
-#define F0900_ERROR1_CONFIG  0xf15b007e
-#define F0900_ERROR1_XOR  0xf15b0001
+#define R0900_ERROR1CFG 0xf15b
+#define F0900_ERROR1_OPD 0xf15b0080
+#define F0900_ERROR1_CONFIG 0xf15b007e
+#define F0900_ERROR1_XOR 0xf15b0001
 
 /*DPN1CFG*/
-#define R0900_DPN1CFG  0xf15c
-#define F0900_DPN1_OPD  0xf15c0080
-#define F0900_DPN1_CONFIG  0xf15c007e
-#define F0900_DPN1_XOR  0xf15c0001
+#define R0900_DPN1CFG 0xf15c
+#define F0900_DPN1_OPD 0xf15c0080
+#define F0900_DPN1_CONFIG 0xf15c007e
+#define F0900_DPN1_XOR 0xf15c0001
 
 /*STROUT1CFG*/
-#define R0900_STROUT1CFG  0xf15d
-#define F0900_STROUT1_OPD  0xf15d0080
-#define F0900_STROUT1_CONFIG  0xf15d007e
-#define F0900_STROUT1_XOR  0xf15d0001
+#define R0900_STROUT1CFG 0xf15d
+#define F0900_STROUT1_OPD 0xf15d0080
+#define F0900_STROUT1_CONFIG 0xf15d007e
+#define F0900_STROUT1_XOR 0xf15d0001
 
 /*CLKOUT1CFG*/
-#define R0900_CLKOUT1CFG  0xf15e
-#define F0900_CLKOUT1_OPD  0xf15e0080
-#define F0900_CLKOUT1_CONFIG  0xf15e007e
-#define F0900_CLKOUT1_XOR  0xf15e0001
+#define R0900_CLKOUT1CFG 0xf15e
+#define F0900_CLKOUT1_OPD 0xf15e0080
+#define F0900_CLKOUT1_CONFIG 0xf15e007e
+#define F0900_CLKOUT1_XOR 0xf15e0001
 
 /*DATA71CFG*/
-#define R0900_DATA71CFG  0xf15f
-#define F0900_DATA71_OPD  0xf15f0080
-#define F0900_DATA71_CONFIG  0xf15f007e
-#define F0900_DATA71_XOR  0xf15f0001
+#define R0900_DATA71CFG 0xf15f
+#define F0900_DATA71_OPD 0xf15f0080
+#define F0900_DATA71_CONFIG 0xf15f007e
+#define F0900_DATA71_XOR 0xf15f0001
 
 /*ERROR2CFG*/
-#define R0900_ERROR2CFG  0xf160
-#define F0900_ERROR2_OPD  0xf1600080
-#define F0900_ERROR2_CONFIG  0xf160007e
-#define F0900_ERROR2_XOR  0xf1600001
+#define R0900_ERROR2CFG 0xf160
+#define F0900_ERROR2_OPD 0xf1600080
+#define F0900_ERROR2_CONFIG 0xf160007e
+#define F0900_ERROR2_XOR 0xf1600001
 
 /*DPN2CFG*/
-#define R0900_DPN2CFG  0xf161
-#define F0900_DPN2_OPD  0xf1610080
-#define F0900_DPN2_CONFIG  0xf161007e
-#define F0900_DPN2_XOR  0xf1610001
+#define R0900_DPN2CFG 0xf161
+#define F0900_DPN2_OPD 0xf1610080
+#define F0900_DPN2_CONFIG 0xf161007e
+#define F0900_DPN2_XOR 0xf1610001
 
 /*STROUT2CFG*/
-#define R0900_STROUT2CFG  0xf162
-#define F0900_STROUT2_OPD  0xf1620080
-#define F0900_STROUT2_CONFIG  0xf162007e
-#define F0900_STROUT2_XOR  0xf1620001
+#define R0900_STROUT2CFG 0xf162
+#define F0900_STROUT2_OPD 0xf1620080
+#define F0900_STROUT2_CONFIG 0xf162007e
+#define F0900_STROUT2_XOR 0xf1620001
 
 /*CLKOUT2CFG*/
-#define R0900_CLKOUT2CFG  0xf163
-#define F0900_CLKOUT2_OPD  0xf1630080
-#define F0900_CLKOUT2_CONFIG  0xf163007e
-#define F0900_CLKOUT2_XOR  0xf1630001
+#define R0900_CLKOUT2CFG 0xf163
+#define F0900_CLKOUT2_OPD 0xf1630080
+#define F0900_CLKOUT2_CONFIG 0xf163007e
+#define F0900_CLKOUT2_XOR 0xf1630001
 
 /*DATA72CFG*/
-#define R0900_DATA72CFG  0xf164
-#define F0900_DATA72_OPD  0xf1640080
-#define F0900_DATA72_CONFIG  0xf164007e
-#define F0900_DATA72_XOR  0xf1640001
+#define R0900_DATA72CFG 0xf164
+#define F0900_DATA72_OPD 0xf1640080
+#define F0900_DATA72_CONFIG 0xf164007e
+#define F0900_DATA72_XOR 0xf1640001
 
 /*ERROR3CFG*/
-#define R0900_ERROR3CFG  0xf165
-#define F0900_ERROR3_OPD  0xf1650080
-#define F0900_ERROR3_CONFIG  0xf165007e
-#define F0900_ERROR3_XOR  0xf1650001
+#define R0900_ERROR3CFG 0xf165
+#define F0900_ERROR3_OPD 0xf1650080
+#define F0900_ERROR3_CONFIG 0xf165007e
+#define F0900_ERROR3_XOR 0xf1650001
 
 /*DPN3CFG*/
-#define R0900_DPN3CFG  0xf166
-#define F0900_DPN3_OPD  0xf1660080
-#define F0900_DPN3_CONFIG  0xf166007e
-#define F0900_DPN3_XOR  0xf1660001
+#define R0900_DPN3CFG 0xf166
+#define F0900_DPN3_OPD 0xf1660080
+#define F0900_DPN3_CONFIG 0xf166007e
+#define F0900_DPN3_XOR 0xf1660001
 
 /*STROUT3CFG*/
-#define R0900_STROUT3CFG  0xf167
-#define F0900_STROUT3_OPD  0xf1670080
-#define F0900_STROUT3_CONFIG  0xf167007e
-#define F0900_STROUT3_XOR  0xf1670001
+#define R0900_STROUT3CFG 0xf167
+#define F0900_STROUT3_OPD 0xf1670080
+#define F0900_STROUT3_CONFIG 0xf167007e
+#define F0900_STROUT3_XOR 0xf1670001
 
 /*CLKOUT3CFG*/
-#define R0900_CLKOUT3CFG  0xf168
-#define F0900_CLKOUT3_OPD  0xf1680080
-#define F0900_CLKOUT3_CONFIG  0xf168007e
-#define F0900_CLKOUT3_XOR  0xf1680001
+#define R0900_CLKOUT3CFG 0xf168
+#define F0900_CLKOUT3_OPD 0xf1680080
+#define F0900_CLKOUT3_CONFIG 0xf168007e
+#define F0900_CLKOUT3_XOR 0xf1680001
 
 /*DATA73CFG*/
-#define R0900_DATA73CFG  0xf169
-#define F0900_DATA73_OPD  0xf1690080
-#define F0900_DATA73_CONFIG  0xf169007e
-#define F0900_DATA73_XOR  0xf1690001
+#define R0900_DATA73CFG 0xf169
+#define F0900_DATA73_OPD 0xf1690080
+#define F0900_DATA73_CONFIG 0xf169007e
+#define F0900_DATA73_XOR 0xf1690001
+
+/*STRSTATUS1*/
+#define R0900_STRSTATUS1 0xf16a
+#define F0900_STRSTATUS_SEL2 0xf16a00f0
+#define F0900_STRSTATUS_SEL1 0xf16a000f
+
+/*STRSTATUS2*/
+#define R0900_STRSTATUS2 0xf16b
+#define F0900_STRSTATUS_SEL4 0xf16b00f0
+#define F0900_STRSTATUS_SEL3 0xf16b000f
+
+/*STRSTATUS3*/
+#define R0900_STRSTATUS3 0xf16c
+#define F0900_STRSTATUS_SEL6 0xf16c00f0
+#define F0900_STRSTATUS_SEL5 0xf16c000f
 
 /*FSKTFC2*/
-#define R0900_FSKTFC2  0xf170
-#define F0900_FSKT_KMOD  0xf17000fc
-#define F0900_FSKT_CAR2  0xf1700003
+#define R0900_FSKTFC2 0xf170
+#define F0900_FSKT_KMOD 0xf17000fc
+#define F0900_FSKT_CAR2 0xf1700003
 
 /*FSKTFC1*/
-#define R0900_FSKTFC1  0xf171
-#define F0900_FSKT_CAR1  0xf17100ff
+#define R0900_FSKTFC1 0xf171
+#define F0900_FSKT_CAR1 0xf17100ff
 
 /*FSKTFC0*/
-#define R0900_FSKTFC0  0xf172
-#define F0900_FSKT_CAR0  0xf17200ff
+#define R0900_FSKTFC0 0xf172
+#define F0900_FSKT_CAR0 0xf17200ff
 
 /*FSKTDELTAF1*/
-#define R0900_FSKTDELTAF1  0xf173
-#define F0900_FSKT_DELTAF1  0xf173000f
+#define R0900_FSKTDELTAF1 0xf173
+#define F0900_FSKT_DELTAF1 0xf173000f
 
 /*FSKTDELTAF0*/
-#define R0900_FSKTDELTAF0  0xf174
-#define F0900_FSKT_DELTAF0  0xf17400ff
+#define R0900_FSKTDELTAF0 0xf174
+#define F0900_FSKT_DELTAF0 0xf17400ff
 
 /*FSKTCTRL*/
-#define R0900_FSKTCTRL  0xf175
-#define F0900_FSKT_EN_SGN  0xf1750040
-#define F0900_FSKT_MOD_SGN  0xf1750020
-#define F0900_FSKT_MOD_EN  0xf175001c
-#define F0900_FSKT_DACMODE  0xf1750003
+#define R0900_FSKTCTRL 0xf175
+#define F0900_FSKT_EN_SGN 0xf1750040
+#define F0900_FSKT_MOD_SGN 0xf1750020
+#define F0900_FSKT_MOD_EN 0xf175001c
+#define F0900_FSKT_DACMODE 0xf1750003
 
 /*FSKRFC2*/
-#define R0900_FSKRFC2  0xf176
-#define F0900_FSKR_DETSGN  0xf1760040
-#define F0900_FSKR_OUTSGN  0xf1760020
-#define F0900_FSKR_KAGC  0xf176001c
-#define F0900_FSKR_CAR2  0xf1760003
+#define R0900_FSKRFC2 0xf176
+#define F0900_FSKR_DETSGN 0xf1760040
+#define F0900_FSKR_OUTSGN 0xf1760020
+#define F0900_FSKR_KAGC 0xf176001c
+#define F0900_FSKR_CAR2 0xf1760003
 
 /*FSKRFC1*/
-#define R0900_FSKRFC1  0xf177
-#define F0900_FSKR_CAR1  0xf17700ff
+#define R0900_FSKRFC1 0xf177
+#define F0900_FSKR_CAR1 0xf17700ff
 
 /*FSKRFC0*/
-#define R0900_FSKRFC0  0xf178
-#define F0900_FSKR_CAR0  0xf17800ff
+#define R0900_FSKRFC0 0xf178
+#define F0900_FSKR_CAR0 0xf17800ff
 
 /*FSKRK1*/
-#define R0900_FSKRK1  0xf179
-#define F0900_FSKR_K1_EXP  0xf17900e0
-#define F0900_FSKR_K1_MANT  0xf179001f
+#define R0900_FSKRK1 0xf179
+#define F0900_FSKR_K1_EXP 0xf17900e0
+#define F0900_FSKR_K1_MANT 0xf179001f
 
 /*FSKRK2*/
-#define R0900_FSKRK2  0xf17a
-#define F0900_FSKR_K2_EXP  0xf17a00e0
-#define F0900_FSKR_K2_MANT  0xf17a001f
+#define R0900_FSKRK2 0xf17a
+#define F0900_FSKR_K2_EXP 0xf17a00e0
+#define F0900_FSKR_K2_MANT 0xf17a001f
 
 /*FSKRAGCR*/
-#define R0900_FSKRAGCR  0xf17b
-#define F0900_FSKR_OUTCTL  0xf17b00c0
-#define F0900_FSKR_AGC_REF  0xf17b003f
+#define R0900_FSKRAGCR 0xf17b
+#define F0900_FSKR_OUTCTL 0xf17b00c0
+#define F0900_FSKR_AGC_REF 0xf17b003f
 
 /*FSKRAGC*/
-#define R0900_FSKRAGC  0xf17c
-#define F0900_FSKR_AGC_ACCU  0xf17c00ff
+#define R0900_FSKRAGC 0xf17c
+#define F0900_FSKR_AGC_ACCU 0xf17c00ff
 
 /*FSKRALPHA*/
-#define R0900_FSKRALPHA  0xf17d
-#define F0900_FSKR_ALPHA_EXP  0xf17d001c
-#define F0900_FSKR_ALPHA_M  0xf17d0003
+#define R0900_FSKRALPHA 0xf17d
+#define F0900_FSKR_ALPHA_EXP 0xf17d001c
+#define F0900_FSKR_ALPHA_M 0xf17d0003
 
 /*FSKRPLTH1*/
-#define R0900_FSKRPLTH1  0xf17e
-#define F0900_FSKR_BETA  0xf17e00f0
-#define F0900_FSKR_PLL_TRESH1  0xf17e000f
+#define R0900_FSKRPLTH1 0xf17e
+#define F0900_FSKR_BETA 0xf17e00f0
+#define F0900_FSKR_PLL_TRESH1 0xf17e000f
 
 /*FSKRPLTH0*/
-#define R0900_FSKRPLTH0  0xf17f
-#define F0900_FSKR_PLL_TRESH0  0xf17f00ff
+#define R0900_FSKRPLTH0 0xf17f
+#define F0900_FSKR_PLL_TRESH0 0xf17f00ff
 
 /*FSKRDF1*/
-#define R0900_FSKRDF1  0xf180
-#define F0900_FSKR_OUT  0xf1800080
-#define F0900_FSKR_DELTAF1  0xf180001f
+#define R0900_FSKRDF1 0xf180
+#define F0900_FSKR_OUT 0xf1800080
+#define F0900_FSKR_DELTAF1 0xf180001f
 
 /*FSKRDF0*/
-#define R0900_FSKRDF0  0xf181
-#define F0900_FSKR_DELTAF0  0xf18100ff
+#define R0900_FSKRDF0 0xf181
+#define F0900_FSKR_DELTAF0 0xf18100ff
 
 /*FSKRSTEPP*/
-#define R0900_FSKRSTEPP  0xf182
-#define F0900_FSKR_STEP_PLUS  0xf18200ff
+#define R0900_FSKRSTEPP 0xf182
+#define F0900_FSKR_STEP_PLUS 0xf18200ff
 
 /*FSKRSTEPM*/
-#define R0900_FSKRSTEPM  0xf183
-#define F0900_FSKR_STEP_MINUS  0xf18300ff
+#define R0900_FSKRSTEPM 0xf183
+#define F0900_FSKR_STEP_MINUS 0xf18300ff
 
 /*FSKRDET1*/
-#define R0900_FSKRDET1  0xf184
-#define F0900_FSKR_DETECT  0xf1840080
-#define F0900_FSKR_CARDET_ACCU1  0xf184000f
+#define R0900_FSKRDET1 0xf184
+#define F0900_FSKR_DETECT 0xf1840080
+#define F0900_FSKR_CARDET_ACCU1 0xf184000f
 
 /*FSKRDET0*/
-#define R0900_FSKRDET0  0xf185
-#define F0900_FSKR_CARDET_ACCU0  0xf18500ff
+#define R0900_FSKRDET0 0xf185
+#define F0900_FSKR_CARDET_ACCU0 0xf18500ff
 
 /*FSKRDTH1*/
-#define R0900_FSKRDTH1  0xf186
-#define F0900_FSKR_CARLOSS_THRESH1  0xf18600f0
-#define F0900_FSKR_CARDET_THRESH1  0xf186000f
+#define R0900_FSKRDTH1 0xf186
+#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0
+#define F0900_FSKR_CARDET_THRESH1 0xf186000f
 
 /*FSKRDTH0*/
-#define R0900_FSKRDTH0  0xf187
-#define F0900_FSKR_CARDET_THRESH0  0xf18700ff
+#define R0900_FSKRDTH0 0xf187
+#define F0900_FSKR_CARDET_THRESH0 0xf18700ff
 
 /*FSKRLOSS*/
-#define R0900_FSKRLOSS  0xf188
-#define F0900_FSKR_CARLOSS_THRESH0  0xf18800ff
+#define R0900_FSKRLOSS 0xf188
+#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff
 
 /*P2_DISTXCTL*/
-#define R0900_P2_DISTXCTL  0xf190
-#define F0900_P2_TIM_OFF  0xf1900080
-#define F0900_P2_DISEQC_RESET  0xf1900040
-#define F0900_P2_TIM_CMD  0xf1900030
-#define F0900_P2_DIS_PRECHARGE  0xf1900008
-#define F0900_P2_DISTX_MODE  0xf1900007
+#define R0900_P2_DISTXCTL 0xf190
+#define F0900_P2_TIM_OFF 0xf1900080
+#define F0900_P2_DISEQC_RESET 0xf1900040
+#define F0900_P2_TIM_CMD 0xf1900030
+#define F0900_P2_DIS_PRECHARGE 0xf1900008
+#define F0900_P2_DISTX_MODE 0xf1900007
 
 /*P2_DISRXCTL*/
-#define R0900_P2_DISRXCTL  0xf191
-#define F0900_P2_RECEIVER_ON  0xf1910080
-#define F0900_P2_IGNO_SHORT22K  0xf1910040
-#define F0900_P2_ONECHIP_TRX  0xf1910020
-#define F0900_P2_EXT_ENVELOP  0xf1910010
-#define F0900_P2_PIN_SELECT  0xf191000c
-#define F0900_P2_IRQ_RXEND  0xf1910002
-#define F0900_P2_IRQ_4NBYTES  0xf1910001
+#define R0900_P2_DISRXCTL 0xf191
+#define F0900_P2_RECEIVER_ON 0xf1910080
+#define F0900_P2_IGNO_SHORT22K 0xf1910040
+#define F0900_P2_ONECHIP_TRX 0xf1910020
+#define F0900_P2_EXT_ENVELOP 0xf1910010
+#define F0900_P2_PIN_SELECT0 0xf191000c
+#define F0900_P2_IRQ_RXEND 0xf1910002
+#define F0900_P2_IRQ_4NBYTES 0xf1910001
 
 /*P2_DISRX_ST0*/
-#define R0900_P2_DISRX_ST0  0xf194
-#define F0900_P2_RX_END  0xf1940080
-#define F0900_P2_RX_ACTIVE  0xf1940040
-#define F0900_P2_SHORT_22KHZ  0xf1940020
-#define F0900_P2_CONT_TONE  0xf1940010
-#define F0900_P2_FIFO_4BREADY  0xf1940008
-#define F0900_P2_FIFO_EMPTY  0xf1940004
-#define F0900_P2_ABORT_DISRX  0xf1940001
+#define R0900_P2_DISRX_ST0 0xf194
+#define F0900_P2_RX_END 0xf1940080
+#define F0900_P2_RX_ACTIVE 0xf1940040
+#define F0900_P2_SHORT_22KHZ 0xf1940020
+#define F0900_P2_CONT_TONE 0xf1940010
+#define F0900_P2_FIFO_4BREADY 0xf1940008
+#define F0900_P2_FIFO_EMPTY 0xf1940004
+#define F0900_P2_ABORT_DISRX 0xf1940001
 
 /*P2_DISRX_ST1*/
-#define R0900_P2_DISRX_ST1  0xf195
-#define F0900_P2_RX_FAIL  0xf1950080
-#define F0900_P2_FIFO_PARITYFAIL  0xf1950040
-#define F0900_P2_RX_NONBYTE  0xf1950020
-#define F0900_P2_FIFO_OVERFLOW  0xf1950010
-#define F0900_P2_FIFO_BYTENBR  0xf195000f
+#define R0900_P2_DISRX_ST1 0xf195
+#define F0900_P2_RX_FAIL 0xf1950080
+#define F0900_P2_FIFO_PARITYFAIL 0xf1950040
+#define F0900_P2_RX_NONBYTE 0xf1950020
+#define F0900_P2_FIFO_OVERFLOW 0xf1950010
+#define F0900_P2_FIFO_BYTENBR 0xf195000f
 
 /*P2_DISRXDATA*/
-#define R0900_P2_DISRXDATA  0xf196
-#define F0900_P2_DISRX_DATA  0xf19600ff
+#define R0900_P2_DISRXDATA 0xf196
+#define F0900_P2_DISRX_DATA 0xf19600ff
 
 /*P2_DISTXDATA*/
-#define R0900_P2_DISTXDATA  0xf197
-#define F0900_P2_DISEQC_FIFO  0xf19700ff
+#define R0900_P2_DISTXDATA 0xf197
+#define F0900_P2_DISEQC_FIFO 0xf19700ff
 
 /*P2_DISTXSTATUS*/
-#define R0900_P2_DISTXSTATUS  0xf198
-#define F0900_P2_TX_FAIL  0xf1980080
-#define F0900_P2_FIFO_FULL  0xf1980040
-#define F0900_P2_TX_IDLE  0xf1980020
-#define F0900_P2_GAP_BURST  0xf1980010
-#define F0900_P2_TXFIFO_BYTES  0xf198000f
+#define R0900_P2_DISTXSTATUS 0xf198
+#define F0900_P2_TX_FAIL 0xf1980080
+#define F0900_P2_FIFO_FULL 0xf1980040
+#define F0900_P2_TX_IDLE 0xf1980020
+#define F0900_P2_GAP_BURST 0xf1980010
+#define F0900_P2_TXFIFO_BYTES 0xf198000f
 
 /*P2_F22TX*/
-#define R0900_P2_F22TX  0xf199
-#define F0900_P2_F22_REG  0xf19900ff
+#define R0900_P2_F22TX 0xf199
+#define F0900_P2_F22_REG 0xf19900ff
 
 /*P2_F22RX*/
-#define R0900_P2_F22RX  0xf19a
-#define F0900_P2_F22RX_REG  0xf19a00ff
+#define R0900_P2_F22RX 0xf19a
+#define F0900_P2_F22RX_REG 0xf19a00ff
 
 /*P2_ACRPRESC*/
-#define R0900_P2_ACRPRESC  0xf19c
-#define F0900_P2_ACR_CODFRDY  0xf19c0008
-#define F0900_P2_ACR_PRESC  0xf19c0007
+#define R0900_P2_ACRPRESC 0xf19c
+#define F0900_P2_ACR_PRESC 0xf19c0007
 
 /*P2_ACRDIV*/
-#define R0900_P2_ACRDIV  0xf19d
-#define F0900_P2_ACR_DIV  0xf19d00ff
+#define R0900_P2_ACRDIV 0xf19d
+#define F0900_P2_ACR_DIV 0xf19d00ff
 
 /*P1_DISTXCTL*/
-#define R0900_P1_DISTXCTL  0xf1a0
-#define F0900_P1_TIM_OFF  0xf1a00080
-#define F0900_P1_DISEQC_RESET  0xf1a00040
-#define F0900_P1_TIM_CMD  0xf1a00030
-#define F0900_P1_DIS_PRECHARGE  0xf1a00008
-#define F0900_P1_DISTX_MODE  0xf1a00007
+#define R0900_P1_DISTXCTL 0xf1a0
+#define DISTXCTL shiftx(R0900_P1_DISTXCTL, demod, 0x10)
+#define F0900_P1_TIM_OFF 0xf1a00080
+#define F0900_P1_DISEQC_RESET 0xf1a00040
+#define DISEQC_RESET shiftx(F0900_P1_DISEQC_RESET, demod, 0x100000)
+#define F0900_P1_TIM_CMD 0xf1a00030
+#define F0900_P1_DIS_PRECHARGE 0xf1a00008
+#define DIS_PRECHARGE shiftx(F0900_P1_DIS_PRECHARGE, demod, 0x100000)
+#define F0900_P1_DISTX_MODE 0xf1a00007
+#define DISTX_MODE shiftx(F0900_P1_DISTX_MODE, demod, 0x100000)
 
 /*P1_DISRXCTL*/
-#define R0900_P1_DISRXCTL  0xf1a1
-#define F0900_P1_RECEIVER_ON  0xf1a10080
-#define F0900_P1_IGNO_SHORT22K  0xf1a10040
-#define F0900_P1_ONECHIP_TRX  0xf1a10020
-#define F0900_P1_EXT_ENVELOP  0xf1a10010
-#define F0900_P1_PIN_SELECT  0xf1a1000c
-#define F0900_P1_IRQ_RXEND  0xf1a10002
-#define F0900_P1_IRQ_4NBYTES  0xf1a10001
+#define R0900_P1_DISRXCTL 0xf1a1
+#define DISRXCTL shiftx(R0900_P1_DISRXCTL, demod, 0x10)
+#define F0900_P1_RECEIVER_ON 0xf1a10080
+#define F0900_P1_IGNO_SHORT22K 0xf1a10040
+#define F0900_P1_ONECHIP_TRX 0xf1a10020
+#define F0900_P1_EXT_ENVELOP 0xf1a10010
+#define F0900_P1_PIN_SELECT0 0xf1a1000c
+#define F0900_P1_IRQ_RXEND 0xf1a10002
+#define F0900_P1_IRQ_4NBYTES 0xf1a10001
 
 /*P1_DISRX_ST0*/
-#define R0900_P1_DISRX_ST0  0xf1a4
-#define F0900_P1_RX_END  0xf1a40080
-#define F0900_P1_RX_ACTIVE  0xf1a40040
-#define F0900_P1_SHORT_22KHZ  0xf1a40020
-#define F0900_P1_CONT_TONE  0xf1a40010
-#define F0900_P1_FIFO_4BREADY  0xf1a40008
-#define F0900_P1_FIFO_EMPTY  0xf1a40004
-#define F0900_P1_ABORT_DISRX  0xf1a40001
+#define R0900_P1_DISRX_ST0 0xf1a4
+#define DISRX_ST0 shiftx(R0900_P1_DISRX_ST0, demod, 0x10)
+#define F0900_P1_RX_END 0xf1a40080
+#define RX_END shiftx(F0900_P1_RX_END, demod, 0x100000)
+#define F0900_P1_RX_ACTIVE 0xf1a40040
+#define F0900_P1_SHORT_22KHZ 0xf1a40020
+#define F0900_P1_CONT_TONE 0xf1a40010
+#define F0900_P1_FIFO_4BREADY 0xf1a40008
+#define F0900_P1_FIFO_EMPTY 0xf1a40004
+#define F0900_P1_ABORT_DISRX 0xf1a40001
 
 /*P1_DISRX_ST1*/
-#define R0900_P1_DISRX_ST1  0xf1a5
-#define F0900_P1_RX_FAIL  0xf1a50080
-#define F0900_P1_FIFO_PARITYFAIL  0xf1a50040
-#define F0900_P1_RX_NONBYTE  0xf1a50020
-#define F0900_P1_FIFO_OVERFLOW  0xf1a50010
-#define F0900_P1_FIFO_BYTENBR  0xf1a5000f
+#define R0900_P1_DISRX_ST1 0xf1a5
+#define DISRX_ST1 shiftx(R0900_P1_DISRX_ST1, demod, 0x10)
+#define F0900_P1_RX_FAIL 0xf1a50080
+#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040
+#define F0900_P1_RX_NONBYTE 0xf1a50020
+#define F0900_P1_FIFO_OVERFLOW 0xf1a50010
+#define F0900_P1_FIFO_BYTENBR 0xf1a5000f
+#define FIFO_BYTENBR shiftx(F0900_P1_FIFO_BYTENBR, demod, 0x100000)
 
 /*P1_DISRXDATA*/
-#define R0900_P1_DISRXDATA  0xf1a6
-#define F0900_P1_DISRX_DATA  0xf1a600ff
+#define R0900_P1_DISRXDATA 0xf1a6
+#define DISRXDATA shiftx(R0900_P1_DISRXDATA, demod, 0x10)
+#define F0900_P1_DISRX_DATA 0xf1a600ff
 
 /*P1_DISTXDATA*/
-#define R0900_P1_DISTXDATA  0xf1a7
-#define F0900_P1_DISEQC_FIFO  0xf1a700ff
+#define R0900_P1_DISTXDATA 0xf1a7
+#define DISTXDATA shiftx(R0900_P1_DISTXDATA, demod, 0x10)
+#define F0900_P1_DISEQC_FIFO 0xf1a700ff
 
 /*P1_DISTXSTATUS*/
-#define R0900_P1_DISTXSTATUS  0xf1a8
-#define F0900_P1_TX_FAIL  0xf1a80080
-#define F0900_P1_FIFO_FULL  0xf1a80040
-#define F0900_P1_TX_IDLE  0xf1a80020
-#define F0900_P1_GAP_BURST  0xf1a80010
-#define F0900_P1_TXFIFO_BYTES  0xf1a8000f
+#define R0900_P1_DISTXSTATUS 0xf1a8
+#define F0900_P1_TX_FAIL 0xf1a80080
+#define F0900_P1_FIFO_FULL 0xf1a80040
+#define FIFO_FULL shiftx(F0900_P1_FIFO_FULL, demod, 0x100000)
+#define F0900_P1_TX_IDLE 0xf1a80020
+#define TX_IDLE shiftx(F0900_P1_TX_IDLE, demod, 0x100000)
+#define F0900_P1_GAP_BURST 0xf1a80010
+#define F0900_P1_TXFIFO_BYTES 0xf1a8000f
 
 /*P1_F22TX*/
-#define R0900_P1_F22TX  0xf1a9
-#define F0900_P1_F22_REG  0xf1a900ff
+#define R0900_P1_F22TX 0xf1a9
+#define F22TX shiftx(R0900_P1_F22TX, demod, 0x10)
+#define F0900_P1_F22_REG 0xf1a900ff
 
 /*P1_F22RX*/
-#define R0900_P1_F22RX  0xf1aa
-#define F0900_P1_F22RX_REG  0xf1aa00ff
+#define R0900_P1_F22RX 0xf1aa
+#define F22RX shiftx(R0900_P1_F22RX, demod, 0x10)
+#define F0900_P1_F22RX_REG 0xf1aa00ff
 
 /*P1_ACRPRESC*/
-#define R0900_P1_ACRPRESC  0xf1ac
-#define F0900_P1_ACR_CODFRDY  0xf1ac0008
-#define F0900_P1_ACR_PRESC  0xf1ac0007
+#define R0900_P1_ACRPRESC 0xf1ac
+#define ACRPRESC shiftx(R0900_P1_ACRPRESC, demod, 0x10)
+#define F0900_P1_ACR_PRESC 0xf1ac0007
 
 /*P1_ACRDIV*/
-#define R0900_P1_ACRDIV  0xf1ad
-#define F0900_P1_ACR_DIV  0xf1ad00ff
+#define R0900_P1_ACRDIV 0xf1ad
+#define ACRDIV shiftx(R0900_P1_ACRDIV, demod, 0x10)
+#define F0900_P1_ACR_DIV 0xf1ad00ff
 
 /*NCOARSE*/
-#define R0900_NCOARSE  0xf1b3
-#define F0900_M_DIV  0xf1b300ff
+#define R0900_NCOARSE 0xf1b3
+#define F0900_M_DIV 0xf1b300ff
 
 /*SYNTCTRL*/
-#define R0900_SYNTCTRL  0xf1b6
-#define F0900_STANDBY  0xf1b60080
-#define F0900_BYPASSPLLCORE  0xf1b60040
-#define F0900_SELX1RATIO  0xf1b60020
-#define F0900_I2C_TUD  0xf1b60010
-#define F0900_STOP_PLL  0xf1b60008
-#define F0900_BYPASSPLLFSK  0xf1b60004
-#define F0900_SELOSCI  0xf1b60002
-#define F0900_BYPASSPLLADC  0xf1b60001
+#define R0900_SYNTCTRL 0xf1b6
+#define F0900_STANDBY 0xf1b60080
+#define F0900_BYPASSPLLCORE 0xf1b60040
+#define F0900_SELX1RATIO 0xf1b60020
+#define F0900_STOP_PLL 0xf1b60008
+#define F0900_BYPASSPLLFSK 0xf1b60004
+#define F0900_SELOSCI 0xf1b60002
+#define F0900_BYPASSPLLADC 0xf1b60001
 
 /*FILTCTRL*/
-#define R0900_FILTCTRL  0xf1b7
-#define F0900_INV_CLK135  0xf1b70080
-#define F0900_PERM_BYPDIS  0xf1b70040
-#define F0900_SEL_FSKCKDIV  0xf1b70004
-#define F0900_INV_CLKFSK  0xf1b70002
-#define F0900_BYPASS_APPLI  0xf1b70001
+#define R0900_FILTCTRL 0xf1b7
+#define F0900_INV_CLK135 0xf1b70080
+#define F0900_SEL_FSKCKDIV 0xf1b70004
+#define F0900_INV_CLKFSK 0xf1b70002
+#define F0900_BYPASS_APPLI 0xf1b70001
 
 /*PLLSTAT*/
-#define R0900_PLLSTAT  0xf1b8
-#define F0900_ACM_SEL  0xf1b80080
-#define F0900_DTV_SEL  0xf1b80040
-#define F0900_PLLLOCK  0xf1b80001
+#define R0900_PLLSTAT 0xf1b8
+#define F0900_PLLLOCK 0xf1b80001
 
 /*STOPCLK1*/
-#define R0900_STOPCLK1  0xf1c2
-#define F0900_STOP_CLKPKDT2  0xf1c20040
-#define F0900_STOP_CLKPKDT1  0xf1c20020
-#define F0900_STOP_CLKFEC  0xf1c20010
-#define F0900_STOP_CLKADCI2  0xf1c20008
-#define F0900_INV_CLKADCI2  0xf1c20004
-#define F0900_STOP_CLKADCI1  0xf1c20002
-#define F0900_INV_CLKADCI1  0xf1c20001
+#define R0900_STOPCLK1 0xf1c2
+#define F0900_STOP_CLKPKDT2 0xf1c20040
+#define F0900_STOP_CLKPKDT1 0xf1c20020
+#define F0900_STOP_CLKFEC 0xf1c20010
+#define F0900_STOP_CLKADCI2 0xf1c20008
+#define F0900_INV_CLKADCI2 0xf1c20004
+#define F0900_STOP_CLKADCI1 0xf1c20002
+#define F0900_INV_CLKADCI1 0xf1c20001
 
 /*STOPCLK2*/
-#define R0900_STOPCLK2  0xf1c3
-#define F0900_STOP_CLKSAMP2  0xf1c30010
-#define F0900_STOP_CLKSAMP1  0xf1c30008
-#define F0900_STOP_CLKVIT2  0xf1c30004
-#define F0900_STOP_CLKVIT1  0xf1c30002
-#define F0900_STOP_CLKTS  0xf1c30001
+#define R0900_STOPCLK2 0xf1c3
+#define F0900_STOP_CLKSAMP2 0xf1c30010
+#define F0900_STOP_CLKSAMP1 0xf1c30008
+#define F0900_STOP_CLKVIT2 0xf1c30004
+#define F0900_STOP_CLKVIT1 0xf1c30002
+#define STOP_CLKVIT shiftx(F0900_STOP_CLKVIT1, demod, -2)
+#define F0900_STOP_CLKTS 0xf1c30001
 
 /*TSTTNR0*/
-#define R0900_TSTTNR0  0xf1df
-#define F0900_SEL_FSK  0xf1df0080
-#define F0900_FSK_PON  0xf1df0004
-#define F0900_FSK_OPENLOOP  0xf1df0002
+#define R0900_TSTTNR0 0xf1df
+#define F0900_SEL_FSK 0xf1df0080
+#define F0900_FSK_PON 0xf1df0004
 
 /*TSTTNR1*/
-#define R0900_TSTTNR1  0xf1e0
-#define F0900_BYPASS_ADC1  0xf1e00080
-#define F0900_INVADC1_CKOUT  0xf1e00040
-#define F0900_SELIQSRC1  0xf1e00030
-#define F0900_ADC1_PON  0xf1e00002
-#define F0900_ADC1_INMODE  0xf1e00001
+#define R0900_TSTTNR1 0xf1e0
+#define F0900_ADC1_PON 0xf1e00002
+#define F0900_ADC1_INMODE 0xf1e00001
 
 /*TSTTNR2*/
-#define R0900_TSTTNR2  0xf1e1
-#define F0900_DISEQC1_PON  0xf1e10020
-#define F0900_DISEQC1_TEST  0xf1e1001f
+#define R0900_TSTTNR2 0xf1e1
+#define F0900_DISEQC1_PON 0xf1e10020
 
 /*TSTTNR3*/
-#define R0900_TSTTNR3  0xf1e2
-#define F0900_BYPASS_ADC2  0xf1e20080
-#define F0900_INVADC2_CKOUT  0xf1e20040
-#define F0900_SELIQSRC2  0xf1e20030
-#define F0900_ADC2_PON  0xf1e20002
-#define F0900_ADC2_INMODE  0xf1e20001
+#define R0900_TSTTNR3 0xf1e2
+#define F0900_ADC2_PON 0xf1e20002
+#define F0900_ADC2_INMODE 0xf1e20001
 
 /*TSTTNR4*/
-#define R0900_TSTTNR4  0xf1e3
-#define F0900_DISEQC2_PON  0xf1e30020
-#define F0900_DISEQC2_TEST  0xf1e3001f
+#define R0900_TSTTNR4 0xf1e3
+#define F0900_DISEQC2_PON 0xf1e30020
 
 /*P2_IQCONST*/
-#define R0900_P2_IQCONST  0xf200
-#define F0900_P2_CONSTEL_SELECT  0xf2000060
-#define F0900_P2_IQSYMB_SEL  0xf200001f
+#define R0900_P2_IQCONST 0xf200
+#define F0900_P2_CONSTEL_SELECT 0xf2000060
+#define F0900_P2_IQSYMB_SEL 0xf200001f
 
 /*P2_NOSCFG*/
-#define R0900_P2_NOSCFG  0xf201
-#define F0900_P2_DUMMYPL_NOSDATA  0xf2010020
-#define F0900_P2_NOSPLH_BETA  0xf2010018
-#define F0900_P2_NOSDATA_BETA  0xf2010007
+#define R0900_P2_NOSCFG 0xf201
+#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020
+#define F0900_P2_NOSPLH_BETA 0xf2010018
+#define F0900_P2_NOSDATA_BETA 0xf2010007
 
 /*P2_ISYMB*/
-#define R0900_P2_ISYMB  0xf202
-#define F0900_P2_I_SYMBOL  0xf20201ff
+#define R0900_P2_ISYMB 0xf202
+#define F0900_P2_I_SYMBOL 0xf20201ff
 
 /*P2_QSYMB*/
-#define R0900_P2_QSYMB  0xf203
-#define F0900_P2_Q_SYMBOL  0xf20301ff
+#define R0900_P2_QSYMB 0xf203
+#define F0900_P2_Q_SYMBOL 0xf20301ff
 
 /*P2_AGC1CFG*/
-#define R0900_P2_AGC1CFG  0xf204
-#define F0900_P2_DC_FROZEN  0xf2040080
-#define F0900_P2_DC_CORRECT  0xf2040040
-#define F0900_P2_AMM_FROZEN  0xf2040020
-#define F0900_P2_AMM_CORRECT  0xf2040010
-#define F0900_P2_QUAD_FROZEN  0xf2040008
-#define F0900_P2_QUAD_CORRECT  0xf2040004
-#define F0900_P2_DCCOMP_SLOW  0xf2040002
-#define F0900_P2_IQMISM_SLOW  0xf2040001
+#define R0900_P2_AGC1CFG 0xf204
+#define F0900_P2_DC_FROZEN 0xf2040080
+#define F0900_P2_DC_CORRECT 0xf2040040
+#define F0900_P2_AMM_FROZEN 0xf2040020
+#define F0900_P2_AMM_CORRECT 0xf2040010
+#define F0900_P2_QUAD_FROZEN 0xf2040008
+#define F0900_P2_QUAD_CORRECT 0xf2040004
 
 /*P2_AGC1CN*/
-#define R0900_P2_AGC1CN  0xf206
-#define F0900_P2_AGC1_LOCKED  0xf2060080
-#define F0900_P2_AGC1_OVERFLOW  0xf2060040
-#define F0900_P2_AGC1_NOSLOWLK  0xf2060020
-#define F0900_P2_AGC1_MINPOWER  0xf2060010
-#define F0900_P2_AGCOUT_FAST  0xf2060008
-#define F0900_P2_AGCIQ_BETA  0xf2060007
+#define R0900_P2_AGC1CN 0xf206
+#define F0900_P2_AGC1_LOCKED 0xf2060080
+#define F0900_P2_AGC1_MINPOWER 0xf2060010
+#define F0900_P2_AGCOUT_FAST 0xf2060008
+#define F0900_P2_AGCIQ_BETA 0xf2060007
 
 /*P2_AGC1REF*/
-#define R0900_P2_AGC1REF  0xf207
-#define F0900_P2_AGCIQ_REF  0xf20700ff
+#define R0900_P2_AGC1REF 0xf207
+#define F0900_P2_AGCIQ_REF 0xf20700ff
 
 /*P2_IDCCOMP*/
-#define R0900_P2_IDCCOMP  0xf208
-#define F0900_P2_IAVERAGE_ADJ  0xf20801ff
+#define R0900_P2_IDCCOMP 0xf208
+#define F0900_P2_IAVERAGE_ADJ 0xf20801ff
 
 /*P2_QDCCOMP*/
-#define R0900_P2_QDCCOMP  0xf209
-#define F0900_P2_QAVERAGE_ADJ  0xf20901ff
+#define R0900_P2_QDCCOMP 0xf209
+#define F0900_P2_QAVERAGE_ADJ 0xf20901ff
 
 /*P2_POWERI*/
-#define R0900_P2_POWERI  0xf20a
-#define F0900_P2_POWER_I  0xf20a00ff
+#define R0900_P2_POWERI 0xf20a
+#define F0900_P2_POWER_I 0xf20a00ff
 
 /*P2_POWERQ*/
-#define R0900_P2_POWERQ  0xf20b
-#define F0900_P2_POWER_Q  0xf20b00ff
+#define R0900_P2_POWERQ 0xf20b
+#define F0900_P2_POWER_Q 0xf20b00ff
 
 /*P2_AGC1AMM*/
-#define R0900_P2_AGC1AMM  0xf20c
-#define F0900_P2_AMM_VALUE  0xf20c00ff
+#define R0900_P2_AGC1AMM 0xf20c
+#define F0900_P2_AMM_VALUE 0xf20c00ff
 
 /*P2_AGC1QUAD*/
-#define R0900_P2_AGC1QUAD  0xf20d
-#define F0900_P2_QUAD_VALUE  0xf20d01ff
+#define R0900_P2_AGC1QUAD 0xf20d
+#define F0900_P2_QUAD_VALUE 0xf20d01ff
 
 /*P2_AGCIQIN1*/
-#define R0900_P2_AGCIQIN1  0xf20e
-#define F0900_P2_AGCIQ_VALUE1  0xf20e00ff
+#define R0900_P2_AGCIQIN1 0xf20e
+#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff
 
 /*P2_AGCIQIN0*/
-#define R0900_P2_AGCIQIN0  0xf20f
-#define F0900_P2_AGCIQ_VALUE0  0xf20f00ff
+#define R0900_P2_AGCIQIN0 0xf20f
+#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff
 
 /*P2_DEMOD*/
-#define R0900_P2_DEMOD  0xf210
-#define F0900_P2_DEMOD_STOP  0xf2100040
-#define F0900_P2_SPECINV_CONTROL  0xf2100030
-#define F0900_P2_FORCE_ENASAMP  0xf2100008
-#define F0900_P2_MANUAL_ROLLOFF  0xf2100004
-#define F0900_P2_ROLLOFF_CONTROL  0xf2100003
+#define R0900_P2_DEMOD 0xf210
+#define F0900_P2_MANUALS2_ROLLOFF 0xf2100080
+#define F0900_P2_SPECINV_CONTROL 0xf2100030
+#define F0900_P2_FORCE_ENASAMP 0xf2100008
+#define F0900_P2_MANUALSX_ROLLOFF 0xf2100004
+#define F0900_P2_ROLLOFF_CONTROL 0xf2100003
 
 /*P2_DMDMODCOD*/
-#define R0900_P2_DMDMODCOD  0xf211
-#define F0900_P2_MANUAL_MODCOD  0xf2110080
-#define F0900_P2_DEMOD_MODCOD  0xf211007c
-#define F0900_P2_DEMOD_TYPE  0xf2110003
+#define R0900_P2_DMDMODCOD 0xf211
+#define F0900_P2_MANUAL_MODCOD 0xf2110080
+#define F0900_P2_DEMOD_MODCOD 0xf211007c
+#define F0900_P2_DEMOD_TYPE 0xf2110003
 
 /*P2_DSTATUS*/
-#define R0900_P2_DSTATUS  0xf212
-#define F0900_P2_CAR_LOCK  0xf2120080
-#define F0900_P2_TMGLOCK_QUALITY  0xf2120060
-#define F0900_P2_SDVBS1_ENABLE  0xf2120010
-#define F0900_P2_LOCK_DEFINITIF  0xf2120008
-#define F0900_P2_TIMING_IS_LOCKED  0xf2120004
-#define F0900_P2_COARSE_TMGLOCK  0xf2120002
-#define F0900_P2_COARSE_CARLOCK  0xf2120001
+#define R0900_P2_DSTATUS 0xf212
+#define F0900_P2_CAR_LOCK 0xf2120080
+#define F0900_P2_TMGLOCK_QUALITY 0xf2120060
+#define F0900_P2_LOCK_DEFINITIF 0xf2120008
+#define F0900_P2_OVADC_DETECT 0xf2120001
 
 /*P2_DSTATUS2*/
-#define R0900_P2_DSTATUS2  0xf213
-#define F0900_P2_DEMOD_DELOCK  0xf2130080
-#define F0900_P2_DEMOD_TIMEOUT  0xf2130040
-#define F0900_P2_MODCODRQ_SYNCTAG  0xf2130020
-#define F0900_P2_POLYPH_SATEVENT  0xf2130010
-#define F0900_P2_AGC1_NOSIGNALACK  0xf2130008
-#define F0900_P2_AGC2_OVERFLOW  0xf2130004
-#define F0900_P2_CFR_OVERFLOW  0xf2130002
-#define F0900_P2_GAMMA_OVERUNDER  0xf2130001
+#define R0900_P2_DSTATUS2 0xf213
+#define F0900_P2_DEMOD_DELOCK 0xf2130080
+#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008
+#define F0900_P2_AGC2_OVERFLOW 0xf2130004
+#define F0900_P2_CFR_OVERFLOW 0xf2130002
+#define F0900_P2_GAMMA_OVERUNDER 0xf2130001
 
 /*P2_DMDCFGMD*/
-#define R0900_P2_DMDCFGMD  0xf214
-#define F0900_P2_DVBS2_ENABLE  0xf2140080
-#define F0900_P2_DVBS1_ENABLE  0xf2140040
-#define F0900_P2_CFR_AUTOSCAN  0xf2140020
-#define F0900_P2_SCAN_ENABLE  0xf2140010
-#define F0900_P2_TUN_AUTOSCAN  0xf2140008
-#define F0900_P2_NOFORCE_RELOCK  0xf2140004
-#define F0900_P2_TUN_RNG  0xf2140003
+#define R0900_P2_DMDCFGMD 0xf214
+#define F0900_P2_DVBS2_ENABLE 0xf2140080
+#define F0900_P2_DVBS1_ENABLE 0xf2140040
+#define F0900_P2_SCAN_ENABLE 0xf2140010
+#define F0900_P2_CFR_AUTOSCAN 0xf2140008
+#define F0900_P2_TUN_RNG 0xf2140003
 
 /*P2_DMDCFG2*/
-#define R0900_P2_DMDCFG2  0xf215
-#define F0900_P2_AGC1_WAITLOCK  0xf2150080
-#define F0900_P2_S1S2_SEQUENTIAL  0xf2150040
-#define F0900_P2_OVERFLOW_TIMEOUT  0xf2150020
-#define F0900_P2_SCANFAIL_TIMEOUT  0xf2150010
-#define F0900_P2_DMDTOUT_BACK  0xf2150008
-#define F0900_P2_CARLOCK_S1ENABLE  0xf2150004
-#define F0900_P2_COARSE_LK3MODE  0xf2150002
-#define F0900_P2_COARSE_LK2MODE  0xf2150001
+#define R0900_P2_DMDCFG2 0xf215
+#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040
+#define F0900_P2_INFINITE_RELOCK 0xf2150010
 
 /*P2_DMDISTATE*/
-#define R0900_P2_DMDISTATE  0xf216
-#define F0900_P2_I2C_NORESETDMODE  0xf2160080
-#define F0900_P2_FORCE_ETAPED  0xf2160040
-#define F0900_P2_SDMDRST_DIRCLK  0xf2160020
-#define F0900_P2_I2C_DEMOD_MODE  0xf216001f
+#define R0900_P2_DMDISTATE 0xf216
+#define F0900_P2_I2C_DEMOD_MODE 0xf216001f
 
 /*P2_DMDT0M*/
-#define R0900_P2_DMDT0M  0xf217
-#define F0900_P2_DMDT0_MIN  0xf21700ff
+#define R0900_P2_DMDT0M 0xf217
+#define F0900_P2_DMDT0_MIN 0xf21700ff
 
 /*P2_DMDSTATE*/
-#define R0900_P2_DMDSTATE  0xf21b
-#define F0900_P2_DEMOD_LOCKED  0xf21b0080
-#define F0900_P2_HEADER_MODE  0xf21b0060
-#define F0900_P2_DEMOD_MODE  0xf21b001f
+#define R0900_P2_DMDSTATE 0xf21b
+#define F0900_P2_HEADER_MODE 0xf21b0060
 
 /*P2_DMDFLYW*/
-#define R0900_P2_DMDFLYW  0xf21c
-#define F0900_P2_I2C_IRQVAL  0xf21c00f0
-#define F0900_P2_FLYWHEEL_CPT  0xf21c000f
+#define R0900_P2_DMDFLYW 0xf21c
+#define F0900_P2_I2C_IRQVAL 0xf21c00f0
+#define F0900_P2_FLYWHEEL_CPT 0xf21c000f
 
 /*P2_DSTATUS3*/
-#define R0900_P2_DSTATUS3  0xf21d
-#define F0900_P2_CFR_ZIGZAG  0xf21d0080
-#define F0900_P2_DEMOD_CFGMODE  0xf21d0060
-#define F0900_P2_GAMMA_LOWBAUDRATE  0xf21d0010
-#define F0900_P2_RELOCK_MODE  0xf21d0008
-#define F0900_P2_DEMOD_FAIL  0xf21d0004
-#define F0900_P2_ETAPE1A_DVBXMEM  0xf21d0003
+#define R0900_P2_DSTATUS3 0xf21d
+#define F0900_P2_DEMOD_CFGMODE 0xf21d0060
 
 /*P2_DMDCFG3*/
-#define R0900_P2_DMDCFG3  0xf21e
-#define F0900_P2_DVBS1_TMGWAIT  0xf21e0080
-#define F0900_P2_NO_BWCENTERING  0xf21e0040
-#define F0900_P2_INV_SEQSRCH  0xf21e0020
-#define F0900_P2_DIS_SFRUPLOW_TRK  0xf21e0010
-#define F0900_P2_NOSTOP_FIFOFULL  0xf21e0008
-#define F0900_P2_LOCKTIME_MODE  0xf21e0007
+#define R0900_P2_DMDCFG3 0xf21e
+#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008
 
 /*P2_DMDCFG4*/
-#define R0900_P2_DMDCFG4  0xf21f
-#define F0900_P2_TUNER_NRELAUNCH  0xf21f0008
-#define F0900_P2_DIS_CLKENABLE  0xf21f0004
-#define F0900_P2_DIS_HDRDIVLOCK  0xf21f0002
-#define F0900_P2_NO_TNRWBINIT  0xf21f0001
+#define R0900_P2_DMDCFG4 0xf21f
+#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008
 
 /*P2_CORRELMANT*/
-#define R0900_P2_CORRELMANT  0xf220
-#define F0900_P2_CORREL_MANT  0xf22000ff
+#define R0900_P2_CORRELMANT 0xf220
+#define F0900_P2_CORREL_MANT 0xf22000ff
 
 /*P2_CORRELABS*/
-#define R0900_P2_CORRELABS  0xf221
-#define F0900_P2_CORREL_ABS  0xf22100ff
+#define R0900_P2_CORRELABS 0xf221
+#define F0900_P2_CORREL_ABS 0xf22100ff
 
 /*P2_CORRELEXP*/
-#define R0900_P2_CORRELEXP  0xf222
-#define F0900_P2_CORREL_ABSEXP  0xf22200f0
-#define F0900_P2_CORREL_EXP  0xf222000f
+#define R0900_P2_CORRELEXP 0xf222
+#define F0900_P2_CORREL_ABSEXP 0xf22200f0
+#define F0900_P2_CORREL_EXP 0xf222000f
 
 /*P2_PLHMODCOD*/
-#define R0900_P2_PLHMODCOD  0xf224
-#define F0900_P2_SPECINV_DEMOD  0xf2240080
-#define F0900_P2_PLH_MODCOD  0xf224007c
-#define F0900_P2_PLH_TYPE  0xf2240003
-
-/*P2_AGCK32*/
-#define R0900_P2_AGCK32  0xf22b
-#define F0900_P2_R3ADJOFF_32APSK  0xf22b0080
-#define F0900_P2_R2ADJOFF_32APSK  0xf22b0040
-#define F0900_P2_R1ADJOFF_32APSK  0xf22b0020
-#define F0900_P2_RADJ_32APSK  0xf22b001f
+#define R0900_P2_PLHMODCOD 0xf224
+#define F0900_P2_SPECINV_DEMOD 0xf2240080
+#define F0900_P2_PLH_MODCOD 0xf224007c
+#define F0900_P2_PLH_TYPE 0xf2240003
+
+/*P2_DMDREG*/
+#define R0900_P2_DMDREG 0xf225
+#define F0900_P2_DECIM_PLFRAMES 0xf2250001
 
 /*P2_AGC2O*/
-#define R0900_P2_AGC2O  0xf22c
-#define F0900_P2_AGC2REF_ADJUSTING  0xf22c0080
-#define F0900_P2_AGC2_COARSEFAST  0xf22c0040
-#define F0900_P2_AGC2_LKSQRT  0xf22c0020
-#define F0900_P2_AGC2_LKMODE  0xf22c0010
-#define F0900_P2_AGC2_LKEQUA  0xf22c0008
-#define F0900_P2_AGC2_COEF  0xf22c0007
+#define R0900_P2_AGC2O 0xf22c
+#define F0900_P2_AGC2_COEF 0xf22c0007
 
 /*P2_AGC2REF*/
-#define R0900_P2_AGC2REF  0xf22d
-#define F0900_P2_AGC2_REF  0xf22d00ff
+#define R0900_P2_AGC2REF 0xf22d
+#define F0900_P2_AGC2_REF 0xf22d00ff
 
 /*P2_AGC1ADJ*/
-#define R0900_P2_AGC1ADJ  0xf22e
-#define F0900_P2_AGC1ADJ_MANUAL  0xf22e0080
-#define F0900_P2_AGC1_ADJUSTED  0xf22e017f
+#define R0900_P2_AGC1ADJ 0xf22e
+#define F0900_P2_AGC1_ADJUSTED 0xf22e007f
 
 /*P2_AGC2I1*/
-#define R0900_P2_AGC2I1  0xf236
-#define F0900_P2_AGC2_INTEGRATOR1  0xf23600ff
+#define R0900_P2_AGC2I1 0xf236
+#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff
 
 /*P2_AGC2I0*/
-#define R0900_P2_AGC2I0  0xf237
-#define F0900_P2_AGC2_INTEGRATOR0  0xf23700ff
+#define R0900_P2_AGC2I0 0xf237
+#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff
 
 /*P2_CARCFG*/
-#define R0900_P2_CARCFG  0xf238
-#define F0900_P2_CFRUPLOW_AUTO  0xf2380080
-#define F0900_P2_CFRUPLOW_TEST  0xf2380040
-#define F0900_P2_EN_CAR2CENTER  0xf2380020
-#define F0900_P2_CARHDR_NODIV8  0xf2380010
-#define F0900_P2_I2C_ROTA  0xf2380008
-#define F0900_P2_ROTAON  0xf2380004
-#define F0900_P2_PH_DET_ALGO  0xf2380003
+#define R0900_P2_CARCFG 0xf238
+#define F0900_P2_CFRUPLOW_AUTO 0xf2380080
+#define F0900_P2_CFRUPLOW_TEST 0xf2380040
+#define F0900_P2_ROTAON 0xf2380004
+#define F0900_P2_PH_DET_ALGO 0xf2380003
 
 /*P2_ACLC*/
-#define R0900_P2_ACLC  0xf239
-#define F0900_P2_STOP_S2ALPHA  0xf23900c0
-#define F0900_P2_CAR_ALPHA_MANT  0xf2390030
-#define F0900_P2_CAR_ALPHA_EXP  0xf239000f
+#define R0900_P2_ACLC 0xf239
+#define F0900_P2_CAR_ALPHA_MANT 0xf2390030
+#define F0900_P2_CAR_ALPHA_EXP 0xf239000f
 
 /*P2_BCLC*/
-#define R0900_P2_BCLC  0xf23a
-#define F0900_P2_STOP_S2BETA  0xf23a00c0
-#define F0900_P2_CAR_BETA_MANT  0xf23a0030
-#define F0900_P2_CAR_BETA_EXP  0xf23a000f
+#define R0900_P2_BCLC 0xf23a
+#define F0900_P2_CAR_BETA_MANT 0xf23a0030
+#define F0900_P2_CAR_BETA_EXP 0xf23a000f
 
 /*P2_CARFREQ*/
-#define R0900_P2_CARFREQ  0xf23d
-#define F0900_P2_KC_COARSE_EXP  0xf23d00f0
-#define F0900_P2_BETA_FREQ  0xf23d000f
+#define R0900_P2_CARFREQ 0xf23d
+#define F0900_P2_KC_COARSE_EXP 0xf23d00f0
+#define F0900_P2_BETA_FREQ 0xf23d000f
 
 /*P2_CARHDR*/
-#define R0900_P2_CARHDR  0xf23e
-#define F0900_P2_K_FREQ_HDR  0xf23e00ff
+#define R0900_P2_CARHDR 0xf23e
+#define F0900_P2_K_FREQ_HDR 0xf23e00ff
 
 /*P2_LDT*/
-#define R0900_P2_LDT  0xf23f
-#define F0900_P2_CARLOCK_THRES  0xf23f01ff
+#define R0900_P2_LDT 0xf23f
+#define F0900_P2_CARLOCK_THRES 0xf23f01ff
 
 /*P2_LDT2*/
-#define R0900_P2_LDT2  0xf240
-#define F0900_P2_CARLOCK_THRES2  0xf24001ff
+#define R0900_P2_LDT2 0xf240
+#define F0900_P2_CARLOCK_THRES2 0xf24001ff
 
 /*P2_CFRICFG*/
-#define R0900_P2_CFRICFG  0xf241
-#define F0900_P2_CFRINIT_UNVALRNG  0xf2410080
-#define F0900_P2_CFRINIT_LUNVALCPT  0xf2410040
-#define F0900_P2_CFRINIT_ABORTDBL  0xf2410020
-#define F0900_P2_CFRINIT_ABORTPRED  0xf2410010
-#define F0900_P2_CFRINIT_UNVALSKIP  0xf2410008
-#define F0900_P2_CFRINIT_CSTINC  0xf2410004
-#define F0900_P2_NEG_CFRSTEP  0xf2410001
+#define R0900_P2_CFRICFG 0xf241
+#define F0900_P2_NEG_CFRSTEP 0xf2410001
 
 /*P2_CFRUP1*/
-#define R0900_P2_CFRUP1  0xf242
-#define F0900_P2_CFR_UP1  0xf24201ff
+#define R0900_P2_CFRUP1 0xf242
+#define F0900_P2_CFR_UP1 0xf24201ff
 
 /*P2_CFRUP0*/
-#define R0900_P2_CFRUP0  0xf243
-#define F0900_P2_CFR_UP0  0xf24300ff
+#define R0900_P2_CFRUP0 0xf243
+#define F0900_P2_CFR_UP0 0xf24300ff
 
 /*P2_CFRLOW1*/
-#define R0900_P2_CFRLOW1  0xf246
-#define F0900_P2_CFR_LOW1  0xf24601ff
+#define R0900_P2_CFRLOW1 0xf246
+#define F0900_P2_CFR_LOW1 0xf24601ff
 
 /*P2_CFRLOW0*/
-#define R0900_P2_CFRLOW0  0xf247
-#define F0900_P2_CFR_LOW0  0xf24700ff
+#define R0900_P2_CFRLOW0 0xf247
+#define F0900_P2_CFR_LOW0 0xf24700ff
 
 /*P2_CFRINIT1*/
-#define R0900_P2_CFRINIT1  0xf248
-#define F0900_P2_CFR_INIT1  0xf24801ff
+#define R0900_P2_CFRINIT1 0xf248
+#define F0900_P2_CFR_INIT1 0xf24801ff
 
 /*P2_CFRINIT0*/
-#define R0900_P2_CFRINIT0  0xf249
-#define F0900_P2_CFR_INIT0  0xf24900ff
+#define R0900_P2_CFRINIT0 0xf249
+#define F0900_P2_CFR_INIT0 0xf24900ff
 
 /*P2_CFRINC1*/
-#define R0900_P2_CFRINC1  0xf24a
-#define F0900_P2_MANUAL_CFRINC  0xf24a0080
-#define F0900_P2_CFR_INC1  0xf24a017f
+#define R0900_P2_CFRINC1 0xf24a
+#define F0900_P2_MANUAL_CFRINC 0xf24a0080
+#define F0900_P2_CFR_INC1 0xf24a003f
 
 /*P2_CFRINC0*/
-#define R0900_P2_CFRINC0  0xf24b
-#define F0900_P2_CFR_INC0  0xf24b00f0
+#define R0900_P2_CFRINC0 0xf24b
+#define F0900_P2_CFR_INC0 0xf24b00f8
 
 /*P2_CFR2*/
-#define R0900_P2_CFR2  0xf24c
-#define F0900_P2_CAR_FREQ2  0xf24c01ff
+#define R0900_P2_CFR2 0xf24c
+#define F0900_P2_CAR_FREQ2 0xf24c01ff
 
 /*P2_CFR1*/
-#define R0900_P2_CFR1  0xf24d
-#define F0900_P2_CAR_FREQ1  0xf24d00ff
+#define R0900_P2_CFR1 0xf24d
+#define F0900_P2_CAR_FREQ1 0xf24d00ff
 
 /*P2_CFR0*/
-#define R0900_P2_CFR0  0xf24e
-#define F0900_P2_CAR_FREQ0  0xf24e00ff
+#define R0900_P2_CFR0 0xf24e
+#define F0900_P2_CAR_FREQ0 0xf24e00ff
 
 /*P2_LDI*/
-#define R0900_P2_LDI  0xf24f
-#define F0900_P2_LOCK_DET_INTEGR  0xf24f01ff
+#define R0900_P2_LDI 0xf24f
+#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff
 
 /*P2_TMGCFG*/
-#define R0900_P2_TMGCFG  0xf250
-#define F0900_P2_TMGLOCK_BETA  0xf25000c0
-#define F0900_P2_NOTMG_GROUPDELAY  0xf2500020
-#define F0900_P2_DO_TIMING_CORR  0xf2500010
-#define F0900_P2_MANUAL_SCAN  0xf250000c
-#define F0900_P2_TMG_MINFREQ  0xf2500003
+#define R0900_P2_TMGCFG 0xf250
+#define F0900_P2_TMGLOCK_BETA 0xf25000c0
+#define F0900_P2_DO_TIMING_CORR 0xf2500010
+#define F0900_P2_TMG_MINFREQ 0xf2500003
 
 /*P2_RTC*/
-#define R0900_P2_RTC  0xf251
-#define F0900_P2_TMGALPHA_EXP  0xf25100f0
-#define F0900_P2_TMGBETA_EXP  0xf251000f
+#define R0900_P2_RTC 0xf251
+#define F0900_P2_TMGALPHA_EXP 0xf25100f0
+#define F0900_P2_TMGBETA_EXP 0xf251000f
 
 /*P2_RTCS2*/
-#define R0900_P2_RTCS2  0xf252
-#define F0900_P2_TMGALPHAS2_EXP  0xf25200f0
-#define F0900_P2_TMGBETAS2_EXP  0xf252000f
+#define R0900_P2_RTCS2 0xf252
+#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0
+#define F0900_P2_TMGBETAS2_EXP 0xf252000f
 
 /*P2_TMGTHRISE*/
-#define R0900_P2_TMGTHRISE  0xf253
-#define F0900_P2_TMGLOCK_THRISE  0xf25300ff
+#define R0900_P2_TMGTHRISE 0xf253
+#define F0900_P2_TMGLOCK_THRISE 0xf25300ff
 
 /*P2_TMGTHFALL*/
-#define R0900_P2_TMGTHFALL  0xf254
-#define F0900_P2_TMGLOCK_THFALL  0xf25400ff
+#define R0900_P2_TMGTHFALL 0xf254
+#define F0900_P2_TMGLOCK_THFALL 0xf25400ff
 
 /*P2_SFRUPRATIO*/
-#define R0900_P2_SFRUPRATIO  0xf255
-#define F0900_P2_SFR_UPRATIO  0xf25500ff
+#define R0900_P2_SFRUPRATIO 0xf255
+#define F0900_P2_SFR_UPRATIO 0xf25500ff
 
 /*P2_SFRLOWRATIO*/
-#define R0900_P2_SFRLOWRATIO  0xf256
-#define F0900_P2_SFR_LOWRATIO  0xf25600ff
+#define R0900_P2_SFRLOWRATIO 0xf256
+#define F0900_P2_SFR_LOWRATIO 0xf25600ff
 
 /*P2_KREFTMG*/
-#define R0900_P2_KREFTMG  0xf258
-#define F0900_P2_KREF_TMG  0xf25800ff
+#define R0900_P2_KREFTMG 0xf258
+#define F0900_P2_KREF_TMG 0xf25800ff
 
 /*P2_SFRSTEP*/
-#define R0900_P2_SFRSTEP  0xf259
-#define F0900_P2_SFR_SCANSTEP  0xf25900f0
-#define F0900_P2_SFR_CENTERSTEP  0xf259000f
+#define R0900_P2_SFRSTEP 0xf259
+#define F0900_P2_SFR_SCANSTEP 0xf25900f0
+#define F0900_P2_SFR_CENTERSTEP 0xf259000f
 
 /*P2_TMGCFG2*/
-#define R0900_P2_TMGCFG2  0xf25a
-#define F0900_P2_DIS_AUTOSAMP  0xf25a0008
-#define F0900_P2_SCANINIT_QUART  0xf25a0004
-#define F0900_P2_NOTMG_DVBS1DERAT  0xf25a0002
-#define F0900_P2_SFRRATIO_FINE  0xf25a0001
+#define R0900_P2_TMGCFG2 0xf25a
+#define F0900_P2_SFRRATIO_FINE 0xf25a0001
+
+/*P2_KREFTMG2*/
+#define R0900_P2_KREFTMG2 0xf25b
+#define F0900_P2_KREF_TMG2 0xf25b00ff
 
 /*P2_SFRINIT1*/
-#define R0900_P2_SFRINIT1  0xf25e
-#define F0900_P2_SFR_INIT1  0xf25e00ff
+#define R0900_P2_SFRINIT1 0xf25e
+#define F0900_P2_SFR_INIT1 0xf25e007f
 
 /*P2_SFRINIT0*/
-#define R0900_P2_SFRINIT0  0xf25f
-#define F0900_P2_SFR_INIT0  0xf25f00ff
+#define R0900_P2_SFRINIT0 0xf25f
+#define F0900_P2_SFR_INIT0 0xf25f00ff
 
 /*P2_SFRUP1*/
-#define R0900_P2_SFRUP1  0xf260
-#define F0900_P2_AUTO_GUP  0xf2600080
-#define F0900_P2_SYMB_FREQ_UP1  0xf260007f
+#define R0900_P2_SFRUP1 0xf260
+#define F0900_P2_AUTO_GUP 0xf2600080
+#define F0900_P2_SYMB_FREQ_UP1 0xf260007f
 
 /*P2_SFRUP0*/
-#define R0900_P2_SFRUP0  0xf261
-#define F0900_P2_SYMB_FREQ_UP0  0xf26100ff
+#define R0900_P2_SFRUP0 0xf261
+#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff
 
 /*P2_SFRLOW1*/
-#define R0900_P2_SFRLOW1  0xf262
-#define F0900_P2_AUTO_GLOW  0xf2620080
-#define F0900_P2_SYMB_FREQ_LOW1  0xf262007f
+#define R0900_P2_SFRLOW1 0xf262
+#define F0900_P2_AUTO_GLOW 0xf2620080
+#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f
 
 /*P2_SFRLOW0*/
-#define R0900_P2_SFRLOW0  0xf263
-#define F0900_P2_SYMB_FREQ_LOW0  0xf26300ff
+#define R0900_P2_SFRLOW0 0xf263
+#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff
 
 /*P2_SFR3*/
-#define R0900_P2_SFR3  0xf264
-#define F0900_P2_SYMB_FREQ3  0xf26400ff
+#define R0900_P2_SFR3 0xf264
+#define F0900_P2_SYMB_FREQ3 0xf26400ff
 
 /*P2_SFR2*/
-#define R0900_P2_SFR2  0xf265
-#define F0900_P2_SYMB_FREQ2  0xf26500ff
+#define R0900_P2_SFR2 0xf265
+#define F0900_P2_SYMB_FREQ2 0xf26500ff
 
 /*P2_SFR1*/
-#define R0900_P2_SFR1  0xf266
-#define F0900_P2_SYMB_FREQ1  0xf26600ff
+#define R0900_P2_SFR1 0xf266
+#define F0900_P2_SYMB_FREQ1 0xf26600ff
 
 /*P2_SFR0*/
-#define R0900_P2_SFR0  0xf267
-#define F0900_P2_SYMB_FREQ0  0xf26700ff
+#define R0900_P2_SFR0 0xf267
+#define F0900_P2_SYMB_FREQ0 0xf26700ff
 
 /*P2_TMGREG2*/
-#define R0900_P2_TMGREG2  0xf268
-#define F0900_P2_TMGREG2  0xf26800ff
+#define R0900_P2_TMGREG2 0xf268
+#define F0900_P2_TMGREG2 0xf26800ff
 
 /*P2_TMGREG1*/
-#define R0900_P2_TMGREG1  0xf269
-#define F0900_P2_TMGREG1  0xf26900ff
+#define R0900_P2_TMGREG1 0xf269
+#define F0900_P2_TMGREG1 0xf26900ff
 
 /*P2_TMGREG0*/
-#define R0900_P2_TMGREG0  0xf26a
-#define F0900_P2_TMGREG0  0xf26a00ff
+#define R0900_P2_TMGREG0 0xf26a
+#define F0900_P2_TMGREG0 0xf26a00ff
 
 /*P2_TMGLOCK1*/
-#define R0900_P2_TMGLOCK1  0xf26b
-#define F0900_P2_TMGLOCK_LEVEL1  0xf26b01ff
+#define R0900_P2_TMGLOCK1 0xf26b
+#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff
 
 /*P2_TMGLOCK0*/
-#define R0900_P2_TMGLOCK0  0xf26c
-#define F0900_P2_TMGLOCK_LEVEL0  0xf26c00ff
+#define R0900_P2_TMGLOCK0 0xf26c
+#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff
 
 /*P2_TMGOBS*/
-#define R0900_P2_TMGOBS  0xf26d
-#define F0900_P2_ROLLOFF_STATUS  0xf26d00c0
-#define F0900_P2_SCAN_SIGN  0xf26d0030
-#define F0900_P2_TMG_SCANNING  0xf26d0008
-#define F0900_P2_CHCENTERING_MODE  0xf26d0004
-#define F0900_P2_TMG_SCANFAIL  0xf26d0002
+#define R0900_P2_TMGOBS 0xf26d
+#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0
 
 /*P2_EQUALCFG*/
-#define R0900_P2_EQUALCFG  0xf26f
-#define F0900_P2_NOTMG_NEGALWAIT  0xf26f0080
-#define F0900_P2_EQUAL_ON  0xf26f0040
-#define F0900_P2_SEL_EQUALCOR  0xf26f0038
-#define F0900_P2_MU_EQUALDFE  0xf26f0007
+#define R0900_P2_EQUALCFG 0xf26f
+#define F0900_P2_EQUAL_ON 0xf26f0040
+#define F0900_P2_MU_EQUALDFE 0xf26f0007
 
 /*P2_EQUAI1*/
-#define R0900_P2_EQUAI1  0xf270
-#define F0900_P2_EQUA_ACCI1  0xf27001ff
+#define R0900_P2_EQUAI1 0xf270
+#define F0900_P2_EQUA_ACCI1 0xf27001ff
 
 /*P2_EQUAQ1*/
-#define R0900_P2_EQUAQ1  0xf271
-#define F0900_P2_EQUA_ACCQ1  0xf27101ff
+#define R0900_P2_EQUAQ1 0xf271
+#define F0900_P2_EQUA_ACCQ1 0xf27101ff
 
 /*P2_EQUAI2*/
-#define R0900_P2_EQUAI2  0xf272
-#define F0900_P2_EQUA_ACCI2  0xf27201ff
+#define R0900_P2_EQUAI2 0xf272
+#define F0900_P2_EQUA_ACCI2 0xf27201ff
 
 /*P2_EQUAQ2*/
-#define R0900_P2_EQUAQ2  0xf273
-#define F0900_P2_EQUA_ACCQ2  0xf27301ff
+#define R0900_P2_EQUAQ2 0xf273
+#define F0900_P2_EQUA_ACCQ2 0xf27301ff
 
 /*P2_EQUAI3*/
-#define R0900_P2_EQUAI3  0xf274
-#define F0900_P2_EQUA_ACCI3  0xf27401ff
+#define R0900_P2_EQUAI3 0xf274
+#define F0900_P2_EQUA_ACCI3 0xf27401ff
 
 /*P2_EQUAQ3*/
-#define R0900_P2_EQUAQ3  0xf275
-#define F0900_P2_EQUA_ACCQ3  0xf27501ff
+#define R0900_P2_EQUAQ3 0xf275
+#define F0900_P2_EQUA_ACCQ3 0xf27501ff
 
 /*P2_EQUAI4*/
-#define R0900_P2_EQUAI4  0xf276
-#define F0900_P2_EQUA_ACCI4  0xf27601ff
+#define R0900_P2_EQUAI4 0xf276
+#define F0900_P2_EQUA_ACCI4 0xf27601ff
 
 /*P2_EQUAQ4*/
-#define R0900_P2_EQUAQ4  0xf277
-#define F0900_P2_EQUA_ACCQ4  0xf27701ff
+#define R0900_P2_EQUAQ4 0xf277
+#define F0900_P2_EQUA_ACCQ4 0xf27701ff
 
 /*P2_EQUAI5*/
-#define R0900_P2_EQUAI5  0xf278
-#define F0900_P2_EQUA_ACCI5  0xf27801ff
+#define R0900_P2_EQUAI5 0xf278
+#define F0900_P2_EQUA_ACCI5 0xf27801ff
 
 /*P2_EQUAQ5*/
-#define R0900_P2_EQUAQ5  0xf279
-#define F0900_P2_EQUA_ACCQ5  0xf27901ff
+#define R0900_P2_EQUAQ5 0xf279
+#define F0900_P2_EQUA_ACCQ5 0xf27901ff
 
 /*P2_EQUAI6*/
-#define R0900_P2_EQUAI6  0xf27a
-#define F0900_P2_EQUA_ACCI6  0xf27a01ff
+#define R0900_P2_EQUAI6 0xf27a
+#define F0900_P2_EQUA_ACCI6 0xf27a01ff
 
 /*P2_EQUAQ6*/
-#define R0900_P2_EQUAQ6  0xf27b
-#define F0900_P2_EQUA_ACCQ6  0xf27b01ff
+#define R0900_P2_EQUAQ6 0xf27b
+#define F0900_P2_EQUA_ACCQ6 0xf27b01ff
 
 /*P2_EQUAI7*/
-#define R0900_P2_EQUAI7  0xf27c
-#define F0900_P2_EQUA_ACCI7  0xf27c01ff
+#define R0900_P2_EQUAI7 0xf27c
+#define F0900_P2_EQUA_ACCI7 0xf27c01ff
 
 /*P2_EQUAQ7*/
-#define R0900_P2_EQUAQ7  0xf27d
-#define F0900_P2_EQUA_ACCQ7  0xf27d01ff
+#define R0900_P2_EQUAQ7 0xf27d
+#define F0900_P2_EQUA_ACCQ7 0xf27d01ff
 
 /*P2_EQUAI8*/
-#define R0900_P2_EQUAI8  0xf27e
-#define F0900_P2_EQUA_ACCI8  0xf27e01ff
+#define R0900_P2_EQUAI8 0xf27e
+#define F0900_P2_EQUA_ACCI8 0xf27e01ff
 
 /*P2_EQUAQ8*/
-#define R0900_P2_EQUAQ8  0xf27f
-#define F0900_P2_EQUA_ACCQ8  0xf27f01ff
+#define R0900_P2_EQUAQ8 0xf27f
+#define F0900_P2_EQUA_ACCQ8 0xf27f01ff
 
 /*P2_NNOSDATAT1*/
-#define R0900_P2_NNOSDATAT1  0xf280
-#define F0900_P2_NOSDATAT_NORMED1  0xf28000ff
+#define R0900_P2_NNOSDATAT1 0xf280
+#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff
 
 /*P2_NNOSDATAT0*/
-#define R0900_P2_NNOSDATAT0  0xf281
-#define F0900_P2_NOSDATAT_NORMED0  0xf28100ff
+#define R0900_P2_NNOSDATAT0 0xf281
+#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff
 
 /*P2_NNOSDATA1*/
-#define R0900_P2_NNOSDATA1  0xf282
-#define F0900_P2_NOSDATA_NORMED1  0xf28200ff
+#define R0900_P2_NNOSDATA1 0xf282
+#define F0900_P2_NOSDATA_NORMED1 0xf28200ff
 
 /*P2_NNOSDATA0*/
-#define R0900_P2_NNOSDATA0  0xf283
-#define F0900_P2_NOSDATA_NORMED0  0xf28300ff
+#define R0900_P2_NNOSDATA0 0xf283
+#define F0900_P2_NOSDATA_NORMED0 0xf28300ff
 
 /*P2_NNOSPLHT1*/
-#define R0900_P2_NNOSPLHT1  0xf284
-#define F0900_P2_NOSPLHT_NORMED1  0xf28400ff
+#define R0900_P2_NNOSPLHT1 0xf284
+#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff
 
 /*P2_NNOSPLHT0*/
-#define R0900_P2_NNOSPLHT0  0xf285
-#define F0900_P2_NOSPLHT_NORMED0  0xf28500ff
+#define R0900_P2_NNOSPLHT0 0xf285
+#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff
 
 /*P2_NNOSPLH1*/
-#define R0900_P2_NNOSPLH1  0xf286
-#define F0900_P2_NOSPLH_NORMED1  0xf28600ff
+#define R0900_P2_NNOSPLH1 0xf286
+#define F0900_P2_NOSPLH_NORMED1 0xf28600ff
 
 /*P2_NNOSPLH0*/
-#define R0900_P2_NNOSPLH0  0xf287
-#define F0900_P2_NOSPLH_NORMED0  0xf28700ff
+#define R0900_P2_NNOSPLH0 0xf287
+#define F0900_P2_NOSPLH_NORMED0 0xf28700ff
 
 /*P2_NOSDATAT1*/
-#define R0900_P2_NOSDATAT1  0xf288
-#define F0900_P2_NOSDATAT_UNNORMED1  0xf28800ff
+#define R0900_P2_NOSDATAT1 0xf288
+#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff
 
 /*P2_NOSDATAT0*/
-#define R0900_P2_NOSDATAT0  0xf289
-#define F0900_P2_NOSDATAT_UNNORMED0  0xf28900ff
+#define R0900_P2_NOSDATAT0 0xf289
+#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff
 
 /*P2_NOSDATA1*/
-#define R0900_P2_NOSDATA1  0xf28a
-#define F0900_P2_NOSDATA_UNNORMED1  0xf28a00ff
+#define R0900_P2_NOSDATA1 0xf28a
+#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff
 
 /*P2_NOSDATA0*/
-#define R0900_P2_NOSDATA0  0xf28b
-#define F0900_P2_NOSDATA_UNNORMED0  0xf28b00ff
+#define R0900_P2_NOSDATA0 0xf28b
+#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff
 
 /*P2_NOSPLHT1*/
-#define R0900_P2_NOSPLHT1  0xf28c
-#define F0900_P2_NOSPLHT_UNNORMED1  0xf28c00ff
+#define R0900_P2_NOSPLHT1 0xf28c
+#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff
 
 /*P2_NOSPLHT0*/
-#define R0900_P2_NOSPLHT0  0xf28d
-#define F0900_P2_NOSPLHT_UNNORMED0  0xf28d00ff
+#define R0900_P2_NOSPLHT0 0xf28d
+#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff
 
 /*P2_NOSPLH1*/
-#define R0900_P2_NOSPLH1  0xf28e
-#define F0900_P2_NOSPLH_UNNORMED1  0xf28e00ff
+#define R0900_P2_NOSPLH1 0xf28e
+#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff
 
 /*P2_NOSPLH0*/
-#define R0900_P2_NOSPLH0  0xf28f
-#define F0900_P2_NOSPLH_UNNORMED0  0xf28f00ff
+#define R0900_P2_NOSPLH0 0xf28f
+#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff
 
 /*P2_CAR2CFG*/
-#define R0900_P2_CAR2CFG  0xf290
-#define F0900_P2_DESCRAMB_OFF  0xf2900080
-#define F0900_P2_PN4_SELECT  0xf2900040
-#define F0900_P2_CFR2_STOPDVBS1  0xf2900020
-#define F0900_P2_STOP_CFR2UPDATE  0xf2900010
-#define F0900_P2_STOP_NCO2UPDATE  0xf2900008
-#define F0900_P2_ROTA2ON  0xf2900004
-#define F0900_P2_PH_DET_ALGO2  0xf2900003
-
-/*P2_ACLC2*/
-#define R0900_P2_ACLC2  0xf291
-#define F0900_P2_CAR2_PUNCT_ADERAT  0xf2910040
-#define F0900_P2_CAR2_ALPHA_MANT  0xf2910030
-#define F0900_P2_CAR2_ALPHA_EXP  0xf291000f
-
-/*P2_BCLC2*/
-#define R0900_P2_BCLC2  0xf292
-#define F0900_P2_DVBS2_NIP  0xf2920080
-#define F0900_P2_CAR2_PUNCT_BDERAT  0xf2920040
-#define F0900_P2_CAR2_BETA_MANT  0xf2920030
-#define F0900_P2_CAR2_BETA_EXP  0xf292000f
+#define R0900_P2_CAR2CFG 0xf290
+#define F0900_P2_CARRIER3_DISABLE 0xf2900040
+#define F0900_P2_ROTA2ON 0xf2900004
+#define F0900_P2_PH_DET_ALGO2 0xf2900003
+
+/*P2_CFR2CFR1*/
+#define R0900_P2_CFR2CFR1 0xf291
+#define F0900_P2_CFR2TOCFR1_DVBS1 0xf29100c0
+#define F0900_P2_EN_S2CAR2CENTER 0xf2910020
+#define F0900_P2_DIS_BCHERRCFR2 0xf2910010
+#define F0900_P2_CFR2TOCFR1_BETA 0xf2910007
 
 /*P2_CFR22*/
-#define R0900_P2_CFR22  0xf293
-#define F0900_P2_CAR2_FREQ2  0xf29301ff
+#define R0900_P2_CFR22 0xf293
+#define F0900_P2_CAR2_FREQ2 0xf29301ff
 
 /*P2_CFR21*/
-#define R0900_P2_CFR21  0xf294
-#define F0900_P2_CAR2_FREQ1  0xf29400ff
+#define R0900_P2_CFR21 0xf294
+#define F0900_P2_CAR2_FREQ1 0xf29400ff
 
 /*P2_CFR20*/
-#define R0900_P2_CFR20  0xf295
-#define F0900_P2_CAR2_FREQ0  0xf29500ff
+#define R0900_P2_CFR20 0xf295
+#define F0900_P2_CAR2_FREQ0 0xf29500ff
 
 /*P2_ACLC2S2Q*/
-#define R0900_P2_ACLC2S2Q  0xf297
-#define F0900_P2_ENAB_SPSKSYMB  0xf2970080
-#define F0900_P2_CAR2S2_QADERAT  0xf2970040
-#define F0900_P2_CAR2S2_Q_ALPH_M  0xf2970030
-#define F0900_P2_CAR2S2_Q_ALPH_E  0xf297000f
+#define R0900_P2_ACLC2S2Q 0xf297
+#define F0900_P2_ENAB_SPSKSYMB 0xf2970080
+#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030
+#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f
 
 /*P2_ACLC2S28*/
-#define R0900_P2_ACLC2S28  0xf298
-#define F0900_P2_OLDI3Q_MODE  0xf2980080
-#define F0900_P2_CAR2S2_8ADERAT  0xf2980040
-#define F0900_P2_CAR2S2_8_ALPH_M  0xf2980030
-#define F0900_P2_CAR2S2_8_ALPH_E  0xf298000f
+#define R0900_P2_ACLC2S28 0xf298
+#define F0900_P2_OLDI3Q_MODE 0xf2980080
+#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030
+#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f
 
 /*P2_ACLC2S216A*/
-#define R0900_P2_ACLC2S216A  0xf299
-#define F0900_P2_CAR2S2_16ADERAT  0xf2990040
-#define F0900_P2_CAR2S2_16A_ALPH_M  0xf2990030
-#define F0900_P2_CAR2S2_16A_ALPH_E  0xf299000f
+#define R0900_P2_ACLC2S216A 0xf299
+#define F0900_P2_DIS_C3STOPA2 0xf2990080
+#define F0900_P2_CAR2S2_16ADERAT 0xf2990040
+#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030
+#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f
 
 /*P2_ACLC2S232A*/
-#define R0900_P2_ACLC2S232A  0xf29a
-#define F0900_P2_CAR2S2_32ADERAT  0xf29a0040
-#define F0900_P2_CAR2S2_32A_ALPH_M  0xf29a0030
-#define F0900_P2_CAR2S2_32A_ALPH_E  0xf29a000f
+#define R0900_P2_ACLC2S232A 0xf29a
+#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040
+#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030
+#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f
 
 /*P2_BCLC2S2Q*/
-#define R0900_P2_BCLC2S2Q  0xf29c
-#define F0900_P2_DVBS2S2Q_NIP  0xf29c0080
-#define F0900_P2_CAR2S2_QBDERAT  0xf29c0040
-#define F0900_P2_CAR2S2_Q_BETA_M  0xf29c0030
-#define F0900_P2_CAR2S2_Q_BETA_E  0xf29c000f
+#define R0900_P2_BCLC2S2Q 0xf29c
+#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030
+#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f
 
 /*P2_BCLC2S28*/
-#define R0900_P2_BCLC2S28  0xf29d
-#define F0900_P2_DVBS2S28_NIP  0xf29d0080
-#define F0900_P2_CAR2S2_8BDERAT  0xf29d0040
-#define F0900_P2_CAR2S2_8_BETA_M  0xf29d0030
-#define F0900_P2_CAR2S2_8_BETA_E  0xf29d000f
+#define R0900_P2_BCLC2S28 0xf29d
+#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030
+#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f
 
 /*P2_BCLC2S216A*/
-#define R0900_P2_BCLC2S216A  0xf29e
-#define F0900_P2_DVBS2S216A_NIP  0xf29e0080
-#define F0900_P2_CAR2S2_16BDERAT  0xf29e0040
-#define F0900_P2_CAR2S2_16A_BETA_M  0xf29e0030
-#define F0900_P2_CAR2S2_16A_BETA_E  0xf29e000f
+#define R0900_P2_BCLC2S216A 0xf29e
 
 /*P2_BCLC2S232A*/
-#define R0900_P2_BCLC2S232A  0xf29f
-#define F0900_P2_DVBS2S232A_NIP  0xf29f0080
-#define F0900_P2_CAR2S2_32BDERAT  0xf29f0040
-#define F0900_P2_CAR2S2_32A_BETA_M  0xf29f0030
-#define F0900_P2_CAR2S2_32A_BETA_E  0xf29f000f
+#define R0900_P2_BCLC2S232A 0xf29f
 
 /*P2_PLROOT2*/
-#define R0900_P2_PLROOT2  0xf2ac
-#define F0900_P2_SHORTFR_DISABLE  0xf2ac0080
-#define F0900_P2_LONGFR_DISABLE  0xf2ac0040
-#define F0900_P2_DUMMYPL_DISABLE  0xf2ac0020
-#define F0900_P2_SHORTFR_AVOID  0xf2ac0010
-#define F0900_P2_PLSCRAMB_MODE  0xf2ac000c
-#define F0900_P2_PLSCRAMB_ROOT2  0xf2ac0003
+#define R0900_P2_PLROOT2 0xf2ac
+#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c
+#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003
 
 /*P2_PLROOT1*/
-#define R0900_P2_PLROOT1  0xf2ad
-#define F0900_P2_PLSCRAMB_ROOT1  0xf2ad00ff
+#define R0900_P2_PLROOT1 0xf2ad
+#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff
 
 /*P2_PLROOT0*/
-#define R0900_P2_PLROOT0  0xf2ae
-#define F0900_P2_PLSCRAMB_ROOT0  0xf2ae00ff
+#define R0900_P2_PLROOT0 0xf2ae
+#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff
 
 /*P2_MODCODLST0*/
-#define R0900_P2_MODCODLST0  0xf2b0
-#define F0900_P2_EN_TOKEN31  0xf2b00080
-#define F0900_P2_SYNCTAG_SELECT  0xf2b00040
-#define F0900_P2_MODCODRQ_MODE  0xf2b00030
+#define R0900_P2_MODCODLST0 0xf2b0
 
 /*P2_MODCODLST1*/
-#define R0900_P2_MODCODLST1  0xf2b1
-#define F0900_P2_DIS_MODCOD29  0xf2b100f0
-#define F0900_P2_DIS_32PSK_9_10  0xf2b1000f
+#define R0900_P2_MODCODLST1 0xf2b1
+#define F0900_P2_DIS_MODCOD29 0xf2b100f0
+#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f
 
 /*P2_MODCODLST2*/
-#define R0900_P2_MODCODLST2  0xf2b2
-#define F0900_P2_DIS_32PSK_8_9  0xf2b200f0
-#define F0900_P2_DIS_32PSK_5_6  0xf2b2000f
+#define R0900_P2_MODCODLST2 0xf2b2
+#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0
+#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f
 
 /*P2_MODCODLST3*/
-#define R0900_P2_MODCODLST3  0xf2b3
-#define F0900_P2_DIS_32PSK_4_5  0xf2b300f0
-#define F0900_P2_DIS_32PSK_3_4  0xf2b3000f
+#define R0900_P2_MODCODLST3 0xf2b3
+#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0
+#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f
 
 /*P2_MODCODLST4*/
-#define R0900_P2_MODCODLST4  0xf2b4
-#define F0900_P2_DIS_16PSK_9_10  0xf2b400f0
-#define F0900_P2_DIS_16PSK_8_9  0xf2b4000f
+#define R0900_P2_MODCODLST4 0xf2b4
+#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0
+#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f
 
 /*P2_MODCODLST5*/
-#define R0900_P2_MODCODLST5  0xf2b5
-#define F0900_P2_DIS_16PSK_5_6  0xf2b500f0
-#define F0900_P2_DIS_16PSK_4_5  0xf2b5000f
+#define R0900_P2_MODCODLST5 0xf2b5
+#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0
+#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f
 
 /*P2_MODCODLST6*/
-#define R0900_P2_MODCODLST6  0xf2b6
-#define F0900_P2_DIS_16PSK_3_4  0xf2b600f0
-#define F0900_P2_DIS_16PSK_2_3  0xf2b6000f
+#define R0900_P2_MODCODLST6 0xf2b6
+#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0
+#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f
 
 /*P2_MODCODLST7*/
-#define R0900_P2_MODCODLST7  0xf2b7
-#define F0900_P2_DIS_8P_9_10  0xf2b700f0
-#define F0900_P2_DIS_8P_8_9  0xf2b7000f
+#define R0900_P2_MODCODLST7 0xf2b7
+#define F0900_P2_DIS_8P_9_10 0xf2b700f0
+#define F0900_P2_DIS_8P_8_9 0xf2b7000f
 
 /*P2_MODCODLST8*/
-#define R0900_P2_MODCODLST8  0xf2b8
-#define F0900_P2_DIS_8P_5_6  0xf2b800f0
-#define F0900_P2_DIS_8P_3_4  0xf2b8000f
+#define R0900_P2_MODCODLST8 0xf2b8
+#define F0900_P2_DIS_8P_5_6 0xf2b800f0
+#define F0900_P2_DIS_8P_3_4 0xf2b8000f
 
 /*P2_MODCODLST9*/
-#define R0900_P2_MODCODLST9  0xf2b9
-#define F0900_P2_DIS_8P_2_3  0xf2b900f0
-#define F0900_P2_DIS_8P_3_5  0xf2b9000f
+#define R0900_P2_MODCODLST9 0xf2b9
+#define F0900_P2_DIS_8P_2_3 0xf2b900f0
+#define F0900_P2_DIS_8P_3_5 0xf2b9000f
 
 /*P2_MODCODLSTA*/
-#define R0900_P2_MODCODLSTA  0xf2ba
-#define F0900_P2_DIS_QP_9_10  0xf2ba00f0
-#define F0900_P2_DIS_QP_8_9  0xf2ba000f
+#define R0900_P2_MODCODLSTA 0xf2ba
+#define F0900_P2_DIS_QP_9_10 0xf2ba00f0
+#define F0900_P2_DIS_QP_8_9 0xf2ba000f
 
 /*P2_MODCODLSTB*/
-#define R0900_P2_MODCODLSTB  0xf2bb
-#define F0900_P2_DIS_QP_5_6  0xf2bb00f0
-#define F0900_P2_DIS_QP_4_5  0xf2bb000f
+#define R0900_P2_MODCODLSTB 0xf2bb
+#define F0900_P2_DIS_QP_5_6 0xf2bb00f0
+#define F0900_P2_DIS_QP_4_5 0xf2bb000f
 
 /*P2_MODCODLSTC*/
-#define R0900_P2_MODCODLSTC  0xf2bc
-#define F0900_P2_DIS_QP_3_4  0xf2bc00f0
-#define F0900_P2_DIS_QP_2_3  0xf2bc000f
+#define R0900_P2_MODCODLSTC 0xf2bc
+#define F0900_P2_DIS_QP_3_4 0xf2bc00f0
+#define F0900_P2_DIS_QP_2_3 0xf2bc000f
 
 /*P2_MODCODLSTD*/
-#define R0900_P2_MODCODLSTD  0xf2bd
-#define F0900_P2_DIS_QP_3_5  0xf2bd00f0
-#define F0900_P2_DIS_QP_1_2  0xf2bd000f
+#define R0900_P2_MODCODLSTD 0xf2bd
+#define F0900_P2_DIS_QP_3_5 0xf2bd00f0
+#define F0900_P2_DIS_QP_1_2 0xf2bd000f
 
 /*P2_MODCODLSTE*/
-#define R0900_P2_MODCODLSTE  0xf2be
-#define F0900_P2_DIS_QP_2_5  0xf2be00f0
-#define F0900_P2_DIS_QP_1_3  0xf2be000f
+#define R0900_P2_MODCODLSTE 0xf2be
+#define F0900_P2_DIS_QP_2_5 0xf2be00f0
+#define F0900_P2_DIS_QP_1_3 0xf2be000f
 
 /*P2_MODCODLSTF*/
-#define R0900_P2_MODCODLSTF  0xf2bf
-#define F0900_P2_DIS_QP_1_4  0xf2bf00f0
-#define F0900_P2_DDEMOD_SET  0xf2bf0002
-#define F0900_P2_DDEMOD_MASK  0xf2bf0001
+#define R0900_P2_MODCODLSTF 0xf2bf
+#define F0900_P2_DIS_QP_1_4 0xf2bf00f0
+
+/*P2_GAUSSR0*/
+#define R0900_P2_GAUSSR0 0xf2c0
+#define F0900_P2_EN_CCIMODE 0xf2c00080
+#define F0900_P2_R0_GAUSSIEN 0xf2c0007f
+
+/*P2_CCIR0*/
+#define R0900_P2_CCIR0 0xf2c1
+#define F0900_P2_CCIDETECT_PLHONLY 0xf2c10080
+#define F0900_P2_R0_CCI 0xf2c1007f
+
+/*P2_CCIQUANT*/
+#define R0900_P2_CCIQUANT 0xf2c2
+#define F0900_P2_CCI_BETA 0xf2c200e0
+#define F0900_P2_CCI_QUANT 0xf2c2001f
+
+/*P2_CCITHRES*/
+#define R0900_P2_CCITHRES 0xf2c3
+#define F0900_P2_CCI_THRESHOLD 0xf2c300ff
+
+/*P2_CCIACC*/
+#define R0900_P2_CCIACC 0xf2c4
+#define F0900_P2_CCI_VALUE 0xf2c400ff
 
 /*P2_DMDRESCFG*/
-#define R0900_P2_DMDRESCFG  0xf2c6
-#define F0900_P2_DMDRES_RESET  0xf2c60080
-#define F0900_P2_DMDRES_NOISESQR  0xf2c60010
-#define F0900_P2_DMDRES_STRALL  0xf2c60008
-#define F0900_P2_DMDRES_NEWONLY  0xf2c60004
-#define F0900_P2_DMDRES_NOSTORE  0xf2c60002
-#define F0900_P2_DMDRES_AGC2MEM  0xf2c60001
+#define R0900_P2_DMDRESCFG 0xf2c6
+#define F0900_P2_DMDRES_RESET 0xf2c60080
+#define F0900_P2_DMDRES_STRALL 0xf2c60008
+#define F0900_P2_DMDRES_NEWONLY 0xf2c60004
+#define F0900_P2_DMDRES_NOSTORE 0xf2c60002
 
 /*P2_DMDRESADR*/
-#define R0900_P2_DMDRESADR  0xf2c7
-#define F0900_P2_SUSP_PREDCANAL  0xf2c70080
-#define F0900_P2_DMDRES_VALIDCFR  0xf2c70040
-#define F0900_P2_DMDRES_MEMFULL  0xf2c70030
-#define F0900_P2_DMDRES_RESNBR  0xf2c7000f
+#define R0900_P2_DMDRESADR 0xf2c7
+#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040
+#define F0900_P2_DMDRES_MEMFULL 0xf2c70030
+#define F0900_P2_DMDRES_RESNBR 0xf2c7000f
 
 /*P2_DMDRESDATA7*/
-#define R0900_P2_DMDRESDATA7  0xf2c8
-#define F0900_P2_DMDRES_DATA7  0xf2c800ff
+#define R0900_P2_DMDRESDATA7 0xf2c8
+#define F0900_P2_DMDRES_DATA7 0xf2c800ff
 
 /*P2_DMDRESDATA6*/
-#define R0900_P2_DMDRESDATA6  0xf2c9
-#define F0900_P2_DMDRES_DATA6  0xf2c900ff
+#define R0900_P2_DMDRESDATA6 0xf2c9
+#define F0900_P2_DMDRES_DATA6 0xf2c900ff
 
 /*P2_DMDRESDATA5*/
-#define R0900_P2_DMDRESDATA5  0xf2ca
-#define F0900_P2_DMDRES_DATA5  0xf2ca00ff
+#define R0900_P2_DMDRESDATA5 0xf2ca
+#define F0900_P2_DMDRES_DATA5 0xf2ca00ff
 
 /*P2_DMDRESDATA4*/
-#define R0900_P2_DMDRESDATA4  0xf2cb
-#define F0900_P2_DMDRES_DATA4  0xf2cb00ff
+#define R0900_P2_DMDRESDATA4 0xf2cb
+#define F0900_P2_DMDRES_DATA4 0xf2cb00ff
 
 /*P2_DMDRESDATA3*/
-#define R0900_P2_DMDRESDATA3  0xf2cc
-#define F0900_P2_DMDRES_DATA3  0xf2cc00ff
+#define R0900_P2_DMDRESDATA3 0xf2cc
+#define F0900_P2_DMDRES_DATA3 0xf2cc00ff
 
 /*P2_DMDRESDATA2*/
-#define R0900_P2_DMDRESDATA2  0xf2cd
-#define F0900_P2_DMDRES_DATA2  0xf2cd00ff
+#define R0900_P2_DMDRESDATA2 0xf2cd
+#define F0900_P2_DMDRES_DATA2 0xf2cd00ff
 
 /*P2_DMDRESDATA1*/
-#define R0900_P2_DMDRESDATA1  0xf2ce
-#define F0900_P2_DMDRES_DATA1  0xf2ce00ff
+#define R0900_P2_DMDRESDATA1 0xf2ce
+#define F0900_P2_DMDRES_DATA1 0xf2ce00ff
 
 /*P2_DMDRESDATA0*/
-#define R0900_P2_DMDRESDATA0  0xf2cf
-#define F0900_P2_DMDRES_DATA0  0xf2cf00ff
+#define R0900_P2_DMDRESDATA0 0xf2cf
+#define F0900_P2_DMDRES_DATA0 0xf2cf00ff
 
 /*P2_FFEI1*/
-#define R0900_P2_FFEI1  0xf2d0
-#define F0900_P2_FFE_ACCI1  0xf2d001ff
+#define R0900_P2_FFEI1 0xf2d0
+#define F0900_P2_FFE_ACCI1 0xf2d001ff
 
 /*P2_FFEQ1*/
-#define R0900_P2_FFEQ1  0xf2d1
-#define F0900_P2_FFE_ACCQ1  0xf2d101ff
+#define R0900_P2_FFEQ1 0xf2d1
+#define F0900_P2_FFE_ACCQ1 0xf2d101ff
 
 /*P2_FFEI2*/
-#define R0900_P2_FFEI2  0xf2d2
-#define F0900_P2_FFE_ACCI2  0xf2d201ff
+#define R0900_P2_FFEI2 0xf2d2
+#define F0900_P2_FFE_ACCI2 0xf2d201ff
 
 /*P2_FFEQ2*/
-#define R0900_P2_FFEQ2  0xf2d3
-#define F0900_P2_FFE_ACCQ2  0xf2d301ff
+#define R0900_P2_FFEQ2 0xf2d3
+#define F0900_P2_FFE_ACCQ2 0xf2d301ff
 
 /*P2_FFEI3*/
-#define R0900_P2_FFEI3  0xf2d4
-#define F0900_P2_FFE_ACCI3  0xf2d401ff
+#define R0900_P2_FFEI3 0xf2d4
+#define F0900_P2_FFE_ACCI3 0xf2d401ff
 
 /*P2_FFEQ3*/
-#define R0900_P2_FFEQ3  0xf2d5
-#define F0900_P2_FFE_ACCQ3  0xf2d501ff
+#define R0900_P2_FFEQ3 0xf2d5
+#define F0900_P2_FFE_ACCQ3 0xf2d501ff
 
 /*P2_FFEI4*/
-#define R0900_P2_FFEI4  0xf2d6
-#define F0900_P2_FFE_ACCI4  0xf2d601ff
+#define R0900_P2_FFEI4 0xf2d6
+#define F0900_P2_FFE_ACCI4 0xf2d601ff
 
 /*P2_FFEQ4*/
-#define R0900_P2_FFEQ4  0xf2d7
-#define F0900_P2_FFE_ACCQ4  0xf2d701ff
+#define R0900_P2_FFEQ4 0xf2d7
+#define F0900_P2_FFE_ACCQ4 0xf2d701ff
 
 /*P2_FFECFG*/
-#define R0900_P2_FFECFG  0xf2d8
-#define F0900_P2_EQUALFFE_ON  0xf2d80040
-#define F0900_P2_EQUAL_USEDSYMB  0xf2d80030
-#define F0900_P2_MU_EQUALFFE  0xf2d80007
+#define R0900_P2_FFECFG 0xf2d8
+#define F0900_P2_EQUALFFE_ON 0xf2d80040
+#define F0900_P2_MU_EQUALFFE 0xf2d80007
 
 /*P2_TNRCFG*/
-#define R0900_P2_TNRCFG  0xf2e0
-#define F0900_P2_TUN_ACKFAIL  0xf2e00080
-#define F0900_P2_TUN_TYPE  0xf2e00070
-#define F0900_P2_TUN_SECSTOP  0xf2e00008
-#define F0900_P2_TUN_VCOSRCH  0xf2e00004
-#define F0900_P2_TUN_MADDRESS  0xf2e00003
+#define R0900_P2_TNRCFG 0xf2e0
+#define F0900_P2_TUN_ACKFAIL 0xf2e00080
+#define F0900_P2_TUN_TYPE 0xf2e00070
+#define F0900_P2_TUN_SECSTOP 0xf2e00008
+#define F0900_P2_TUN_VCOSRCH 0xf2e00004
+#define F0900_P2_TUN_MADDRESS 0xf2e00003
 
 /*P2_TNRCFG2*/
-#define R0900_P2_TNRCFG2  0xf2e1
-#define F0900_P2_TUN_IQSWAP  0xf2e10080
-#define F0900_P2_STB6110_STEP2MHZ  0xf2e10040
-#define F0900_P2_STB6120_DBLI2C  0xf2e10020
-#define F0900_P2_DIS_FCCK  0xf2e10010
-#define F0900_P2_DIS_LPEN  0xf2e10008
-#define F0900_P2_DIS_BWCALC  0xf2e10004
-#define F0900_P2_SHORT_WAITSTATES  0xf2e10002
-#define F0900_P2_DIS_2BWAGC1  0xf2e10001
+#define R0900_P2_TNRCFG2 0xf2e1
+#define F0900_P2_TUN_IQSWAP 0xf2e10080
+#define F0900_P2_DIS_BWCALC 0xf2e10004
+#define F0900_P2_SHORT_WAITSTATES 0xf2e10002
 
 /*P2_TNRXTAL*/
-#define R0900_P2_TNRXTAL  0xf2e4
-#define F0900_P2_TUN_MCLKDECIMAL  0xf2e400e0
-#define F0900_P2_TUN_XTALFREQ  0xf2e4001f
+#define R0900_P2_TNRXTAL 0xf2e4
+#define F0900_P2_TUN_XTALFREQ 0xf2e4001f
 
 /*P2_TNRSTEPS*/
-#define R0900_P2_TNRSTEPS  0xf2e7
-#define F0900_P2_TUNER_BW1P6  0xf2e70080
-#define F0900_P2_BWINC_OFFSET  0xf2e70070
-#define F0900_P2_SOFTSTEP_RNG  0xf2e70008
-#define F0900_P2_TUN_BWOFFSET  0xf2e70107
+#define R0900_P2_TNRSTEPS 0xf2e7
+#define F0900_P2_TUNER_BW0P125 0xf2e70080
+#define F0900_P2_BWINC_OFFSET 0xf2e70170
+#define F0900_P2_SOFTSTEP_RNG 0xf2e70008
+#define F0900_P2_TUN_BWOFFSET 0xf2e70007
 
 /*P2_TNRGAIN*/
-#define R0900_P2_TNRGAIN  0xf2e8
-#define F0900_P2_TUN_KDIVEN  0xf2e800c0
-#define F0900_P2_STB6X00_OCK  0xf2e80030
-#define F0900_P2_TUN_GAIN  0xf2e8000f
+#define R0900_P2_TNRGAIN 0xf2e8
+#define F0900_P2_TUN_KDIVEN 0xf2e800c0
+#define F0900_P2_STB6X00_OCK 0xf2e80030
+#define F0900_P2_TUN_GAIN 0xf2e8000f
 
 /*P2_TNRRF1*/
-#define R0900_P2_TNRRF1  0xf2e9
-#define F0900_P2_TUN_RFFREQ2  0xf2e900ff
+#define R0900_P2_TNRRF1 0xf2e9
+#define F0900_P2_TUN_RFFREQ2 0xf2e900ff
 
 /*P2_TNRRF0*/
-#define R0900_P2_TNRRF0  0xf2ea
-#define F0900_P2_TUN_RFFREQ1  0xf2ea00ff
+#define R0900_P2_TNRRF0 0xf2ea
+#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff
 
 /*P2_TNRBW*/
-#define R0900_P2_TNRBW  0xf2eb
-#define F0900_P2_TUN_RFFREQ0  0xf2eb00c0
-#define F0900_P2_TUN_BW  0xf2eb003f
+#define R0900_P2_TNRBW 0xf2eb
+#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0
+#define F0900_P2_TUN_BW 0xf2eb003f
 
 /*P2_TNRADJ*/
-#define R0900_P2_TNRADJ  0xf2ec
-#define F0900_P2_STB61X0_RCLK  0xf2ec0080
-#define F0900_P2_STB61X0_CALTIME  0xf2ec0040
-#define F0900_P2_STB6X00_DLB  0xf2ec0038
-#define F0900_P2_STB6000_FCL  0xf2ec0007
+#define R0900_P2_TNRADJ 0xf2ec
+#define F0900_P2_STB61X0_CALTIME 0xf2ec0040
 
 /*P2_TNRCTL2*/
-#define R0900_P2_TNRCTL2  0xf2ed
-#define F0900_P2_STB61X0_LCP1_RCCKOFF  0xf2ed0080
-#define F0900_P2_STB61X0_LCP0  0xf2ed0040
-#define F0900_P2_STB61X0_XTOUT_RFOUTS  0xf2ed0020
-#define F0900_P2_STB61X0_XTON_MCKDV  0xf2ed0010
-#define F0900_P2_STB61X0_CALOFF_DCOFF  0xf2ed0008
-#define F0900_P2_STB6110_LPT  0xf2ed0004
-#define F0900_P2_STB6110_RX  0xf2ed0002
-#define F0900_P2_STB6110_SYN  0xf2ed0001
+#define R0900_P2_TNRCTL2 0xf2ed
+#define F0900_P2_STB61X0_RCCKOFF 0xf2ed0080
+#define F0900_P2_STB61X0_ICP_SDOFF 0xf2ed0040
+#define F0900_P2_STB61X0_DCLOOPOFF 0xf2ed0020
+#define F0900_P2_STB61X0_REFOUTSEL 0xf2ed0010
+#define F0900_P2_STB61X0_CALOFF 0xf2ed0008
+#define F0900_P2_STB6XX0_LPT_BEN 0xf2ed0004
+#define F0900_P2_STB6XX0_RX_OSCP 0xf2ed0002
+#define F0900_P2_STB6XX0_SYN 0xf2ed0001
 
 /*P2_TNRCFG3*/
-#define R0900_P2_TNRCFG3  0xf2ee
-#define F0900_P2_STB6120_DISCTRL1  0xf2ee0080
-#define F0900_P2_STB6120_INVORDER  0xf2ee0040
-#define F0900_P2_STB6120_ENCTRL6  0xf2ee0020
-#define F0900_P2_TUN_PLLFREQ  0xf2ee001c
-#define F0900_P2_TUN_I2CFREQ_MODE  0xf2ee0003
+#define R0900_P2_TNRCFG3 0xf2ee
+#define F0900_P2_TUN_PLLFREQ 0xf2ee001c
+#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003
 
 /*P2_TNRLAUNCH*/
-#define R0900_P2_TNRLAUNCH  0xf2f0
+#define R0900_P2_TNRLAUNCH 0xf2f0
 
 /*P2_TNRLD*/
-#define R0900_P2_TNRLD  0xf2f0
-#define F0900_P2_TUNLD_VCOING  0xf2f00080
-#define F0900_P2_TUN_REG1FAIL  0xf2f00040
-#define F0900_P2_TUN_REG2FAIL  0xf2f00020
-#define F0900_P2_TUN_REG3FAIL  0xf2f00010
-#define F0900_P2_TUN_REG4FAIL  0xf2f00008
-#define F0900_P2_TUN_REG5FAIL  0xf2f00004
-#define F0900_P2_TUN_BWING  0xf2f00002
-#define F0900_P2_TUN_LOCKED  0xf2f00001
+#define R0900_P2_TNRLD 0xf2f0
+#define F0900_P2_TUNLD_VCOING 0xf2f00080
+#define F0900_P2_TUN_REG1FAIL 0xf2f00040
+#define F0900_P2_TUN_REG2FAIL 0xf2f00020
+#define F0900_P2_TUN_REG3FAIL 0xf2f00010
+#define F0900_P2_TUN_REG4FAIL 0xf2f00008
+#define F0900_P2_TUN_REG5FAIL 0xf2f00004
+#define F0900_P2_TUN_BWING 0xf2f00002
+#define F0900_P2_TUN_LOCKED 0xf2f00001
 
 /*P2_TNROBSL*/
-#define R0900_P2_TNROBSL  0xf2f6
-#define F0900_P2_TUN_I2CABORTED  0xf2f60080
-#define F0900_P2_TUN_LPEN  0xf2f60040
-#define F0900_P2_TUN_FCCK  0xf2f60020
-#define F0900_P2_TUN_I2CLOCKED  0xf2f60010
-#define F0900_P2_TUN_PROGDONE  0xf2f6000c
-#define F0900_P2_TUN_RFRESTE1  0xf2f60003
+#define R0900_P2_TNROBSL 0xf2f6
+#define F0900_P2_TUN_I2CABORTED 0xf2f60080
+#define F0900_P2_TUN_LPEN 0xf2f60040
+#define F0900_P2_TUN_FCCK 0xf2f60020
+#define F0900_P2_TUN_I2CLOCKED 0xf2f60010
+#define F0900_P2_TUN_PROGDONE 0xf2f6000c
+#define F0900_P2_TUN_RFRESTE1 0xf2f60003
 
 /*P2_TNRRESTE*/
-#define R0900_P2_TNRRESTE  0xf2f7
-#define F0900_P2_TUN_RFRESTE0  0xf2f700ff
+#define R0900_P2_TNRRESTE 0xf2f7
+#define F0900_P2_TUN_RFRESTE0 0xf2f700ff
 
 /*P2_SMAPCOEF7*/
-#define R0900_P2_SMAPCOEF7  0xf300
-#define F0900_P2_DIS_QSCALE  0xf3000080
-#define F0900_P2_SMAPCOEF_Q_LLR12  0xf300017f
+#define R0900_P2_SMAPCOEF7 0xf300
+#define F0900_P2_DIS_QSCALE 0xf3000080
+#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f
 
 /*P2_SMAPCOEF6*/
-#define R0900_P2_SMAPCOEF6  0xf301
-#define F0900_P2_DIS_NEWSCALE  0xf3010008
-#define F0900_P2_ADJ_8PSKLLR1  0xf3010004
-#define F0900_P2_OLD_8PSKLLR1  0xf3010002
-#define F0900_P2_DIS_AB8PSK  0xf3010001
+#define R0900_P2_SMAPCOEF6 0xf301
+#define F0900_P2_ADJ_8PSKLLR1 0xf3010004
+#define F0900_P2_OLD_8PSKLLR1 0xf3010002
+#define F0900_P2_DIS_AB8PSK 0xf3010001
 
 /*P2_SMAPCOEF5*/
-#define R0900_P2_SMAPCOEF5  0xf302
-#define F0900_P2_DIS_8SCALE  0xf3020080
-#define F0900_P2_SMAPCOEF_8P_LLR23  0xf302017f
+#define R0900_P2_SMAPCOEF5 0xf302
+#define F0900_P2_DIS_8SCALE 0xf3020080
+#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f
+
+/*P2_NCO2MAX1*/
+#define R0900_P2_NCO2MAX1 0xf314
+#define F0900_P2_TETA2_MAXVABS1 0xf31400ff
+
+/*P2_NCO2MAX0*/
+#define R0900_P2_NCO2MAX0 0xf315
+#define F0900_P2_TETA2_MAXVABS0 0xf31500ff
+
+/*P2_NCO2FR1*/
+#define R0900_P2_NCO2FR1 0xf316
+#define F0900_P2_NCO2FINAL_ANGLE1 0xf31600ff
+
+/*P2_NCO2FR0*/
+#define R0900_P2_NCO2FR0 0xf317
+#define F0900_P2_NCO2FINAL_ANGLE0 0xf31700ff
+
+/*P2_CFR2AVRGE1*/
+#define R0900_P2_CFR2AVRGE1 0xf318
+#define F0900_P2_I2C_CFR2AVERAGE1 0xf31800ff
+
+/*P2_CFR2AVRGE0*/
+#define R0900_P2_CFR2AVRGE0 0xf319
+#define F0900_P2_I2C_CFR2AVERAGE0 0xf31900ff
 
 /*P2_DMDPLHSTAT*/
-#define R0900_P2_DMDPLHSTAT  0xf320
-#define F0900_P2_PLH_STATISTIC  0xf32000ff
+#define R0900_P2_DMDPLHSTAT 0xf320
+#define F0900_P2_PLH_STATISTIC 0xf32000ff
 
 /*P2_LOCKTIME3*/
-#define R0900_P2_LOCKTIME3  0xf322
-#define F0900_P2_DEMOD_LOCKTIME3  0xf32200ff
+#define R0900_P2_LOCKTIME3 0xf322
+#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff
 
 /*P2_LOCKTIME2*/
-#define R0900_P2_LOCKTIME2  0xf323
-#define F0900_P2_DEMOD_LOCKTIME2  0xf32300ff
+#define R0900_P2_LOCKTIME2 0xf323
+#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff
 
 /*P2_LOCKTIME1*/
-#define R0900_P2_LOCKTIME1  0xf324
-#define F0900_P2_DEMOD_LOCKTIME1  0xf32400ff
+#define R0900_P2_LOCKTIME1 0xf324
+#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff
 
 /*P2_LOCKTIME0*/
-#define R0900_P2_LOCKTIME0  0xf325
-#define F0900_P2_DEMOD_LOCKTIME0  0xf32500ff
+#define R0900_P2_LOCKTIME0 0xf325
+#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff
 
 /*P2_VITSCALE*/
-#define R0900_P2_VITSCALE  0xf332
-#define F0900_P2_NVTH_NOSRANGE  0xf3320080
-#define F0900_P2_VERROR_MAXMODE  0xf3320040
-#define F0900_P2_KDIV_MODE  0xf3320030
-#define F0900_P2_NSLOWSN_LOCKED  0xf3320008
-#define F0900_P2_DELOCK_PRFLOSS  0xf3320004
-#define F0900_P2_DIS_RSFLOCK  0xf3320002
+#define R0900_P2_VITSCALE 0xf332
+#define F0900_P2_NVTH_NOSRANGE 0xf3320080
+#define F0900_P2_VERROR_MAXMODE 0xf3320040
+#define F0900_P2_NSLOWSN_LOCKED 0xf3320008
+#define F0900_P2_DIS_RSFLOCK 0xf3320002
 
 /*P2_FECM*/
-#define R0900_P2_FECM  0xf333
-#define F0900_P2_DSS_DVB  0xf3330080
-#define F0900_P2_DEMOD_BYPASS  0xf3330040
-#define F0900_P2_CMP_SLOWMODE  0xf3330020
-#define F0900_P2_DSS_SRCH  0xf3330010
-#define F0900_P2_DIFF_MODEVIT  0xf3330004
-#define F0900_P2_SYNCVIT  0xf3330002
-#define F0900_P2_IQINV  0xf3330001
+#define R0900_P2_FECM 0xf333
+#define F0900_P2_DSS_DVB 0xf3330080
+#define F0900_P2_DSS_SRCH 0xf3330010
+#define F0900_P2_SYNCVIT 0xf3330002
+#define F0900_P2_IQINV 0xf3330001
 
 /*P2_VTH12*/
-#define R0900_P2_VTH12  0xf334
-#define F0900_P2_VTH12  0xf33400ff
+#define R0900_P2_VTH12 0xf334
+#define F0900_P2_VTH12 0xf33400ff
 
 /*P2_VTH23*/
-#define R0900_P2_VTH23  0xf335
-#define F0900_P2_VTH23  0xf33500ff
+#define R0900_P2_VTH23 0xf335
+#define F0900_P2_VTH23 0xf33500ff
 
 /*P2_VTH34*/
-#define R0900_P2_VTH34  0xf336
-#define F0900_P2_VTH34  0xf33600ff
+#define R0900_P2_VTH34 0xf336
+#define F0900_P2_VTH34 0xf33600ff
 
 /*P2_VTH56*/
-#define R0900_P2_VTH56  0xf337
-#define F0900_P2_VTH56  0xf33700ff
+#define R0900_P2_VTH56 0xf337
+#define F0900_P2_VTH56 0xf33700ff
 
 /*P2_VTH67*/
-#define R0900_P2_VTH67  0xf338
-#define F0900_P2_VTH67  0xf33800ff
+#define R0900_P2_VTH67 0xf338
+#define F0900_P2_VTH67 0xf33800ff
 
 /*P2_VTH78*/
-#define R0900_P2_VTH78  0xf339
-#define F0900_P2_VTH78  0xf33900ff
+#define R0900_P2_VTH78 0xf339
+#define F0900_P2_VTH78 0xf33900ff
 
 /*P2_VITCURPUN*/
-#define R0900_P2_VITCURPUN  0xf33a
-#define F0900_P2_VIT_MAPPING  0xf33a00e0
-#define F0900_P2_VIT_CURPUN  0xf33a001f
+#define R0900_P2_VITCURPUN 0xf33a
+#define F0900_P2_VIT_CURPUN 0xf33a001f
 
 /*P2_VERROR*/
-#define R0900_P2_VERROR  0xf33b
-#define F0900_P2_REGERR_VIT  0xf33b00ff
+#define R0900_P2_VERROR 0xf33b
+#define F0900_P2_REGERR_VIT 0xf33b00ff
 
 /*P2_PRVIT*/
-#define R0900_P2_PRVIT  0xf33c
-#define F0900_P2_DIS_VTHLOCK  0xf33c0040
-#define F0900_P2_E7_8VIT  0xf33c0020
-#define F0900_P2_E6_7VIT  0xf33c0010
-#define F0900_P2_E5_6VIT  0xf33c0008
-#define F0900_P2_E3_4VIT  0xf33c0004
-#define F0900_P2_E2_3VIT  0xf33c0002
-#define F0900_P2_E1_2VIT  0xf33c0001
+#define R0900_P2_PRVIT 0xf33c
+#define F0900_P2_DIS_VTHLOCK 0xf33c0040
+#define F0900_P2_E7_8VIT 0xf33c0020
+#define F0900_P2_E6_7VIT 0xf33c0010
+#define F0900_P2_E5_6VIT 0xf33c0008
+#define F0900_P2_E3_4VIT 0xf33c0004
+#define F0900_P2_E2_3VIT 0xf33c0002
+#define F0900_P2_E1_2VIT 0xf33c0001
 
 /*P2_VAVSRVIT*/
-#define R0900_P2_VAVSRVIT  0xf33d
-#define F0900_P2_AMVIT  0xf33d0080
-#define F0900_P2_FROZENVIT  0xf33d0040
-#define F0900_P2_SNVIT  0xf33d0030
-#define F0900_P2_TOVVIT  0xf33d000c
-#define F0900_P2_HYPVIT  0xf33d0003
+#define R0900_P2_VAVSRVIT 0xf33d
+#define F0900_P2_AMVIT 0xf33d0080
+#define F0900_P2_FROZENVIT 0xf33d0040
+#define F0900_P2_SNVIT 0xf33d0030
+#define F0900_P2_TOVVIT 0xf33d000c
+#define F0900_P2_HYPVIT 0xf33d0003
 
 /*P2_VSTATUSVIT*/
-#define R0900_P2_VSTATUSVIT  0xf33e
-#define F0900_P2_VITERBI_ON  0xf33e0080
-#define F0900_P2_END_LOOPVIT  0xf33e0040
-#define F0900_P2_VITERBI_DEPRF  0xf33e0020
-#define F0900_P2_PRFVIT  0xf33e0010
-#define F0900_P2_LOCKEDVIT  0xf33e0008
-#define F0900_P2_VITERBI_DELOCK  0xf33e0004
-#define F0900_P2_VIT_DEMODSEL  0xf33e0002
-#define F0900_P2_VITERBI_COMPOUT  0xf33e0001
+#define R0900_P2_VSTATUSVIT 0xf33e
+#define F0900_P2_PRFVIT 0xf33e0010
+#define F0900_P2_LOCKEDVIT 0xf33e0008
 
 /*P2_VTHINUSE*/
-#define R0900_P2_VTHINUSE  0xf33f
-#define F0900_P2_VIT_INUSE  0xf33f00ff
+#define R0900_P2_VTHINUSE 0xf33f
+#define F0900_P2_VIT_INUSE 0xf33f00ff
 
 /*P2_KDIV12*/
-#define R0900_P2_KDIV12  0xf340
-#define F0900_P2_KDIV12_MANUAL  0xf3400080
-#define F0900_P2_K_DIVIDER_12  0xf340007f
+#define R0900_P2_KDIV12 0xf340
+#define F0900_P2_K_DIVIDER_12 0xf340007f
 
 /*P2_KDIV23*/
-#define R0900_P2_KDIV23  0xf341
-#define F0900_P2_KDIV23_MANUAL  0xf3410080
-#define F0900_P2_K_DIVIDER_23  0xf341007f
+#define R0900_P2_KDIV23 0xf341
+#define F0900_P2_K_DIVIDER_23 0xf341007f
 
 /*P2_KDIV34*/
-#define R0900_P2_KDIV34  0xf342
-#define F0900_P2_KDIV34_MANUAL  0xf3420080
-#define F0900_P2_K_DIVIDER_34  0xf342007f
+#define R0900_P2_KDIV34 0xf342
+#define F0900_P2_K_DIVIDER_34 0xf342007f
 
 /*P2_KDIV56*/
-#define R0900_P2_KDIV56  0xf343
-#define F0900_P2_KDIV56_MANUAL  0xf3430080
-#define F0900_P2_K_DIVIDER_56  0xf343007f
+#define R0900_P2_KDIV56 0xf343
+#define F0900_P2_K_DIVIDER_56 0xf343007f
 
 /*P2_KDIV67*/
-#define R0900_P2_KDIV67  0xf344
-#define F0900_P2_KDIV67_MANUAL  0xf3440080
-#define F0900_P2_K_DIVIDER_67  0xf344007f
+#define R0900_P2_KDIV67 0xf344
+#define F0900_P2_K_DIVIDER_67 0xf344007f
 
 /*P2_KDIV78*/
-#define R0900_P2_KDIV78  0xf345
-#define F0900_P2_KDIV78_MANUAL  0xf3450080
-#define F0900_P2_K_DIVIDER_78  0xf345007f
+#define R0900_P2_KDIV78 0xf345
+#define F0900_P2_K_DIVIDER_78 0xf345007f
 
 /*P2_PDELCTRL1*/
-#define R0900_P2_PDELCTRL1  0xf350
-#define F0900_P2_INV_MISMASK  0xf3500080
-#define F0900_P2_FORCE_ACCEPTED  0xf3500040
-#define F0900_P2_FILTER_EN  0xf3500020
-#define F0900_P2_FORCE_PKTDELINUSE  0xf3500010
-#define F0900_P2_HYSTEN  0xf3500008
-#define F0900_P2_HYSTSWRST  0xf3500004
-#define F0900_P2_EN_MIS00  0xf3500002
-#define F0900_P2_ALGOSWRST  0xf3500001
+#define R0900_P2_PDELCTRL1 0xf350
+#define F0900_P2_INV_MISMASK 0xf3500080
+#define F0900_P2_FILTER_EN 0xf3500020
+#define F0900_P2_EN_MIS00 0xf3500002
+#define F0900_P2_ALGOSWRST 0xf3500001
 
 /*P2_PDELCTRL2*/
-#define R0900_P2_PDELCTRL2  0xf351
-#define F0900_P2_FORCE_CONTINUOUS  0xf3510080
-#define F0900_P2_RESET_UPKO_COUNT  0xf3510040
-#define F0900_P2_USER_PKTDELIN_NB  0xf3510020
-#define F0900_P2_FORCE_LOCKED  0xf3510010
-#define F0900_P2_DATA_UNBBSCRAM  0xf3510008
-#define F0900_P2_FORCE_LONGPKT  0xf3510004
-#define F0900_P2_FRAME_MODE  0xf3510002
+#define R0900_P2_PDELCTRL2 0xf351
+#define F0900_P2_RESET_UPKO_COUNT 0xf3510040
+#define F0900_P2_FRAME_MODE 0xf3510002
+#define F0900_P2_NOBCHERRFLG_USE 0xf3510001
 
 /*P2_HYSTTHRESH*/
-#define R0900_P2_HYSTTHRESH  0xf354
-#define F0900_P2_UNLCK_THRESH  0xf35400f0
-#define F0900_P2_DELIN_LCK_THRESH  0xf354000f
+#define R0900_P2_HYSTTHRESH 0xf354
+#define F0900_P2_UNLCK_THRESH 0xf35400f0
+#define F0900_P2_DELIN_LCK_THRESH 0xf354000f
 
 /*P2_ISIENTRY*/
-#define R0900_P2_ISIENTRY  0xf35e
-#define F0900_P2_ISI_ENTRY  0xf35e00ff
+#define R0900_P2_ISIENTRY 0xf35e
+#define F0900_P2_ISI_ENTRY 0xf35e00ff
 
 /*P2_ISIBITENA*/
-#define R0900_P2_ISIBITENA  0xf35f
-#define F0900_P2_ISI_BIT_EN  0xf35f00ff
+#define R0900_P2_ISIBITENA 0xf35f
+#define F0900_P2_ISI_BIT_EN 0xf35f00ff
 
 /*P2_MATSTR1*/
-#define R0900_P2_MATSTR1  0xf360
-#define F0900_P2_MATYPE_CURRENT1  0xf36000ff
+#define R0900_P2_MATSTR1 0xf360
+#define F0900_P2_MATYPE_CURRENT1 0xf36000ff
 
 /*P2_MATSTR0*/
-#define R0900_P2_MATSTR0  0xf361
-#define F0900_P2_MATYPE_CURRENT0  0xf36100ff
+#define R0900_P2_MATSTR0 0xf361
+#define F0900_P2_MATYPE_CURRENT0 0xf36100ff
 
 /*P2_UPLSTR1*/
-#define R0900_P2_UPLSTR1  0xf362
-#define F0900_P2_UPL_CURRENT1  0xf36200ff
+#define R0900_P2_UPLSTR1 0xf362
+#define F0900_P2_UPL_CURRENT1 0xf36200ff
 
 /*P2_UPLSTR0*/
-#define R0900_P2_UPLSTR0  0xf363
-#define F0900_P2_UPL_CURRENT0  0xf36300ff
+#define R0900_P2_UPLSTR0 0xf363
+#define F0900_P2_UPL_CURRENT0 0xf36300ff
 
 /*P2_DFLSTR1*/
-#define R0900_P2_DFLSTR1  0xf364
-#define F0900_P2_DFL_CURRENT1  0xf36400ff
+#define R0900_P2_DFLSTR1 0xf364
+#define F0900_P2_DFL_CURRENT1 0xf36400ff
 
 /*P2_DFLSTR0*/
-#define R0900_P2_DFLSTR0  0xf365
-#define F0900_P2_DFL_CURRENT0  0xf36500ff
+#define R0900_P2_DFLSTR0 0xf365
+#define F0900_P2_DFL_CURRENT0 0xf36500ff
 
 /*P2_SYNCSTR*/
-#define R0900_P2_SYNCSTR  0xf366
-#define F0900_P2_SYNC_CURRENT  0xf36600ff
+#define R0900_P2_SYNCSTR 0xf366
+#define F0900_P2_SYNC_CURRENT 0xf36600ff
 
 /*P2_SYNCDSTR1*/
-#define R0900_P2_SYNCDSTR1  0xf367
-#define F0900_P2_SYNCD_CURRENT1  0xf36700ff
+#define R0900_P2_SYNCDSTR1 0xf367
+#define F0900_P2_SYNCD_CURRENT1 0xf36700ff
 
 /*P2_SYNCDSTR0*/
-#define R0900_P2_SYNCDSTR0  0xf368
-#define F0900_P2_SYNCD_CURRENT0  0xf36800ff
+#define R0900_P2_SYNCDSTR0 0xf368
+#define F0900_P2_SYNCD_CURRENT0 0xf36800ff
 
 /*P2_PDELSTATUS1*/
-#define R0900_P2_PDELSTATUS1  0xf369
-#define F0900_P2_PKTDELIN_DELOCK  0xf3690080
-#define F0900_P2_SYNCDUPDFL_BADDFL  0xf3690040
-#define F0900_P2_CONTINUOUS_STREAM  0xf3690020
-#define F0900_P2_UNACCEPTED_STREAM  0xf3690010
-#define F0900_P2_BCH_ERROR_FLAG  0xf3690008
-#define F0900_P2_BBHCRCKO  0xf3690004
-#define F0900_P2_PKTDELIN_LOCK  0xf3690002
-#define F0900_P2_FIRST_LOCK  0xf3690001
+#define R0900_P2_PDELSTATUS1 0xf369
+#define F0900_P2_PKTDELIN_DELOCK 0xf3690080
+#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040
+#define F0900_P2_CONTINUOUS_STREAM 0xf3690020
+#define F0900_P2_UNACCEPTED_STREAM 0xf3690010
+#define F0900_P2_BCH_ERROR_FLAG 0xf3690008
+#define F0900_P2_PKTDELIN_LOCK 0xf3690002
+#define F0900_P2_FIRST_LOCK 0xf3690001
 
 /*P2_PDELSTATUS2*/
-#define R0900_P2_PDELSTATUS2  0xf36a
-#define F0900_P2_PKTDEL_DEMODSEL  0xf36a0080
-#define F0900_P2_FRAME_MODCOD  0xf36a007c
-#define F0900_P2_FRAME_TYPE  0xf36a0003
+#define R0900_P2_PDELSTATUS2 0xf36a
+#define F0900_P2_FRAME_MODCOD 0xf36a007c
+#define F0900_P2_FRAME_TYPE 0xf36a0003
 
 /*P2_BBFCRCKO1*/
-#define R0900_P2_BBFCRCKO1  0xf36b
-#define F0900_P2_BBHCRC_KOCNT1  0xf36b00ff
+#define R0900_P2_BBFCRCKO1 0xf36b
+#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff
 
 /*P2_BBFCRCKO0*/
-#define R0900_P2_BBFCRCKO0  0xf36c
-#define F0900_P2_BBHCRC_KOCNT0  0xf36c00ff
+#define R0900_P2_BBFCRCKO0 0xf36c
+#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff
 
 /*P2_UPCRCKO1*/
-#define R0900_P2_UPCRCKO1  0xf36d
-#define F0900_P2_PKTCRC_KOCNT1  0xf36d00ff
+#define R0900_P2_UPCRCKO1 0xf36d
+#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff
 
 /*P2_UPCRCKO0*/
-#define R0900_P2_UPCRCKO0  0xf36e
-#define F0900_P2_PKTCRC_KOCNT0  0xf36e00ff
+#define R0900_P2_UPCRCKO0 0xf36e
+#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff
+
+/*P2_PDELCTRL3*/
+#define R0900_P2_PDELCTRL3 0xf36f
+#define F0900_P2_PKTDEL_CONTFAIL 0xf36f0080
+#define F0900_P2_NOFIFO_BCHERR 0xf36f0020
 
 /*P2_TSSTATEM*/
-#define R0900_P2_TSSTATEM  0xf370
-#define F0900_P2_TSDIL_ON  0xf3700080
-#define F0900_P2_TSSKIPRS_ON  0xf3700040
-#define F0900_P2_TSRS_ON  0xf3700020
-#define F0900_P2_TSDESCRAMB_ON  0xf3700010
-#define F0900_P2_TSFRAME_MODE  0xf3700008
-#define F0900_P2_TS_DISABLE  0xf3700004
-#define F0900_P2_TSACM_MODE  0xf3700002
-#define F0900_P2_TSOUT_NOSYNC  0xf3700001
+#define R0900_P2_TSSTATEM 0xf370
+#define F0900_P2_TSDIL_ON 0xf3700080
+#define F0900_P2_TSRS_ON 0xf3700020
+#define F0900_P2_TSDESCRAMB_ON 0xf3700010
+#define F0900_P2_TSFRAME_MODE 0xf3700008
+#define F0900_P2_TS_DISABLE 0xf3700004
+#define F0900_P2_TSOUT_NOSYNC 0xf3700001
 
 /*P2_TSCFGH*/
-#define R0900_P2_TSCFGH  0xf372
-#define F0900_P2_TSFIFO_DVBCI  0xf3720080
-#define F0900_P2_TSFIFO_SERIAL  0xf3720040
-#define F0900_P2_TSFIFO_TEIUPDATE  0xf3720020
-#define F0900_P2_TSFIFO_DUTY50  0xf3720010
-#define F0900_P2_TSFIFO_HSGNLOUT  0xf3720008
-#define F0900_P2_TSFIFO_ERRMODE  0xf3720006
-#define F0900_P2_RST_HWARE  0xf3720001
+#define R0900_P2_TSCFGH 0xf372
+#define F0900_P2_TSFIFO_DVBCI 0xf3720080
+#define F0900_P2_TSFIFO_SERIAL 0xf3720040
+#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020
+#define F0900_P2_TSFIFO_DUTY50 0xf3720010
+#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008
+#define F0900_P2_TSFIFO_ERRMODE 0xf3720006
+#define F0900_P2_RST_HWARE 0xf3720001
 
 /*P2_TSCFGM*/
-#define R0900_P2_TSCFGM  0xf373
-#define F0900_P2_TSFIFO_MANSPEED  0xf37300c0
-#define F0900_P2_TSFIFO_PERMDATA  0xf3730020
-#define F0900_P2_TSFIFO_NONEWSGNL  0xf3730010
-#define F0900_P2_TSFIFO_BITSPEED  0xf3730008
-#define F0900_P2_NPD_SPECDVBS2  0xf3730004
-#define F0900_P2_TSFIFO_STOPCKDIS  0xf3730002
-#define F0900_P2_TSFIFO_INVDATA  0xf3730001
+#define R0900_P2_TSCFGM 0xf373
+#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0
+#define F0900_P2_TSFIFO_PERMDATA 0xf3730020
+#define F0900_P2_TSFIFO_DPUNACT 0xf3730002
+#define F0900_P2_TSFIFO_INVDATA 0xf3730001
 
 /*P2_TSCFGL*/
-#define R0900_P2_TSCFGL  0xf374
-#define F0900_P2_TSFIFO_BCLKDEL1CK  0xf37400c0
-#define F0900_P2_BCHERROR_MODE  0xf3740030
-#define F0900_P2_TSFIFO_NSGNL2DATA  0xf3740008
-#define F0900_P2_TSFIFO_EMBINDVB  0xf3740004
-#define F0900_P2_TSFIFO_DPUNACT  0xf3740002
-#define F0900_P2_TSFIFO_NPDOFF  0xf3740001
+#define R0900_P2_TSCFGL 0xf374
+#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0
+#define F0900_P2_BCHERROR_MODE 0xf3740030
+#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008
+#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004
+#define F0900_P2_TSFIFO_BITSPEED 0xf3740003
 
 /*P2_TSINSDELH*/
-#define R0900_P2_TSINSDELH  0xf376
-#define F0900_P2_TSDEL_SYNCBYTE  0xf3760080
-#define F0900_P2_TSDEL_XXHEADER  0xf3760040
-#define F0900_P2_TSDEL_BBHEADER  0xf3760020
-#define F0900_P2_TSDEL_DATAFIELD  0xf3760010
-#define F0900_P2_TSINSDEL_ISCR  0xf3760008
-#define F0900_P2_TSINSDEL_NPD  0xf3760004
-#define F0900_P2_TSINSDEL_RSPARITY  0xf3760002
-#define F0900_P2_TSINSDEL_CRC8  0xf3760001
+#define R0900_P2_TSINSDELH 0xf376
+#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080
+#define F0900_P2_TSDEL_XXHEADER 0xf3760040
+#define F0900_P2_TSDEL_BBHEADER 0xf3760020
+#define F0900_P2_TSDEL_DATAFIELD 0xf3760010
+#define F0900_P2_TSINSDEL_ISCR 0xf3760008
+#define F0900_P2_TSINSDEL_NPD 0xf3760004
+#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002
+#define F0900_P2_TSINSDEL_CRC8 0xf3760001
+
+/*P2_TSDIVN*/
+#define R0900_P2_TSDIVN 0xf379
+#define F0900_P2_TSFIFO_SPEEDMODE 0xf37900c0
+
+/*P2_TSCFG4*/
+#define R0900_P2_TSCFG4 0xf37a
+#define F0900_P2_TSFIFO_TSSPEEDMODE 0xf37a00c0
 
 /*P2_TSSPEED*/
-#define R0900_P2_TSSPEED  0xf380
-#define F0900_P2_TSFIFO_OUTSPEED  0xf38000ff
+#define R0900_P2_TSSPEED 0xf380
+#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff
 
 /*P2_TSSTATUS*/
-#define R0900_P2_TSSTATUS  0xf381
-#define F0900_P2_TSFIFO_LINEOK  0xf3810080
-#define F0900_P2_TSFIFO_ERROR  0xf3810040
-#define F0900_P2_TSFIFO_DATA7  0xf3810020
-#define F0900_P2_TSFIFO_NOSYNC  0xf3810010
-#define F0900_P2_ISCR_INITIALIZED  0xf3810008
-#define F0900_P2_ISCR_UPDATED  0xf3810004
-#define F0900_P2_SOFFIFO_UNREGUL  0xf3810002
-#define F0900_P2_DIL_READY  0xf3810001
+#define R0900_P2_TSSTATUS 0xf381
+#define F0900_P2_TSFIFO_LINEOK 0xf3810080
+#define F0900_P2_TSFIFO_ERROR 0xf3810040
+#define F0900_P2_DIL_READY 0xf3810001
 
 /*P2_TSSTATUS2*/
-#define R0900_P2_TSSTATUS2  0xf382
-#define F0900_P2_TSFIFO_DEMODSEL  0xf3820080
-#define F0900_P2_TSFIFOSPEED_STORE  0xf3820040
-#define F0900_P2_DILXX_RESET  0xf3820020
-#define F0900_P2_TSSERIAL_IMPOS  0xf3820010
-#define F0900_P2_TSFIFO_LINENOK  0xf3820008
-#define F0900_P2_BITSPEED_EVENT  0xf3820004
-#define F0900_P2_SCRAMBDETECT  0xf3820002
-#define F0900_P2_ULDTV67_FALSELOCK  0xf3820001
+#define R0900_P2_TSSTATUS2 0xf382
+#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080
+#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040
+#define F0900_P2_DILXX_RESET 0xf3820020
+#define F0900_P2_TSSERIAL_IMPOS 0xf3820010
+#define F0900_P2_SCRAMBDETECT 0xf3820002
 
 /*P2_TSBITRATE1*/
-#define R0900_P2_TSBITRATE1  0xf383
-#define F0900_P2_TSFIFO_BITRATE1  0xf38300ff
+#define R0900_P2_TSBITRATE1 0xf383
+#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff
 
 /*P2_TSBITRATE0*/
-#define R0900_P2_TSBITRATE0  0xf384
-#define F0900_P2_TSFIFO_BITRATE0  0xf38400ff
+#define R0900_P2_TSBITRATE0 0xf384
+#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff
 
 /*P2_ERRCTRL1*/
-#define R0900_P2_ERRCTRL1  0xf398
-#define F0900_P2_ERR_SOURCE1  0xf39800f0
-#define F0900_P2_NUM_EVENT1  0xf3980007
+#define R0900_P2_ERRCTRL1 0xf398
+#define F0900_P2_ERR_SOURCE1 0xf39800f0
+#define F0900_P2_NUM_EVENT1 0xf3980007
 
 /*P2_ERRCNT12*/
-#define R0900_P2_ERRCNT12  0xf399
-#define F0900_P2_ERRCNT1_OLDVALUE  0xf3990080
-#define F0900_P2_ERR_CNT12  0xf399007f
+#define R0900_P2_ERRCNT12 0xf399
+#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080
+#define F0900_P2_ERR_CNT12 0xf399007f
 
 /*P2_ERRCNT11*/
-#define R0900_P2_ERRCNT11  0xf39a
-#define F0900_P2_ERR_CNT11  0xf39a00ff
+#define R0900_P2_ERRCNT11 0xf39a
+#define F0900_P2_ERR_CNT11 0xf39a00ff
 
 /*P2_ERRCNT10*/
-#define R0900_P2_ERRCNT10  0xf39b
-#define F0900_P2_ERR_CNT10  0xf39b00ff
+#define R0900_P2_ERRCNT10 0xf39b
+#define F0900_P2_ERR_CNT10 0xf39b00ff
 
 /*P2_ERRCTRL2*/
-#define R0900_P2_ERRCTRL2  0xf39c
-#define F0900_P2_ERR_SOURCE2  0xf39c00f0
-#define F0900_P2_NUM_EVENT2  0xf39c0007
+#define R0900_P2_ERRCTRL2 0xf39c
+#define F0900_P2_ERR_SOURCE2 0xf39c00f0
+#define F0900_P2_NUM_EVENT2 0xf39c0007
 
 /*P2_ERRCNT22*/
-#define R0900_P2_ERRCNT22  0xf39d
-#define F0900_P2_ERRCNT2_OLDVALUE  0xf39d0080
-#define F0900_P2_ERR_CNT22  0xf39d007f
+#define R0900_P2_ERRCNT22 0xf39d
+#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080
+#define F0900_P2_ERR_CNT22 0xf39d007f
 
 /*P2_ERRCNT21*/
-#define R0900_P2_ERRCNT21  0xf39e
-#define F0900_P2_ERR_CNT21  0xf39e00ff
+#define R0900_P2_ERRCNT21 0xf39e
+#define F0900_P2_ERR_CNT21 0xf39e00ff
 
 /*P2_ERRCNT20*/
-#define R0900_P2_ERRCNT20  0xf39f
-#define F0900_P2_ERR_CNT20  0xf39f00ff
+#define R0900_P2_ERRCNT20 0xf39f
+#define F0900_P2_ERR_CNT20 0xf39f00ff
 
 /*P2_FECSPY*/
-#define R0900_P2_FECSPY  0xf3a0
-#define F0900_P2_SPY_ENABLE  0xf3a00080
-#define F0900_P2_NO_SYNCBYTE  0xf3a00040
-#define F0900_P2_SERIAL_MODE  0xf3a00020
-#define F0900_P2_UNUSUAL_PACKET  0xf3a00010
-#define F0900_P2_BER_PACKMODE  0xf3a00008
-#define F0900_P2_BERMETER_LMODE  0xf3a00002
-#define F0900_P2_BERMETER_RESET  0xf3a00001
+#define R0900_P2_FECSPY 0xf3a0
+#define F0900_P2_SPY_ENABLE 0xf3a00080
+#define F0900_P2_NO_SYNCBYTE 0xf3a00040
+#define F0900_P2_SERIAL_MODE 0xf3a00020
+#define F0900_P2_UNUSUAL_PACKET 0xf3a00010
+#define F0900_P2_BERMETER_DATAMODE 0xf3a00008
+#define F0900_P2_BERMETER_LMODE 0xf3a00002
+#define F0900_P2_BERMETER_RESET 0xf3a00001
 
 /*P2_FSPYCFG*/
-#define R0900_P2_FSPYCFG  0xf3a1
-#define F0900_P2_FECSPY_INPUT  0xf3a100c0
-#define F0900_P2_RST_ON_ERROR  0xf3a10020
-#define F0900_P2_ONE_SHOT  0xf3a10010
-#define F0900_P2_I2C_MODE  0xf3a1000c
-#define F0900_P2_SPY_HYSTERESIS  0xf3a10003
+#define R0900_P2_FSPYCFG 0xf3a1
+#define F0900_P2_FECSPY_INPUT 0xf3a100c0
+#define F0900_P2_RST_ON_ERROR 0xf3a10020
+#define F0900_P2_ONE_SHOT 0xf3a10010
+#define F0900_P2_I2C_MODE 0xf3a1000c
+#define F0900_P2_SPY_HYSTERESIS 0xf3a10003
 
 /*P2_FSPYDATA*/
-#define R0900_P2_FSPYDATA  0xf3a2
-#define F0900_P2_SPY_STUFFING  0xf3a20080
-#define F0900_P2_NOERROR_PKTJITTER  0xf3a20040
-#define F0900_P2_SPY_CNULLPKT  0xf3a20020
-#define F0900_P2_SPY_OUTDATA_MODE  0xf3a2001f
+#define R0900_P2_FSPYDATA 0xf3a2
+#define F0900_P2_SPY_STUFFING 0xf3a20080
+#define F0900_P2_SPY_CNULLPKT 0xf3a20020
+#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f
 
 /*P2_FSPYOUT*/
-#define R0900_P2_FSPYOUT  0xf3a3
-#define F0900_P2_FSPY_DIRECT  0xf3a30080
-#define F0900_P2_SPY_OUTDATA_BUS  0xf3a30038
-#define F0900_P2_STUFF_MODE  0xf3a30007
+#define R0900_P2_FSPYOUT 0xf3a3
+#define F0900_P2_FSPY_DIRECT 0xf3a30080
+#define F0900_P2_STUFF_MODE 0xf3a30007
 
 /*P2_FSTATUS*/
-#define R0900_P2_FSTATUS  0xf3a4
-#define F0900_P2_SPY_ENDSIM  0xf3a40080
-#define F0900_P2_VALID_SIM  0xf3a40040
-#define F0900_P2_FOUND_SIGNAL  0xf3a40020
-#define F0900_P2_DSS_SYNCBYTE  0xf3a40010
-#define F0900_P2_RESULT_STATE  0xf3a4000f
+#define R0900_P2_FSTATUS 0xf3a4
+#define F0900_P2_SPY_ENDSIM 0xf3a40080
+#define F0900_P2_VALID_SIM 0xf3a40040
+#define F0900_P2_FOUND_SIGNAL 0xf3a40020
+#define F0900_P2_DSS_SYNCBYTE 0xf3a40010
+#define F0900_P2_RESULT_STATE 0xf3a4000f
 
 /*P2_FBERCPT4*/
-#define R0900_P2_FBERCPT4  0xf3a8
-#define F0900_P2_FBERMETER_CPT4  0xf3a800ff
+#define R0900_P2_FBERCPT4 0xf3a8
+#define F0900_P2_FBERMETER_CPT4 0xf3a800ff
 
 /*P2_FBERCPT3*/
-#define R0900_P2_FBERCPT3  0xf3a9
-#define F0900_P2_FBERMETER_CPT3  0xf3a900ff
+#define R0900_P2_FBERCPT3 0xf3a9
+#define F0900_P2_FBERMETER_CPT3 0xf3a900ff
 
 /*P2_FBERCPT2*/
-#define R0900_P2_FBERCPT2  0xf3aa
-#define F0900_P2_FBERMETER_CPT2  0xf3aa00ff
+#define R0900_P2_FBERCPT2 0xf3aa
+#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff
 
 /*P2_FBERCPT1*/
-#define R0900_P2_FBERCPT1  0xf3ab
-#define F0900_P2_FBERMETER_CPT1  0xf3ab00ff
+#define R0900_P2_FBERCPT1 0xf3ab
+#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff
 
 /*P2_FBERCPT0*/
-#define R0900_P2_FBERCPT0  0xf3ac
-#define F0900_P2_FBERMETER_CPT0  0xf3ac00ff
+#define R0900_P2_FBERCPT0 0xf3ac
+#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff
 
 /*P2_FBERERR2*/
-#define R0900_P2_FBERERR2  0xf3ad
-#define F0900_P2_FBERMETER_ERR2  0xf3ad00ff
+#define R0900_P2_FBERERR2 0xf3ad
+#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff
 
 /*P2_FBERERR1*/
-#define R0900_P2_FBERERR1  0xf3ae
-#define F0900_P2_FBERMETER_ERR1  0xf3ae00ff
+#define R0900_P2_FBERERR1 0xf3ae
+#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff
 
 /*P2_FBERERR0*/
-#define R0900_P2_FBERERR0  0xf3af
-#define F0900_P2_FBERMETER_ERR0  0xf3af00ff
+#define R0900_P2_FBERERR0 0xf3af
+#define F0900_P2_FBERMETER_ERR0 0xf3af00ff
 
 /*P2_FSPYBER*/
-#define R0900_P2_FSPYBER  0xf3b2
-#define F0900_P2_FSPYOBS_XORREAD  0xf3b20040
-#define F0900_P2_FSPYBER_OBSMODE  0xf3b20020
-#define F0900_P2_FSPYBER_SYNCBYTE  0xf3b20010
-#define F0900_P2_FSPYBER_UNSYNC  0xf3b20008
-#define F0900_P2_FSPYBER_CTIME  0xf3b20007
+#define R0900_P2_FSPYBER 0xf3b2
+#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010
+#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008
+#define F0900_P2_FSPYBER_CTIME 0xf3b20007
 
 /*P1_IQCONST*/
-#define R0900_P1_IQCONST  0xf400
-#define F0900_P1_CONSTEL_SELECT  0xf4000060
-#define F0900_P1_IQSYMB_SEL  0xf400001f
+#define R0900_P1_IQCONST 0xf400
+#define IQCONST REGx(R0900_P1_IQCONST)
+#define F0900_P1_CONSTEL_SELECT 0xf4000060
+#define F0900_P1_IQSYMB_SEL 0xf400001f
 
 /*P1_NOSCFG*/
-#define R0900_P1_NOSCFG  0xf401
-#define F0900_P1_DUMMYPL_NOSDATA  0xf4010020
-#define F0900_P1_NOSPLH_BETA  0xf4010018
-#define F0900_P1_NOSDATA_BETA  0xf4010007
+#define R0900_P1_NOSCFG 0xf401
+#define NOSCFG REGx(R0900_P1_NOSCFG)
+#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020
+#define F0900_P1_NOSPLH_BETA 0xf4010018
+#define F0900_P1_NOSDATA_BETA 0xf4010007
 
 /*P1_ISYMB*/
-#define R0900_P1_ISYMB  0xf402
-#define F0900_P1_I_SYMBOL  0xf40201ff
+#define R0900_P1_ISYMB 0xf402
+#define ISYMB REGx(R0900_P1_ISYMB)
+#define F0900_P1_I_SYMBOL 0xf40201ff
 
 /*P1_QSYMB*/
-#define R0900_P1_QSYMB  0xf403
-#define F0900_P1_Q_SYMBOL  0xf40301ff
+#define R0900_P1_QSYMB 0xf403
+#define QSYMB REGx(R0900_P1_QSYMB)
+#define F0900_P1_Q_SYMBOL 0xf40301ff
 
 /*P1_AGC1CFG*/
-#define R0900_P1_AGC1CFG  0xf404
-#define F0900_P1_DC_FROZEN  0xf4040080
-#define F0900_P1_DC_CORRECT  0xf4040040
-#define F0900_P1_AMM_FROZEN  0xf4040020
-#define F0900_P1_AMM_CORRECT  0xf4040010
-#define F0900_P1_QUAD_FROZEN  0xf4040008
-#define F0900_P1_QUAD_CORRECT  0xf4040004
-#define F0900_P1_DCCOMP_SLOW  0xf4040002
-#define F0900_P1_IQMISM_SLOW  0xf4040001
+#define R0900_P1_AGC1CFG 0xf404
+#define AGC1CFG REGx(R0900_P1_AGC1CFG)
+#define F0900_P1_DC_FROZEN 0xf4040080
+#define F0900_P1_DC_CORRECT 0xf4040040
+#define F0900_P1_AMM_FROZEN 0xf4040020
+#define F0900_P1_AMM_CORRECT 0xf4040010
+#define F0900_P1_QUAD_FROZEN 0xf4040008
+#define F0900_P1_QUAD_CORRECT 0xf4040004
 
 /*P1_AGC1CN*/
-#define R0900_P1_AGC1CN  0xf406
-#define F0900_P1_AGC1_LOCKED  0xf4060080
-#define F0900_P1_AGC1_OVERFLOW  0xf4060040
-#define F0900_P1_AGC1_NOSLOWLK  0xf4060020
-#define F0900_P1_AGC1_MINPOWER  0xf4060010
-#define F0900_P1_AGCOUT_FAST  0xf4060008
-#define F0900_P1_AGCIQ_BETA  0xf4060007
+#define R0900_P1_AGC1CN 0xf406
+#define AGC1CN REGx(R0900_P1_AGC1CN)
+#define F0900_P1_AGC1_LOCKED 0xf4060080
+#define F0900_P1_AGC1_MINPOWER 0xf4060010
+#define F0900_P1_AGCOUT_FAST 0xf4060008
+#define F0900_P1_AGCIQ_BETA 0xf4060007
 
 /*P1_AGC1REF*/
-#define R0900_P1_AGC1REF  0xf407
-#define F0900_P1_AGCIQ_REF  0xf40700ff
+#define R0900_P1_AGC1REF 0xf407
+#define AGC1REF REGx(R0900_P1_AGC1REF)
+#define F0900_P1_AGCIQ_REF 0xf40700ff
 
 /*P1_IDCCOMP*/
-#define R0900_P1_IDCCOMP  0xf408
-#define F0900_P1_IAVERAGE_ADJ  0xf40801ff
+#define R0900_P1_IDCCOMP 0xf408
+#define IDCCOMP REGx(R0900_P1_IDCCOMP)
+#define F0900_P1_IAVERAGE_ADJ 0xf40801ff
 
 /*P1_QDCCOMP*/
-#define R0900_P1_QDCCOMP  0xf409
-#define F0900_P1_QAVERAGE_ADJ  0xf40901ff
+#define R0900_P1_QDCCOMP 0xf409
+#define QDCCOMP REGx(R0900_P1_QDCCOMP)
+#define F0900_P1_QAVERAGE_ADJ 0xf40901ff
 
 /*P1_POWERI*/
-#define R0900_P1_POWERI  0xf40a
-#define F0900_P1_POWER_I  0xf40a00ff
+#define R0900_P1_POWERI 0xf40a
+#define POWERI REGx(R0900_P1_POWERI)
+#define F0900_P1_POWER_I 0xf40a00ff
+#define POWER_I FLDx(F0900_P1_POWER_I)
 
 /*P1_POWERQ*/
-#define R0900_P1_POWERQ  0xf40b
-#define F0900_P1_POWER_Q  0xf40b00ff
+#define R0900_P1_POWERQ 0xf40b
+#define POWERQ REGx(R0900_P1_POWERQ)
+#define F0900_P1_POWER_Q 0xf40b00ff
+#define POWER_Q FLDx(F0900_P1_POWER_Q)
 
 /*P1_AGC1AMM*/
-#define R0900_P1_AGC1AMM  0xf40c
-#define F0900_P1_AMM_VALUE  0xf40c00ff
+#define R0900_P1_AGC1AMM 0xf40c
+#define AGC1AMM REGx(R0900_P1_AGC1AMM)
+#define F0900_P1_AMM_VALUE 0xf40c00ff
 
 /*P1_AGC1QUAD*/
-#define R0900_P1_AGC1QUAD  0xf40d
-#define F0900_P1_QUAD_VALUE  0xf40d01ff
+#define R0900_P1_AGC1QUAD 0xf40d
+#define AGC1QUAD REGx(R0900_P1_AGC1QUAD)
+#define F0900_P1_QUAD_VALUE 0xf40d01ff
 
 /*P1_AGCIQIN1*/
-#define R0900_P1_AGCIQIN1  0xf40e
-#define F0900_P1_AGCIQ_VALUE1  0xf40e00ff
+#define R0900_P1_AGCIQIN1 0xf40e
+#define AGCIQIN1 REGx(R0900_P1_AGCIQIN1)
+#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff
+#define AGCIQ_VALUE1 FLDx(F0900_P1_AGCIQ_VALUE1)
 
 /*P1_AGCIQIN0*/
-#define R0900_P1_AGCIQIN0  0xf40f
-#define F0900_P1_AGCIQ_VALUE0  0xf40f00ff
+#define R0900_P1_AGCIQIN0 0xf40f
+#define AGCIQIN0 REGx(R0900_P1_AGCIQIN0)
+#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff
+#define AGCIQ_VALUE0 FLDx(F0900_P1_AGCIQ_VALUE0)
 
 /*P1_DEMOD*/
-#define R0900_P1_DEMOD  0xf410
-#define F0900_P1_DEMOD_STOP  0xf4100040
-#define F0900_P1_SPECINV_CONTROL  0xf4100030
-#define F0900_P1_FORCE_ENASAMP  0xf4100008
-#define F0900_P1_MANUAL_ROLLOFF  0xf4100004
-#define F0900_P1_ROLLOFF_CONTROL  0xf4100003
+#define R0900_P1_DEMOD 0xf410
+#define DEMOD REGx(R0900_P1_DEMOD)
+#define F0900_P1_MANUALS2_ROLLOFF 0xf4100080
+#define MANUALS2_ROLLOFF FLDx(F0900_P1_MANUALS2_ROLLOFF)
+
+#define F0900_P1_SPECINV_CONTROL 0xf4100030
+#define SPECINV_CONTROL FLDx(F0900_P1_SPECINV_CONTROL)
+#define F0900_P1_FORCE_ENASAMP 0xf4100008
+#define F0900_P1_MANUALSX_ROLLOFF 0xf4100004
+#define MANUALSX_ROLLOFF FLDx(F0900_P1_MANUALSX_ROLLOFF)
+#define F0900_P1_ROLLOFF_CONTROL 0xf4100003
+#define ROLLOFF_CONTROL FLDx(F0900_P1_ROLLOFF_CONTROL)
 
 /*P1_DMDMODCOD*/
-#define R0900_P1_DMDMODCOD  0xf411
-#define F0900_P1_MANUAL_MODCOD  0xf4110080
-#define F0900_P1_DEMOD_MODCOD  0xf411007c
-#define F0900_P1_DEMOD_TYPE  0xf4110003
+#define R0900_P1_DMDMODCOD 0xf411
+#define DMDMODCOD REGx(R0900_P1_DMDMODCOD)
+#define F0900_P1_MANUAL_MODCOD 0xf4110080
+#define F0900_P1_DEMOD_MODCOD 0xf411007c
+#define DEMOD_MODCOD FLDx(F0900_P1_DEMOD_MODCOD)
+#define F0900_P1_DEMOD_TYPE 0xf4110003
+#define DEMOD_TYPE FLDx(F0900_P1_DEMOD_TYPE)
 
 /*P1_DSTATUS*/
-#define R0900_P1_DSTATUS  0xf412
-#define F0900_P1_CAR_LOCK  0xf4120080
-#define F0900_P1_TMGLOCK_QUALITY  0xf4120060
-#define F0900_P1_SDVBS1_ENABLE  0xf4120010
-#define F0900_P1_LOCK_DEFINITIF  0xf4120008
-#define F0900_P1_TIMING_IS_LOCKED  0xf4120004
-#define F0900_P1_COARSE_TMGLOCK  0xf4120002
-#define F0900_P1_COARSE_CARLOCK  0xf4120001
+#define R0900_P1_DSTATUS 0xf412
+#define DSTATUS REGx(R0900_P1_DSTATUS)
+#define F0900_P1_CAR_LOCK 0xf4120080
+#define F0900_P1_TMGLOCK_QUALITY 0xf4120060
+#define TMGLOCK_QUALITY FLDx(F0900_P1_TMGLOCK_QUALITY)
+#define F0900_P1_LOCK_DEFINITIF 0xf4120008
+#define LOCK_DEFINITIF FLDx(F0900_P1_LOCK_DEFINITIF)
+#define F0900_P1_OVADC_DETECT 0xf4120001
 
 /*P1_DSTATUS2*/
-#define R0900_P1_DSTATUS2  0xf413
-#define F0900_P1_DEMOD_DELOCK  0xf4130080
-#define F0900_P1_DEMOD_TIMEOUT  0xf4130040
-#define F0900_P1_MODCODRQ_SYNCTAG  0xf4130020
-#define F0900_P1_POLYPH_SATEVENT  0xf4130010
-#define F0900_P1_AGC1_NOSIGNALACK  0xf4130008
-#define F0900_P1_AGC2_OVERFLOW  0xf4130004
-#define F0900_P1_CFR_OVERFLOW  0xf4130002
-#define F0900_P1_GAMMA_OVERUNDER  0xf4130001
+#define R0900_P1_DSTATUS2 0xf413
+#define DSTATUS2 REGx(R0900_P1_DSTATUS2)
+#define F0900_P1_DEMOD_DELOCK 0xf4130080
+#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008
+#define F0900_P1_AGC2_OVERFLOW 0xf4130004
+#define F0900_P1_CFR_OVERFLOW 0xf4130002
+#define F0900_P1_GAMMA_OVERUNDER 0xf4130001
 
 /*P1_DMDCFGMD*/
-#define R0900_P1_DMDCFGMD  0xf414
-#define F0900_P1_DVBS2_ENABLE  0xf4140080
-#define F0900_P1_DVBS1_ENABLE  0xf4140040
-#define F0900_P1_CFR_AUTOSCAN  0xf4140020
-#define F0900_P1_SCAN_ENABLE  0xf4140010
-#define F0900_P1_TUN_AUTOSCAN  0xf4140008
-#define F0900_P1_NOFORCE_RELOCK  0xf4140004
-#define F0900_P1_TUN_RNG  0xf4140003
+#define R0900_P1_DMDCFGMD 0xf414
+#define DMDCFGMD REGx(R0900_P1_DMDCFGMD)
+#define F0900_P1_DVBS2_ENABLE 0xf4140080
+#define DVBS2_ENABLE FLDx(F0900_P1_DVBS2_ENABLE)
+#define F0900_P1_DVBS1_ENABLE 0xf4140040
+#define DVBS1_ENABLE FLDx(F0900_P1_DVBS1_ENABLE)
+#define F0900_P1_SCAN_ENABLE 0xf4140010
+#define SCAN_ENABLE FLDx(F0900_P1_SCAN_ENABLE)
+#define F0900_P1_CFR_AUTOSCAN 0xf4140008
+#define CFR_AUTOSCAN FLDx(F0900_P1_CFR_AUTOSCAN)
+#define F0900_P1_TUN_RNG 0xf4140003
 
 /*P1_DMDCFG2*/
-#define R0900_P1_DMDCFG2  0xf415
-#define F0900_P1_AGC1_WAITLOCK  0xf4150080
-#define F0900_P1_S1S2_SEQUENTIAL  0xf4150040
-#define F0900_P1_OVERFLOW_TIMEOUT  0xf4150020
-#define F0900_P1_SCANFAIL_TIMEOUT  0xf4150010
-#define F0900_P1_DMDTOUT_BACK  0xf4150008
-#define F0900_P1_CARLOCK_S1ENABLE  0xf4150004
-#define F0900_P1_COARSE_LK3MODE  0xf4150002
-#define F0900_P1_COARSE_LK2MODE  0xf4150001
+#define R0900_P1_DMDCFG2 0xf415
+#define DMDCFG2 REGx(R0900_P1_DMDCFG2)
+#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040
+#define S1S2_SEQUENTIAL FLDx(F0900_P1_S1S2_SEQUENTIAL)
+#define F0900_P1_INFINITE_RELOCK 0xf4150010
 
 /*P1_DMDISTATE*/
-#define R0900_P1_DMDISTATE  0xf416
-#define F0900_P1_I2C_NORESETDMODE  0xf4160080
-#define F0900_P1_FORCE_ETAPED  0xf4160040
-#define F0900_P1_SDMDRST_DIRCLK  0xf4160020
-#define F0900_P1_I2C_DEMOD_MODE  0xf416001f
+#define R0900_P1_DMDISTATE 0xf416
+#define DMDISTATE REGx(R0900_P1_DMDISTATE)
+#define F0900_P1_I2C_DEMOD_MODE 0xf416001f
+#define DEMOD_MODE FLDx(F0900_P1_I2C_DEMOD_MODE)
 
 /*P1_DMDT0M*/
-#define R0900_P1_DMDT0M  0xf417
-#define F0900_P1_DMDT0_MIN  0xf41700ff
+#define R0900_P1_DMDT0M 0xf417
+#define DMDT0M REGx(R0900_P1_DMDT0M)
+#define F0900_P1_DMDT0_MIN 0xf41700ff
 
 /*P1_DMDSTATE*/
-#define R0900_P1_DMDSTATE  0xf41b
-#define F0900_P1_DEMOD_LOCKED  0xf41b0080
-#define F0900_P1_HEADER_MODE  0xf41b0060
-#define F0900_P1_DEMOD_MODE  0xf41b001f
+#define R0900_P1_DMDSTATE 0xf41b
+#define DMDSTATE REGx(R0900_P1_DMDSTATE)
+#define F0900_P1_HEADER_MODE 0xf41b0060
+#define HEADER_MODE FLDx(F0900_P1_HEADER_MODE)
 
 /*P1_DMDFLYW*/
-#define R0900_P1_DMDFLYW  0xf41c
-#define F0900_P1_I2C_IRQVAL  0xf41c00f0
-#define F0900_P1_FLYWHEEL_CPT  0xf41c000f
+#define R0900_P1_DMDFLYW 0xf41c
+#define DMDFLYW REGx(R0900_P1_DMDFLYW)
+#define F0900_P1_I2C_IRQVAL 0xf41c00f0
+#define F0900_P1_FLYWHEEL_CPT 0xf41c000f
+#define FLYWHEEL_CPT FLDx(F0900_P1_FLYWHEEL_CPT)
 
 /*P1_DSTATUS3*/
-#define R0900_P1_DSTATUS3  0xf41d
-#define F0900_P1_CFR_ZIGZAG  0xf41d0080
-#define F0900_P1_DEMOD_CFGMODE  0xf41d0060
-#define F0900_P1_GAMMA_LOWBAUDRATE  0xf41d0010
-#define F0900_P1_RELOCK_MODE  0xf41d0008
-#define F0900_P1_DEMOD_FAIL  0xf41d0004
-#define F0900_P1_ETAPE1A_DVBXMEM  0xf41d0003
+#define R0900_P1_DSTATUS3 0xf41d
+#define DSTATUS3 REGx(R0900_P1_DSTATUS3)
+#define F0900_P1_DEMOD_CFGMODE 0xf41d0060
 
 /*P1_DMDCFG3*/
-#define R0900_P1_DMDCFG3  0xf41e
-#define F0900_P1_DVBS1_TMGWAIT  0xf41e0080
-#define F0900_P1_NO_BWCENTERING  0xf41e0040
-#define F0900_P1_INV_SEQSRCH  0xf41e0020
-#define F0900_P1_DIS_SFRUPLOW_TRK  0xf41e0010
-#define F0900_P1_NOSTOP_FIFOFULL  0xf41e0008
-#define F0900_P1_LOCKTIME_MODE  0xf41e0007
+#define R0900_P1_DMDCFG3 0xf41e
+#define DMDCFG3 REGx(R0900_P1_DMDCFG3)
+#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008
 
 /*P1_DMDCFG4*/
-#define R0900_P1_DMDCFG4  0xf41f
-#define F0900_P1_TUNER_NRELAUNCH  0xf41f0008
-#define F0900_P1_DIS_CLKENABLE  0xf41f0004
-#define F0900_P1_DIS_HDRDIVLOCK  0xf41f0002
-#define F0900_P1_NO_TNRWBINIT  0xf41f0001
+#define R0900_P1_DMDCFG4 0xf41f
+#define DMDCFG4 REGx(R0900_P1_DMDCFG4)
+#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008
 
 /*P1_CORRELMANT*/
-#define R0900_P1_CORRELMANT  0xf420
-#define F0900_P1_CORREL_MANT  0xf42000ff
+#define R0900_P1_CORRELMANT 0xf420
+#define CORRELMANT REGx(R0900_P1_CORRELMANT)
+#define F0900_P1_CORREL_MANT 0xf42000ff
 
 /*P1_CORRELABS*/
-#define R0900_P1_CORRELABS  0xf421
-#define F0900_P1_CORREL_ABS  0xf42100ff
+#define R0900_P1_CORRELABS 0xf421
+#define CORRELABS REGx(R0900_P1_CORRELABS)
+#define F0900_P1_CORREL_ABS 0xf42100ff
 
 /*P1_CORRELEXP*/
-#define R0900_P1_CORRELEXP  0xf422
-#define F0900_P1_CORREL_ABSEXP  0xf42200f0
-#define F0900_P1_CORREL_EXP  0xf422000f
+#define R0900_P1_CORRELEXP 0xf422
+#define CORRELEXP REGx(R0900_P1_CORRELEXP)
+#define F0900_P1_CORREL_ABSEXP 0xf42200f0
+#define F0900_P1_CORREL_EXP 0xf422000f
 
 /*P1_PLHMODCOD*/
-#define R0900_P1_PLHMODCOD  0xf424
-#define F0900_P1_SPECINV_DEMOD  0xf4240080
-#define F0900_P1_PLH_MODCOD  0xf424007c
-#define F0900_P1_PLH_TYPE  0xf4240003
-
-/*P1_AGCK32*/
-#define R0900_P1_AGCK32  0xf42b
-#define F0900_P1_R3ADJOFF_32APSK  0xf42b0080
-#define F0900_P1_R2ADJOFF_32APSK  0xf42b0040
-#define F0900_P1_R1ADJOFF_32APSK  0xf42b0020
-#define F0900_P1_RADJ_32APSK  0xf42b001f
+#define R0900_P1_PLHMODCOD 0xf424
+#define PLHMODCOD REGx(R0900_P1_PLHMODCOD)
+#define F0900_P1_SPECINV_DEMOD 0xf4240080
+#define SPECINV_DEMOD FLDx(F0900_P1_SPECINV_DEMOD)
+#define F0900_P1_PLH_MODCOD 0xf424007c
+#define F0900_P1_PLH_TYPE 0xf4240003
+
+/*P1_DMDREG*/
+#define R0900_P1_DMDREG 0xf425
+#define DMDREG REGx(R0900_P1_DMDREG)
+#define F0900_P1_DECIM_PLFRAMES 0xf4250001
 
 /*P1_AGC2O*/
-#define R0900_P1_AGC2O  0xf42c
-#define F0900_P1_AGC2REF_ADJUSTING  0xf42c0080
-#define F0900_P1_AGC2_COARSEFAST  0xf42c0040
-#define F0900_P1_AGC2_LKSQRT  0xf42c0020
-#define F0900_P1_AGC2_LKMODE  0xf42c0010
-#define F0900_P1_AGC2_LKEQUA  0xf42c0008
-#define F0900_P1_AGC2_COEF  0xf42c0007
+#define R0900_P1_AGC2O 0xf42c
+#define AGC2O REGx(R0900_P1_AGC2O)
+#define F0900_P1_AGC2_COEF 0xf42c0007
 
 /*P1_AGC2REF*/
-#define R0900_P1_AGC2REF  0xf42d
-#define F0900_P1_AGC2_REF  0xf42d00ff
+#define R0900_P1_AGC2REF 0xf42d
+#define AGC2REF REGx(R0900_P1_AGC2REF)
+#define F0900_P1_AGC2_REF 0xf42d00ff
 
 /*P1_AGC1ADJ*/
-#define R0900_P1_AGC1ADJ  0xf42e
-#define F0900_P1_AGC1ADJ_MANUAL  0xf42e0080
-#define F0900_P1_AGC1_ADJUSTED  0xf42e017f
+#define R0900_P1_AGC1ADJ 0xf42e
+#define AGC1ADJ REGx(R0900_P1_AGC1ADJ)
+#define F0900_P1_AGC1_ADJUSTED 0xf42e007f
 
 /*P1_AGC2I1*/
-#define R0900_P1_AGC2I1  0xf436
-#define F0900_P1_AGC2_INTEGRATOR1  0xf43600ff
+#define R0900_P1_AGC2I1 0xf436
+#define AGC2I1 REGx(R0900_P1_AGC2I1)
+#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff
 
 /*P1_AGC2I0*/
-#define R0900_P1_AGC2I0  0xf437
-#define F0900_P1_AGC2_INTEGRATOR0  0xf43700ff
+#define R0900_P1_AGC2I0 0xf437
+#define AGC2I0 REGx(R0900_P1_AGC2I0)
+#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff
 
 /*P1_CARCFG*/
-#define R0900_P1_CARCFG  0xf438
-#define F0900_P1_CFRUPLOW_AUTO  0xf4380080
-#define F0900_P1_CFRUPLOW_TEST  0xf4380040
-#define F0900_P1_EN_CAR2CENTER  0xf4380020
-#define F0900_P1_CARHDR_NODIV8  0xf4380010
-#define F0900_P1_I2C_ROTA  0xf4380008
-#define F0900_P1_ROTAON  0xf4380004
-#define F0900_P1_PH_DET_ALGO  0xf4380003
+#define R0900_P1_CARCFG 0xf438
+#define CARCFG REGx(R0900_P1_CARCFG)
+#define F0900_P1_CFRUPLOW_AUTO 0xf4380080
+#define F0900_P1_CFRUPLOW_TEST 0xf4380040
+#define F0900_P1_ROTAON 0xf4380004
+#define F0900_P1_PH_DET_ALGO 0xf4380003
 
 /*P1_ACLC*/
-#define R0900_P1_ACLC  0xf439
-#define F0900_P1_STOP_S2ALPHA  0xf43900c0
-#define F0900_P1_CAR_ALPHA_MANT  0xf4390030
-#define F0900_P1_CAR_ALPHA_EXP  0xf439000f
+#define R0900_P1_ACLC 0xf439
+#define ACLC REGx(R0900_P1_ACLC)
+#define F0900_P1_CAR_ALPHA_MANT 0xf4390030
+#define F0900_P1_CAR_ALPHA_EXP 0xf439000f
 
 /*P1_BCLC*/
-#define R0900_P1_BCLC  0xf43a
-#define F0900_P1_STOP_S2BETA  0xf43a00c0
-#define F0900_P1_CAR_BETA_MANT  0xf43a0030
-#define F0900_P1_CAR_BETA_EXP  0xf43a000f
+#define R0900_P1_BCLC 0xf43a
+#define BCLC REGx(R0900_P1_BCLC)
+#define F0900_P1_CAR_BETA_MANT 0xf43a0030
+#define F0900_P1_CAR_BETA_EXP 0xf43a000f
 
 /*P1_CARFREQ*/
-#define R0900_P1_CARFREQ  0xf43d
-#define F0900_P1_KC_COARSE_EXP  0xf43d00f0
-#define F0900_P1_BETA_FREQ  0xf43d000f
+#define R0900_P1_CARFREQ 0xf43d
+#define CARFREQ REGx(R0900_P1_CARFREQ)
+#define F0900_P1_KC_COARSE_EXP 0xf43d00f0
+#define F0900_P1_BETA_FREQ 0xf43d000f
 
 /*P1_CARHDR*/
-#define R0900_P1_CARHDR  0xf43e
-#define F0900_P1_K_FREQ_HDR  0xf43e00ff
+#define R0900_P1_CARHDR 0xf43e
+#define CARHDR REGx(R0900_P1_CARHDR)
+#define F0900_P1_K_FREQ_HDR 0xf43e00ff
 
 /*P1_LDT*/
-#define R0900_P1_LDT  0xf43f
-#define F0900_P1_CARLOCK_THRES  0xf43f01ff
+#define R0900_P1_LDT 0xf43f
+#define LDT REGx(R0900_P1_LDT)
+#define F0900_P1_CARLOCK_THRES 0xf43f01ff
 
 /*P1_LDT2*/
-#define R0900_P1_LDT2  0xf440
-#define F0900_P1_CARLOCK_THRES2  0xf44001ff
+#define R0900_P1_LDT2 0xf440
+#define LDT2 REGx(R0900_P1_LDT2)
+#define F0900_P1_CARLOCK_THRES2 0xf44001ff
 
 /*P1_CFRICFG*/
-#define R0900_P1_CFRICFG  0xf441
-#define F0900_P1_CFRINIT_UNVALRNG  0xf4410080
-#define F0900_P1_CFRINIT_LUNVALCPT  0xf4410040
-#define F0900_P1_CFRINIT_ABORTDBL  0xf4410020
-#define F0900_P1_CFRINIT_ABORTPRED  0xf4410010
-#define F0900_P1_CFRINIT_UNVALSKIP  0xf4410008
-#define F0900_P1_CFRINIT_CSTINC  0xf4410004
-#define F0900_P1_NEG_CFRSTEP  0xf4410001
+#define R0900_P1_CFRICFG 0xf441
+#define CFRICFG REGx(R0900_P1_CFRICFG)
+#define F0900_P1_NEG_CFRSTEP 0xf4410001
 
 /*P1_CFRUP1*/
-#define R0900_P1_CFRUP1  0xf442
-#define F0900_P1_CFR_UP1  0xf44201ff
+#define R0900_P1_CFRUP1 0xf442
+#define CFRUP1 REGx(R0900_P1_CFRUP1)
+#define F0900_P1_CFR_UP1 0xf44201ff
+#define CFR_UP1 FLDx(F0900_P1_CFR_UP1)
 
 /*P1_CFRUP0*/
-#define R0900_P1_CFRUP0  0xf443
-#define F0900_P1_CFR_UP0  0xf44300ff
+#define R0900_P1_CFRUP0 0xf443
+#define CFRUP0 REGx(R0900_P1_CFRUP0)
+#define F0900_P1_CFR_UP0 0xf44300ff
+#define CFR_UP0 FLDx(F0900_P1_CFR_UP0)
 
 /*P1_CFRLOW1*/
-#define R0900_P1_CFRLOW1  0xf446
-#define F0900_P1_CFR_LOW1  0xf44601ff
+#define R0900_P1_CFRLOW1 0xf446
+#define CFRLOW1 REGx(R0900_P1_CFRLOW1)
+#define F0900_P1_CFR_LOW1 0xf44601ff
+#define CFR_LOW1 FLDx(F0900_P1_CFR_LOW1)
 
 /*P1_CFRLOW0*/
-#define R0900_P1_CFRLOW0  0xf447
-#define F0900_P1_CFR_LOW0  0xf44700ff
+#define R0900_P1_CFRLOW0 0xf447
+#define CFRLOW0 REGx(R0900_P1_CFRLOW0)
+#define F0900_P1_CFR_LOW0 0xf44700ff
+#define CFR_LOW0 FLDx(F0900_P1_CFR_LOW0)
 
 /*P1_CFRINIT1*/
-#define R0900_P1_CFRINIT1  0xf448
-#define F0900_P1_CFR_INIT1  0xf44801ff
+#define R0900_P1_CFRINIT1 0xf448
+#define CFRINIT1 REGx(R0900_P1_CFRINIT1)
+#define F0900_P1_CFR_INIT1 0xf44801ff
+#define CFR_INIT1 FLDx(F0900_P1_CFR_INIT1)
 
 /*P1_CFRINIT0*/
-#define R0900_P1_CFRINIT0  0xf449
-#define F0900_P1_CFR_INIT0  0xf44900ff
+#define R0900_P1_CFRINIT0 0xf449
+#define CFRINIT0 REGx(R0900_P1_CFRINIT0)
+#define F0900_P1_CFR_INIT0 0xf44900ff
+#define CFR_INIT0 FLDx(F0900_P1_CFR_INIT0)
 
 /*P1_CFRINC1*/
-#define R0900_P1_CFRINC1  0xf44a
-#define F0900_P1_MANUAL_CFRINC  0xf44a0080
-#define F0900_P1_CFR_INC1  0xf44a017f
+#define R0900_P1_CFRINC1 0xf44a
+#define CFRINC1 REGx(R0900_P1_CFRINC1)
+#define F0900_P1_MANUAL_CFRINC 0xf44a0080
+#define F0900_P1_CFR_INC1 0xf44a003f
 
 /*P1_CFRINC0*/
-#define R0900_P1_CFRINC0  0xf44b
-#define F0900_P1_CFR_INC0  0xf44b00f0
+#define R0900_P1_CFRINC0 0xf44b
+#define CFRINC0 REGx(R0900_P1_CFRINC0)
+#define F0900_P1_CFR_INC0 0xf44b00f8
 
 /*P1_CFR2*/
-#define R0900_P1_CFR2  0xf44c
-#define F0900_P1_CAR_FREQ2  0xf44c01ff
+#define R0900_P1_CFR2 0xf44c
+#define CFR2 REGx(R0900_P1_CFR2)
+#define F0900_P1_CAR_FREQ2 0xf44c01ff
+#define CAR_FREQ2 FLDx(F0900_P1_CAR_FREQ2)
 
 /*P1_CFR1*/
-#define R0900_P1_CFR1  0xf44d
-#define F0900_P1_CAR_FREQ1  0xf44d00ff
+#define R0900_P1_CFR1 0xf44d
+#define CFR1 REGx(R0900_P1_CFR1)
+#define F0900_P1_CAR_FREQ1 0xf44d00ff
+#define CAR_FREQ1 FLDx(F0900_P1_CAR_FREQ1)
 
 /*P1_CFR0*/
-#define R0900_P1_CFR0  0xf44e
-#define F0900_P1_CAR_FREQ0  0xf44e00ff
+#define R0900_P1_CFR0 0xf44e
+#define CFR0 REGx(R0900_P1_CFR0)
+#define F0900_P1_CAR_FREQ0 0xf44e00ff
+#define CAR_FREQ0 FLDx(F0900_P1_CAR_FREQ0)
 
 /*P1_LDI*/
-#define R0900_P1_LDI  0xf44f
-#define F0900_P1_LOCK_DET_INTEGR  0xf44f01ff
+#define R0900_P1_LDI 0xf44f
+#define LDI REGx(R0900_P1_LDI)
+#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff
 
 /*P1_TMGCFG*/
-#define R0900_P1_TMGCFG  0xf450
-#define F0900_P1_TMGLOCK_BETA  0xf45000c0
-#define F0900_P1_NOTMG_GROUPDELAY  0xf4500020
-#define F0900_P1_DO_TIMING_CORR  0xf4500010
-#define F0900_P1_MANUAL_SCAN  0xf450000c
-#define F0900_P1_TMG_MINFREQ  0xf4500003
+#define R0900_P1_TMGCFG 0xf450
+#define TMGCFG REGx(R0900_P1_TMGCFG)
+#define F0900_P1_TMGLOCK_BETA 0xf45000c0
+#define F0900_P1_DO_TIMING_CORR 0xf4500010
+#define F0900_P1_TMG_MINFREQ 0xf4500003
 
 /*P1_RTC*/
-#define R0900_P1_RTC  0xf451
-#define F0900_P1_TMGALPHA_EXP  0xf45100f0
-#define F0900_P1_TMGBETA_EXP  0xf451000f
+#define R0900_P1_RTC 0xf451
+#define RTC REGx(R0900_P1_RTC)
+#define F0900_P1_TMGALPHA_EXP 0xf45100f0
+#define F0900_P1_TMGBETA_EXP 0xf451000f
 
 /*P1_RTCS2*/
-#define R0900_P1_RTCS2  0xf452
-#define F0900_P1_TMGALPHAS2_EXP  0xf45200f0
-#define F0900_P1_TMGBETAS2_EXP  0xf452000f
+#define R0900_P1_RTCS2 0xf452
+#define RTCS2 REGx(R0900_P1_RTCS2)
+#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0
+#define F0900_P1_TMGBETAS2_EXP 0xf452000f
 
 /*P1_TMGTHRISE*/
-#define R0900_P1_TMGTHRISE  0xf453
-#define F0900_P1_TMGLOCK_THRISE  0xf45300ff
+#define R0900_P1_TMGTHRISE 0xf453
+#define TMGTHRISE REGx(R0900_P1_TMGTHRISE)
+#define F0900_P1_TMGLOCK_THRISE 0xf45300ff
 
 /*P1_TMGTHFALL*/
-#define R0900_P1_TMGTHFALL  0xf454
-#define F0900_P1_TMGLOCK_THFALL  0xf45400ff
+#define R0900_P1_TMGTHFALL 0xf454
+#define TMGTHFALL REGx(R0900_P1_TMGTHFALL)
+#define F0900_P1_TMGLOCK_THFALL 0xf45400ff
 
 /*P1_SFRUPRATIO*/
-#define R0900_P1_SFRUPRATIO  0xf455
-#define F0900_P1_SFR_UPRATIO  0xf45500ff
+#define R0900_P1_SFRUPRATIO 0xf455
+#define SFRUPRATIO REGx(R0900_P1_SFRUPRATIO)
+#define F0900_P1_SFR_UPRATIO 0xf45500ff
 
 /*P1_SFRLOWRATIO*/
-#define R0900_P1_SFRLOWRATIO  0xf456
-#define F0900_P1_SFR_LOWRATIO  0xf45600ff
+#define R0900_P1_SFRLOWRATIO 0xf456
+#define F0900_P1_SFR_LOWRATIO 0xf45600ff
 
 /*P1_KREFTMG*/
-#define R0900_P1_KREFTMG  0xf458
-#define F0900_P1_KREF_TMG  0xf45800ff
+#define R0900_P1_KREFTMG 0xf458
+#define KREFTMG REGx(R0900_P1_KREFTMG)
+#define F0900_P1_KREF_TMG 0xf45800ff
 
 /*P1_SFRSTEP*/
-#define R0900_P1_SFRSTEP  0xf459
-#define F0900_P1_SFR_SCANSTEP  0xf45900f0
-#define F0900_P1_SFR_CENTERSTEP  0xf459000f
+#define R0900_P1_SFRSTEP 0xf459
+#define SFRSTEP REGx(R0900_P1_SFRSTEP)
+#define F0900_P1_SFR_SCANSTEP 0xf45900f0
+#define F0900_P1_SFR_CENTERSTEP 0xf459000f
 
 /*P1_TMGCFG2*/
-#define R0900_P1_TMGCFG2  0xf45a
-#define F0900_P1_DIS_AUTOSAMP  0xf45a0008
-#define F0900_P1_SCANINIT_QUART  0xf45a0004
-#define F0900_P1_NOTMG_DVBS1DERAT  0xf45a0002
-#define F0900_P1_SFRRATIO_FINE  0xf45a0001
+#define R0900_P1_TMGCFG2 0xf45a
+#define TMGCFG2 REGx(R0900_P1_TMGCFG2)
+#define F0900_P1_SFRRATIO_FINE 0xf45a0001
+
+/*P1_KREFTMG2*/
+#define R0900_P1_KREFTMG2 0xf45b
+#define KREFTMG2 REGx(R0900_P1_KREFTMG2)
+#define F0900_P1_KREF_TMG2 0xf45b00ff
 
 /*P1_SFRINIT1*/
-#define R0900_P1_SFRINIT1  0xf45e
-#define F0900_P1_SFR_INIT1  0xf45e00ff
+#define R0900_P1_SFRINIT1 0xf45e
+#define SFRINIT1 REGx(R0900_P1_SFRINIT1)
+#define F0900_P1_SFR_INIT1 0xf45e007f
 
 /*P1_SFRINIT0*/
-#define R0900_P1_SFRINIT0  0xf45f
-#define F0900_P1_SFR_INIT0  0xf45f00ff
+#define R0900_P1_SFRINIT0 0xf45f
+#define SFRINIT0 REGx(R0900_P1_SFRINIT0)
+#define F0900_P1_SFR_INIT0 0xf45f00ff
 
 /*P1_SFRUP1*/
-#define R0900_P1_SFRUP1  0xf460
-#define F0900_P1_AUTO_GUP  0xf4600080
-#define F0900_P1_SYMB_FREQ_UP1  0xf460007f
+#define R0900_P1_SFRUP1 0xf460
+#define SFRUP1 REGx(R0900_P1_SFRUP1)
+#define F0900_P1_AUTO_GUP 0xf4600080
+#define AUTO_GUP FLDx(F0900_P1_AUTO_GUP)
+#define F0900_P1_SYMB_FREQ_UP1 0xf460007f
 
 /*P1_SFRUP0*/
-#define R0900_P1_SFRUP0  0xf461
-#define F0900_P1_SYMB_FREQ_UP0  0xf46100ff
+#define R0900_P1_SFRUP0 0xf461
+#define SFRUP0 REGx(R0900_P1_SFRUP0)
+#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff
 
 /*P1_SFRLOW1*/
-#define R0900_P1_SFRLOW1  0xf462
-#define F0900_P1_AUTO_GLOW  0xf4620080
-#define F0900_P1_SYMB_FREQ_LOW1  0xf462007f
+#define R0900_P1_SFRLOW1 0xf462
+#define SFRLOW1 REGx(R0900_P1_SFRLOW1)
+#define F0900_P1_AUTO_GLOW 0xf4620080
+#define AUTO_GLOW FLDx(F0900_P1_AUTO_GLOW)
+#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f
 
 /*P1_SFRLOW0*/
-#define R0900_P1_SFRLOW0  0xf463
-#define F0900_P1_SYMB_FREQ_LOW0  0xf46300ff
+#define R0900_P1_SFRLOW0 0xf463
+#define SFRLOW0 REGx(R0900_P1_SFRLOW0)
+#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff
 
 /*P1_SFR3*/
-#define R0900_P1_SFR3  0xf464
-#define F0900_P1_SYMB_FREQ3  0xf46400ff
+#define R0900_P1_SFR3 0xf464
+#define SFR3 REGx(R0900_P1_SFR3)
+#define F0900_P1_SYMB_FREQ3 0xf46400ff
+#define SYMB_FREQ3 FLDx(F0900_P1_SYMB_FREQ3)
 
 /*P1_SFR2*/
-#define R0900_P1_SFR2  0xf465
-#define F0900_P1_SYMB_FREQ2  0xf46500ff
+#define R0900_P1_SFR2 0xf465
+#define SFR2 REGx(R0900_P1_SFR2)
+#define F0900_P1_SYMB_FREQ2 0xf46500ff
+#define SYMB_FREQ2 FLDx(F0900_P1_SYMB_FREQ2)
 
 /*P1_SFR1*/
-#define R0900_P1_SFR1  0xf466
-#define F0900_P1_SYMB_FREQ1  0xf46600ff
+#define R0900_P1_SFR1 0xf466
+#define SFR1 REGx(R0900_P1_SFR1)
+#define F0900_P1_SYMB_FREQ1 0xf46600ff
+#define SYMB_FREQ1 FLDx(F0900_P1_SYMB_FREQ1)
 
 /*P1_SFR0*/
-#define R0900_P1_SFR0  0xf467
-#define F0900_P1_SYMB_FREQ0  0xf46700ff
+#define R0900_P1_SFR0 0xf467
+#define SFR0 REGx(R0900_P1_SFR0)
+#define F0900_P1_SYMB_FREQ0 0xf46700ff
+#define SYMB_FREQ0 FLDx(F0900_P1_SYMB_FREQ0)
 
 /*P1_TMGREG2*/
-#define R0900_P1_TMGREG2  0xf468
-#define F0900_P1_TMGREG2  0xf46800ff
+#define R0900_P1_TMGREG2 0xf468
+#define TMGREG2 REGx(R0900_P1_TMGREG2)
+#define F0900_P1_TMGREG2 0xf46800ff
 
 /*P1_TMGREG1*/
-#define R0900_P1_TMGREG1  0xf469
-#define F0900_P1_TMGREG1  0xf46900ff
+#define R0900_P1_TMGREG1 0xf469
+#define TMGREG1 REGx(R0900_P1_TMGREG1)
+#define F0900_P1_TMGREG1 0xf46900ff
 
 /*P1_TMGREG0*/
-#define R0900_P1_TMGREG0  0xf46a
-#define F0900_P1_TMGREG0  0xf46a00ff
+#define R0900_P1_TMGREG0 0xf46a
+#define TMGREG0 REGx(R0900_P1_TMGREG0)
+#define F0900_P1_TMGREG0 0xf46a00ff
 
 /*P1_TMGLOCK1*/
-#define R0900_P1_TMGLOCK1  0xf46b
-#define F0900_P1_TMGLOCK_LEVEL1  0xf46b01ff
+#define R0900_P1_TMGLOCK1 0xf46b
+#define TMGLOCK1 REGx(R0900_P1_TMGLOCK1)
+#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff
 
 /*P1_TMGLOCK0*/
-#define R0900_P1_TMGLOCK0  0xf46c
-#define F0900_P1_TMGLOCK_LEVEL0  0xf46c00ff
+#define R0900_P1_TMGLOCK0 0xf46c
+#define TMGLOCK0 REGx(R0900_P1_TMGLOCK0)
+#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff
 
 /*P1_TMGOBS*/
-#define R0900_P1_TMGOBS  0xf46d
-#define F0900_P1_ROLLOFF_STATUS  0xf46d00c0
-#define F0900_P1_SCAN_SIGN  0xf46d0030
-#define F0900_P1_TMG_SCANNING  0xf46d0008
-#define F0900_P1_CHCENTERING_MODE  0xf46d0004
-#define F0900_P1_TMG_SCANFAIL  0xf46d0002
+#define R0900_P1_TMGOBS 0xf46d
+#define TMGOBS REGx(R0900_P1_TMGOBS)
+#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0
+#define ROLLOFF_STATUS FLDx(F0900_P1_ROLLOFF_STATUS)
 
 /*P1_EQUALCFG*/
-#define R0900_P1_EQUALCFG  0xf46f
-#define F0900_P1_NOTMG_NEGALWAIT  0xf46f0080
-#define F0900_P1_EQUAL_ON  0xf46f0040
-#define F0900_P1_SEL_EQUALCOR  0xf46f0038
-#define F0900_P1_MU_EQUALDFE  0xf46f0007
+#define R0900_P1_EQUALCFG 0xf46f
+#define EQUALCFG REGx(R0900_P1_EQUALCFG)
+#define F0900_P1_EQUAL_ON 0xf46f0040
+#define F0900_P1_MU_EQUALDFE 0xf46f0007
 
 /*P1_EQUAI1*/
-#define R0900_P1_EQUAI1  0xf470
-#define F0900_P1_EQUA_ACCI1  0xf47001ff
+#define R0900_P1_EQUAI1 0xf470
+#define EQUAI1 REGx(R0900_P1_EQUAI1)
+#define F0900_P1_EQUA_ACCI1 0xf47001ff
 
 /*P1_EQUAQ1*/
-#define R0900_P1_EQUAQ1  0xf471
-#define F0900_P1_EQUA_ACCQ1  0xf47101ff
+#define R0900_P1_EQUAQ1 0xf471
+#define EQUAQ1 REGx(R0900_P1_EQUAQ1)
+#define F0900_P1_EQUA_ACCQ1 0xf47101ff
 
 /*P1_EQUAI2*/
-#define R0900_P1_EQUAI2  0xf472
-#define F0900_P1_EQUA_ACCI2  0xf47201ff
+#define R0900_P1_EQUAI2 0xf472
+#define EQUAI2 REGx(R0900_P1_EQUAI2)
+#define F0900_P1_EQUA_ACCI2 0xf47201ff
 
 /*P1_EQUAQ2*/
-#define R0900_P1_EQUAQ2  0xf473
-#define F0900_P1_EQUA_ACCQ2  0xf47301ff
+#define R0900_P1_EQUAQ2 0xf473
+#define EQUAQ2 REGx(R0900_P1_EQUAQ2)
+#define F0900_P1_EQUA_ACCQ2 0xf47301ff
 
 /*P1_EQUAI3*/
-#define R0900_P1_EQUAI3  0xf474
-#define F0900_P1_EQUA_ACCI3  0xf47401ff
+#define R0900_P1_EQUAI3 0xf474
+#define EQUAI3 REGx(R0900_P1_EQUAI3)
+#define F0900_P1_EQUA_ACCI3 0xf47401ff
 
 /*P1_EQUAQ3*/
-#define R0900_P1_EQUAQ3  0xf475
-#define F0900_P1_EQUA_ACCQ3  0xf47501ff
+#define R0900_P1_EQUAQ3 0xf475
+#define EQUAQ3 REGx(R0900_P1_EQUAQ3)
+#define F0900_P1_EQUA_ACCQ3 0xf47501ff
 
 /*P1_EQUAI4*/
-#define R0900_P1_EQUAI4  0xf476
-#define F0900_P1_EQUA_ACCI4  0xf47601ff
+#define R0900_P1_EQUAI4 0xf476
+#define EQUAI4 REGx(R0900_P1_EQUAI4)
+#define F0900_P1_EQUA_ACCI4 0xf47601ff
 
 /*P1_EQUAQ4*/
-#define R0900_P1_EQUAQ4  0xf477
-#define F0900_P1_EQUA_ACCQ4  0xf47701ff
+#define R0900_P1_EQUAQ4 0xf477
+#define EQUAQ4 REGx(R0900_P1_EQUAQ4)
+#define F0900_P1_EQUA_ACCQ4 0xf47701ff
 
 /*P1_EQUAI5*/
-#define R0900_P1_EQUAI5  0xf478
-#define F0900_P1_EQUA_ACCI5  0xf47801ff
+#define R0900_P1_EQUAI5 0xf478
+#define EQUAI5 REGx(R0900_P1_EQUAI5)
+#define F0900_P1_EQUA_ACCI5 0xf47801ff
 
 /*P1_EQUAQ5*/
-#define R0900_P1_EQUAQ5  0xf479
-#define F0900_P1_EQUA_ACCQ5  0xf47901ff
+#define R0900_P1_EQUAQ5 0xf479
+#define EQUAQ5 REGx(R0900_P1_EQUAQ5)
+#define F0900_P1_EQUA_ACCQ5 0xf47901ff
 
 /*P1_EQUAI6*/
-#define R0900_P1_EQUAI6  0xf47a
-#define F0900_P1_EQUA_ACCI6  0xf47a01ff
+#define R0900_P1_EQUAI6 0xf47a
+#define EQUAI6 REGx(R0900_P1_EQUAI6)
+#define F0900_P1_EQUA_ACCI6 0xf47a01ff
 
 /*P1_EQUAQ6*/
-#define R0900_P1_EQUAQ6  0xf47b
-#define F0900_P1_EQUA_ACCQ6  0xf47b01ff
+#define R0900_P1_EQUAQ6 0xf47b
+#define EQUAQ6 REGx(R0900_P1_EQUAQ6)
+#define F0900_P1_EQUA_ACCQ6 0xf47b01ff
 
 /*P1_EQUAI7*/
-#define R0900_P1_EQUAI7  0xf47c
-#define F0900_P1_EQUA_ACCI7  0xf47c01ff
+#define R0900_P1_EQUAI7 0xf47c
+#define EQUAI7 REGx(R0900_P1_EQUAI7)
+#define F0900_P1_EQUA_ACCI7 0xf47c01ff
 
 /*P1_EQUAQ7*/
-#define R0900_P1_EQUAQ7  0xf47d
-#define F0900_P1_EQUA_ACCQ7  0xf47d01ff
+#define R0900_P1_EQUAQ7 0xf47d
+#define EQUAQ7 REGx(R0900_P1_EQUAQ7)
+#define F0900_P1_EQUA_ACCQ7 0xf47d01ff
 
 /*P1_EQUAI8*/
-#define R0900_P1_EQUAI8  0xf47e
-#define F0900_P1_EQUA_ACCI8  0xf47e01ff
+#define R0900_P1_EQUAI8 0xf47e
+#define EQUAI8 REGx(R0900_P1_EQUAI8)
+#define F0900_P1_EQUA_ACCI8 0xf47e01ff
 
 /*P1_EQUAQ8*/
-#define R0900_P1_EQUAQ8  0xf47f
-#define F0900_P1_EQUA_ACCQ8  0xf47f01ff
+#define R0900_P1_EQUAQ8 0xf47f
+#define EQUAQ8 REGx(R0900_P1_EQUAQ8)
+#define F0900_P1_EQUA_ACCQ8 0xf47f01ff
 
 /*P1_NNOSDATAT1*/
-#define R0900_P1_NNOSDATAT1  0xf480
-#define F0900_P1_NOSDATAT_NORMED1  0xf48000ff
+#define R0900_P1_NNOSDATAT1 0xf480
+#define NNOSDATAT1 REGx(R0900_P1_NNOSDATAT1)
+#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff
+#define NOSDATAT_NORMED1 FLDx(F0900_P1_NOSDATAT_NORMED1)
 
 /*P1_NNOSDATAT0*/
-#define R0900_P1_NNOSDATAT0  0xf481
-#define F0900_P1_NOSDATAT_NORMED0  0xf48100ff
+#define R0900_P1_NNOSDATAT0 0xf481
+#define NNOSDATAT0 REGx(R0900_P1_NNOSDATAT0)
+#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff
+#define NOSDATAT_NORMED0 FLDx(F0900_P1_NOSDATAT_NORMED0)
 
 /*P1_NNOSDATA1*/
-#define R0900_P1_NNOSDATA1  0xf482
-#define F0900_P1_NOSDATA_NORMED1  0xf48200ff
+#define R0900_P1_NNOSDATA1 0xf482
+#define NNOSDATA1 REGx(R0900_P1_NNOSDATA1)
+#define F0900_P1_NOSDATA_NORMED1 0xf48200ff
 
 /*P1_NNOSDATA0*/
-#define R0900_P1_NNOSDATA0  0xf483
-#define F0900_P1_NOSDATA_NORMED0  0xf48300ff
+#define R0900_P1_NNOSDATA0 0xf483
+#define NNOSDATA0 REGx(R0900_P1_NNOSDATA0)
+#define F0900_P1_NOSDATA_NORMED0 0xf48300ff
 
 /*P1_NNOSPLHT1*/
-#define R0900_P1_NNOSPLHT1  0xf484
-#define F0900_P1_NOSPLHT_NORMED1  0xf48400ff
+#define R0900_P1_NNOSPLHT1 0xf484
+#define NNOSPLHT1 REGx(R0900_P1_NNOSPLHT1)
+#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff
+#define NOSPLHT_NORMED1 FLDx(F0900_P1_NOSPLHT_NORMED1)
 
 /*P1_NNOSPLHT0*/
-#define R0900_P1_NNOSPLHT0  0xf485
-#define F0900_P1_NOSPLHT_NORMED0  0xf48500ff
+#define R0900_P1_NNOSPLHT0 0xf485
+#define NNOSPLHT0 REGx(R0900_P1_NNOSPLHT0)
+#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff
+#define NOSPLHT_NORMED0 FLDx(F0900_P1_NOSPLHT_NORMED0)
 
 /*P1_NNOSPLH1*/
-#define R0900_P1_NNOSPLH1  0xf486
-#define F0900_P1_NOSPLH_NORMED1  0xf48600ff
+#define R0900_P1_NNOSPLH1 0xf486
+#define NNOSPLH1 REGx(R0900_P1_NNOSPLH1)
+#define F0900_P1_NOSPLH_NORMED1 0xf48600ff
 
 /*P1_NNOSPLH0*/
-#define R0900_P1_NNOSPLH0  0xf487
-#define F0900_P1_NOSPLH_NORMED0  0xf48700ff
+#define R0900_P1_NNOSPLH0 0xf487
+#define NNOSPLH0 REGx(R0900_P1_NNOSPLH0)
+#define F0900_P1_NOSPLH_NORMED0 0xf48700ff
 
 /*P1_NOSDATAT1*/
-#define R0900_P1_NOSDATAT1  0xf488
-#define F0900_P1_NOSDATAT_UNNORMED1  0xf48800ff
+#define R0900_P1_NOSDATAT1 0xf488
+#define NOSDATAT1 REGx(R0900_P1_NOSDATAT1)
+#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff
 
 /*P1_NOSDATAT0*/
-#define R0900_P1_NOSDATAT0  0xf489
-#define F0900_P1_NOSDATAT_UNNORMED0  0xf48900ff
+#define R0900_P1_NOSDATAT0 0xf489
+#define NOSDATAT0 REGx(R0900_P1_NOSDATAT0)
+#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff
 
 /*P1_NOSDATA1*/
-#define R0900_P1_NOSDATA1  0xf48a
-#define F0900_P1_NOSDATA_UNNORMED1  0xf48a00ff
+#define R0900_P1_NOSDATA1 0xf48a
+#define NOSDATA1 REGx(R0900_P1_NOSDATA1)
+#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff
 
 /*P1_NOSDATA0*/
-#define R0900_P1_NOSDATA0  0xf48b
-#define F0900_P1_NOSDATA_UNNORMED0  0xf48b00ff
+#define R0900_P1_NOSDATA0 0xf48b
+#define NOSDATA0 REGx(R0900_P1_NOSDATA0)
+#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff
 
 /*P1_NOSPLHT1*/
-#define R0900_P1_NOSPLHT1  0xf48c
-#define F0900_P1_NOSPLHT_UNNORMED1  0xf48c00ff
+#define R0900_P1_NOSPLHT1 0xf48c
+#define NOSPLHT1 REGx(R0900_P1_NOSPLHT1)
+#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff
 
 /*P1_NOSPLHT0*/
-#define R0900_P1_NOSPLHT0  0xf48d
-#define F0900_P1_NOSPLHT_UNNORMED0  0xf48d00ff
+#define R0900_P1_NOSPLHT0 0xf48d
+#define NOSPLHT0 REGx(R0900_P1_NOSPLHT0)
+#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff
 
 /*P1_NOSPLH1*/
-#define R0900_P1_NOSPLH1  0xf48e
-#define F0900_P1_NOSPLH_UNNORMED1  0xf48e00ff
+#define R0900_P1_NOSPLH1 0xf48e
+#define NOSPLH1 REGx(R0900_P1_NOSPLH1)
+#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff
 
 /*P1_NOSPLH0*/
-#define R0900_P1_NOSPLH0  0xf48f
-#define F0900_P1_NOSPLH_UNNORMED0  0xf48f00ff
+#define R0900_P1_NOSPLH0 0xf48f
+#define NOSPLH0 REGx(R0900_P1_NOSPLH0)
+#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff
 
 /*P1_CAR2CFG*/
-#define R0900_P1_CAR2CFG  0xf490
-#define F0900_P1_DESCRAMB_OFF  0xf4900080
-#define F0900_P1_PN4_SELECT  0xf4900040
-#define F0900_P1_CFR2_STOPDVBS1  0xf4900020
-#define F0900_P1_STOP_CFR2UPDATE  0xf4900010
-#define F0900_P1_STOP_NCO2UPDATE  0xf4900008
-#define F0900_P1_ROTA2ON  0xf4900004
-#define F0900_P1_PH_DET_ALGO2  0xf4900003
-
-/*P1_ACLC2*/
-#define R0900_P1_ACLC2  0xf491
-#define F0900_P1_CAR2_PUNCT_ADERAT  0xf4910040
-#define F0900_P1_CAR2_ALPHA_MANT  0xf4910030
-#define F0900_P1_CAR2_ALPHA_EXP  0xf491000f
-
-/*P1_BCLC2*/
-#define R0900_P1_BCLC2  0xf492
-#define F0900_P1_DVBS2_NIP  0xf4920080
-#define F0900_P1_CAR2_PUNCT_BDERAT  0xf4920040
-#define F0900_P1_CAR2_BETA_MANT  0xf4920030
-#define F0900_P1_CAR2_BETA_EXP  0xf492000f
+#define R0900_P1_CAR2CFG 0xf490
+#define CAR2CFG REGx(R0900_P1_CAR2CFG)
+#define F0900_P1_CARRIER3_DISABLE 0xf4900040
+#define F0900_P1_ROTA2ON 0xf4900004
+#define F0900_P1_PH_DET_ALGO2 0xf4900003
+
+/*P1_CFR2CFR1*/
+#define R0900_P1_CFR2CFR1 0xf491
+#define CFR2CFR1 REGx(R0900_P1_CFR2CFR1)
+#define F0900_P1_CFR2TOCFR1_DVBS1 0xf49100c0
+#define F0900_P1_EN_S2CAR2CENTER 0xf4910020
+#define F0900_P1_DIS_BCHERRCFR2 0xf4910010
+#define F0900_P1_CFR2TOCFR1_BETA 0xf4910007
 
 /*P1_CFR22*/
-#define R0900_P1_CFR22  0xf493
-#define F0900_P1_CAR2_FREQ2  0xf49301ff
+#define R0900_P1_CFR22 0xf493
+#define CFR22 REGx(R0900_P1_CFR22)
+#define F0900_P1_CAR2_FREQ2 0xf49301ff
 
 /*P1_CFR21*/
-#define R0900_P1_CFR21  0xf494
-#define F0900_P1_CAR2_FREQ1  0xf49400ff
+#define R0900_P1_CFR21 0xf494
+#define CFR21 REGx(R0900_P1_CFR21)
+#define F0900_P1_CAR2_FREQ1 0xf49400ff
 
 /*P1_CFR20*/
-#define R0900_P1_CFR20  0xf495
-#define F0900_P1_CAR2_FREQ0  0xf49500ff
+#define R0900_P1_CFR20 0xf495
+#define CFR20 REGx(R0900_P1_CFR20)
+#define F0900_P1_CAR2_FREQ0 0xf49500ff
 
 /*P1_ACLC2S2Q*/
-#define R0900_P1_ACLC2S2Q  0xf497
-#define F0900_P1_ENAB_SPSKSYMB  0xf4970080
-#define F0900_P1_CAR2S2_QADERAT  0xf4970040
-#define F0900_P1_CAR2S2_Q_ALPH_M  0xf4970030
-#define F0900_P1_CAR2S2_Q_ALPH_E  0xf497000f
+#define R0900_P1_ACLC2S2Q 0xf497
+#define ACLC2S2Q REGx(R0900_P1_ACLC2S2Q)
+#define F0900_P1_ENAB_SPSKSYMB 0xf4970080
+#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030
+#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f
 
 /*P1_ACLC2S28*/
-#define R0900_P1_ACLC2S28  0xf498
-#define F0900_P1_OLDI3Q_MODE  0xf4980080
-#define F0900_P1_CAR2S2_8ADERAT  0xf4980040
-#define F0900_P1_CAR2S2_8_ALPH_M  0xf4980030
-#define F0900_P1_CAR2S2_8_ALPH_E  0xf498000f
+#define R0900_P1_ACLC2S28 0xf498
+#define ACLC2S28 REGx(R0900_P1_ACLC2S28)
+#define F0900_P1_OLDI3Q_MODE 0xf4980080
+#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030
+#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f
 
 /*P1_ACLC2S216A*/
-#define R0900_P1_ACLC2S216A  0xf499
-#define F0900_P1_CAR2S2_16ADERAT  0xf4990040
-#define F0900_P1_CAR2S2_16A_ALPH_M  0xf4990030
-#define F0900_P1_CAR2S2_16A_ALPH_E  0xf499000f
+#define R0900_P1_ACLC2S216A 0xf499
+#define ACLC2S216A REGx(R0900_P1_ACLC2S216A)
+#define F0900_P1_DIS_C3STOPA2 0xf4990080
+#define F0900_P1_CAR2S2_16ADERAT 0xf4990040
+#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030
+#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f
 
 /*P1_ACLC2S232A*/
-#define R0900_P1_ACLC2S232A  0xf49a
-#define F0900_P1_CAR2S2_32ADERAT  0xf49a0040
-#define F0900_P1_CAR2S2_32A_ALPH_M  0xf49a0030
-#define F0900_P1_CAR2S2_32A_ALPH_E  0xf49a000f
+#define R0900_P1_ACLC2S232A 0xf49a
+#define ACLC2S232A REGx(R0900_P1_ACLC2S232A)
+#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040
+#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030
+#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f
 
 /*P1_BCLC2S2Q*/
-#define R0900_P1_BCLC2S2Q  0xf49c
-#define F0900_P1_DVBS2S2Q_NIP  0xf49c0080
-#define F0900_P1_CAR2S2_QBDERAT  0xf49c0040
-#define F0900_P1_CAR2S2_Q_BETA_M  0xf49c0030
-#define F0900_P1_CAR2S2_Q_BETA_E  0xf49c000f
+#define R0900_P1_BCLC2S2Q 0xf49c
+#define BCLC2S2Q REGx(R0900_P1_BCLC2S2Q)
+#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030
+#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f
 
 /*P1_BCLC2S28*/
-#define R0900_P1_BCLC2S28  0xf49d
-#define F0900_P1_DVBS2S28_NIP  0xf49d0080
-#define F0900_P1_CAR2S2_8BDERAT  0xf49d0040
-#define F0900_P1_CAR2S2_8_BETA_M  0xf49d0030
-#define F0900_P1_CAR2S2_8_BETA_E  0xf49d000f
+#define R0900_P1_BCLC2S28 0xf49d
+#define BCLC2S28 REGx(R0900_P1_BCLC2S28)
+#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030
+#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f
 
 /*P1_BCLC2S216A*/
-#define R0900_P1_BCLC2S216A  0xf49e
-#define F0900_P1_DVBS2S216A_NIP  0xf49e0080
-#define F0900_P1_CAR2S2_16BDERAT  0xf49e0040
-#define F0900_P1_CAR2S2_16A_BETA_M  0xf49e0030
-#define F0900_P1_CAR2S2_16A_BETA_E  0xf49e000f
+#define R0900_P1_BCLC2S216A 0xf49e
+#define BCLC2S216A REGx(R0900_P1_BCLC2S216A)
 
 /*P1_BCLC2S232A*/
-#define R0900_P1_BCLC2S232A  0xf49f
-#define F0900_P1_DVBS2S232A_NIP  0xf49f0080
-#define F0900_P1_CAR2S2_32BDERAT  0xf49f0040
-#define F0900_P1_CAR2S2_32A_BETA_M  0xf49f0030
-#define F0900_P1_CAR2S2_32A_BETA_E  0xf49f000f
+#define R0900_P1_BCLC2S232A 0xf49f
+#define BCLC2S232A REGx(R0900_P1_BCLC2S232A)
 
 /*P1_PLROOT2*/
-#define R0900_P1_PLROOT2  0xf4ac
-#define F0900_P1_SHORTFR_DISABLE  0xf4ac0080
-#define F0900_P1_LONGFR_DISABLE  0xf4ac0040
-#define F0900_P1_DUMMYPL_DISABLE  0xf4ac0020
-#define F0900_P1_SHORTFR_AVOID  0xf4ac0010
-#define F0900_P1_PLSCRAMB_MODE  0xf4ac000c
-#define F0900_P1_PLSCRAMB_ROOT2  0xf4ac0003
+#define R0900_P1_PLROOT2 0xf4ac
+#define PLROOT2 REGx(R0900_P1_PLROOT2)
+#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c
+#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003
 
 /*P1_PLROOT1*/
-#define R0900_P1_PLROOT1  0xf4ad
-#define F0900_P1_PLSCRAMB_ROOT1  0xf4ad00ff
+#define R0900_P1_PLROOT1 0xf4ad
+#define PLROOT1 REGx(R0900_P1_PLROOT1)
+#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff
 
 /*P1_PLROOT0*/
-#define R0900_P1_PLROOT0  0xf4ae
-#define F0900_P1_PLSCRAMB_ROOT0  0xf4ae00ff
+#define R0900_P1_PLROOT0 0xf4ae
+#define PLROOT0 REGx(R0900_P1_PLROOT0)
+#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff
 
 /*P1_MODCODLST0*/
-#define R0900_P1_MODCODLST0  0xf4b0
-#define F0900_P1_EN_TOKEN31  0xf4b00080
-#define F0900_P1_SYNCTAG_SELECT  0xf4b00040
-#define F0900_P1_MODCODRQ_MODE  0xf4b00030
+#define R0900_P1_MODCODLST0 0xf4b0
+#define MODCODLST0 REGx(R0900_P1_MODCODLST0)
 
 /*P1_MODCODLST1*/
-#define R0900_P1_MODCODLST1  0xf4b1
-#define F0900_P1_DIS_MODCOD29  0xf4b100f0
-#define F0900_P1_DIS_32PSK_9_10  0xf4b1000f
+#define R0900_P1_MODCODLST1 0xf4b1
+#define MODCODLST1 REGx(R0900_P1_MODCODLST1)
+#define F0900_P1_DIS_MODCOD29 0xf4b100f0
+#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f
 
 /*P1_MODCODLST2*/
-#define R0900_P1_MODCODLST2  0xf4b2
-#define F0900_P1_DIS_32PSK_8_9  0xf4b200f0
-#define F0900_P1_DIS_32PSK_5_6  0xf4b2000f
+#define R0900_P1_MODCODLST2 0xf4b2
+#define MODCODLST2 REGx(R0900_P1_MODCODLST2)
+#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0
+#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f
 
 /*P1_MODCODLST3*/
-#define R0900_P1_MODCODLST3  0xf4b3
-#define F0900_P1_DIS_32PSK_4_5  0xf4b300f0
-#define F0900_P1_DIS_32PSK_3_4  0xf4b3000f
+#define R0900_P1_MODCODLST3 0xf4b3
+#define MODCODLST3 REGx(R0900_P1_MODCODLST3)
+#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0
+#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f
 
 /*P1_MODCODLST4*/
-#define R0900_P1_MODCODLST4  0xf4b4
-#define F0900_P1_DIS_16PSK_9_10  0xf4b400f0
-#define F0900_P1_DIS_16PSK_8_9  0xf4b4000f
+#define R0900_P1_MODCODLST4 0xf4b4
+#define MODCODLST4 REGx(R0900_P1_MODCODLST4)
+#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0
+#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f
 
 /*P1_MODCODLST5*/
-#define R0900_P1_MODCODLST5  0xf4b5
-#define F0900_P1_DIS_16PSK_5_6  0xf4b500f0
-#define F0900_P1_DIS_16PSK_4_5  0xf4b5000f
+#define R0900_P1_MODCODLST5 0xf4b5
+#define MODCODLST5 REGx(R0900_P1_MODCODLST5)
+#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0
+#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f
 
 /*P1_MODCODLST6*/
-#define R0900_P1_MODCODLST6  0xf4b6
-#define F0900_P1_DIS_16PSK_3_4  0xf4b600f0
-#define F0900_P1_DIS_16PSK_2_3  0xf4b6000f
+#define R0900_P1_MODCODLST6 0xf4b6
+#define MODCODLST6 REGx(R0900_P1_MODCODLST6)
+#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0
+#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f
 
 /*P1_MODCODLST7*/
-#define R0900_P1_MODCODLST7  0xf4b7
-#define F0900_P1_DIS_8P_9_10  0xf4b700f0
-#define F0900_P1_DIS_8P_8_9  0xf4b7000f
+#define R0900_P1_MODCODLST7 0xf4b7
+#define MODCODLST7 REGx(R0900_P1_MODCODLST7)
+#define F0900_P1_DIS_8P_9_10 0xf4b700f0
+#define F0900_P1_DIS_8P_8_9 0xf4b7000f
 
 /*P1_MODCODLST8*/
-#define R0900_P1_MODCODLST8  0xf4b8
-#define F0900_P1_DIS_8P_5_6  0xf4b800f0
-#define F0900_P1_DIS_8P_3_4  0xf4b8000f
+#define R0900_P1_MODCODLST8 0xf4b8
+#define MODCODLST8 REGx(R0900_P1_MODCODLST8)
+#define F0900_P1_DIS_8P_5_6 0xf4b800f0
+#define F0900_P1_DIS_8P_3_4 0xf4b8000f
 
 /*P1_MODCODLST9*/
-#define R0900_P1_MODCODLST9  0xf4b9
-#define F0900_P1_DIS_8P_2_3  0xf4b900f0
-#define F0900_P1_DIS_8P_3_5  0xf4b9000f
+#define R0900_P1_MODCODLST9 0xf4b9
+#define MODCODLST9 REGx(R0900_P1_MODCODLST9)
+#define F0900_P1_DIS_8P_2_3 0xf4b900f0
+#define F0900_P1_DIS_8P_3_5 0xf4b9000f
 
 /*P1_MODCODLSTA*/
-#define R0900_P1_MODCODLSTA  0xf4ba
-#define F0900_P1_DIS_QP_9_10  0xf4ba00f0
-#define F0900_P1_DIS_QP_8_9  0xf4ba000f
+#define R0900_P1_MODCODLSTA 0xf4ba
+#define MODCODLSTA REGx(R0900_P1_MODCODLSTA)
+#define F0900_P1_DIS_QP_9_10 0xf4ba00f0
+#define F0900_P1_DIS_QP_8_9 0xf4ba000f
 
 /*P1_MODCODLSTB*/
-#define R0900_P1_MODCODLSTB  0xf4bb
-#define F0900_P1_DIS_QP_5_6  0xf4bb00f0
-#define F0900_P1_DIS_QP_4_5  0xf4bb000f
+#define R0900_P1_MODCODLSTB 0xf4bb
+#define MODCODLSTB REGx(R0900_P1_MODCODLSTB)
+#define F0900_P1_DIS_QP_5_6 0xf4bb00f0
+#define F0900_P1_DIS_QP_4_5 0xf4bb000f
 
 /*P1_MODCODLSTC*/
-#define R0900_P1_MODCODLSTC  0xf4bc
-#define F0900_P1_DIS_QP_3_4  0xf4bc00f0
-#define F0900_P1_DIS_QP_2_3  0xf4bc000f
+#define R0900_P1_MODCODLSTC 0xf4bc
+#define MODCODLSTC REGx(R0900_P1_MODCODLSTC)
+#define F0900_P1_DIS_QP_3_4 0xf4bc00f0
+#define F0900_P1_DIS_QP_2_3 0xf4bc000f
 
 /*P1_MODCODLSTD*/
-#define R0900_P1_MODCODLSTD  0xf4bd
-#define F0900_P1_DIS_QP_3_5  0xf4bd00f0
-#define F0900_P1_DIS_QP_1_2  0xf4bd000f
+#define R0900_P1_MODCODLSTD 0xf4bd
+#define MODCODLSTD REGx(R0900_P1_MODCODLSTD)
+#define F0900_P1_DIS_QP_3_5 0xf4bd00f0
+#define F0900_P1_DIS_QP_1_2 0xf4bd000f
 
 /*P1_MODCODLSTE*/
-#define R0900_P1_MODCODLSTE  0xf4be
-#define F0900_P1_DIS_QP_2_5  0xf4be00f0
-#define F0900_P1_DIS_QP_1_3  0xf4be000f
+#define R0900_P1_MODCODLSTE 0xf4be
+#define MODCODLSTE REGx(R0900_P1_MODCODLSTE)
+#define F0900_P1_DIS_QP_2_5 0xf4be00f0
+#define F0900_P1_DIS_QP_1_3 0xf4be000f
 
 /*P1_MODCODLSTF*/
-#define R0900_P1_MODCODLSTF  0xf4bf
-#define F0900_P1_DIS_QP_1_4  0xf4bf00f0
-#define F0900_P1_DDEMOD_SET  0xf4bf0002
-#define F0900_P1_DDEMOD_MASK  0xf4bf0001
+#define R0900_P1_MODCODLSTF 0xf4bf
+#define MODCODLSTF REGx(R0900_P1_MODCODLSTF)
+#define F0900_P1_DIS_QP_1_4 0xf4bf00f0
+
+/*P1_GAUSSR0*/
+#define R0900_P1_GAUSSR0 0xf4c0
+#define GAUSSR0 REGx(R0900_P1_GAUSSR0)
+#define F0900_P1_EN_CCIMODE 0xf4c00080
+#define F0900_P1_R0_GAUSSIEN 0xf4c0007f
+
+/*P1_CCIR0*/
+#define R0900_P1_CCIR0 0xf4c1
+#define CCIR0 REGx(R0900_P1_CCIR0)
+#define F0900_P1_CCIDETECT_PLHONLY 0xf4c10080
+#define F0900_P1_R0_CCI 0xf4c1007f
+
+/*P1_CCIQUANT*/
+#define R0900_P1_CCIQUANT 0xf4c2
+#define CCIQUANT REGx(R0900_P1_CCIQUANT)
+#define F0900_P1_CCI_BETA 0xf4c200e0
+#define F0900_P1_CCI_QUANT 0xf4c2001f
+
+/*P1_CCITHRES*/
+#define R0900_P1_CCITHRES 0xf4c3
+#define CCITHRES REGx(R0900_P1_CCITHRES)
+#define F0900_P1_CCI_THRESHOLD 0xf4c300ff
+
+/*P1_CCIACC*/
+#define R0900_P1_CCIACC 0xf4c4
+#define CCIACC REGx(R0900_P1_CCIACC)
+#define F0900_P1_CCI_VALUE 0xf4c400ff
 
 /*P1_DMDRESCFG*/
-#define R0900_P1_DMDRESCFG  0xf4c6
-#define F0900_P1_DMDRES_RESET  0xf4c60080
-#define F0900_P1_DMDRES_NOISESQR  0xf4c60010
-#define F0900_P1_DMDRES_STRALL  0xf4c60008
-#define F0900_P1_DMDRES_NEWONLY  0xf4c60004
-#define F0900_P1_DMDRES_NOSTORE  0xf4c60002
-#define F0900_P1_DMDRES_AGC2MEM  0xf4c60001
+#define R0900_P1_DMDRESCFG 0xf4c6
+#define DMDRESCFG REGx(R0900_P1_DMDRESCFG)
+#define F0900_P1_DMDRES_RESET 0xf4c60080
+#define F0900_P1_DMDRES_STRALL 0xf4c60008
+#define F0900_P1_DMDRES_NEWONLY 0xf4c60004
+#define F0900_P1_DMDRES_NOSTORE 0xf4c60002
 
 /*P1_DMDRESADR*/
-#define R0900_P1_DMDRESADR  0xf4c7
-#define F0900_P1_SUSP_PREDCANAL  0xf4c70080
-#define F0900_P1_DMDRES_VALIDCFR  0xf4c70040
-#define F0900_P1_DMDRES_MEMFULL  0xf4c70030
-#define F0900_P1_DMDRES_RESNBR  0xf4c7000f
+#define R0900_P1_DMDRESADR 0xf4c7
+#define DMDRESADR REGx(R0900_P1_DMDRESADR)
+#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040
+#define F0900_P1_DMDRES_MEMFULL 0xf4c70030
+#define F0900_P1_DMDRES_RESNBR 0xf4c7000f
 
 /*P1_DMDRESDATA7*/
-#define R0900_P1_DMDRESDATA7  0xf4c8
-#define F0900_P1_DMDRES_DATA7  0xf4c800ff
+#define R0900_P1_DMDRESDATA7 0xf4c8
+#define F0900_P1_DMDRES_DATA7 0xf4c800ff
 
 /*P1_DMDRESDATA6*/
-#define R0900_P1_DMDRESDATA6  0xf4c9
-#define F0900_P1_DMDRES_DATA6  0xf4c900ff
+#define R0900_P1_DMDRESDATA6 0xf4c9
+#define F0900_P1_DMDRES_DATA6 0xf4c900ff
 
 /*P1_DMDRESDATA5*/
-#define R0900_P1_DMDRESDATA5  0xf4ca
-#define F0900_P1_DMDRES_DATA5  0xf4ca00ff
+#define R0900_P1_DMDRESDATA5 0xf4ca
+#define F0900_P1_DMDRES_DATA5 0xf4ca00ff
 
 /*P1_DMDRESDATA4*/
-#define R0900_P1_DMDRESDATA4  0xf4cb
-#define F0900_P1_DMDRES_DATA4  0xf4cb00ff
+#define R0900_P1_DMDRESDATA4 0xf4cb
+#define F0900_P1_DMDRES_DATA4 0xf4cb00ff
 
 /*P1_DMDRESDATA3*/
-#define R0900_P1_DMDRESDATA3  0xf4cc
-#define F0900_P1_DMDRES_DATA3  0xf4cc00ff
+#define R0900_P1_DMDRESDATA3 0xf4cc
+#define F0900_P1_DMDRES_DATA3 0xf4cc00ff
 
 /*P1_DMDRESDATA2*/
-#define R0900_P1_DMDRESDATA2  0xf4cd
-#define F0900_P1_DMDRES_DATA2  0xf4cd00ff
+#define R0900_P1_DMDRESDATA2 0xf4cd
+#define F0900_P1_DMDRES_DATA2 0xf4cd00ff
 
 /*P1_DMDRESDATA1*/
-#define R0900_P1_DMDRESDATA1  0xf4ce
-#define F0900_P1_DMDRES_DATA1  0xf4ce00ff
+#define R0900_P1_DMDRESDATA1 0xf4ce
+#define F0900_P1_DMDRES_DATA1 0xf4ce00ff
 
 /*P1_DMDRESDATA0*/
-#define R0900_P1_DMDRESDATA0  0xf4cf
-#define F0900_P1_DMDRES_DATA0  0xf4cf00ff
+#define R0900_P1_DMDRESDATA0 0xf4cf
+#define F0900_P1_DMDRES_DATA0 0xf4cf00ff
 
 /*P1_FFEI1*/
-#define R0900_P1_FFEI1  0xf4d0
-#define F0900_P1_FFE_ACCI1  0xf4d001ff
+#define R0900_P1_FFEI1 0xf4d0
+#define FFEI1 REGx(R0900_P1_FFEI1)
+#define F0900_P1_FFE_ACCI1 0xf4d001ff
 
 /*P1_FFEQ1*/
-#define R0900_P1_FFEQ1  0xf4d1
-#define F0900_P1_FFE_ACCQ1  0xf4d101ff
+#define R0900_P1_FFEQ1 0xf4d1
+#define FFEQ1 REGx(R0900_P1_FFEQ1)
+#define F0900_P1_FFE_ACCQ1 0xf4d101ff
 
 /*P1_FFEI2*/
-#define R0900_P1_FFEI2  0xf4d2
-#define F0900_P1_FFE_ACCI2  0xf4d201ff
+#define R0900_P1_FFEI2 0xf4d2
+#define FFEI2 REGx(R0900_P1_FFEI2)
+#define F0900_P1_FFE_ACCI2 0xf4d201ff
 
 /*P1_FFEQ2*/
-#define R0900_P1_FFEQ2  0xf4d3
-#define F0900_P1_FFE_ACCQ2  0xf4d301ff
+#define R0900_P1_FFEQ2 0xf4d3
+#define FFEQ2 REGx(R0900_P1_FFEQ2)
+#define F0900_P1_FFE_ACCQ2 0xf4d301ff
 
 /*P1_FFEI3*/
-#define R0900_P1_FFEI3  0xf4d4
-#define F0900_P1_FFE_ACCI3  0xf4d401ff
+#define R0900_P1_FFEI3 0xf4d4
+#define FFEI3 REGx(R0900_P1_FFEI3)
+#define F0900_P1_FFE_ACCI3 0xf4d401ff
 
 /*P1_FFEQ3*/
-#define R0900_P1_FFEQ3  0xf4d5
-#define F0900_P1_FFE_ACCQ3  0xf4d501ff
+#define R0900_P1_FFEQ3 0xf4d5
+#define FFEQ3 REGx(R0900_P1_FFEQ3)
+#define F0900_P1_FFE_ACCQ3 0xf4d501ff
 
 /*P1_FFEI4*/
-#define R0900_P1_FFEI4  0xf4d6
-#define F0900_P1_FFE_ACCI4  0xf4d601ff
+#define R0900_P1_FFEI4 0xf4d6
+#define FFEI4 REGx(R0900_P1_FFEI4)
+#define F0900_P1_FFE_ACCI4 0xf4d601ff
 
 /*P1_FFEQ4*/
-#define R0900_P1_FFEQ4  0xf4d7
-#define F0900_P1_FFE_ACCQ4  0xf4d701ff
+#define R0900_P1_FFEQ4 0xf4d7
+#define FFEQ4 REGx(R0900_P1_FFEQ4)
+#define F0900_P1_FFE_ACCQ4 0xf4d701ff
 
 /*P1_FFECFG*/
-#define R0900_P1_FFECFG  0xf4d8
-#define F0900_P1_EQUALFFE_ON  0xf4d80040
-#define F0900_P1_EQUAL_USEDSYMB  0xf4d80030
-#define F0900_P1_MU_EQUALFFE  0xf4d80007
+#define R0900_P1_FFECFG 0xf4d8
+#define FFECFG REGx(R0900_P1_FFECFG)
+#define F0900_P1_EQUALFFE_ON 0xf4d80040
+#define F0900_P1_MU_EQUALFFE 0xf4d80007
 
 /*P1_TNRCFG*/
-#define R0900_P1_TNRCFG  0xf4e0
-#define F0900_P1_TUN_ACKFAIL  0xf4e00080
-#define F0900_P1_TUN_TYPE  0xf4e00070
-#define F0900_P1_TUN_SECSTOP  0xf4e00008
-#define F0900_P1_TUN_VCOSRCH  0xf4e00004
-#define F0900_P1_TUN_MADDRESS  0xf4e00003
+#define R0900_P1_TNRCFG 0xf4e0
+#define TNRCFG REGx(R0900_P1_TNRCFG)
+#define F0900_P1_TUN_ACKFAIL 0xf4e00080
+#define F0900_P1_TUN_TYPE 0xf4e00070
+#define F0900_P1_TUN_SECSTOP 0xf4e00008
+#define F0900_P1_TUN_VCOSRCH 0xf4e00004
+#define F0900_P1_TUN_MADDRESS 0xf4e00003
 
 /*P1_TNRCFG2*/
-#define R0900_P1_TNRCFG2  0xf4e1
-#define F0900_P1_TUN_IQSWAP  0xf4e10080
-#define F0900_P1_STB6110_STEP2MHZ  0xf4e10040
-#define F0900_P1_STB6120_DBLI2C  0xf4e10020
-#define F0900_P1_DIS_FCCK  0xf4e10010
-#define F0900_P1_DIS_LPEN  0xf4e10008
-#define F0900_P1_DIS_BWCALC  0xf4e10004
-#define F0900_P1_SHORT_WAITSTATES  0xf4e10002
-#define F0900_P1_DIS_2BWAGC1  0xf4e10001
+#define R0900_P1_TNRCFG2 0xf4e1
+#define TNRCFG2 REGx(R0900_P1_TNRCFG2)
+#define F0900_P1_TUN_IQSWAP 0xf4e10080
+#define F0900_P1_DIS_BWCALC 0xf4e10004
+#define F0900_P1_SHORT_WAITSTATES 0xf4e10002
 
 /*P1_TNRXTAL*/
-#define R0900_P1_TNRXTAL  0xf4e4
-#define F0900_P1_TUN_MCLKDECIMAL  0xf4e400e0
-#define F0900_P1_TUN_XTALFREQ  0xf4e4001f
+#define R0900_P1_TNRXTAL 0xf4e4
+#define TNRXTAL REGx(R0900_P1_TNRXTAL)
+#define F0900_P1_TUN_XTALFREQ 0xf4e4001f
 
 /*P1_TNRSTEPS*/
-#define R0900_P1_TNRSTEPS  0xf4e7
-#define F0900_P1_TUNER_BW1P6  0xf4e70080
-#define F0900_P1_BWINC_OFFSET  0xf4e70070
-#define F0900_P1_SOFTSTEP_RNG  0xf4e70008
-#define F0900_P1_TUN_BWOFFSET  0xf4e70107
+#define R0900_P1_TNRSTEPS 0xf4e7
+#define TNRSTEPS REGx(R0900_P1_TNRSTEPS)
+#define F0900_P1_TUNER_BW0P125 0xf4e70080
+#define F0900_P1_BWINC_OFFSET 0xf4e70170
+#define F0900_P1_SOFTSTEP_RNG 0xf4e70008
+#define F0900_P1_TUN_BWOFFSET 0xf4e70007
 
 /*P1_TNRGAIN*/
-#define R0900_P1_TNRGAIN  0xf4e8
-#define F0900_P1_TUN_KDIVEN  0xf4e800c0
-#define F0900_P1_STB6X00_OCK  0xf4e80030
-#define F0900_P1_TUN_GAIN  0xf4e8000f
+#define R0900_P1_TNRGAIN 0xf4e8
+#define TNRGAIN REGx(R0900_P1_TNRGAIN)
+#define F0900_P1_TUN_KDIVEN 0xf4e800c0
+#define F0900_P1_STB6X00_OCK 0xf4e80030
+#define F0900_P1_TUN_GAIN 0xf4e8000f
 
 /*P1_TNRRF1*/
-#define R0900_P1_TNRRF1  0xf4e9
-#define F0900_P1_TUN_RFFREQ2  0xf4e900ff
+#define R0900_P1_TNRRF1 0xf4e9
+#define TNRRF1 REGx(R0900_P1_TNRRF1)
+#define F0900_P1_TUN_RFFREQ2 0xf4e900ff
 
 /*P1_TNRRF0*/
-#define R0900_P1_TNRRF0  0xf4ea
-#define F0900_P1_TUN_RFFREQ1  0xf4ea00ff
+#define R0900_P1_TNRRF0 0xf4ea
+#define TNRRF0 REGx(R0900_P1_TNRRF0)
+#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff
 
 /*P1_TNRBW*/
-#define R0900_P1_TNRBW  0xf4eb
-#define F0900_P1_TUN_RFFREQ0  0xf4eb00c0
-#define F0900_P1_TUN_BW  0xf4eb003f
+#define R0900_P1_TNRBW 0xf4eb
+#define TNRBW REGx(R0900_P1_TNRBW)
+#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0
+#define F0900_P1_TUN_BW 0xf4eb003f
 
 /*P1_TNRADJ*/
-#define R0900_P1_TNRADJ  0xf4ec
-#define F0900_P1_STB61X0_RCLK  0xf4ec0080
-#define F0900_P1_STB61X0_CALTIME  0xf4ec0040
-#define F0900_P1_STB6X00_DLB  0xf4ec0038
-#define F0900_P1_STB6000_FCL  0xf4ec0007
+#define R0900_P1_TNRADJ 0xf4ec
+#define TNRADJ REGx(R0900_P1_TNRADJ)
+#define F0900_P1_STB61X0_CALTIME 0xf4ec0040
 
 /*P1_TNRCTL2*/
-#define R0900_P1_TNRCTL2  0xf4ed
-#define F0900_P1_STB61X0_LCP1_RCCKOFF  0xf4ed0080
-#define F0900_P1_STB61X0_LCP0  0xf4ed0040
-#define F0900_P1_STB61X0_XTOUT_RFOUTS  0xf4ed0020
-#define F0900_P1_STB61X0_XTON_MCKDV  0xf4ed0010
-#define F0900_P1_STB61X0_CALOFF_DCOFF  0xf4ed0008
-#define F0900_P1_STB6110_LPT  0xf4ed0004
-#define F0900_P1_STB6110_RX  0xf4ed0002
-#define F0900_P1_STB6110_SYN  0xf4ed0001
+#define R0900_P1_TNRCTL2 0xf4ed
+#define TNRCTL2 REGx(R0900_P1_TNRCTL2)
+#define F0900_P1_STB61X0_RCCKOFF 0xf4ed0080
+#define F0900_P1_STB61X0_ICP_SDOFF 0xf4ed0040
+#define F0900_P1_STB61X0_DCLOOPOFF 0xf4ed0020
+#define F0900_P1_STB61X0_REFOUTSEL 0xf4ed0010
+#define F0900_P1_STB61X0_CALOFF 0xf4ed0008
+#define F0900_P1_STB6XX0_LPT_BEN 0xf4ed0004
+#define F0900_P1_STB6XX0_RX_OSCP 0xf4ed0002
+#define F0900_P1_STB6XX0_SYN 0xf4ed0001
 
 /*P1_TNRCFG3*/
-#define R0900_P1_TNRCFG3  0xf4ee
-#define F0900_P1_STB6120_DISCTRL1  0xf4ee0080
-#define F0900_P1_STB6120_INVORDER  0xf4ee0040
-#define F0900_P1_STB6120_ENCTRL6  0xf4ee0020
-#define F0900_P1_TUN_PLLFREQ  0xf4ee001c
-#define F0900_P1_TUN_I2CFREQ_MODE  0xf4ee0003
+#define R0900_P1_TNRCFG3 0xf4ee
+#define TNRCFG3 REGx(R0900_P1_TNRCFG3)
+#define F0900_P1_TUN_PLLFREQ 0xf4ee001c
+#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003
 
 /*P1_TNRLAUNCH*/
-#define R0900_P1_TNRLAUNCH  0xf4f0
+#define R0900_P1_TNRLAUNCH 0xf4f0
+#define TNRLAUNCH REGx(R0900_P1_TNRLAUNCH)
 
 /*P1_TNRLD*/
-#define R0900_P1_TNRLD  0xf4f0
-#define F0900_P1_TUNLD_VCOING  0xf4f00080
-#define F0900_P1_TUN_REG1FAIL  0xf4f00040
-#define F0900_P1_TUN_REG2FAIL  0xf4f00020
-#define F0900_P1_TUN_REG3FAIL  0xf4f00010
-#define F0900_P1_TUN_REG4FAIL  0xf4f00008
-#define F0900_P1_TUN_REG5FAIL  0xf4f00004
-#define F0900_P1_TUN_BWING  0xf4f00002
-#define F0900_P1_TUN_LOCKED  0xf4f00001
+#define R0900_P1_TNRLD 0xf4f0
+#define TNRLD REGx(R0900_P1_TNRLD)
+#define F0900_P1_TUNLD_VCOING 0xf4f00080
+#define F0900_P1_TUN_REG1FAIL 0xf4f00040
+#define F0900_P1_TUN_REG2FAIL 0xf4f00020
+#define F0900_P1_TUN_REG3FAIL 0xf4f00010
+#define F0900_P1_TUN_REG4FAIL 0xf4f00008
+#define F0900_P1_TUN_REG5FAIL 0xf4f00004
+#define F0900_P1_TUN_BWING 0xf4f00002
+#define F0900_P1_TUN_LOCKED 0xf4f00001
 
 /*P1_TNROBSL*/
-#define R0900_P1_TNROBSL  0xf4f6
-#define F0900_P1_TUN_I2CABORTED  0xf4f60080
-#define F0900_P1_TUN_LPEN  0xf4f60040
-#define F0900_P1_TUN_FCCK  0xf4f60020
-#define F0900_P1_TUN_I2CLOCKED  0xf4f60010
-#define F0900_P1_TUN_PROGDONE  0xf4f6000c
-#define F0900_P1_TUN_RFRESTE1  0xf4f60003
+#define R0900_P1_TNROBSL 0xf4f6
+#define TNROBSL REGx(R0900_P1_TNROBSL)
+#define F0900_P1_TUN_I2CABORTED 0xf4f60080
+#define F0900_P1_TUN_LPEN 0xf4f60040
+#define F0900_P1_TUN_FCCK 0xf4f60020
+#define F0900_P1_TUN_I2CLOCKED 0xf4f60010
+#define F0900_P1_TUN_PROGDONE 0xf4f6000c
+#define F0900_P1_TUN_RFRESTE1 0xf4f60003
 
 /*P1_TNRRESTE*/
-#define R0900_P1_TNRRESTE  0xf4f7
-#define F0900_P1_TUN_RFRESTE0  0xf4f700ff
+#define R0900_P1_TNRRESTE 0xf4f7
+#define TNRRESTE REGx(R0900_P1_TNRRESTE)
+#define F0900_P1_TUN_RFRESTE0 0xf4f700ff
 
 /*P1_SMAPCOEF7*/
-#define R0900_P1_SMAPCOEF7  0xf500
-#define F0900_P1_DIS_QSCALE  0xf5000080
-#define F0900_P1_SMAPCOEF_Q_LLR12  0xf500017f
+#define R0900_P1_SMAPCOEF7 0xf500
+#define SMAPCOEF7 REGx(R0900_P1_SMAPCOEF7)
+#define F0900_P1_DIS_QSCALE 0xf5000080
+#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f
 
 /*P1_SMAPCOEF6*/
-#define R0900_P1_SMAPCOEF6  0xf501
-#define F0900_P1_DIS_NEWSCALE  0xf5010008
-#define F0900_P1_ADJ_8PSKLLR1  0xf5010004
-#define F0900_P1_OLD_8PSKLLR1  0xf5010002
-#define F0900_P1_DIS_AB8PSK  0xf5010001
+#define R0900_P1_SMAPCOEF6 0xf501
+#define SMAPCOEF6 REGx(R0900_P1_SMAPCOEF6)
+#define F0900_P1_ADJ_8PSKLLR1 0xf5010004
+#define F0900_P1_OLD_8PSKLLR1 0xf5010002
+#define F0900_P1_DIS_AB8PSK 0xf5010001
 
 /*P1_SMAPCOEF5*/
-#define R0900_P1_SMAPCOEF5  0xf502
-#define F0900_P1_DIS_8SCALE  0xf5020080
-#define F0900_P1_SMAPCOEF_8P_LLR23  0xf502017f
+#define R0900_P1_SMAPCOEF5 0xf502
+#define SMAPCOEF5 REGx(R0900_P1_SMAPCOEF5)
+#define F0900_P1_DIS_8SCALE 0xf5020080
+#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f
+
+/*P1_NCO2MAX1*/
+#define R0900_P1_NCO2MAX1 0xf514
+#define NCO2MAX1 REGx(R0900_P1_NCO2MAX1)
+#define F0900_P1_TETA2_MAXVABS1 0xf51400ff
+
+/*P1_NCO2MAX0*/
+#define R0900_P1_NCO2MAX0 0xf515
+#define NCO2MAX0 REGx(R0900_P1_NCO2MAX0)
+#define F0900_P1_TETA2_MAXVABS0 0xf51500ff
+
+/*P1_NCO2FR1*/
+#define R0900_P1_NCO2FR1 0xf516
+#define NCO2FR1 REGx(R0900_P1_NCO2FR1)
+#define F0900_P1_NCO2FINAL_ANGLE1 0xf51600ff
+
+/*P1_NCO2FR0*/
+#define R0900_P1_NCO2FR0 0xf517
+#define NCO2FR0 REGx(R0900_P1_NCO2FR0)
+#define F0900_P1_NCO2FINAL_ANGLE0 0xf51700ff
+
+/*P1_CFR2AVRGE1*/
+#define R0900_P1_CFR2AVRGE1 0xf518
+#define CFR2AVRGE1 REGx(R0900_P1_CFR2AVRGE1)
+#define F0900_P1_I2C_CFR2AVERAGE1 0xf51800ff
+
+/*P1_CFR2AVRGE0*/
+#define R0900_P1_CFR2AVRGE0 0xf519
+#define CFR2AVRGE0 REGx(R0900_P1_CFR2AVRGE0)
+#define F0900_P1_I2C_CFR2AVERAGE0 0xf51900ff
 
 /*P1_DMDPLHSTAT*/
-#define R0900_P1_DMDPLHSTAT  0xf520
-#define F0900_P1_PLH_STATISTIC  0xf52000ff
+#define R0900_P1_DMDPLHSTAT 0xf520
+#define DMDPLHSTAT REGx(R0900_P1_DMDPLHSTAT)
+#define F0900_P1_PLH_STATISTIC 0xf52000ff
 
 /*P1_LOCKTIME3*/
-#define R0900_P1_LOCKTIME3  0xf522
-#define F0900_P1_DEMOD_LOCKTIME3  0xf52200ff
+#define R0900_P1_LOCKTIME3 0xf522
+#define LOCKTIME3 REGx(R0900_P1_LOCKTIME3)
+#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff
 
 /*P1_LOCKTIME2*/
-#define R0900_P1_LOCKTIME2  0xf523
-#define F0900_P1_DEMOD_LOCKTIME2  0xf52300ff
+#define R0900_P1_LOCKTIME2 0xf523
+#define LOCKTIME2 REGx(R0900_P1_LOCKTIME2)
+#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff
 
 /*P1_LOCKTIME1*/
-#define R0900_P1_LOCKTIME1  0xf524
-#define F0900_P1_DEMOD_LOCKTIME1  0xf52400ff
+#define R0900_P1_LOCKTIME1 0xf524
+#define LOCKTIME1 REGx(R0900_P1_LOCKTIME1)
+#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff
 
 /*P1_LOCKTIME0*/
-#define R0900_P1_LOCKTIME0  0xf525
-#define F0900_P1_DEMOD_LOCKTIME0  0xf52500ff
+#define R0900_P1_LOCKTIME0 0xf525
+#define LOCKTIME0 REGx(R0900_P1_LOCKTIME0)
+#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff
 
 /*P1_VITSCALE*/
-#define R0900_P1_VITSCALE  0xf532
-#define F0900_P1_NVTH_NOSRANGE  0xf5320080
-#define F0900_P1_VERROR_MAXMODE  0xf5320040
-#define F0900_P1_KDIV_MODE  0xf5320030
-#define F0900_P1_NSLOWSN_LOCKED  0xf5320008
-#define F0900_P1_DELOCK_PRFLOSS  0xf5320004
-#define F0900_P1_DIS_RSFLOCK  0xf5320002
+#define R0900_P1_VITSCALE 0xf532
+#define VITSCALE REGx(R0900_P1_VITSCALE)
+#define F0900_P1_NVTH_NOSRANGE 0xf5320080
+#define F0900_P1_VERROR_MAXMODE 0xf5320040
+#define F0900_P1_NSLOWSN_LOCKED 0xf5320008
+#define F0900_P1_DIS_RSFLOCK 0xf5320002
 
 /*P1_FECM*/
-#define R0900_P1_FECM  0xf533
-#define F0900_P1_DSS_DVB  0xf5330080
-#define F0900_P1_DEMOD_BYPASS  0xf5330040
-#define F0900_P1_CMP_SLOWMODE  0xf5330020
-#define F0900_P1_DSS_SRCH  0xf5330010
-#define F0900_P1_DIFF_MODEVIT  0xf5330004
-#define F0900_P1_SYNCVIT  0xf5330002
-#define F0900_P1_IQINV  0xf5330001
+#define R0900_P1_FECM 0xf533
+#define FECM REGx(R0900_P1_FECM)
+#define F0900_P1_DSS_DVB 0xf5330080
+#define DSS_DVB FLDx(F0900_P1_DSS_DVB)
+#define F0900_P1_DSS_SRCH 0xf5330010
+#define F0900_P1_SYNCVIT 0xf5330002
+#define F0900_P1_IQINV 0xf5330001
+#define IQINV FLDx(F0900_P1_IQINV)
 
 /*P1_VTH12*/
-#define R0900_P1_VTH12  0xf534
-#define F0900_P1_VTH12  0xf53400ff
+#define R0900_P1_VTH12 0xf534
+#define VTH12 REGx(R0900_P1_VTH12)
+#define F0900_P1_VTH12 0xf53400ff
 
 /*P1_VTH23*/
-#define R0900_P1_VTH23  0xf535
-#define F0900_P1_VTH23  0xf53500ff
+#define R0900_P1_VTH23 0xf535
+#define VTH23 REGx(R0900_P1_VTH23)
+#define F0900_P1_VTH23 0xf53500ff
 
 /*P1_VTH34*/
-#define R0900_P1_VTH34  0xf536
-#define F0900_P1_VTH34  0xf53600ff
+#define R0900_P1_VTH34 0xf536
+#define VTH34 REGx(R0900_P1_VTH34)
+#define F0900_P1_VTH34 0xf53600ff
 
 /*P1_VTH56*/
-#define R0900_P1_VTH56  0xf537
-#define F0900_P1_VTH56  0xf53700ff
+#define R0900_P1_VTH56 0xf537
+#define VTH56 REGx(R0900_P1_VTH56)
+#define F0900_P1_VTH56 0xf53700ff
 
 /*P1_VTH67*/
-#define R0900_P1_VTH67  0xf538
-#define F0900_P1_VTH67  0xf53800ff
+#define R0900_P1_VTH67 0xf538
+#define VTH67 REGx(R0900_P1_VTH67)
+#define F0900_P1_VTH67 0xf53800ff
 
 /*P1_VTH78*/
-#define R0900_P1_VTH78  0xf539
-#define F0900_P1_VTH78  0xf53900ff
+#define R0900_P1_VTH78 0xf539
+#define VTH78 REGx(R0900_P1_VTH78)
+#define F0900_P1_VTH78 0xf53900ff
 
 /*P1_VITCURPUN*/
-#define R0900_P1_VITCURPUN  0xf53a
-#define F0900_P1_VIT_MAPPING  0xf53a00e0
-#define F0900_P1_VIT_CURPUN  0xf53a001f
+#define R0900_P1_VITCURPUN 0xf53a
+#define VITCURPUN REGx(R0900_P1_VITCURPUN)
+#define F0900_P1_VIT_CURPUN 0xf53a001f
+#define VIT_CURPUN FLDx(F0900_P1_VIT_CURPUN)
 
 /*P1_VERROR*/
-#define R0900_P1_VERROR  0xf53b
-#define F0900_P1_REGERR_VIT  0xf53b00ff
+#define R0900_P1_VERROR 0xf53b
+#define VERROR REGx(R0900_P1_VERROR)
+#define F0900_P1_REGERR_VIT 0xf53b00ff
 
 /*P1_PRVIT*/
-#define R0900_P1_PRVIT  0xf53c
-#define F0900_P1_DIS_VTHLOCK  0xf53c0040
-#define F0900_P1_E7_8VIT  0xf53c0020
-#define F0900_P1_E6_7VIT  0xf53c0010
-#define F0900_P1_E5_6VIT  0xf53c0008
-#define F0900_P1_E3_4VIT  0xf53c0004
-#define F0900_P1_E2_3VIT  0xf53c0002
-#define F0900_P1_E1_2VIT  0xf53c0001
+#define R0900_P1_PRVIT 0xf53c
+#define PRVIT REGx(R0900_P1_PRVIT)
+#define F0900_P1_DIS_VTHLOCK 0xf53c0040
+#define F0900_P1_E7_8VIT 0xf53c0020
+#define F0900_P1_E6_7VIT 0xf53c0010
+#define F0900_P1_E5_6VIT 0xf53c0008
+#define F0900_P1_E3_4VIT 0xf53c0004
+#define F0900_P1_E2_3VIT 0xf53c0002
+#define F0900_P1_E1_2VIT 0xf53c0001
 
 /*P1_VAVSRVIT*/
-#define R0900_P1_VAVSRVIT  0xf53d
-#define F0900_P1_AMVIT  0xf53d0080
-#define F0900_P1_FROZENVIT  0xf53d0040
-#define F0900_P1_SNVIT  0xf53d0030
-#define F0900_P1_TOVVIT  0xf53d000c
-#define F0900_P1_HYPVIT  0xf53d0003
+#define R0900_P1_VAVSRVIT 0xf53d
+#define VAVSRVIT REGx(R0900_P1_VAVSRVIT)
+#define F0900_P1_AMVIT 0xf53d0080
+#define F0900_P1_FROZENVIT 0xf53d0040
+#define F0900_P1_SNVIT 0xf53d0030
+#define F0900_P1_TOVVIT 0xf53d000c
+#define F0900_P1_HYPVIT 0xf53d0003
 
 /*P1_VSTATUSVIT*/
-#define R0900_P1_VSTATUSVIT  0xf53e
-#define F0900_P1_VITERBI_ON  0xf53e0080
-#define F0900_P1_END_LOOPVIT  0xf53e0040
-#define F0900_P1_VITERBI_DEPRF  0xf53e0020
-#define F0900_P1_PRFVIT  0xf53e0010
-#define F0900_P1_LOCKEDVIT  0xf53e0008
-#define F0900_P1_VITERBI_DELOCK  0xf53e0004
-#define F0900_P1_VIT_DEMODSEL  0xf53e0002
-#define F0900_P1_VITERBI_COMPOUT  0xf53e0001
+#define R0900_P1_VSTATUSVIT 0xf53e
+#define VSTATUSVIT REGx(R0900_P1_VSTATUSVIT)
+#define F0900_P1_PRFVIT 0xf53e0010
+#define PRFVIT FLDx(F0900_P1_PRFVIT)
+#define F0900_P1_LOCKEDVIT 0xf53e0008
+#define LOCKEDVIT FLDx(F0900_P1_LOCKEDVIT)
 
 /*P1_VTHINUSE*/
-#define R0900_P1_VTHINUSE  0xf53f
-#define F0900_P1_VIT_INUSE  0xf53f00ff
+#define R0900_P1_VTHINUSE 0xf53f
+#define VTHINUSE REGx(R0900_P1_VTHINUSE)
+#define F0900_P1_VIT_INUSE 0xf53f00ff
 
 /*P1_KDIV12*/
-#define R0900_P1_KDIV12  0xf540
-#define F0900_P1_KDIV12_MANUAL  0xf5400080
-#define F0900_P1_K_DIVIDER_12  0xf540007f
+#define R0900_P1_KDIV12 0xf540
+#define KDIV12 REGx(R0900_P1_KDIV12)
+#define F0900_P1_K_DIVIDER_12 0xf540007f
 
 /*P1_KDIV23*/
-#define R0900_P1_KDIV23  0xf541
-#define F0900_P1_KDIV23_MANUAL  0xf5410080
-#define F0900_P1_K_DIVIDER_23  0xf541007f
+#define R0900_P1_KDIV23 0xf541
+#define KDIV23 REGx(R0900_P1_KDIV23)
+#define F0900_P1_K_DIVIDER_23 0xf541007f
 
 /*P1_KDIV34*/
-#define R0900_P1_KDIV34  0xf542
-#define F0900_P1_KDIV34_MANUAL  0xf5420080
-#define F0900_P1_K_DIVIDER_34  0xf542007f
+#define R0900_P1_KDIV34 0xf542
+#define KDIV34 REGx(R0900_P1_KDIV34)
+#define F0900_P1_K_DIVIDER_34 0xf542007f
 
 /*P1_KDIV56*/
-#define R0900_P1_KDIV56  0xf543
-#define F0900_P1_KDIV56_MANUAL  0xf5430080
-#define F0900_P1_K_DIVIDER_56  0xf543007f
+#define R0900_P1_KDIV56 0xf543
+#define KDIV56 REGx(R0900_P1_KDIV56)
+#define F0900_P1_K_DIVIDER_56 0xf543007f
 
 /*P1_KDIV67*/
-#define R0900_P1_KDIV67  0xf544
-#define F0900_P1_KDIV67_MANUAL  0xf5440080
-#define F0900_P1_K_DIVIDER_67  0xf544007f
+#define R0900_P1_KDIV67 0xf544
+#define KDIV67 REGx(R0900_P1_KDIV67)
+#define F0900_P1_K_DIVIDER_67 0xf544007f
 
 /*P1_KDIV78*/
-#define R0900_P1_KDIV78  0xf545
-#define F0900_P1_KDIV78_MANUAL  0xf5450080
-#define F0900_P1_K_DIVIDER_78  0xf545007f
+#define R0900_P1_KDIV78 0xf545
+#define KDIV78 REGx(R0900_P1_KDIV78)
+#define F0900_P1_K_DIVIDER_78 0xf545007f
 
 /*P1_PDELCTRL1*/
-#define R0900_P1_PDELCTRL1  0xf550
-#define F0900_P1_INV_MISMASK  0xf5500080
-#define F0900_P1_FORCE_ACCEPTED  0xf5500040
-#define F0900_P1_FILTER_EN  0xf5500020
-#define F0900_P1_FORCE_PKTDELINUSE  0xf5500010
-#define F0900_P1_HYSTEN  0xf5500008
-#define F0900_P1_HYSTSWRST  0xf5500004
-#define F0900_P1_EN_MIS00  0xf5500002
-#define F0900_P1_ALGOSWRST  0xf5500001
+#define R0900_P1_PDELCTRL1 0xf550
+#define PDELCTRL1 REGx(R0900_P1_PDELCTRL1)
+#define F0900_P1_INV_MISMASK 0xf5500080
+#define F0900_P1_FILTER_EN 0xf5500020
+#define F0900_P1_EN_MIS00 0xf5500002
+#define F0900_P1_ALGOSWRST 0xf5500001
+#define ALGOSWRST FLDx(F0900_P1_ALGOSWRST)
 
 /*P1_PDELCTRL2*/
-#define R0900_P1_PDELCTRL2  0xf551
-#define F0900_P1_FORCE_CONTINUOUS  0xf5510080
-#define F0900_P1_RESET_UPKO_COUNT  0xf5510040
-#define F0900_P1_USER_PKTDELIN_NB  0xf5510020
-#define F0900_P1_FORCE_LOCKED  0xf5510010
-#define F0900_P1_DATA_UNBBSCRAM  0xf5510008
-#define F0900_P1_FORCE_LONGPKT  0xf5510004
-#define F0900_P1_FRAME_MODE  0xf5510002
+#define R0900_P1_PDELCTRL2 0xf551
+#define PDELCTRL2 REGx(R0900_P1_PDELCTRL2)
+#define F0900_P1_RESET_UPKO_COUNT 0xf5510040
+#define RESET_UPKO_COUNT FLDx(F0900_P1_RESET_UPKO_COUNT)
+#define F0900_P1_FRAME_MODE 0xf5510002
+#define F0900_P1_NOBCHERRFLG_USE 0xf5510001
 
 /*P1_HYSTTHRESH*/
-#define R0900_P1_HYSTTHRESH  0xf554
-#define F0900_P1_UNLCK_THRESH  0xf55400f0
-#define F0900_P1_DELIN_LCK_THRESH  0xf554000f
+#define R0900_P1_HYSTTHRESH 0xf554
+#define HYSTTHRESH REGx(R0900_P1_HYSTTHRESH)
+#define F0900_P1_UNLCK_THRESH 0xf55400f0
+#define F0900_P1_DELIN_LCK_THRESH 0xf554000f
 
 /*P1_ISIENTRY*/
-#define R0900_P1_ISIENTRY  0xf55e
-#define F0900_P1_ISI_ENTRY  0xf55e00ff
+#define R0900_P1_ISIENTRY 0xf55e
+#define ISIENTRY REGx(R0900_P1_ISIENTRY)
+#define F0900_P1_ISI_ENTRY 0xf55e00ff
 
 /*P1_ISIBITENA*/
-#define R0900_P1_ISIBITENA  0xf55f
-#define F0900_P1_ISI_BIT_EN  0xf55f00ff
+#define R0900_P1_ISIBITENA 0xf55f
+#define ISIBITENA REGx(R0900_P1_ISIBITENA)
+#define F0900_P1_ISI_BIT_EN 0xf55f00ff
 
 /*P1_MATSTR1*/
-#define R0900_P1_MATSTR1  0xf560
-#define F0900_P1_MATYPE_CURRENT1  0xf56000ff
+#define R0900_P1_MATSTR1 0xf560
+#define MATSTR1 REGx(R0900_P1_MATSTR1)
+#define F0900_P1_MATYPE_CURRENT1 0xf56000ff
 
 /*P1_MATSTR0*/
-#define R0900_P1_MATSTR0  0xf561
-#define F0900_P1_MATYPE_CURRENT0  0xf56100ff
+#define R0900_P1_MATSTR0 0xf561
+#define MATSTR0 REGx(R0900_P1_MATSTR0)
+#define F0900_P1_MATYPE_CURRENT0 0xf56100ff
 
 /*P1_UPLSTR1*/
-#define R0900_P1_UPLSTR1  0xf562
-#define F0900_P1_UPL_CURRENT1  0xf56200ff
+#define R0900_P1_UPLSTR1 0xf562
+#define UPLSTR1 REGx(R0900_P1_UPLSTR1)
+#define F0900_P1_UPL_CURRENT1 0xf56200ff
 
 /*P1_UPLSTR0*/
-#define R0900_P1_UPLSTR0  0xf563
-#define F0900_P1_UPL_CURRENT0  0xf56300ff
+#define R0900_P1_UPLSTR0 0xf563
+#define UPLSTR0 REGx(R0900_P1_UPLSTR0)
+#define F0900_P1_UPL_CURRENT0 0xf56300ff
 
 /*P1_DFLSTR1*/
-#define R0900_P1_DFLSTR1  0xf564
-#define F0900_P1_DFL_CURRENT1  0xf56400ff
+#define R0900_P1_DFLSTR1 0xf564
+#define DFLSTR1 REGx(R0900_P1_DFLSTR1)
+#define F0900_P1_DFL_CURRENT1 0xf56400ff
 
 /*P1_DFLSTR0*/
-#define R0900_P1_DFLSTR0  0xf565
-#define F0900_P1_DFL_CURRENT0  0xf56500ff
+#define R0900_P1_DFLSTR0 0xf565
+#define DFLSTR0 REGx(R0900_P1_DFLSTR0)
+#define F0900_P1_DFL_CURRENT0 0xf56500ff
 
 /*P1_SYNCSTR*/
-#define R0900_P1_SYNCSTR  0xf566
-#define F0900_P1_SYNC_CURRENT  0xf56600ff
+#define R0900_P1_SYNCSTR 0xf566
+#define SYNCSTR REGx(R0900_P1_SYNCSTR)
+#define F0900_P1_SYNC_CURRENT 0xf56600ff
 
 /*P1_SYNCDSTR1*/
-#define R0900_P1_SYNCDSTR1  0xf567
-#define F0900_P1_SYNCD_CURRENT1  0xf56700ff
+#define R0900_P1_SYNCDSTR1 0xf567
+#define SYNCDSTR1 REGx(R0900_P1_SYNCDSTR1)
+#define F0900_P1_SYNCD_CURRENT1 0xf56700ff
 
 /*P1_SYNCDSTR0*/
-#define R0900_P1_SYNCDSTR0  0xf568
-#define F0900_P1_SYNCD_CURRENT0  0xf56800ff
+#define R0900_P1_SYNCDSTR0 0xf568
+#define SYNCDSTR0 REGx(R0900_P1_SYNCDSTR0)
+#define F0900_P1_SYNCD_CURRENT0 0xf56800ff
 
 /*P1_PDELSTATUS1*/
-#define R0900_P1_PDELSTATUS1  0xf569
-#define F0900_P1_PKTDELIN_DELOCK  0xf5690080
-#define F0900_P1_SYNCDUPDFL_BADDFL  0xf5690040
-#define F0900_P1_CONTINUOUS_STREAM  0xf5690020
-#define F0900_P1_UNACCEPTED_STREAM  0xf5690010
-#define F0900_P1_BCH_ERROR_FLAG  0xf5690008
-#define F0900_P1_BBHCRCKO  0xf5690004
-#define F0900_P1_PKTDELIN_LOCK  0xf5690002
-#define F0900_P1_FIRST_LOCK  0xf5690001
+#define R0900_P1_PDELSTATUS1 0xf569
+#define F0900_P1_PKTDELIN_DELOCK 0xf5690080
+#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040
+#define F0900_P1_CONTINUOUS_STREAM 0xf5690020
+#define F0900_P1_UNACCEPTED_STREAM 0xf5690010
+#define F0900_P1_BCH_ERROR_FLAG 0xf5690008
+#define F0900_P1_PKTDELIN_LOCK 0xf5690002
+#define PKTDELIN_LOCK FLDx(F0900_P1_PKTDELIN_LOCK)
+#define F0900_P1_FIRST_LOCK 0xf5690001
 
 /*P1_PDELSTATUS2*/
-#define R0900_P1_PDELSTATUS2  0xf56a
-#define F0900_P1_PKTDEL_DEMODSEL  0xf56a0080
-#define F0900_P1_FRAME_MODCOD  0xf56a007c
-#define F0900_P1_FRAME_TYPE  0xf56a0003
+#define R0900_P1_PDELSTATUS2 0xf56a
+#define F0900_P1_FRAME_MODCOD 0xf56a007c
+#define F0900_P1_FRAME_TYPE 0xf56a0003
 
 /*P1_BBFCRCKO1*/
-#define R0900_P1_BBFCRCKO1  0xf56b
-#define F0900_P1_BBHCRC_KOCNT1  0xf56b00ff
+#define R0900_P1_BBFCRCKO1 0xf56b
+#define BBFCRCKO1 REGx(R0900_P1_BBFCRCKO1)
+#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff
 
 /*P1_BBFCRCKO0*/
-#define R0900_P1_BBFCRCKO0  0xf56c
-#define F0900_P1_BBHCRC_KOCNT0  0xf56c00ff
+#define R0900_P1_BBFCRCKO0 0xf56c
+#define BBFCRCKO0 REGx(R0900_P1_BBFCRCKO0)
+#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff
 
 /*P1_UPCRCKO1*/
-#define R0900_P1_UPCRCKO1  0xf56d
-#define F0900_P1_PKTCRC_KOCNT1  0xf56d00ff
+#define R0900_P1_UPCRCKO1 0xf56d
+#define UPCRCKO1 REGx(R0900_P1_UPCRCKO1)
+#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff
 
 /*P1_UPCRCKO0*/
-#define R0900_P1_UPCRCKO0  0xf56e
-#define F0900_P1_PKTCRC_KOCNT0  0xf56e00ff
+#define R0900_P1_UPCRCKO0 0xf56e
+#define UPCRCKO0 REGx(R0900_P1_UPCRCKO0)
+#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff
+
+/*P1_PDELCTRL3*/
+#define R0900_P1_PDELCTRL3 0xf56f
+#define PDELCTRL3 REGx(R0900_P1_PDELCTRL3)
+#define F0900_P1_PKTDEL_CONTFAIL 0xf56f0080
+#define F0900_P1_NOFIFO_BCHERR 0xf56f0020
 
 /*P1_TSSTATEM*/
-#define R0900_P1_TSSTATEM  0xf570
-#define F0900_P1_TSDIL_ON  0xf5700080
-#define F0900_P1_TSSKIPRS_ON  0xf5700040
-#define F0900_P1_TSRS_ON  0xf5700020
-#define F0900_P1_TSDESCRAMB_ON  0xf5700010
-#define F0900_P1_TSFRAME_MODE  0xf5700008
-#define F0900_P1_TS_DISABLE  0xf5700004
-#define F0900_P1_TSACM_MODE  0xf5700002
-#define F0900_P1_TSOUT_NOSYNC  0xf5700001
+#define R0900_P1_TSSTATEM 0xf570
+#define TSSTATEM REGx(R0900_P1_TSSTATEM)
+#define F0900_P1_TSDIL_ON 0xf5700080
+#define F0900_P1_TSRS_ON 0xf5700020
+#define F0900_P1_TSDESCRAMB_ON 0xf5700010
+#define F0900_P1_TSFRAME_MODE 0xf5700008
+#define F0900_P1_TS_DISABLE 0xf5700004
+#define F0900_P1_TSOUT_NOSYNC 0xf5700001
 
 /*P1_TSCFGH*/
-#define R0900_P1_TSCFGH  0xf572
-#define F0900_P1_TSFIFO_DVBCI  0xf5720080
-#define F0900_P1_TSFIFO_SERIAL  0xf5720040
-#define F0900_P1_TSFIFO_TEIUPDATE  0xf5720020
-#define F0900_P1_TSFIFO_DUTY50  0xf5720010
-#define F0900_P1_TSFIFO_HSGNLOUT  0xf5720008
-#define F0900_P1_TSFIFO_ERRMODE  0xf5720006
-#define F0900_P1_RST_HWARE  0xf5720001
+#define R0900_P1_TSCFGH 0xf572
+#define TSCFGH REGx(R0900_P1_TSCFGH)
+#define F0900_P1_TSFIFO_DVBCI 0xf5720080
+#define F0900_P1_TSFIFO_SERIAL 0xf5720040
+#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020
+#define F0900_P1_TSFIFO_DUTY50 0xf5720010
+#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008
+#define F0900_P1_TSFIFO_ERRMODE 0xf5720006
+#define F0900_P1_RST_HWARE 0xf5720001
+#define RST_HWARE FLDx(F0900_P1_RST_HWARE)
 
 /*P1_TSCFGM*/
-#define R0900_P1_TSCFGM  0xf573
-#define F0900_P1_TSFIFO_MANSPEED  0xf57300c0
-#define F0900_P1_TSFIFO_PERMDATA  0xf5730020
-#define F0900_P1_TSFIFO_NONEWSGNL  0xf5730010
-#define F0900_P1_TSFIFO_BITSPEED  0xf5730008
-#define F0900_P1_NPD_SPECDVBS2  0xf5730004
-#define F0900_P1_TSFIFO_STOPCKDIS  0xf5730002
-#define F0900_P1_TSFIFO_INVDATA  0xf5730001
+#define R0900_P1_TSCFGM 0xf573
+#define TSCFGM REGx(R0900_P1_TSCFGM)
+#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0
+#define F0900_P1_TSFIFO_PERMDATA 0xf5730020
+#define F0900_P1_TSFIFO_DPUNACT 0xf5730002
+#define F0900_P1_TSFIFO_INVDATA 0xf5730001
 
 /*P1_TSCFGL*/
-#define R0900_P1_TSCFGL  0xf574
-#define F0900_P1_TSFIFO_BCLKDEL1CK  0xf57400c0
-#define F0900_P1_BCHERROR_MODE  0xf5740030
-#define F0900_P1_TSFIFO_NSGNL2DATA  0xf5740008
-#define F0900_P1_TSFIFO_EMBINDVB  0xf5740004
-#define F0900_P1_TSFIFO_DPUNACT  0xf5740002
-#define F0900_P1_TSFIFO_NPDOFF  0xf5740001
+#define R0900_P1_TSCFGL 0xf574
+#define TSCFGL REGx(R0900_P1_TSCFGL)
+#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0
+#define F0900_P1_BCHERROR_MODE 0xf5740030
+#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008
+#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004
+#define F0900_P1_TSFIFO_BITSPEED 0xf5740003
 
 /*P1_TSINSDELH*/
-#define R0900_P1_TSINSDELH  0xf576
-#define F0900_P1_TSDEL_SYNCBYTE  0xf5760080
-#define F0900_P1_TSDEL_XXHEADER  0xf5760040
-#define F0900_P1_TSDEL_BBHEADER  0xf5760020
-#define F0900_P1_TSDEL_DATAFIELD  0xf5760010
-#define F0900_P1_TSINSDEL_ISCR  0xf5760008
-#define F0900_P1_TSINSDEL_NPD  0xf5760004
-#define F0900_P1_TSINSDEL_RSPARITY  0xf5760002
-#define F0900_P1_TSINSDEL_CRC8  0xf5760001
+#define R0900_P1_TSINSDELH 0xf576
+#define TSINSDELH REGx(R0900_P1_TSINSDELH)
+#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080
+#define F0900_P1_TSDEL_XXHEADER 0xf5760040
+#define F0900_P1_TSDEL_BBHEADER 0xf5760020
+#define F0900_P1_TSDEL_DATAFIELD 0xf5760010
+#define F0900_P1_TSINSDEL_ISCR 0xf5760008
+#define F0900_P1_TSINSDEL_NPD 0xf5760004
+#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002
+#define F0900_P1_TSINSDEL_CRC8 0xf5760001
+
+/*P1_TSDIVN*/
+#define R0900_P1_TSDIVN 0xf579
+#define TSDIVN REGx(R0900_P1_TSDIVN)
+#define F0900_P1_TSFIFO_SPEEDMODE 0xf57900c0
+
+/*P1_TSCFG4*/
+#define R0900_P1_TSCFG4 0xf57a
+#define TSCFG4 REGx(R0900_P1_TSCFG4)
+#define F0900_P1_TSFIFO_TSSPEEDMODE 0xf57a00c0
 
 /*P1_TSSPEED*/
-#define R0900_P1_TSSPEED  0xf580
-#define F0900_P1_TSFIFO_OUTSPEED  0xf58000ff
+#define R0900_P1_TSSPEED 0xf580
+#define TSSPEED REGx(R0900_P1_TSSPEED)
+#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff
 
 /*P1_TSSTATUS*/
-#define R0900_P1_TSSTATUS  0xf581
-#define F0900_P1_TSFIFO_LINEOK  0xf5810080
-#define F0900_P1_TSFIFO_ERROR  0xf5810040
-#define F0900_P1_TSFIFO_DATA7  0xf5810020
-#define F0900_P1_TSFIFO_NOSYNC  0xf5810010
-#define F0900_P1_ISCR_INITIALIZED  0xf5810008
-#define F0900_P1_ISCR_UPDATED  0xf5810004
-#define F0900_P1_SOFFIFO_UNREGUL  0xf5810002
-#define F0900_P1_DIL_READY  0xf5810001
+#define R0900_P1_TSSTATUS 0xf581
+#define TSSTATUS REGx(R0900_P1_TSSTATUS)
+#define F0900_P1_TSFIFO_LINEOK 0xf5810080
+#define TSFIFO_LINEOK FLDx(F0900_P1_TSFIFO_LINEOK)
+#define F0900_P1_TSFIFO_ERROR 0xf5810040
+#define F0900_P1_DIL_READY 0xf5810001
 
 /*P1_TSSTATUS2*/
-#define R0900_P1_TSSTATUS2  0xf582
-#define F0900_P1_TSFIFO_DEMODSEL  0xf5820080
-#define F0900_P1_TSFIFOSPEED_STORE  0xf5820040
-#define F0900_P1_DILXX_RESET  0xf5820020
-#define F0900_P1_TSSERIAL_IMPOS  0xf5820010
-#define F0900_P1_TSFIFO_LINENOK  0xf5820008
-#define F0900_P1_BITSPEED_EVENT  0xf5820004
-#define F0900_P1_SCRAMBDETECT  0xf5820002
-#define F0900_P1_ULDTV67_FALSELOCK  0xf5820001
+#define R0900_P1_TSSTATUS2 0xf582
+#define TSSTATUS2 REGx(R0900_P1_TSSTATUS2)
+#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080
+#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040
+#define F0900_P1_DILXX_RESET 0xf5820020
+#define F0900_P1_TSSERIAL_IMPOS 0xf5820010
+#define F0900_P1_SCRAMBDETECT 0xf5820002
 
 /*P1_TSBITRATE1*/
-#define R0900_P1_TSBITRATE1  0xf583
-#define F0900_P1_TSFIFO_BITRATE1  0xf58300ff
+#define R0900_P1_TSBITRATE1 0xf583
+#define TSBITRATE1 REGx(R0900_P1_TSBITRATE1)
+#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff
 
 /*P1_TSBITRATE0*/
-#define R0900_P1_TSBITRATE0  0xf584
-#define F0900_P1_TSFIFO_BITRATE0  0xf58400ff
+#define R0900_P1_TSBITRATE0 0xf584
+#define TSBITRATE0 REGx(R0900_P1_TSBITRATE0)
+#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff
 
 /*P1_ERRCTRL1*/
-#define R0900_P1_ERRCTRL1  0xf598
-#define F0900_P1_ERR_SOURCE1  0xf59800f0
-#define F0900_P1_NUM_EVENT1  0xf5980007
+#define R0900_P1_ERRCTRL1 0xf598
+#define ERRCTRL1 REGx(R0900_P1_ERRCTRL1)
+#define F0900_P1_ERR_SOURCE1 0xf59800f0
+#define F0900_P1_NUM_EVENT1 0xf5980007
 
 /*P1_ERRCNT12*/
-#define R0900_P1_ERRCNT12  0xf599
-#define F0900_P1_ERRCNT1_OLDVALUE  0xf5990080
-#define F0900_P1_ERR_CNT12  0xf599007f
+#define R0900_P1_ERRCNT12 0xf599
+#define ERRCNT12 REGx(R0900_P1_ERRCNT12)
+#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080
+#define F0900_P1_ERR_CNT12 0xf599007f
+#define ERR_CNT12 FLDx(F0900_P1_ERR_CNT12)
 
 /*P1_ERRCNT11*/
-#define R0900_P1_ERRCNT11  0xf59a
-#define F0900_P1_ERR_CNT11  0xf59a00ff
+#define R0900_P1_ERRCNT11 0xf59a
+#define ERRCNT11 REGx(R0900_P1_ERRCNT11)
+#define F0900_P1_ERR_CNT11 0xf59a00ff
+#define ERR_CNT11 FLDx(F0900_P1_ERR_CNT11)
 
 /*P1_ERRCNT10*/
-#define R0900_P1_ERRCNT10  0xf59b
-#define F0900_P1_ERR_CNT10  0xf59b00ff
+#define R0900_P1_ERRCNT10 0xf59b
+#define ERRCNT10 REGx(R0900_P1_ERRCNT10)
+#define F0900_P1_ERR_CNT10 0xf59b00ff
+#define ERR_CNT10 FLDx(F0900_P1_ERR_CNT10)
 
 /*P1_ERRCTRL2*/
-#define R0900_P1_ERRCTRL2  0xf59c
-#define F0900_P1_ERR_SOURCE2  0xf59c00f0
-#define F0900_P1_NUM_EVENT2  0xf59c0007
+#define R0900_P1_ERRCTRL2 0xf59c
+#define ERRCTRL2 REGx(R0900_P1_ERRCTRL2)
+#define F0900_P1_ERR_SOURCE2 0xf59c00f0
+#define F0900_P1_NUM_EVENT2 0xf59c0007
 
 /*P1_ERRCNT22*/
-#define R0900_P1_ERRCNT22  0xf59d
-#define F0900_P1_ERRCNT2_OLDVALUE  0xf59d0080
-#define F0900_P1_ERR_CNT22  0xf59d007f
+#define R0900_P1_ERRCNT22 0xf59d
+#define ERRCNT22 REGx(R0900_P1_ERRCNT22)
+#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080
+#define F0900_P1_ERR_CNT22 0xf59d007f
+#define ERR_CNT22 FLDx(F0900_P1_ERR_CNT22)
 
 /*P1_ERRCNT21*/
-#define R0900_P1_ERRCNT21  0xf59e
-#define F0900_P1_ERR_CNT21  0xf59e00ff
+#define R0900_P1_ERRCNT21 0xf59e
+#define ERRCNT21 REGx(R0900_P1_ERRCNT21)
+#define F0900_P1_ERR_CNT21 0xf59e00ff
+#define ERR_CNT21 FLDx(F0900_P1_ERR_CNT21)
 
 /*P1_ERRCNT20*/
-#define R0900_P1_ERRCNT20  0xf59f
-#define F0900_P1_ERR_CNT20  0xf59f00ff
+#define R0900_P1_ERRCNT20 0xf59f
+#define ERRCNT20 REGx(R0900_P1_ERRCNT20)
+#define F0900_P1_ERR_CNT20 0xf59f00ff
+#define ERR_CNT20 FLDx(F0900_P1_ERR_CNT20)
 
 /*P1_FECSPY*/
-#define R0900_P1_FECSPY  0xf5a0
-#define F0900_P1_SPY_ENABLE  0xf5a00080
-#define F0900_P1_NO_SYNCBYTE  0xf5a00040
-#define F0900_P1_SERIAL_MODE  0xf5a00020
-#define F0900_P1_UNUSUAL_PACKET  0xf5a00010
-#define F0900_P1_BER_PACKMODE  0xf5a00008
-#define F0900_P1_BERMETER_LMODE  0xf5a00002
-#define F0900_P1_BERMETER_RESET  0xf5a00001
+#define R0900_P1_FECSPY 0xf5a0
+#define FECSPY REGx(R0900_P1_FECSPY)
+#define F0900_P1_SPY_ENABLE 0xf5a00080
+#define F0900_P1_NO_SYNCBYTE 0xf5a00040
+#define F0900_P1_SERIAL_MODE 0xf5a00020
+#define F0900_P1_UNUSUAL_PACKET 0xf5a00010
+#define F0900_P1_BERMETER_DATAMODE 0xf5a00008
+#define F0900_P1_BERMETER_LMODE 0xf5a00002
+#define F0900_P1_BERMETER_RESET 0xf5a00001
 
 /*P1_FSPYCFG*/
-#define R0900_P1_FSPYCFG  0xf5a1
-#define F0900_P1_FECSPY_INPUT  0xf5a100c0
-#define F0900_P1_RST_ON_ERROR  0xf5a10020
-#define F0900_P1_ONE_SHOT  0xf5a10010
-#define F0900_P1_I2C_MODE  0xf5a1000c
-#define F0900_P1_SPY_HYSTERESIS  0xf5a10003
+#define R0900_P1_FSPYCFG 0xf5a1
+#define FSPYCFG REGx(R0900_P1_FSPYCFG)
+#define F0900_P1_FECSPY_INPUT 0xf5a100c0
+#define F0900_P1_RST_ON_ERROR 0xf5a10020
+#define F0900_P1_ONE_SHOT 0xf5a10010
+#define F0900_P1_I2C_MODE 0xf5a1000c
+#define F0900_P1_SPY_HYSTERESIS 0xf5a10003
 
 /*P1_FSPYDATA*/
-#define R0900_P1_FSPYDATA  0xf5a2
-#define F0900_P1_SPY_STUFFING  0xf5a20080
-#define F0900_P1_NOERROR_PKTJITTER  0xf5a20040
-#define F0900_P1_SPY_CNULLPKT  0xf5a20020
-#define F0900_P1_SPY_OUTDATA_MODE  0xf5a2001f
+#define R0900_P1_FSPYDATA 0xf5a2
+#define FSPYDATA REGx(R0900_P1_FSPYDATA)
+#define F0900_P1_SPY_STUFFING 0xf5a20080
+#define F0900_P1_SPY_CNULLPKT 0xf5a20020
+#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f
 
 /*P1_FSPYOUT*/
-#define R0900_P1_FSPYOUT  0xf5a3
-#define F0900_P1_FSPY_DIRECT  0xf5a30080
-#define F0900_P1_SPY_OUTDATA_BUS  0xf5a30038
-#define F0900_P1_STUFF_MODE  0xf5a30007
+#define R0900_P1_FSPYOUT 0xf5a3
+#define FSPYOUT REGx(R0900_P1_FSPYOUT)
+#define F0900_P1_FSPY_DIRECT 0xf5a30080
+#define F0900_P1_STUFF_MODE 0xf5a30007
 
 /*P1_FSTATUS*/
-#define R0900_P1_FSTATUS  0xf5a4
-#define F0900_P1_SPY_ENDSIM  0xf5a40080
-#define F0900_P1_VALID_SIM  0xf5a40040
-#define F0900_P1_FOUND_SIGNAL  0xf5a40020
-#define F0900_P1_DSS_SYNCBYTE  0xf5a40010
-#define F0900_P1_RESULT_STATE  0xf5a4000f
+#define R0900_P1_FSTATUS 0xf5a4
+#define FSTATUS REGx(R0900_P1_FSTATUS)
+#define F0900_P1_SPY_ENDSIM 0xf5a40080
+#define F0900_P1_VALID_SIM 0xf5a40040
+#define F0900_P1_FOUND_SIGNAL 0xf5a40020
+#define F0900_P1_DSS_SYNCBYTE 0xf5a40010
+#define F0900_P1_RESULT_STATE 0xf5a4000f
 
 /*P1_FBERCPT4*/
-#define R0900_P1_FBERCPT4  0xf5a8
-#define F0900_P1_FBERMETER_CPT4  0xf5a800ff
+#define R0900_P1_FBERCPT4 0xf5a8
+#define FBERCPT4 REGx(R0900_P1_FBERCPT4)
+#define F0900_P1_FBERMETER_CPT4 0xf5a800ff
 
 /*P1_FBERCPT3*/
-#define R0900_P1_FBERCPT3  0xf5a9
-#define F0900_P1_FBERMETER_CPT3  0xf5a900ff
+#define R0900_P1_FBERCPT3 0xf5a9
+#define FBERCPT3 REGx(R0900_P1_FBERCPT3)
+#define F0900_P1_FBERMETER_CPT3 0xf5a900ff
 
 /*P1_FBERCPT2*/
-#define R0900_P1_FBERCPT2  0xf5aa
-#define F0900_P1_FBERMETER_CPT2  0xf5aa00ff
+#define R0900_P1_FBERCPT2 0xf5aa
+#define FBERCPT2 REGx(R0900_P1_FBERCPT2)
+#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff
 
 /*P1_FBERCPT1*/
-#define R0900_P1_FBERCPT1  0xf5ab
-#define F0900_P1_FBERMETER_CPT1  0xf5ab00ff
+#define R0900_P1_FBERCPT1 0xf5ab
+#define FBERCPT1 REGx(R0900_P1_FBERCPT1)
+#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff
 
 /*P1_FBERCPT0*/
-#define R0900_P1_FBERCPT0  0xf5ac
-#define F0900_P1_FBERMETER_CPT0  0xf5ac00ff
+#define R0900_P1_FBERCPT0 0xf5ac
+#define FBERCPT0 REGx(R0900_P1_FBERCPT0)
+#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff
 
 /*P1_FBERERR2*/
-#define R0900_P1_FBERERR2  0xf5ad
-#define F0900_P1_FBERMETER_ERR2  0xf5ad00ff
+#define R0900_P1_FBERERR2 0xf5ad
+#define FBERERR2 REGx(R0900_P1_FBERERR2)
+#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff
 
 /*P1_FBERERR1*/
-#define R0900_P1_FBERERR1  0xf5ae
-#define F0900_P1_FBERMETER_ERR1  0xf5ae00ff
+#define R0900_P1_FBERERR1 0xf5ae
+#define FBERERR1 REGx(R0900_P1_FBERERR1)
+#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff
 
 /*P1_FBERERR0*/
-#define R0900_P1_FBERERR0  0xf5af
-#define F0900_P1_FBERMETER_ERR0  0xf5af00ff
+#define R0900_P1_FBERERR0 0xf5af
+#define FBERERR0 REGx(R0900_P1_FBERERR0)
+#define F0900_P1_FBERMETER_ERR0 0xf5af00ff
 
 /*P1_FSPYBER*/
-#define R0900_P1_FSPYBER  0xf5b2
-#define F0900_P1_FSPYOBS_XORREAD  0xf5b20040
-#define F0900_P1_FSPYBER_OBSMODE  0xf5b20020
-#define F0900_P1_FSPYBER_SYNCBYTE  0xf5b20010
-#define F0900_P1_FSPYBER_UNSYNC  0xf5b20008
-#define F0900_P1_FSPYBER_CTIME  0xf5b20007
-
-/*RCCFGH*/
-#define R0900_RCCFGH  0xf600
-#define F0900_TSRCFIFO_DVBCI  0xf6000080
-#define F0900_TSRCFIFO_SERIAL  0xf6000040
-#define F0900_TSRCFIFO_DISABLE  0xf6000020
-#define F0900_TSFIFO_2TORC  0xf6000010
-#define F0900_TSRCFIFO_HSGNLOUT  0xf6000008
-#define F0900_TSRCFIFO_ERRMODE  0xf6000006
+#define R0900_P1_FSPYBER 0xf5b2
+#define FSPYBER REGx(R0900_P1_FSPYBER)
+#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010
+#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008
+#define F0900_P1_FSPYBER_CTIME 0xf5b20007
+
+/*RCCFG2*/
+#define R0900_RCCFG2 0xf600
 
 /*TSGENERAL*/
-#define R0900_TSGENERAL  0xf630
-#define F0900_TSFIFO_BCLK1ALL  0xf6300020
-#define F0900_MUXSTREAM_OUTMODE  0xf6300008
-#define F0900_TSFIFO_PERMPARAL  0xf6300006
-#define F0900_RST_REEDSOLO  0xf6300001
+#define R0900_TSGENERAL 0xf630
+#define F0900_TSFIFO_DISTS2PAR 0xf6300040
+#define F0900_MUXSTREAM_OUTMODE 0xf6300008
+#define F0900_TSFIFO_PERMPARAL 0xf6300006
 
 /*TSGENERAL1X*/
-#define R0900_TSGENERAL1X  0xf670
-#define F0900_TSFIFO1X_BCLK1ALL  0xf6700020
-#define F0900_MUXSTREAM1X_OUTMODE  0xf6700008
-#define F0900_TSFIFO1X_PERMPARAL  0xf6700006
-#define F0900_RST1X_REEDSOLO  0xf6700001
+#define R0900_TSGENERAL1X 0xf670
 
 /*NBITER_NF4*/
-#define R0900_NBITER_NF4  0xfa03
-#define F0900_NBITER_NF_QP_1_2  0xfa0300ff
+#define R0900_NBITER_NF4 0xfa03
+#define F0900_NBITER_NF_QP_1_2 0xfa0300ff
 
 /*NBITER_NF5*/
-#define R0900_NBITER_NF5  0xfa04
-#define F0900_NBITER_NF_QP_3_5  0xfa0400ff
+#define R0900_NBITER_NF5 0xfa04
+#define F0900_NBITER_NF_QP_3_5 0xfa0400ff
 
 /*NBITER_NF6*/
-#define R0900_NBITER_NF6  0xfa05
-#define F0900_NBITER_NF_QP_2_3  0xfa0500ff
+#define R0900_NBITER_NF6 0xfa05
+#define F0900_NBITER_NF_QP_2_3 0xfa0500ff
 
 /*NBITER_NF7*/
-#define R0900_NBITER_NF7  0xfa06
-#define F0900_NBITER_NF_QP_3_4  0xfa0600ff
+#define R0900_NBITER_NF7 0xfa06
+#define F0900_NBITER_NF_QP_3_4 0xfa0600ff
 
 /*NBITER_NF8*/
-#define R0900_NBITER_NF8  0xfa07
-#define F0900_NBITER_NF_QP_4_5  0xfa0700ff
+#define R0900_NBITER_NF8 0xfa07
+#define F0900_NBITER_NF_QP_4_5 0xfa0700ff
 
 /*NBITER_NF9*/
-#define R0900_NBITER_NF9  0xfa08
-#define F0900_NBITER_NF_QP_5_6  0xfa0800ff
+#define R0900_NBITER_NF9 0xfa08
+#define F0900_NBITER_NF_QP_5_6 0xfa0800ff
 
 /*NBITER_NF10*/
-#define R0900_NBITER_NF10  0xfa09
-#define F0900_NBITER_NF_QP_8_9  0xfa0900ff
+#define R0900_NBITER_NF10 0xfa09
+#define F0900_NBITER_NF_QP_8_9 0xfa0900ff
 
 /*NBITER_NF11*/
-#define R0900_NBITER_NF11  0xfa0a
-#define F0900_NBITER_NF_QP_9_10  0xfa0a00ff
+#define R0900_NBITER_NF11 0xfa0a
+#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff
 
 /*NBITER_NF12*/
-#define R0900_NBITER_NF12  0xfa0b
-#define F0900_NBITER_NF_8P_3_5  0xfa0b00ff
+#define R0900_NBITER_NF12 0xfa0b
+#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff
 
 /*NBITER_NF13*/
-#define R0900_NBITER_NF13  0xfa0c
-#define F0900_NBITER_NF_8P_2_3  0xfa0c00ff
+#define R0900_NBITER_NF13 0xfa0c
+#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff
 
 /*NBITER_NF14*/
-#define R0900_NBITER_NF14  0xfa0d
-#define F0900_NBITER_NF_8P_3_4  0xfa0d00ff
+#define R0900_NBITER_NF14 0xfa0d
+#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff
 
 /*NBITER_NF15*/
-#define R0900_NBITER_NF15  0xfa0e
-#define F0900_NBITER_NF_8P_5_6  0xfa0e00ff
+#define R0900_NBITER_NF15 0xfa0e
+#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff
 
 /*NBITER_NF16*/
-#define R0900_NBITER_NF16  0xfa0f
-#define F0900_NBITER_NF_8P_8_9  0xfa0f00ff
+#define R0900_NBITER_NF16 0xfa0f
+#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff
 
 /*NBITER_NF17*/
-#define R0900_NBITER_NF17  0xfa10
-#define F0900_NBITER_NF_8P_9_10  0xfa1000ff
+#define R0900_NBITER_NF17 0xfa10
+#define F0900_NBITER_NF_8P_9_10 0xfa1000ff
 
 /*NBITERNOERR*/
-#define R0900_NBITERNOERR  0xfa3f
-#define F0900_NBITER_STOP_CRIT  0xfa3f000f
+#define R0900_NBITERNOERR 0xfa3f
+#define F0900_NBITER_STOP_CRIT 0xfa3f000f
 
 /*GAINLLR_NF4*/
-#define R0900_GAINLLR_NF4  0xfa43
-#define F0900_GAINLLR_NF_QP_1_2  0xfa43007f
+#define R0900_GAINLLR_NF4 0xfa43
+#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f
 
 /*GAINLLR_NF5*/
-#define R0900_GAINLLR_NF5  0xfa44
-#define F0900_GAINLLR_NF_QP_3_5  0xfa44007f
+#define R0900_GAINLLR_NF5 0xfa44
+#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f
 
 /*GAINLLR_NF6*/
-#define R0900_GAINLLR_NF6  0xfa45
-#define F0900_GAINLLR_NF_QP_2_3  0xfa45007f
+#define R0900_GAINLLR_NF6 0xfa45
+#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f
 
 /*GAINLLR_NF7*/
-#define R0900_GAINLLR_NF7  0xfa46
-#define F0900_GAINLLR_NF_QP_3_4  0xfa46007f
+#define R0900_GAINLLR_NF7 0xfa46
+#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f
 
 /*GAINLLR_NF8*/
-#define R0900_GAINLLR_NF8  0xfa47
-#define F0900_GAINLLR_NF_QP_4_5  0xfa47007f
+#define R0900_GAINLLR_NF8 0xfa47
+#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f
 
 /*GAINLLR_NF9*/
-#define R0900_GAINLLR_NF9  0xfa48
-#define F0900_GAINLLR_NF_QP_5_6  0xfa48007f
+#define R0900_GAINLLR_NF9 0xfa48
+#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f
 
 /*GAINLLR_NF10*/
-#define R0900_GAINLLR_NF10  0xfa49
-#define F0900_GAINLLR_NF_QP_8_9  0xfa49007f
+#define R0900_GAINLLR_NF10 0xfa49
+#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f
 
 /*GAINLLR_NF11*/
-#define R0900_GAINLLR_NF11  0xfa4a
-#define F0900_GAINLLR_NF_QP_9_10  0xfa4a007f
+#define R0900_GAINLLR_NF11 0xfa4a
+#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f
 
 /*GAINLLR_NF12*/
-#define R0900_GAINLLR_NF12  0xfa4b
-#define F0900_GAINLLR_NF_8P_3_5  0xfa4b007f
+#define R0900_GAINLLR_NF12 0xfa4b
+#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f
 
 /*GAINLLR_NF13*/
-#define R0900_GAINLLR_NF13  0xfa4c
-#define F0900_GAINLLR_NF_8P_2_3  0xfa4c007f
+#define R0900_GAINLLR_NF13 0xfa4c
+#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f
 
 /*GAINLLR_NF14*/
-#define R0900_GAINLLR_NF14  0xfa4d
-#define F0900_GAINLLR_NF_8P_3_4  0xfa4d007f
+#define R0900_GAINLLR_NF14 0xfa4d
+#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f
 
 /*GAINLLR_NF15*/
-#define R0900_GAINLLR_NF15  0xfa4e
-#define F0900_GAINLLR_NF_8P_5_6  0xfa4e007f
+#define R0900_GAINLLR_NF15 0xfa4e
+#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f
 
 /*GAINLLR_NF16*/
-#define R0900_GAINLLR_NF16  0xfa4f
-#define F0900_GAINLLR_NF_8P_8_9  0xfa4f007f
+#define R0900_GAINLLR_NF16 0xfa4f
+#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f
 
 /*GAINLLR_NF17*/
-#define R0900_GAINLLR_NF17  0xfa50
-#define F0900_GAINLLR_NF_8P_9_10  0xfa50007f
+#define R0900_GAINLLR_NF17 0xfa50
+#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f
 
 /*CFGEXT*/
-#define R0900_CFGEXT  0xfa80
-#define F0900_STAGMODE  0xfa800080
-#define F0900_BYPBCH  0xfa800040
-#define F0900_BYPLDPC  0xfa800020
-#define F0900_LDPCMODE  0xfa800010
-#define F0900_INVLLRSIGN  0xfa800008
-#define F0900_SHORTMULT  0xfa800004
-#define F0900_EXTERNTX  0xfa800001
+#define R0900_CFGEXT 0xfa80
+#define F0900_STAGMODE 0xfa800080
+#define F0900_BYPBCH 0xfa800040
+#define F0900_BYPLDPC 0xfa800020
+#define F0900_LDPCMODE 0xfa800010
+#define F0900_INVLLRSIGN 0xfa800008
+#define F0900_SHORTMULT 0xfa800004
+#define F0900_EXTERNTX 0xfa800001
 
 /*GENCFG*/
-#define R0900_GENCFG  0xfa86
-#define F0900_BROADCAST  0xfa860010
-#define F0900_NOSHFRD2  0xfa860008
-#define F0900_BCHERRFLAG  0xfa860004
-#define F0900_PRIORITY  0xfa860002
-#define F0900_DDEMOD  0xfa860001
+#define R0900_GENCFG 0xfa86
+#define F0900_BROADCAST 0xfa860010
+#define F0900_PRIORITY 0xfa860002
+#define F0900_DDEMOD 0xfa860001
 
 /*LDPCERR1*/
-#define R0900_LDPCERR1  0xfa96
-#define F0900_LDPC_ERRORS_COUNTER1  0xfa9600ff
+#define R0900_LDPCERR1 0xfa96
+#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff
 
 /*LDPCERR0*/
-#define R0900_LDPCERR0  0xfa97
-#define F0900_LDPC_ERRORS_COUNTER0  0xfa9700ff
+#define R0900_LDPCERR0 0xfa97
+#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff
 
 /*BCHERR*/
-#define R0900_BCHERR  0xfa98
-#define F0900_ERRORFLAG  0xfa980010
-#define F0900_BCH_ERRORS_COUNTER  0xfa98000f
+#define R0900_BCHERR 0xfa98
+#define F0900_ERRORFLAG 0xfa980010
+#define F0900_BCH_ERRORS_COUNTER 0xfa98000f
 
 /*TSTRES0*/
-#define R0900_TSTRES0  0xff11
-#define F0900_FRESFEC  0xff110080
-#define F0900_FRESTS  0xff110040
-#define F0900_FRESVIT1  0xff110020
-#define F0900_FRESVIT2  0xff110010
-#define F0900_FRESSYM1  0xff110008
-#define F0900_FRESSYM2  0xff110004
-#define F0900_FRESMAS  0xff110002
-#define F0900_FRESINT  0xff110001
+#define R0900_TSTRES0 0xff11
+#define F0900_FRESFEC 0xff110080
+
+/*P2_TCTL4*/
+#define R0900_P2_TCTL4 0xff28
+#define F0900_P2_PN4_SELECT 0xff280020
+
+/*P1_TCTL4*/
+#define R0900_P1_TCTL4 0xff48
+#define TCTL4 shiftx(R0900_P1_TCTL4, demod, 0x20)
+#define F0900_P1_PN4_SELECT 0xff480020
 
 /*P2_TSTDISRX*/
-#define R0900_P2_TSTDISRX  0xff65
-#define F0900_P2_EN_DISRX  0xff650080
-#define F0900_P2_TST_CURRSRC  0xff650040
-#define F0900_P2_IN_DIGSIGNAL  0xff650020
-#define F0900_P2_HIZ_CURRENTSRC  0xff650010
-#define F0900_TST_P2_PIN_SELECT  0xff650008
-#define F0900_P2_TST_DISRX  0xff650007
+#define R0900_P2_TSTDISRX 0xff65
+#define F0900_P2_PIN_SELECT1 0xff650008
 
 /*P1_TSTDISRX*/
-#define R0900_P1_TSTDISRX  0xff67
-#define F0900_P1_EN_DISRX  0xff670080
-#define F0900_P1_TST_CURRSRC  0xff670040
-#define F0900_P1_IN_DIGSIGNAL  0xff670020
-#define F0900_P1_HIZ_CURRENTSRC  0xff670010
-#define F0900_TST_P1_PIN_SELECT  0xff670008
-#define F0900_P1_TST_DISRX  0xff670007
-
-#define STV0900_NBREGS         684
-#define STV0900_NBFIELDS               1702
+#define R0900_P1_TSTDISRX 0xff67
+#define TSTDISRX shiftx(R0900_P1_TSTDISRX, demod, 2)
+#define F0900_P1_PIN_SELECT1 0xff670008
+#define PIN_SELECT1 shiftx(F0900_P1_PIN_SELECT1, demod, 0x20000)
+
+#define STV0900_NBREGS 723
+#define STV0900_NBFIELDS 1420
 
 #endif
 
index 962fde1..b8da87f 100644 (file)
 #include "stv0900_reg.h"
 #include "stv0900_priv.h"
 
-int stv0900_check_signal_presence(struct stv0900_internal *i_params,
+s32 shiftx(s32 x, int demod, s32 shift)
+{
+       if (demod == 1)
+               return x - shift;
+
+       return x;
+}
+
+int stv0900_check_signal_presence(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 carr_offset,
-       agc2_integr,
-       max_carrier;
+       s32     carr_offset,
+               agc2_integr,
+               max_carrier;
 
-       int no_signal;
+       int no_signal = FALSE;
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               carr_offset = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8)
-                                               | stv0900_read_reg(i_params,
-                                               R0900_P1_CFR1);
-               carr_offset = ge2comp(carr_offset, 16);
-               agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
-                                               | stv0900_read_reg(i_params,
-                                               R0900_P1_AGC2I0);
-               max_carrier = i_params->dmd1_srch_range / 1000;
-               break;
-       case STV0900_DEMOD_2:
-               carr_offset = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8)
-                                               | stv0900_read_reg(i_params,
-                                               R0900_P2_CFR1);
-               carr_offset = ge2comp(carr_offset, 16);
-               agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
-                                               | stv0900_read_reg(i_params,
-                                               R0900_P2_AGC2I0);
-               max_carrier = i_params->dmd2_srch_range / 1000;
-               break;
-       }
+       carr_offset = (stv0900_read_reg(intp, CFR2) << 8)
+                                       | stv0900_read_reg(intp, CFR1);
+       carr_offset = ge2comp(carr_offset, 16);
+       agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8)
+                                       | stv0900_read_reg(intp, AGC2I0);
+       max_carrier = intp->srch_range[demod] / 1000;
 
        max_carrier += (max_carrier / 10);
        max_carrier = 65536 * (max_carrier / 2);
-       max_carrier /= i_params->mclk / 1000;
+       max_carrier /= intp->mclk / 1000;
        if (max_carrier > 0x4000)
                max_carrier = 0x4000;
 
        if ((agc2_integr > 0x2000)
-                       || (carr_offset > + 2*max_carrier)
-                       || (carr_offset < -2*max_carrier))
+                       || (carr_offset > (2 * max_carrier))
+                       || (carr_offset < (-2 * max_carrier)))
                no_signal = TRUE;
-       else
-               no_signal = FALSE;
 
        return no_signal;
 }
 
-static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
+static void stv0900_get_sw_loop_params(struct stv0900_internal *intp,
                                s32 *frequency_inc, s32 *sw_timeout,
                                s32 *steps,
                                enum fe_stv0900_demod_num demod)
@@ -85,30 +74,19 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
 
        enum fe_stv0900_search_standard standard;
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               srate = i_params->dmd1_symbol_rate;
-               max_carrier = i_params->dmd1_srch_range / 1000;
-               max_carrier += max_carrier / 10;
-               standard = i_params->dmd1_srch_standard;
-               break;
-       case STV0900_DEMOD_2:
-               srate = i_params->dmd2_symbol_rate;
-               max_carrier = i_params->dmd2_srch_range / 1000;
-               max_carrier += max_carrier / 10;
-               standard = i_params->dmd2_srch_stndrd;
-               break;
-       }
+       srate = intp->symbol_rate[demod];
+       max_carrier = intp->srch_range[demod] / 1000;
+       max_carrier += max_carrier / 10;
+       standard = intp->srch_standard[demod];
 
        max_carrier = 65536 * (max_carrier / 2);
-       max_carrier /= i_params->mclk / 1000;
+       max_carrier /= intp->mclk / 1000;
 
        if (max_carrier > 0x4000)
                max_carrier = 0x4000;
 
        freq_inc = srate;
-       freq_inc /= i_params->mclk >> 10;
+       freq_inc /= intp->mclk >> 10;
        freq_inc = freq_inc << 6;
 
        switch (standard) {
@@ -154,7 +132,7 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
 
 }
 
-static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
+static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp,
                                s32 FreqIncr, s32 Timeout, int zigzag,
                                s32 MaxStep, enum fe_stv0900_demod_num demod)
 {
@@ -164,20 +142,11 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
                freqOffset,
                max_carrier;
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               max_carrier = i_params->dmd1_srch_range / 1000;
-               max_carrier += (max_carrier / 10);
-               break;
-       case STV0900_DEMOD_2:
-               max_carrier = i_params->dmd2_srch_range / 1000;
-               max_carrier += (max_carrier / 10);
-               break;
-       }
+       max_carrier = intp->srch_range[demod] / 1000;
+       max_carrier += (max_carrier / 10);
 
        max_carrier = 65536 * (max_carrier / 2);
-       max_carrier /= i_params->mclk / 1000;
+       max_carrier /= intp->mclk / 1000;
 
        if (max_carrier > 0x4000)
                max_carrier = 0x4000;
@@ -190,40 +159,15 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
        stepCpt = 0;
 
        do {
-               switch (demod) {
-               case STV0900_DEMOD_1:
-               default:
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT1,
-                                       (freqOffset / 256) & 0xFF);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT0,
-                                        freqOffset & 0xFF);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-                       stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1);
-
-                       if (i_params->chip_id == 0x12) {
-                               stv0900_write_bits(i_params,
-                                               F0900_P1_RST_HWARE, 1);
-                               stv0900_write_bits(i_params,
-                                               F0900_P1_RST_HWARE, 0);
-                       }
-                       break;
-               case STV0900_DEMOD_2:
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT1,
-                                       (freqOffset / 256) & 0xFF);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT0,
-                                       freqOffset & 0xFF);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-                       stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1);
-
-                       if (i_params->chip_id == 0x12) {
-                               stv0900_write_bits(i_params,
-                                               F0900_P2_RST_HWARE, 1);
-                               stv0900_write_bits(i_params,
-                                               F0900_P2_RST_HWARE, 0);
-                       }
-                       break;
+               stv0900_write_reg(intp, DMDISTATE, 0x1c);
+               stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff);
+               stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff);
+               stv0900_write_reg(intp, DMDISTATE, 0x18);
+               stv0900_write_bits(intp, ALGOSWRST, 1);
+
+               if (intp->chip_id == 0x12) {
+                       stv0900_write_bits(intp, RST_HWARE, 1);
+                       stv0900_write_bits(intp, RST_HWARE, 0);
                }
 
                if (zigzag == TRUE) {
@@ -235,8 +179,8 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
                        freqOffset += + 2 * FreqIncr;
 
                stepCpt++;
-               lock = stv0900_get_demod_lock(i_params, demod, Timeout);
-               no_signal = stv0900_check_signal_presence(i_params, demod);
+               lock = stv0900_get_demod_lock(intp, demod, Timeout);
+               no_signal = stv0900_check_signal_presence(intp, demod);
 
        } while ((lock == FALSE)
                        && (no_signal == FALSE)
@@ -244,269 +188,138 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
                        && ((freqOffset + FreqIncr) > -max_carrier)
                        && (stepCpt < MaxStep));
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0);
-               break;
-       case STV0900_DEMOD_2:
-               stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0);
-               break;
-       }
+       stv0900_write_bits(intp, ALGOSWRST, 0);
 
        return lock;
 }
 
-int stv0900_sw_algo(struct stv0900_internal *i_params,
+int stv0900_sw_algo(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod)
 {
-       int lock = FALSE;
-
-       int no_signal,
-       zigzag;
-       s32 dvbs2_fly_wheel;
-
-       s32 freqIncrement, softStepTimeout, trialCounter, max_steps;
-
-       stv0900_get_sw_loop_params(i_params, &freqIncrement, &softStepTimeout,
+       int     lock = FALSE,
+               no_signal,
+               zigzag;
+       s32     s2fw,
+               fqc_inc,
+               sft_stp_tout,
+               trial_cntr,
+               max_steps;
+
+       stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout,
                                        &max_steps, demod);
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               switch (i_params->dmd1_srch_standard) {
-               case STV0900_SEARCH_DVBS1:
-               case STV0900_SEARCH_DSS:
-                       if (i_params->chip_id >= 0x20)
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ,
-                                               0x3B);
-                       else
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ,
-                                               0xef);
-
-                       stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x49);
-                       zigzag = FALSE;
-                       break;
-               case STV0900_SEARCH_DVBS2:
-                       if (i_params->chip_id >= 0x20)
-                               stv0900_write_reg(i_params, R0900_P1_CORRELABS,
-                                               0x79);
-                       else
-                               stv0900_write_reg(i_params, R0900_P1_CORRELABS,
-                                               0x68);
+       switch (intp->srch_standard[demod]) {
+       case STV0900_SEARCH_DVBS1:
+       case STV0900_SEARCH_DSS:
+               if (intp->chip_id >= 0x20)
+                       stv0900_write_reg(intp, CARFREQ, 0x3b);
+               else
+                       stv0900_write_reg(intp, CARFREQ, 0xef);
 
-                       stv0900_write_reg(i_params, R0900_P1_DMDCFGMD,
-                                               0x89);
+               stv0900_write_reg(intp, DMDCFGMD, 0x49);
+               zigzag = FALSE;
+               break;
+       case STV0900_SEARCH_DVBS2:
+               if (intp->chip_id >= 0x20)
+                       stv0900_write_reg(intp, CORRELABS, 0x79);
+               else
+                       stv0900_write_reg(intp, CORRELABS, 0x68);
 
-                       zigzag = TRUE;
-                       break;
-               case STV0900_AUTO_SEARCH:
-               default:
-                       if (i_params->chip_id >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ,
-                                                       0x3B);
-                               stv0900_write_reg(i_params, R0900_P1_CORRELABS,
-                                                       0x79);
-                       } else {
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ,
-                                                       0xef);
-                               stv0900_write_reg(i_params, R0900_P1_CORRELABS,
-                                                       0x68);
-                       }
+               stv0900_write_reg(intp, DMDCFGMD, 0x89);
 
-                       stv0900_write_reg(i_params, R0900_P1_DMDCFGMD,
-                                                       0xc9);
-                       zigzag = FALSE;
-                       break;
+               zigzag = TRUE;
+               break;
+       case STV0900_AUTO_SEARCH:
+       default:
+               if (intp->chip_id >= 0x20) {
+                       stv0900_write_reg(intp, CARFREQ, 0x3b);
+                       stv0900_write_reg(intp, CORRELABS, 0x79);
+               } else {
+                       stv0900_write_reg(intp, CARFREQ, 0xef);
+                       stv0900_write_reg(intp, CORRELABS, 0x68);
                }
 
-               trialCounter = 0;
-               do {
-                       lock = stv0900_search_carr_sw_loop(i_params,
-                                                       freqIncrement,
-                                                       softStepTimeout,
-                                                       zigzag,
-                                                       max_steps,
-                                                       demod);
-                       no_signal = stv0900_check_signal_presence(i_params,
-                                                               demod);
-                       trialCounter++;
-                       if ((lock == TRUE)
-                                       || (no_signal == TRUE)
-                                       || (trialCounter == 2)) {
-
-                               if (i_params->chip_id >= 0x20) {
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P1_CARFREQ,
-                                                       0x49);
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P1_CORRELABS,
-                                                       0x9e);
-                               } else {
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P1_CARFREQ,
-                                                       0xed);
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P1_CORRELABS,
-                                                       0x88);
-                               }
-
-                               if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS2_FOUND)) {
-                                       msleep(softStepTimeout);
-                                       dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT);
-
-                                       if (dvbs2_fly_wheel < 0xd) {
-                                               msleep(softStepTimeout);
-                                               dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT);
-                                       }
-
-                                       if (dvbs2_fly_wheel < 0xd) {
-                                               lock = FALSE;
-
-                                               if (trialCounter < 2) {
-                                                       if (i_params->chip_id >= 0x20)
-                                                               stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x79);
-                                                       else
-                                                               stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x68);
-
-                                                       stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x89);
-                                               }
-                                       }
-                               }
-                       }
-
-               } while ((lock == FALSE)
-                       && (trialCounter < 2)
-                       && (no_signal == FALSE));
-
+               stv0900_write_reg(intp, DMDCFGMD, 0xc9);
+               zigzag = FALSE;
                break;
-       case STV0900_DEMOD_2:
-               switch (i_params->dmd2_srch_stndrd) {
-               case STV0900_SEARCH_DVBS1:
-               case STV0900_SEARCH_DSS:
-                       if (i_params->chip_id >= 0x20)
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ,
-                                               0x3b);
-                       else
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ,
-                                               0xef);
-
-                       stv0900_write_reg(i_params, R0900_P2_DMDCFGMD,
-                                               0x49);
-                       zigzag = FALSE;
-                       break;
-               case STV0900_SEARCH_DVBS2:
-                       if (i_params->chip_id >= 0x20)
-                               stv0900_write_reg(i_params, R0900_P2_CORRELABS,
-                                               0x79);
-                       else
-                               stv0900_write_reg(i_params, R0900_P2_CORRELABS,
-                                               0x68);
+       }
 
-                       stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89);
-                       zigzag = TRUE;
-                       break;
-               case STV0900_AUTO_SEARCH:
-               default:
-                       if (i_params->chip_id >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ,
-                                               0x3b);
-                               stv0900_write_reg(i_params, R0900_P2_CORRELABS,
-                                               0x79);
+       trial_cntr = 0;
+       do {
+               lock = stv0900_search_carr_sw_loop(intp,
+                                               fqc_inc,
+                                               sft_stp_tout,
+                                               zigzag,
+                                               max_steps,
+                                               demod);
+               no_signal = stv0900_check_signal_presence(intp, demod);
+               trial_cntr++;
+               if ((lock == TRUE)
+                               || (no_signal == TRUE)
+                               || (trial_cntr == 2)) {
+
+                       if (intp->chip_id >= 0x20) {
+                               stv0900_write_reg(intp, CARFREQ, 0x49);
+                               stv0900_write_reg(intp, CORRELABS, 0x9e);
                        } else {
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ,
-                                               0xef);
-                               stv0900_write_reg(i_params, R0900_P2_CORRELABS,
-                                               0x68);
+                               stv0900_write_reg(intp, CARFREQ, 0xed);
+                               stv0900_write_reg(intp, CORRELABS, 0x88);
                        }
 
-                       stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0xc9);
-
-                       zigzag = FALSE;
-                       break;
-               }
+                       if ((stv0900_get_bits(intp, HEADER_MODE) ==
+                                               STV0900_DVBS2_FOUND) &&
+                                                       (lock == TRUE)) {
+                               msleep(sft_stp_tout);
+                               s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT);
 
-               trialCounter = 0;
-
-               do {
-                       lock = stv0900_search_carr_sw_loop(i_params,
-                                                       freqIncrement,
-                                                       softStepTimeout,
-                                                       zigzag,
-                                                       max_steps,
-                                                       demod);
-                       no_signal = stv0900_check_signal_presence(i_params,
-                                                               demod);
-                       trialCounter++;
-                       if ((lock == TRUE)
-                                       || (no_signal == TRUE)
-                                       || (trialCounter == 2)) {
-                               if (i_params->chip_id >= 0x20) {
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P2_CARFREQ,
-                                                       0x49);
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P2_CORRELABS,
-                                                       0x9e);
-                               } else {
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P2_CARFREQ,
-                                                       0xed);
-                                       stv0900_write_reg(i_params,
-                                                       R0900_P2_CORRELABS,
-                                                       0x88);
+                               if (s2fw < 0xd) {
+                                       msleep(sft_stp_tout);
+                                       s2fw = stv0900_get_bits(intp,
+                                                               FLYWHEEL_CPT);
                                }
 
-                               if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS2_FOUND)) {
-                                       msleep(softStepTimeout);
-                                       dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT);
-                                       if (dvbs2_fly_wheel < 0xd) {
-                                               msleep(softStepTimeout);
-                                               dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT);
-                                       }
+                               if (s2fw < 0xd) {
+                                       lock = FALSE;
 
-                                       if (dvbs2_fly_wheel < 0xd) {
-                                               lock = FALSE;
-                                               if (trialCounter < 2) {
-                                                       if (i_params->chip_id >= 0x20)
-                                                               stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x79);
-                                                       else
-                                                               stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x68);
+                                       if (trial_cntr < 2) {
+                                               if (intp->chip_id >= 0x20)
+                                                       stv0900_write_reg(intp,
+                                                               CORRELABS,
+                                                               0x79);
+                                               else
+                                                       stv0900_write_reg(intp,
+                                                               CORRELABS,
+                                                               0x68);
 
-                                                       stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89);
-                                               }
+                                               stv0900_write_reg(intp,
+                                                               DMDCFGMD,
+                                                               0x89);
                                        }
                                }
                        }
+               }
 
-               } while ((lock == FALSE) && (trialCounter < 2) && (no_signal == FALSE));
-
-               break;
-       }
+       } while ((lock == FALSE)
+               && (trial_cntr < 2)
+               && (no_signal == FALSE));
 
        return lock;
 }
 
-static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params,
+static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp,
                                        u32 mclk,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 sfr_field3, sfr_field2, sfr_field1, sfr_field0,
-       rem1, rem2, intval1, intval2, srate;
-
-       dmd_reg(sfr_field3, F0900_P1_SYMB_FREQ3, F0900_P2_SYMB_FREQ3);
-       dmd_reg(sfr_field2, F0900_P1_SYMB_FREQ2, F0900_P2_SYMB_FREQ2);
-       dmd_reg(sfr_field1, F0900_P1_SYMB_FREQ1, F0900_P2_SYMB_FREQ1);
-       dmd_reg(sfr_field0, F0900_P1_SYMB_FREQ0, F0900_P2_SYMB_FREQ0);
-
-       srate = (stv0900_get_bits(i_params, sfr_field3) << 24) +
-               (stv0900_get_bits(i_params, sfr_field2) << 16) +
-               (stv0900_get_bits(i_params, sfr_field1) << 8) +
-               (stv0900_get_bits(i_params, sfr_field0));
+       s32     rem1, rem2, intval1, intval2, srate;
+
+       srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) +
+               (stv0900_get_bits(intp, SYMB_FREQ2) << 16) +
+               (stv0900_get_bits(intp, SYMB_FREQ1) << 8) +
+               (stv0900_get_bits(intp, SYMB_FREQ0));
        dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n",
-               srate, stv0900_get_bits(i_params, sfr_field0),
-               stv0900_get_bits(i_params, sfr_field1),
-               stv0900_get_bits(i_params, sfr_field2),
-               stv0900_get_bits(i_params, sfr_field3));
+               srate, stv0900_get_bits(intp, SYMB_FREQ0),
+               stv0900_get_bits(intp, SYMB_FREQ1),
+               stv0900_get_bits(intp, SYMB_FREQ2),
+               stv0900_get_bits(intp, SYMB_FREQ3));
 
        intval1 = (mclk) >> 16;
        intval2 = (srate) >> 16;
@@ -520,18 +333,15 @@ static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params,
        return srate;
 }
 
-static void stv0900_set_symbol_rate(struct stv0900_internal *i_params,
+static void stv0900_set_symbol_rate(struct stv0900_internal *intp,
                                        u32 mclk, u32 srate,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 sfr_init_reg;
        u32 symb;
 
-       dprintk(KERN_INFO "%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk,
+       dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk,
                                                        srate, demod);
 
-       dmd_reg(sfr_init_reg, R0900_P1_SFRINIT1, R0900_P2_SFRINIT1);
-
        if (srate > 60000000) {
                symb = srate << 4;
                symb /= (mclk >> 12);
@@ -543,19 +353,16 @@ static void stv0900_set_symbol_rate(struct stv0900_internal *i_params,
                symb /= (mclk >> 7);
        }
 
-       stv0900_write_reg(i_params, sfr_init_reg, (symb >> 8) & 0x7F);
-       stv0900_write_reg(i_params, sfr_init_reg + 1, (symb & 0xFF));
+       stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f);
+       stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff));
 }
 
-static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params,
+static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp,
                                        u32 mclk, u32 srate,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 sfr_max_reg;
        u32 symb;
 
-       dmd_reg(sfr_max_reg, R0900_P1_SFRUP1, R0900_P2_SFRUP1);
-
        srate = 105 * (srate / 100);
 
        if (srate > 60000000) {
@@ -570,23 +377,20 @@ static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params,
        }
 
        if (symb < 0x7fff) {
-               stv0900_write_reg(i_params, sfr_max_reg, (symb >> 8) & 0x7F);
-               stv0900_write_reg(i_params, sfr_max_reg + 1, (symb & 0xFF));
+               stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f);
+               stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff));
        } else {
-               stv0900_write_reg(i_params, sfr_max_reg, 0x7F);
-               stv0900_write_reg(i_params, sfr_max_reg + 1, 0xFF);
+               stv0900_write_reg(intp, SFRUP1, 0x7f);
+               stv0900_write_reg(intp, SFRUP1 + 1, 0xff);
        }
 }
 
-static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params,
+static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp,
                                        u32 mclk, u32 srate,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 sfr_min_reg;
        u32     symb;
 
-       dmd_reg(sfr_min_reg, R0900_P1_SFRLOW1, R0900_P2_SFRLOW1);
-
        srate = 95 * (srate / 100);
        if (srate > 60000000) {
                symb = srate << 4;
@@ -601,22 +405,20 @@ static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params,
                symb /= (mclk >> 7);
        }
 
-       stv0900_write_reg(i_params, sfr_min_reg, (symb >> 8) & 0xFF);
-       stv0900_write_reg(i_params, sfr_min_reg + 1, (symb & 0xFF));
+       stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff);
+       stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff));
 }
 
-static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params,
+static s32 stv0900_get_timing_offst(struct stv0900_internal *intp,
                                        u32 srate,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 tmgreg,
-       timingoffset;
+       s32 timingoffset;
 
-       dmd_reg(tmgreg, R0900_P1_TMGREG2, R0900_P2_TMGREG2);
 
-       timingoffset = (stv0900_read_reg(i_params, tmgreg) << 16) +
-                      (stv0900_read_reg(i_params, tmgreg + 1) << 8) +
-                      (stv0900_read_reg(i_params, tmgreg + 2));
+       timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) +
+                      (stv0900_read_reg(intp, TMGREG2 + 1) << 8) +
+                      (stv0900_read_reg(intp, TMGREG2 + 2));
 
        timingoffset = ge2comp(timingoffset, 24);
 
@@ -630,22 +432,19 @@ static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params,
        return timingoffset;
 }
 
-static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *i_params,
+static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 rolloff, man_fld, matstr_reg, rolloff_ctl_fld;
-
-       dmd_reg(man_fld, F0900_P1_MANUAL_ROLLOFF, F0900_P2_MANUAL_ROLLOFF);
-       dmd_reg(matstr_reg, R0900_P1_MATSTR1, R0900_P2_MATSTR1);
-       dmd_reg(rolloff_ctl_fld, F0900_P1_ROLLOFF_CONTROL,
-                               F0900_P2_ROLLOFF_CONTROL);
-
-       if (i_params->chip_id == 0x10) {
-               stv0900_write_bits(i_params, man_fld, 1);
-               rolloff = stv0900_read_reg(i_params, matstr_reg) & 0x03;
-               stv0900_write_bits(i_params, rolloff_ctl_fld, rolloff);
-       } else
-               stv0900_write_bits(i_params, man_fld, 0);
+       s32 rolloff;
+
+       if (intp->chip_id == 0x10) {
+               stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1);
+               rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03;
+               stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff);
+       } else if (intp->chip_id <= 0x20)
+               stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0);
+       else /* cut 3.0 */
+               stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0);
 }
 
 static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro)
@@ -668,84 +467,47 @@ static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro)
        return srate  + (srate * rolloff) / 100;
 }
 
-static int stv0900_check_timing_lock(struct stv0900_internal *i_params,
+static int stv0900_check_timing_lock(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod)
 {
        int timingLock = FALSE;
-       s32 i,
-       timingcpt = 0;
-       u8 carFreq,
-       tmgTHhigh,
-       tmgTHLow;
-
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               carFreq = stv0900_read_reg(i_params, R0900_P1_CARFREQ);
-               tmgTHhigh = stv0900_read_reg(i_params, R0900_P1_TMGTHRISE);
-               tmgTHLow = stv0900_read_reg(i_params, R0900_P1_TMGTHFALL);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x0);
-               stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
-               stv0900_write_reg(i_params, R0900_P1_RTC, 0x80);
-               stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x40);
-               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x0);
-               stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0x0);
-               stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0x0);
-               stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x65);
-               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-               msleep(7);
-
-               for (i = 0; i < 10; i++) {
-                       if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2)
-                               timingcpt++;
-
-                       msleep(1);
-               }
-
-               if (timingcpt >= 3)
-                       timingLock = TRUE;
-
-               stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
-               stv0900_write_reg(i_params, R0900_P1_RTC, 0x88);
-               stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68);
-               stv0900_write_reg(i_params, R0900_P1_CARFREQ, carFreq);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, tmgTHhigh);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, tmgTHLow);
-               break;
-       case STV0900_DEMOD_2:
-               carFreq = stv0900_read_reg(i_params, R0900_P2_CARFREQ);
-               tmgTHhigh = stv0900_read_reg(i_params, R0900_P2_TMGTHRISE);
-               tmgTHLow = stv0900_read_reg(i_params, R0900_P2_TMGTHFALL);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0);
-               stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
-               stv0900_write_reg(i_params, R0900_P2_RTC, 0x80);
-               stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x40);
-               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x0);
-               stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0x0);
-               stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0x0);
-               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x65);
-               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-               msleep(5);
-               for (i = 0; i < 10; i++) {
-                       if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2)
-                               timingcpt++;
-
-                       msleep(1);
-               }
+       s32     i,
+               timingcpt = 0;
+       u8      car_freq,
+               tmg_th_high,
+               tmg_th_low;
+
+       car_freq = stv0900_read_reg(intp, CARFREQ);
+       tmg_th_high = stv0900_read_reg(intp, TMGTHRISE);
+       tmg_th_low = stv0900_read_reg(intp, TMGTHFALL);
+       stv0900_write_reg(intp, TMGTHRISE, 0x20);
+       stv0900_write_reg(intp, TMGTHFALL, 0x0);
+       stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+       stv0900_write_reg(intp, RTC, 0x80);
+       stv0900_write_reg(intp, RTCS2, 0x40);
+       stv0900_write_reg(intp, CARFREQ, 0x0);
+       stv0900_write_reg(intp, CFRINIT1, 0x0);
+       stv0900_write_reg(intp, CFRINIT0, 0x0);
+       stv0900_write_reg(intp, AGC2REF, 0x65);
+       stv0900_write_reg(intp, DMDISTATE, 0x18);
+       msleep(7);
+
+       for (i = 0; i < 10; i++) {
+               if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2)
+                       timingcpt++;
+
+               msleep(1);
+       }
 
-               if (timingcpt >= 3)
-                       timingLock = TRUE;
+       if (timingcpt >= 3)
+               timingLock = TRUE;
 
-               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
-               stv0900_write_reg(i_params, R0900_P2_RTC, 0x88);
-               stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68);
-               stv0900_write_reg(i_params, R0900_P2_CARFREQ, carFreq);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, tmgTHhigh);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, tmgTHLow);
-               break;
-       }
+       stv0900_write_reg(intp, AGC2REF, 0x38);
+       stv0900_write_reg(intp, RTC, 0x88);
+       stv0900_write_reg(intp, RTCS2, 0x68);
+       stv0900_write_reg(intp, CARFREQ, car_freq);
+       stv0900_write_reg(intp, TMGTHRISE, tmg_th_high);
+       stv0900_write_reg(intp, TMGTHFALL, tmg_th_low);
 
        return  timingLock;
 }
@@ -754,142 +516,114 @@ static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe,
                                        s32 demod_timeout)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
+       int     lock = FALSE,
+               d = demod;
+       s32     srate,
+               search_range,
+               locktimeout,
+               currier_step,
+               nb_steps,
+               current_step,
+               direction,
+               tuner_freq,
+               timeout,
+               freq;
 
-       int lock = FALSE;
-       s32 srate, search_range, locktimeout,
-               currier_step, nb_steps, current_step,
-               direction, tuner_freq, timeout;
-
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               srate = i_params->dmd1_symbol_rate;
-               search_range = i_params->dmd1_srch_range;
-               break;
-
-       case STV0900_DEMOD_2:
-               srate = i_params->dmd2_symbol_rate;
-               search_range = i_params->dmd2_srch_range;
-               break;
-       }
+       srate = intp->symbol_rate[d];
+       search_range = intp->srch_range[d];
 
        if (srate >= 10000000)
                locktimeout = demod_timeout / 3;
        else
                locktimeout = demod_timeout / 2;
 
-       lock = stv0900_get_demod_lock(i_params, demod, locktimeout);
-
-       if (lock == FALSE) {
-               if (srate >= 10000000) {
-                       if (stv0900_check_timing_lock(i_params, demod) == TRUE) {
-                               switch (demod) {
-                               case STV0900_DEMOD_1:
-                               default:
-                                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
-                                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
-                                       break;
-                               case STV0900_DEMOD_2:
-                                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
-                                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
-                                       break;
-                               }
+       lock = stv0900_get_demod_lock(intp, d, locktimeout);
 
-                               lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
-                       } else
-                               lock = FALSE;
-               } else {
-                       if (srate <= 4000000)
-                               currier_step = 1000;
-                       else if (srate <= 7000000)
-                               currier_step = 2000;
-                       else if (srate <= 10000000)
-                               currier_step = 3000;
-                       else
-                               currier_step = 5000;
-
-                       nb_steps = ((search_range / 1000) / currier_step);
-                       nb_steps /= 2;
-                       nb_steps = (2 * (nb_steps + 1));
-                       if (nb_steps < 0)
-                               nb_steps = 2;
-                       else if (nb_steps > 12)
-                               nb_steps = 12;
-
-                       current_step = 1;
-                       direction = 1;
+       if (lock != FALSE)
+               return lock;
+
+       if (srate >= 10000000) {
+               if (stv0900_check_timing_lock(intp, d) == TRUE) {
+                       stv0900_write_reg(intp, DMDISTATE, 0x1f);
+                       stv0900_write_reg(intp, DMDISTATE, 0x15);
+                       lock = stv0900_get_demod_lock(intp, d, demod_timeout);
+               } else
+                       lock = FALSE;
+
+               return lock;
+       }
+
+       if (intp->chip_id <= 0x20) {
+               if (srate <= 1000000)
+                       currier_step = 500;
+               else if (srate <= 4000000)
+                       currier_step = 1000;
+               else if (srate <= 7000000)
+                       currier_step = 2000;
+               else if (srate <= 10000000)
+                       currier_step = 3000;
+               else
+                       currier_step = 5000;
+
+               if (srate >= 2000000) {
                        timeout = (demod_timeout / 3);
                        if (timeout > 1000)
                                timeout = 1000;
+               } else
+                       timeout = (demod_timeout / 2);
+       } else {
+               /*cut 3.0 */
+               currier_step = srate / 4000;
+               timeout = (demod_timeout * 3) / 4;
+       }
 
-                       switch (demod) {
-                       case STV0900_DEMOD_1:
-                       default:
-                               if (lock == FALSE) {
-                                       tuner_freq = i_params->tuner1_freq;
-                                       i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + i_params->dmd1_symbol_rate;
+       nb_steps = ((search_range / 1000) / currier_step);
 
-                                       while ((current_step <= nb_steps) && (lock == FALSE)) {
+       if ((nb_steps % 2) != 0)
+               nb_steps += 1;
 
-                                               if (direction > 0)
-                                                       tuner_freq += (current_step * currier_step);
-                                               else
-                                                       tuner_freq -= (current_step * currier_step);
-
-                                               stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw);
-                                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
-                                               if (i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS2) {
-                                                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
-                                                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-                                                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                                                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
-                                               }
-
-                                               stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0);
-                                               stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0);
-                                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
-                                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
-                                               lock = stv0900_get_demod_lock(i_params, demod, timeout);
-                                               direction *= -1;
-                                               current_step++;
-                                       }
-                               }
-                               break;
-                       case STV0900_DEMOD_2:
-                               if (lock == FALSE) {
-                                       tuner_freq = i_params->tuner2_freq;
-                                       i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + srate;
+       if (nb_steps <= 0)
+               nb_steps = 2;
+       else if (nb_steps > 12)
+               nb_steps = 12;
 
-                                       while ((current_step <= nb_steps) && (lock == FALSE)) {
+       current_step = 1;
+       direction = 1;
 
-                                               if (direction > 0)
-                                                       tuner_freq += (current_step * currier_step);
-                                               else
-                                                       tuner_freq -= (current_step * currier_step);
-
-                                               stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw);
-                                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
-                                               if (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS2) {
-                                                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
-                                                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
-                                                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                                                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
-                                               }
-
-                                               stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0);
-                                               stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0);
-                                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
-                                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
-                                               lock = stv0900_get_demod_lock(i_params, demod, timeout);
-                                               direction *= -1;
-                                               current_step++;
-                                       }
-                               }
-                               break;
-                       }
+       if (intp->chip_id <= 0x20) {
+               tuner_freq = intp->freq[d];
+               intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d],
+                               intp->rolloff) + intp->symbol_rate[d];
+       } else
+               tuner_freq = 0;
+
+       while ((current_step <= nb_steps) && (lock == FALSE)) {
+               if (direction > 0)
+                       tuner_freq += (current_step * currier_step);
+               else
+                       tuner_freq -= (current_step * currier_step);
+
+               if (intp->chip_id <= 0x20) {
+                       stv0900_set_tuner(fe, tuner_freq, intp->bw[d]);
+                       stv0900_write_reg(intp, DMDISTATE, 0x1c);
+                       stv0900_write_reg(intp, CFRINIT1, 0);
+                       stv0900_write_reg(intp, CFRINIT0, 0);
+                       stv0900_write_reg(intp, DMDISTATE, 0x1f);
+                       stv0900_write_reg(intp, DMDISTATE, 0x15);
+               } else {
+                       stv0900_write_reg(intp, DMDISTATE, 0x1c);
+                       freq = (tuner_freq * 65536) / (intp->mclk / 1000);
+                       stv0900_write_bits(intp, CFR_INIT1, MSB(freq));
+                       stv0900_write_bits(intp, CFR_INIT0, LSB(freq));
+                       stv0900_write_reg(intp, DMDISTATE, 0x1f);
+                       stv0900_write_reg(intp, DMDISTATE, 0x05);
                }
+
+               lock = stv0900_get_demod_lock(intp, d, timeout);
+               direction *= -1;
+               current_step++;
        }
 
        return  lock;
@@ -931,9 +665,7 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout,
                } else if (srate <= 20000000) {
                        (*demod_timeout) = 400;
                        (*fec_timeout) = 130;
-               }
-
-               else {
+               } else {
                        (*demod_timeout) = 300;
                        (*fec_timeout) = 100;
                }
@@ -946,95 +678,77 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout,
                (*demod_timeout) /= 2;
 }
 
-static void stv0900_set_viterbi_tracq(struct stv0900_internal *i_params,
+static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
 
-       s32 vth_reg;
-
-       dprintk(KERN_INFO "%s\n", __func__);
+       s32 vth_reg = VTH12;
 
-       dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12);
+       dprintk("%s\n", __func__);
 
-       stv0900_write_reg(i_params, vth_reg++, 0xd0);
-       stv0900_write_reg(i_params, vth_reg++, 0x7d);
-       stv0900_write_reg(i_params, vth_reg++, 0x53);
-       stv0900_write_reg(i_params, vth_reg++, 0x2F);
-       stv0900_write_reg(i_params, vth_reg++, 0x24);
-       stv0900_write_reg(i_params, vth_reg++, 0x1F);
+       stv0900_write_reg(intp, vth_reg++, 0xd0);
+       stv0900_write_reg(intp, vth_reg++, 0x7d);
+       stv0900_write_reg(intp, vth_reg++, 0x53);
+       stv0900_write_reg(intp, vth_reg++, 0x2f);
+       stv0900_write_reg(intp, vth_reg++, 0x24);
+       stv0900_write_reg(intp, vth_reg++, 0x1f);
 }
 
-static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params,
-                                  enum fe_stv0900_search_standard Standard,
-                                  enum fe_stv0900_fec PunctureRate,
+static void stv0900_set_viterbi_standard(struct stv0900_internal *intp,
+                                  enum fe_stv0900_search_standard standard,
+                                  enum fe_stv0900_fec fec,
                                   enum fe_stv0900_demod_num demod)
 {
+       dprintk("%s: ViterbiStandard = ", __func__);
 
-       s32 fecmReg,
-       prvitReg;
-
-       dprintk(KERN_INFO "%s: ViterbiStandard = ", __func__);
-
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               fecmReg = R0900_P1_FECM;
-               prvitReg = R0900_P1_PRVIT;
-               break;
-       case STV0900_DEMOD_2:
-               fecmReg = R0900_P2_FECM;
-               prvitReg = R0900_P2_PRVIT;
-               break;
-       }
-
-       switch (Standard) {
+       switch (standard) {
        case STV0900_AUTO_SEARCH:
                dprintk("Auto\n");
-               stv0900_write_reg(i_params, fecmReg, 0x10);
-               stv0900_write_reg(i_params, prvitReg, 0x3F);
+               stv0900_write_reg(intp, FECM, 0x10);
+               stv0900_write_reg(intp, PRVIT, 0x3f);
                break;
        case STV0900_SEARCH_DVBS1:
                dprintk("DVBS1\n");
-               stv0900_write_reg(i_params, fecmReg, 0x00);
-               switch (PunctureRate) {
+               stv0900_write_reg(intp, FECM, 0x00);
+               switch (fec) {
                case STV0900_FEC_UNKNOWN:
                default:
-                       stv0900_write_reg(i_params, prvitReg, 0x2F);
+                       stv0900_write_reg(intp, PRVIT, 0x2f);
                        break;
                case STV0900_FEC_1_2:
-                       stv0900_write_reg(i_params, prvitReg, 0x01);
+                       stv0900_write_reg(intp, PRVIT, 0x01);
                        break;
                case STV0900_FEC_2_3:
-                       stv0900_write_reg(i_params, prvitReg, 0x02);
+                       stv0900_write_reg(intp, PRVIT, 0x02);
                        break;
                case STV0900_FEC_3_4:
-                       stv0900_write_reg(i_params, prvitReg, 0x04);
+                       stv0900_write_reg(intp, PRVIT, 0x04);
                        break;
                case STV0900_FEC_5_6:
-                       stv0900_write_reg(i_params, prvitReg, 0x08);
+                       stv0900_write_reg(intp, PRVIT, 0x08);
                        break;
                case STV0900_FEC_7_8:
-                       stv0900_write_reg(i_params, prvitReg, 0x20);
+                       stv0900_write_reg(intp, PRVIT, 0x20);
                        break;
                }
 
                break;
        case STV0900_SEARCH_DSS:
                dprintk("DSS\n");
-               stv0900_write_reg(i_params, fecmReg, 0x80);
-               switch (PunctureRate) {
+               stv0900_write_reg(intp, FECM, 0x80);
+               switch (fec) {
                case STV0900_FEC_UNKNOWN:
                default:
-                       stv0900_write_reg(i_params, prvitReg, 0x13);
+                       stv0900_write_reg(intp, PRVIT, 0x13);
                        break;
                case STV0900_FEC_1_2:
-                       stv0900_write_reg(i_params, prvitReg, 0x01);
+                       stv0900_write_reg(intp, PRVIT, 0x01);
                        break;
                case STV0900_FEC_2_3:
-                       stv0900_write_reg(i_params, prvitReg, 0x02);
+                       stv0900_write_reg(intp, PRVIT, 0x02);
                        break;
                case STV0900_FEC_6_7:
-                       stv0900_write_reg(i_params, prvitReg, 0x10);
+                       stv0900_write_reg(intp, PRVIT, 0x10);
                        break;
                }
                break;
@@ -1043,340 +757,277 @@ static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params,
        }
 }
 
-static void stv0900_track_optimization(struct dvb_frontend *fe)
+static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp,
+                                               enum fe_stv0900_demod_num demod)
 {
-       struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
-       enum fe_stv0900_demod_num demod = state->demod;
+       enum fe_stv0900_fec prate;
+       s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN);
 
-       s32 srate, pilots, aclc, freq1, freq0,
-               i = 0, timed, timef, blindTunSw = 0;
+       switch (rate_fld) {
+       case 13:
+               prate = STV0900_FEC_1_2;
+               break;
+       case 18:
+               prate = STV0900_FEC_2_3;
+               break;
+       case 21:
+               prate = STV0900_FEC_3_4;
+               break;
+       case 24:
+               prate = STV0900_FEC_5_6;
+               break;
+       case 25:
+               prate = STV0900_FEC_6_7;
+               break;
+       case 26:
+               prate = STV0900_FEC_7_8;
+               break;
+       default:
+               prate = STV0900_FEC_UNKNOWN;
+               break;
+       }
 
-       enum fe_stv0900_rolloff rolloff;
-       enum fe_stv0900_modcode foundModcod;
+       return prate;
+}
 
-       dprintk(KERN_INFO "%s\n", __func__);
+void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp,
+                                       enum fe_stv0900_demod_num demod,
+                                       u32 srate)
+{
+       if (intp->chip_id >= 0x30) {
+               if (srate >= 15000000) {
+                       stv0900_write_reg(intp, ACLC, 0x2b);
+                       stv0900_write_reg(intp, BCLC, 0x1a);
+               } else if ((srate >= 7000000) && (15000000 > srate)) {
+                       stv0900_write_reg(intp, ACLC, 0x0c);
+                       stv0900_write_reg(intp, BCLC, 0x1b);
+               } else if (srate < 7000000) {
+                       stv0900_write_reg(intp, ACLC, 0x2c);
+                       stv0900_write_reg(intp, BCLC, 0x1c);
+               }
 
-       srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-       srate += stv0900_get_timing_offst(i_params, srate, demod);
+       } else { /*cut 2.0 and 1.x*/
+               stv0900_write_reg(intp, ACLC, 0x1a);
+               stv0900_write_reg(intp, BCLC, 0x09);
+       }
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               switch (i_params->dmd1_rslts.standard) {
-               case STV0900_DVBS1_STANDARD:
-                       if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) {
-                               stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                               stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-                       }
+}
 
-                       stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff);
-                       stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
-                       stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
-                       break;
-               case STV0900_DSS_STANDARD:
-                       if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) {
-                               stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                               stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-                       }
+static void stv0900_track_optimization(struct dvb_frontend *fe)
+{
+       struct stv0900_state *state = fe->demodulator_priv;
+       struct stv0900_internal *intp = state->internal;
+       enum fe_stv0900_demod_num demod = state->demod;
 
-                       stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff);
-                       stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
-                       stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
-                       break;
-               case STV0900_DVBS2_STANDARD:
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
-                       stv0900_write_reg(i_params, R0900_P1_ACLC, 0);
-                       stv0900_write_reg(i_params, R0900_P1_BCLC, 0);
-                       if (i_params->dmd1_rslts.frame_length == STV0900_LONG_FRAME) {
-                               foundModcod = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD);
-                               pilots = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01;
-                               aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id);
-                               if (foundModcod <= STV0900_QPSK_910)
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc);
-                               else if (foundModcod <= STV0900_8PSK_910) {
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc);
-                               }
+       s32     srate,
+               pilots,
+               aclc,
+               freq1,
+               freq0,
+               i = 0,
+               timed,
+               timef,
+               blind_tun_sw = 0,
+               modulation;
 
-                               if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) {
-                                       if (foundModcod <= STV0900_16APSK_910) {
-                                               stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
-                                               stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc);
-                                       } else if (foundModcod <= STV0900_32APSK_910) {
-                                               stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
-                                               stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc);
-                                       }
-                               }
+       enum fe_stv0900_rolloff rolloff;
+       enum fe_stv0900_modcode foundModcod;
 
-                       } else {
-                               aclc = stv0900_get_optim_short_carr_loop(srate, i_params->dmd1_rslts.modulation, i_params->chip_id);
-                               if (i_params->dmd1_rslts.modulation == STV0900_QPSK)
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc);
-
-                               else if (i_params->dmd1_rslts.modulation == STV0900_8PSK) {
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc);
-                               } else if (i_params->dmd1_rslts.modulation == STV0900_16APSK) {
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc);
-                               } else if (i_params->dmd1_rslts.modulation == STV0900_32APSK) {
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc);
-                               }
+       dprintk("%s\n", __func__);
 
-                       }
+       srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+       srate += stv0900_get_timing_offst(intp, srate, demod);
 
-                       if (i_params->chip_id <= 0x11) {
-                               if (i_params->demod_mode != STV0900_SINGLE)
-                                       stv0900_activate_s2_modcode(i_params, demod);
+       switch (intp->result[demod].standard) {
+       case STV0900_DVBS1_STANDARD:
+       case STV0900_DSS_STANDARD:
+               dprintk("%s: found DVB-S or DSS\n", __func__);
+               if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) {
+                       stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+                       stv0900_write_bits(intp, DVBS2_ENABLE, 0);
+               }
 
-                       }
+               stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff);
+               stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1);
 
-                       stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67);
-                       break;
-               case STV0900_UNKNOWN_STANDARD:
-               default:
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
+               if (intp->chip_id < 0x30) {
+                       stv0900_write_reg(intp, ERRCTRL1, 0x75);
                        break;
                }
 
-               freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2);
-               freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1);
-               rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS);
-               if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
-                       stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00);
-                       stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
-                       stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
-                       stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
-                       stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod);
-                       stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod);
-                       blindTunSw = 1;
+               if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) {
+                       stv0900_write_reg(intp, GAUSSR0, 0x98);
+                       stv0900_write_reg(intp, CCIR0, 0x18);
+               } else {
+                       stv0900_write_reg(intp, GAUSSR0, 0x18);
+                       stv0900_write_reg(intp, CCIR0, 0x18);
                }
 
-               if (i_params->chip_id >= 0x20) {
-                       if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) {
-                               stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0a);
-                               stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x0);
+               stv0900_write_reg(intp, ERRCTRL1, 0x75);
+               break;
+       case STV0900_DVBS2_STANDARD:
+               dprintk("%s: found DVB-S2\n", __func__);
+               stv0900_write_bits(intp, DVBS1_ENABLE, 0);
+               stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+               stv0900_write_reg(intp, ACLC, 0);
+               stv0900_write_reg(intp, BCLC, 0);
+               if (intp->result[demod].frame_len == STV0900_LONG_FRAME) {
+                       foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD);
+                       pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01;
+                       aclc = stv0900_get_optim_carr_loop(srate,
+                                                       foundModcod,
+                                                       pilots,
+                                                       intp->chip_id);
+                       if (foundModcod <= STV0900_QPSK_910)
+                               stv0900_write_reg(intp, ACLC2S2Q, aclc);
+                       else if (foundModcod <= STV0900_8PSK_910) {
+                               stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+                               stv0900_write_reg(intp, ACLC2S28, aclc);
                        }
-               }
-
-               if (i_params->chip_id < 0x20)
-                       stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x08);
-
-               if (i_params->chip_id == 0x10)
-                       stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0x0A);
-
-               stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
-
-               if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd1_symbol_rate < 10000000)) {
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
-                       i_params->tuner1_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000;
 
-                       if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) {
-                               if (i_params->dmd1_srch_algo != STV0900_WARM_START)
-                                       stv0900_set_bandwidth(fe, i_params->tuner1_bw);
+                       if ((intp->demod_mode == STV0900_SINGLE) &&
+                                       (foundModcod > STV0900_8PSK_910)) {
+                               if (foundModcod <= STV0900_16APSK_910) {
+                                       stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+                                       stv0900_write_reg(intp, ACLC2S216A,
+                                                                       aclc);
+                               } else if (foundModcod <= STV0900_32APSK_910) {
+                                       stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+                                       stv0900_write_reg(intp, ACLC2S232A,
+                                                                       aclc);
+                               }
                        }
 
-                       if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000))
-                               msleep(50);
-                       else
-                               msleep(5);
-
-                       stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START);
-
-                       if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) {
-                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
-                               stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
-                               stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
-                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-                               i = 0;
-                               while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) {
-                                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
-                                       stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
-                                       stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
-                                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-                                       i++;
-                               }
+               } else {
+                       modulation = intp->result[demod].modulation;
+                       aclc = stv0900_get_optim_short_carr_loop(srate,
+                                       modulation, intp->chip_id);
+                       if (modulation == STV0900_QPSK)
+                               stv0900_write_reg(intp, ACLC2S2Q, aclc);
+                       else if (modulation == STV0900_8PSK) {
+                               stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+                               stv0900_write_reg(intp, ACLC2S28, aclc);
+                       } else if (modulation == STV0900_16APSK) {
+                               stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+                               stv0900_write_reg(intp, ACLC2S216A, aclc);
+                       } else if (modulation == STV0900_32APSK) {
+                               stv0900_write_reg(intp, ACLC2S2Q, 0x2a);
+                               stv0900_write_reg(intp, ACLC2S232A, aclc);
                        }
 
                }
 
-               if (i_params->chip_id >= 0x20)
-                       stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49);
+               if (intp->chip_id <= 0x11) {
+                       if (intp->demod_mode != STV0900_SINGLE)
+                               stv0900_activate_s2_modcod(intp, demod);
 
-               if ((i_params->dmd1_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd1_rslts.standard == STV0900_DSS_STANDARD))
-                       stv0900_set_viterbi_tracq(i_params, demod);
+               }
 
+               stv0900_write_reg(intp, ERRCTRL1, 0x67);
                break;
+       case STV0900_UNKNOWN_STANDARD:
+       default:
+               dprintk("%s: found unknown standard\n", __func__);
+               stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+               stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+               break;
+       }
 
-       case STV0900_DEMOD_2:
-               switch (i_params->dmd2_rslts.standard) {
-               case STV0900_DVBS1_STANDARD:
-
-                       if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) {
-                               stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                               stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
-                       }
+       freq1 = stv0900_read_reg(intp, CFR2);
+       freq0 = stv0900_read_reg(intp, CFR1);
+       rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS);
+       if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) {
+               stv0900_write_reg(intp, SFRSTEP, 0x00);
+               stv0900_write_bits(intp, SCAN_ENABLE, 0);
+               stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+               stv0900_write_reg(intp, TMGCFG2, 0xc1);
+               stv0900_set_symbol_rate(intp, intp->mclk, srate, demod);
+               blind_tun_sw = 1;
+               if (intp->result[demod].standard != STV0900_DVBS2_STANDARD)
+                       stv0900_set_dvbs1_track_car_loop(intp, demod, srate);
 
-                       stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff);
-                       stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
-                       stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
-                       break;
-               case STV0900_DSS_STANDARD:
-                       if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) {
-                               stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                               stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
-                       }
+       }
 
-                       stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff);
-                       stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
-                       stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
-                       break;
-               case STV0900_DVBS2_STANDARD:
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
-                       stv0900_write_reg(i_params, R0900_P2_ACLC, 0);
-                       stv0900_write_reg(i_params, R0900_P2_BCLC, 0);
-                       if (i_params->dmd2_rslts.frame_length == STV0900_LONG_FRAME) {
-                               foundModcod = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD);
-                               pilots = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01;
-                               aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id);
-                               if (foundModcod <= STV0900_QPSK_910)
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc);
-                               else if (foundModcod <= STV0900_8PSK_910) {
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc);
-                               }
+       if (intp->chip_id >= 0x20) {
+               if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) ||
+                               (intp->srch_standard[demod] ==
+                                                       STV0900_SEARCH_DSS) ||
+                               (intp->srch_standard[demod] ==
+                                                       STV0900_AUTO_SEARCH)) {
+                       stv0900_write_reg(intp, VAVSRVIT, 0x0a);
+                       stv0900_write_reg(intp, VITSCALE, 0x0);
+               }
+       }
 
-                               if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) {
-                                       if (foundModcod <= STV0900_16APSK_910) {
-                                               stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
-                                               stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc);
-                                       } else if (foundModcod <= STV0900_32APSK_910) {
-                                               stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
-                                               stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc);
-                                       }
+       if (intp->chip_id < 0x20)
+               stv0900_write_reg(intp, CARHDR, 0x08);
 
-                               }
+       if (intp->chip_id == 0x10)
+               stv0900_write_reg(intp, CORRELEXP, 0x0a);
 
-                       } else {
-                               aclc = stv0900_get_optim_short_carr_loop(srate,
-                                                                       i_params->dmd2_rslts.modulation,
-                                                                       i_params->chip_id);
-
-                               if (i_params->dmd2_rslts.modulation == STV0900_QPSK)
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc);
-
-                               else if (i_params->dmd2_rslts.modulation == STV0900_8PSK) {
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc);
-                               } else if (i_params->dmd2_rslts.modulation == STV0900_16APSK) {
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc);
-                               } else if (i_params->dmd2_rslts.modulation == STV0900_32APSK) {
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
-                                       stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc);
-                               }
-                       }
+       stv0900_write_reg(intp, AGC2REF, 0x38);
 
-                       stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67);
+       if ((intp->chip_id >= 0x20) ||
+                       (blind_tun_sw == 1) ||
+                       (intp->symbol_rate[demod] < 10000000)) {
+               stv0900_write_reg(intp, CFRINIT1, freq1);
+               stv0900_write_reg(intp, CFRINIT0, freq0);
+               intp->bw[demod] = stv0900_carrier_width(srate,
+                                       intp->rolloff) + 10000000;
 
-                       break;
-               case STV0900_UNKNOWN_STANDARD:
-               default:
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
-                       break;
+               if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) {
+                       if (intp->srch_algo[demod] != STV0900_WARM_START)
+                               stv0900_set_bandwidth(fe, intp->bw[demod]);
                }
 
-               freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2);
-               freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1);
-               rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS);
-               if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
-                       stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00);
-                       stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
-                       stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
-                       stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
-                       stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod);
-                       stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod);
-                       blindTunSw = 1;
-               }
+               if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) ||
+                               (intp->symbol_rate[demod] < 10000000))
+                       msleep(50);
+               else
+                       msleep(5);
 
-               if (i_params->chip_id >= 0x20) {
-                       if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) {
-                               stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0a);
-                               stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x0);
+               stv0900_get_lock_timeout(&timed, &timef, srate,
+                                               STV0900_WARM_START);
+
+               if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) {
+                       stv0900_write_reg(intp, DMDISTATE, 0x1f);
+                       stv0900_write_reg(intp, CFRINIT1, freq1);
+                       stv0900_write_reg(intp, CFRINIT0, freq0);
+                       stv0900_write_reg(intp, DMDISTATE, 0x18);
+                       i = 0;
+                       while ((stv0900_get_demod_lock(intp,
+                                                       demod,
+                                                       timed / 2) == FALSE) &&
+                                               (i <= 2)) {
+                               stv0900_write_reg(intp, DMDISTATE, 0x1f);
+                               stv0900_write_reg(intp, CFRINIT1, freq1);
+                               stv0900_write_reg(intp, CFRINIT0, freq0);
+                               stv0900_write_reg(intp, DMDISTATE, 0x18);
+                               i++;
                        }
                }
 
-               if (i_params->chip_id < 0x20)
-                       stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x08);
+       }
 
-               if (i_params->chip_id == 0x10)
-                       stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0x0a);
+       if (intp->chip_id >= 0x20)
+               stv0900_write_reg(intp, CARFREQ, 0x49);
 
-               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
-               if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd2_symbol_rate < 10000000)) {
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
-                       i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000;
+       if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) ||
+                       (intp->result[demod].standard == STV0900_DSS_STANDARD))
+               stv0900_set_viterbi_tracq(intp, demod);
 
-                       if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) {
-                               if (i_params->dmd2_srch_algo != STV0900_WARM_START)
-                                       stv0900_set_bandwidth(fe, i_params->tuner2_bw);
-                       }
-
-                       if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000))
-                               msleep(50);
-                       else
-                               msleep(5);
-
-                       stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START);
-                       if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) {
-                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
-                               stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
-                               stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
-                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-                               i = 0;
-                               while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) {
-                                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
-                                       stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
-                                       stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
-                                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-                                       i++;
-                               }
-                       }
-               }
-
-               if (i_params->chip_id >= 0x20)
-                       stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49);
-
-               if ((i_params->dmd2_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd2_rslts.standard == STV0900_DSS_STANDARD))
-                       stv0900_set_viterbi_tracq(i_params, demod);
-
-               break;
-       }
 }
 
-static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod, s32 time_out)
+static int stv0900_get_fec_lock(struct stv0900_internal *intp,
+                               enum fe_stv0900_demod_num demod, s32 time_out)
 {
-       s32 timer = 0, lock = 0, header_field, pktdelin_field, lock_vit_field;
+       s32 timer = 0, lock = 0;
 
        enum fe_stv0900_search_state dmd_state;
 
-       dprintk(KERN_INFO "%s\n", __func__);
-
-       dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
-       dmd_reg(pktdelin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK);
-       dmd_reg(lock_vit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT);
+       dprintk("%s\n", __func__);
 
-       dmd_state = stv0900_get_bits(i_params, header_field);
+       dmd_state = stv0900_get_bits(intp, HEADER_MODE);
 
        while ((timer < time_out) && (lock == 0)) {
                switch (dmd_state) {
@@ -1386,10 +1037,10 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09
                        lock = 0;
                        break;
                case STV0900_DVBS2_FOUND:
-                       lock = stv0900_get_bits(i_params, pktdelin_field);
+                       lock = stv0900_get_bits(intp, PKTDELIN_LOCK);
                        break;
                case STV0900_DVBS_FOUND:
-                       lock = stv0900_get_bits(i_params, lock_vit_field);
+                       lock = stv0900_get_bits(intp, LOCKEDVIT);
                        break;
                }
 
@@ -1400,46 +1051,44 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09
        }
 
        if (lock)
-               dprintk("DEMOD FEC LOCK OK\n");
+               dprintk("%s: DEMOD FEC LOCK OK\n", __func__);
        else
-               dprintk("DEMOD FEC LOCK FAIL\n");
+               dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__);
 
        return lock;
 }
 
-static int stv0900_wait_for_lock(struct stv0900_internal *i_params,
+static int stv0900_wait_for_lock(struct stv0900_internal *intp,
                                enum fe_stv0900_demod_num demod,
                                s32 dmd_timeout, s32 fec_timeout)
 {
 
-       s32 timer = 0, lock = 0, str_merg_rst_fld, str_merg_lock_fld;
+       s32 timer = 0, lock = 0;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       dmd_reg(str_merg_rst_fld, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE);
-       dmd_reg(str_merg_lock_fld, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK);
-
-       lock = stv0900_get_demod_lock(i_params, demod, dmd_timeout);
+       lock = stv0900_get_demod_lock(intp, demod, dmd_timeout);
 
        if (lock)
-               lock = lock && stv0900_get_fec_lock(i_params, demod, fec_timeout);
+               lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout);
 
        if (lock) {
                lock = 0;
 
-               dprintk(KERN_INFO "%s: Timer = %d, time_out = %d\n", __func__, timer, fec_timeout);
+               dprintk("%s: Timer = %d, time_out = %d\n",
+                               __func__, timer, fec_timeout);
 
                while ((timer < fec_timeout) && (lock == 0)) {
-                       lock = stv0900_get_bits(i_params, str_merg_lock_fld);
+                       lock = stv0900_get_bits(intp, TSFIFO_LINEOK);
                        msleep(1);
                        timer++;
                }
        }
 
        if (lock)
-               dprintk(KERN_INFO "%s: DEMOD LOCK OK\n", __func__);
+               dprintk("%s: DEMOD LOCK OK\n", __func__);
        else
-               dprintk(KERN_INFO "%s: DEMOD LOCK FAIL\n", __func__);
+               dprintk("%s: DEMOD LOCK FAIL\n", __func__);
 
        if (lock)
                return TRUE;
@@ -1451,43 +1100,43 @@ enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
                                                enum fe_stv0900_demod_num demod)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_tracking_standard fnd_standard;
-       s32 state_field,
-       dss_dvb_field;
-
-       dprintk(KERN_INFO "%s\n", __func__);
 
-       dmd_reg(state_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
-       dmd_reg(dss_dvb_field, F0900_P1_DSS_DVB, F0900_P2_DSS_DVB);
+       int hdr_mode = stv0900_get_bits(intp, HEADER_MODE);
 
-       if (stv0900_get_bits(i_params, state_field) == 2)
+       switch (hdr_mode) {
+       case 2:
                fnd_standard = STV0900_DVBS2_STANDARD;
-
-       else if (stv0900_get_bits(i_params, state_field) == 3) {
-               if (stv0900_get_bits(i_params, dss_dvb_field) == 1)
+               break;
+       case 3:
+               if (stv0900_get_bits(intp, DSS_DVB) == 1)
                        fnd_standard = STV0900_DSS_STANDARD;
                else
                        fnd_standard = STV0900_DVBS1_STANDARD;
-       } else
+
+               break;
+       default:
                fnd_standard = STV0900_UNKNOWN_STANDARD;
+       }
+
+       dprintk("%s: standard %d\n", __func__, fnd_standard);
 
        return fnd_standard;
 }
 
-static s32 stv0900_get_carr_freq(struct stv0900_internal *i_params, u32 mclk,
+static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 cfr_field2, cfr_field1, cfr_field0,
-               derot, rem1, rem2, intval1, intval2;
+       s32     derot,
+               rem1,
+               rem2,
+               intval1,
+               intval2;
 
-       dmd_reg(cfr_field2, F0900_P1_CAR_FREQ2, F0900_P2_CAR_FREQ2);
-       dmd_reg(cfr_field1, F0900_P1_CAR_FREQ1, F0900_P2_CAR_FREQ1);
-       dmd_reg(cfr_field0, F0900_P1_CAR_FREQ0, F0900_P2_CAR_FREQ0);
-
-       derot = (stv0900_get_bits(i_params, cfr_field2) << 16) +
-               (stv0900_get_bits(i_params, cfr_field1) << 8) +
-               (stv0900_get_bits(i_params, cfr_field0));
+       derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) +
+               (stv0900_get_bits(intp, CAR_FREQ1) << 8) +
+               (stv0900_get_bits(intp, CAR_FREQ0));
 
        derot = ge2comp(derot, 24);
        intval1 = mclk >> 12;
@@ -1505,7 +1154,7 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe)
 {
        struct dvb_frontend_ops *frontend_ops = NULL;
        struct dvb_tuner_ops *tuner_ops = NULL;
-       u32 frequency = 0;
+       u32 freq = 0;
 
        if (&fe->ops)
                frontend_ops = &fe->ops;
@@ -1514,304 +1163,159 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe)
                tuner_ops = &frontend_ops->tuner_ops;
 
        if (tuner_ops->get_frequency) {
-               if ((tuner_ops->get_frequency(fe, &frequency)) < 0)
+               if ((tuner_ops->get_frequency(fe, &freq)) < 0)
                        dprintk("%s: Invalid parameter\n", __func__);
                else
-                       dprintk("%s: Frequency=%d\n", __func__, frequency);
-
-       }
+                       dprintk("%s: Frequency=%d\n", __func__, freq);
 
-       return frequency;
-}
-
-static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *i_params,
-                                               enum fe_stv0900_demod_num demod)
-{
-       s32 rate_fld, vit_curpun_fld;
-       enum fe_stv0900_fec prate;
-
-       dmd_reg(vit_curpun_fld, F0900_P1_VIT_CURPUN, F0900_P2_VIT_CURPUN);
-       rate_fld = stv0900_get_bits(i_params, vit_curpun_fld);
-
-       switch (rate_fld) {
-       case 13:
-               prate = STV0900_FEC_1_2;
-               break;
-       case 18:
-               prate = STV0900_FEC_2_3;
-               break;
-       case 21:
-               prate = STV0900_FEC_3_4;
-               break;
-       case 24:
-               prate = STV0900_FEC_5_6;
-               break;
-       case 25:
-               prate = STV0900_FEC_6_7;
-               break;
-       case 26:
-               prate = STV0900_FEC_7_8;
-               break;
-       default:
-               prate = STV0900_FEC_UNKNOWN;
-               break;
        }
 
-       return prate;
+       return freq;
 }
 
-static enum fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
+static enum
+fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
        enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE;
-       s32 offsetFreq,
-       srate_offset,
-       i = 0;
+       struct stv0900_signal_info *result = &intp->result[demod];
+       s32     offsetFreq,
+               srate_offset;
+       int     i = 0,
+               d = demod;
 
        u8 timing;
 
        msleep(5);
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
-                       timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2);
-                       i = 0;
-                       stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x5c);
-
-                       while ((i <= 50) && (timing != 0) && (timing != 0xFF)) {
-                               timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2);
-                               msleep(5);
-                               i += 5;
-                       }
-               }
-
-               i_params->dmd1_rslts.standard = stv0900_get_standard(fe, demod);
-               i_params->dmd1_rslts.frequency = stv0900_get_tuner_freq(fe);
-               offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000;
-               i_params->dmd1_rslts.frequency += offsetFreq;
-               i_params->dmd1_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-               srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd1_rslts.symbol_rate, demod);
-               i_params->dmd1_rslts.symbol_rate += srate_offset;
-               i_params->dmd1_rslts.fec = stv0900_get_vit_fec(i_params, demod);
-               i_params->dmd1_rslts.modcode = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD);
-               i_params->dmd1_rslts.pilot = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01;
-               i_params->dmd1_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE)) >> 1;
-               i_params->dmd1_rslts.rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS);
-               switch (i_params->dmd1_rslts.standard) {
-               case STV0900_DVBS2_STANDARD:
-                       i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_SPECINV_DEMOD);
-                       if (i_params->dmd1_rslts.modcode <= STV0900_QPSK_910)
-                               i_params->dmd1_rslts.modulation = STV0900_QPSK;
-                       else if (i_params->dmd1_rslts.modcode <= STV0900_8PSK_910)
-                               i_params->dmd1_rslts.modulation = STV0900_8PSK;
-                       else if (i_params->dmd1_rslts.modcode <= STV0900_16APSK_910)
-                               i_params->dmd1_rslts.modulation = STV0900_16APSK;
-                       else if (i_params->dmd1_rslts.modcode <= STV0900_32APSK_910)
-                               i_params->dmd1_rslts.modulation = STV0900_32APSK;
-                       else
-                               i_params->dmd1_rslts.modulation = STV0900_UNKNOWN;
-                       break;
-               case STV0900_DVBS1_STANDARD:
-               case STV0900_DSS_STANDARD:
-                       i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_IQINV);
-                       i_params->dmd1_rslts.modulation = STV0900_QPSK;
-                       break;
-               default:
-                       break;
+       if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) {
+               timing = stv0900_read_reg(intp, TMGREG2);
+               i = 0;
+               stv0900_write_reg(intp, SFRSTEP, 0x5c);
+
+               while ((i <= 50) && (timing != 0) && (timing != 0xff)) {
+                       timing = stv0900_read_reg(intp, TMGREG2);
+                       msleep(5);
+                       i += 5;
                }
+       }
 
-               if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) {
-                       offsetFreq =    i_params->dmd1_rslts.frequency - i_params->tuner1_freq;
-                       i_params->tuner1_freq = stv0900_get_tuner_freq(fe);
-                       if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500))
-                               range = STV0900_RANGEOK;
-                       else
-                               if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd1_rslts.symbol_rate, i_params->dmd1_rslts.rolloff) / 2000))
-                                       range = STV0900_RANGEOK;
-                               else
-                                       range = STV0900_OUTOFRANGE;
-
-               } else {
-                       if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500))
-                               range = STV0900_RANGEOK;
-                       else
-                               range = STV0900_OUTOFRANGE;
-               }
+       result->standard = stv0900_get_standard(fe, d);
+       result->frequency = stv0900_get_tuner_freq(fe);
+       offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000;
+       result->frequency += offsetFreq;
+       result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d);
+       srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d);
+       result->symbol_rate += srate_offset;
+       result->fec = stv0900_get_vit_fec(intp, d);
+       result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD);
+       result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01;
+       result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1;
+       result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS);
+       switch (result->standard) {
+       case STV0900_DVBS2_STANDARD:
+               result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD);
+               if (result->modcode <= STV0900_QPSK_910)
+                       result->modulation = STV0900_QPSK;
+               else if (result->modcode <= STV0900_8PSK_910)
+                       result->modulation = STV0900_8PSK;
+               else if (result->modcode <= STV0900_16APSK_910)
+                       result->modulation = STV0900_16APSK;
+               else if (result->modcode <= STV0900_32APSK_910)
+                       result->modulation = STV0900_32APSK;
+               else
+                       result->modulation = STV0900_UNKNOWN;
                break;
-       case STV0900_DEMOD_2:
-               if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
-                       timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2);
-                       i = 0;
-                       stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x5c);
-
-                       while ((i <= 50) && (timing != 0) && (timing != 0xff)) {
-                               timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2);
-                               msleep(5);
-                               i += 5;
-                       }
-               }
-
-               i_params->dmd2_rslts.standard = stv0900_get_standard(fe, demod);
-               i_params->dmd2_rslts.frequency = stv0900_get_tuner_freq(fe);
-               offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000;
-               i_params->dmd2_rslts.frequency += offsetFreq;
-               i_params->dmd2_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-               srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd2_rslts.symbol_rate, demod);
-               i_params->dmd2_rslts.symbol_rate += srate_offset;
-               i_params->dmd2_rslts.fec = stv0900_get_vit_fec(i_params, demod);
-               i_params->dmd2_rslts.modcode = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD);
-               i_params->dmd2_rslts.pilot = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01;
-               i_params->dmd2_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE)) >> 1;
-               i_params->dmd2_rslts.rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS);
-               switch (i_params->dmd2_rslts.standard) {
-               case STV0900_DVBS2_STANDARD:
-                       i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_SPECINV_DEMOD);
-                       if (i_params->dmd2_rslts.modcode <= STV0900_QPSK_910)
-                               i_params->dmd2_rslts.modulation = STV0900_QPSK;
-                       else if (i_params->dmd2_rslts.modcode <= STV0900_8PSK_910)
-                               i_params->dmd2_rslts.modulation = STV0900_8PSK;
-                       else if (i_params->dmd2_rslts.modcode <= STV0900_16APSK_910)
-                               i_params->dmd2_rslts.modulation = STV0900_16APSK;
-                       else if (i_params->dmd2_rslts.modcode <= STV0900_32APSK_910)
-                               i_params->dmd2_rslts.modulation = STV0900_32APSK;
-                       else
-                               i_params->dmd2_rslts.modulation = STV0900_UNKNOWN;
-                       break;
-               case STV0900_DVBS1_STANDARD:
-               case STV0900_DSS_STANDARD:
-                       i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_IQINV);
-                       i_params->dmd2_rslts.modulation = STV0900_QPSK;
-                       break;
-               default:
-                       break;
-               }
+       case STV0900_DVBS1_STANDARD:
+       case STV0900_DSS_STANDARD:
+               result->spectrum = stv0900_get_bits(intp, IQINV);
+               result->modulation = STV0900_QPSK;
+               break;
+       default:
+               break;
+       }
 
-               if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) {
-                       offsetFreq =    i_params->dmd2_rslts.frequency - i_params->tuner2_freq;
-                       i_params->tuner2_freq = stv0900_get_tuner_freq(fe);
+       if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) ||
+                               (intp->symbol_rate[d] < 10000000)) {
+               offsetFreq = result->frequency - intp->freq[d];
+               intp->freq[d] = stv0900_get_tuner_freq(fe);
+               if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
+                       range = STV0900_RANGEOK;
+               else if (ABS(offsetFreq) <=
+                               (stv0900_carrier_width(result->symbol_rate,
+                                               result->rolloff) / 2000))
+                       range = STV0900_RANGEOK;
 
-                       if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500))
-                               range = STV0900_RANGEOK;
-                       else
-                               if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd2_rslts.symbol_rate, i_params->dmd2_rslts.rolloff) / 2000))
-                                       range = STV0900_RANGEOK;
-                               else
-                                       range = STV0900_OUTOFRANGE;
-               } else {
-                       if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500))
-                               range = STV0900_RANGEOK;
-                       else
-                               range = STV0900_OUTOFRANGE;
-               }
+       } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500))
+               range = STV0900_RANGEOK;
 
-               break;
-       }
+       dprintk("%s: range %d\n", __func__, range);
 
        return range;
 }
 
-static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe)
+static enum
+fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-
-       s32 srate, demod_timeout,
-               fec_timeout, freq1, freq0;
        enum fe_stv0900_signal_type signal_type = STV0900_NODATA;
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               i_params->dmd1_rslts.locked = FALSE;
-               if (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) {
-                       srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-                       srate += stv0900_get_timing_offst(i_params, srate, demod);
-                       if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH)
-                               stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
-
-                       stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START);
-                       freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2);
-                       freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1);
-                       stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
-                       stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-                       if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
-                               i_params->dmd1_rslts.locked = TRUE;
-                               signal_type = stv0900_get_signal_params(fe);
-                               stv0900_track_optimization(fe);
-                       } else {
-                               stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL);
-                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1c);
-                               stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
-                               stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
-                               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
-                               if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
-                                       i_params->dmd1_rslts.locked = TRUE;
-                                       signal_type = stv0900_get_signal_params(fe);
-                                       stv0900_track_optimization(fe);
-                               }
-
-                       }
-
-               } else
-                       i_params->dmd1_rslts.locked = FALSE;
-
-               break;
-       case STV0900_DEMOD_2:
-               i_params->dmd2_rslts.locked = FALSE;
-               if (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) {
-                       srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-                       srate += stv0900_get_timing_offst(i_params, srate, demod);
-
-                       if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH)
-                               stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
-
-                       stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START);
-                       freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2);
-                       freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1);
-                       stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
-                       stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-
-                       if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
-                               i_params->dmd2_rslts.locked = TRUE;
+       s32     srate,
+               demod_timeout,
+               fec_timeout,
+               freq1,
+               freq0;
+
+       intp->result[demod].locked = FALSE;
+
+       if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) {
+               srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+               srate += stv0900_get_timing_offst(intp, srate, demod);
+               if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH)
+                       stv0900_set_symbol_rate(intp, intp->mclk, srate, demod);
+
+               stv0900_get_lock_timeout(&demod_timeout, &fec_timeout,
+                                       srate, STV0900_WARM_START);
+               freq1 = stv0900_read_reg(intp, CFR2);
+               freq0 = stv0900_read_reg(intp, CFR1);
+               stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+               stv0900_write_bits(intp, SPECINV_CONTROL,
+                                       STV0900_IQ_FORCE_SWAPPED);
+               stv0900_write_reg(intp, DMDISTATE, 0x1c);
+               stv0900_write_reg(intp, CFRINIT1, freq1);
+               stv0900_write_reg(intp, CFRINIT0, freq0);
+               stv0900_write_reg(intp, DMDISTATE, 0x18);
+               if (stv0900_wait_for_lock(intp, demod,
+                               demod_timeout, fec_timeout) == TRUE) {
+                       intp->result[demod].locked = TRUE;
+                       signal_type = stv0900_get_signal_params(fe);
+                       stv0900_track_optimization(fe);
+               } else {
+                       stv0900_write_bits(intp, SPECINV_CONTROL,
+                                       STV0900_IQ_FORCE_NORMAL);
+                       stv0900_write_reg(intp, DMDISTATE, 0x1c);
+                       stv0900_write_reg(intp, CFRINIT1, freq1);
+                       stv0900_write_reg(intp, CFRINIT0, freq0);
+                       stv0900_write_reg(intp, DMDISTATE, 0x18);
+                       if (stv0900_wait_for_lock(intp, demod,
+                                       demod_timeout, fec_timeout) == TRUE) {
+                               intp->result[demod].locked = TRUE;
                                signal_type = stv0900_get_signal_params(fe);
                                stv0900_track_optimization(fe);
-                       } else {
-                               stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL);
-                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1c);
-                               stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
-                               stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
-                               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
-
-                               if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
-                                       i_params->dmd2_rslts.locked = TRUE;
-                                       signal_type = stv0900_get_signal_params(fe);
-                                       stv0900_track_optimization(fe);
-                               }
-
                        }
 
-               } else
-                       i_params->dmd1_rslts.locked = FALSE;
+               }
 
-               break;
-       }
+       } else
+               intp->result[demod].locked = FALSE;
 
        return signal_type;
 }
 
-static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
+static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
        u32 minagc2level = 0xffff,
@@ -1820,105 +1324,54 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
 
        s32 i, j, nb_steps, direction;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
-               stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1);
-               stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1);
+       stv0900_write_reg(intp, AGC2REF, 0x38);
+       stv0900_write_bits(intp, SCAN_ENABLE, 0);
+       stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
 
-               stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83);
-               stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0);
+       stv0900_write_bits(intp, AUTO_GUP, 1);
+       stv0900_write_bits(intp, AUTO_GLOW, 1);
 
-               stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82);
-               stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0);
-               stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0);
+       stv0900_write_reg(intp, DMDT0M, 0x0);
 
-               stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
-               nb_steps = -1 + (i_params->dmd1_srch_range / 1000000);
-               nb_steps /= 2;
-               nb_steps = (2 * nb_steps) + 1;
+       stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod);
+       nb_steps = -1 + (intp->srch_range[demod] / 1000000);
+       nb_steps /= 2;
+       nb_steps = (2 * nb_steps) + 1;
 
-               if (nb_steps < 0)
-                       nb_steps = 1;
+       if (nb_steps < 0)
+               nb_steps = 1;
 
-               direction = 1;
+       direction = 1;
 
-               freq_step = (1000000 << 8) / (i_params->mclk >> 8);
+       freq_step = (1000000 << 8) / (intp->mclk >> 8);
 
-               init_freq = 0;
+       init_freq = 0;
 
-               for (i = 0; i < nb_steps; i++) {
-                       if (direction > 0)
-                               init_freq = init_freq + (freq_step * i);
-                       else
-                               init_freq = init_freq - (freq_step * i);
-
-                       direction *= -1;
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (init_freq >> 8) & 0xff);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT0, init_freq  & 0xff);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x58);
-                       msleep(10);
-                       agc2level = 0;
-
-                       for (j = 0; j < 10; j++)
-                               agc2level += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
-                                               | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
-
-                       agc2level /= 10;
-
-                       if (agc2level < minagc2level)
-                               minagc2level = agc2level;
-               }
-               break;
-       case STV0900_DEMOD_2:
-               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
-               stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1);
-               stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1);
-               stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83);
-               stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0);
-               stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82);
-               stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0);
-               stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0);
-               stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
-               nb_steps = -1 + (i_params->dmd2_srch_range / 1000000);
-               nb_steps /= 2;
-               nb_steps = (2 * nb_steps) + 1;
-
-               if (nb_steps < 0)
-                       nb_steps = 1;
-
-               direction = 1;
-               freq_step = (1000000 << 8) / (i_params->mclk >> 8);
-               init_freq = 0;
-               for (i = 0; i < nb_steps; i++) {
-                       if (direction > 0)
-                               init_freq = init_freq + (freq_step * i);
-                       else
-                               init_freq = init_freq - (freq_step * i);
+       for (i = 0; i < nb_steps; i++) {
+               if (direction > 0)
+                       init_freq = init_freq + (freq_step * i);
+               else
+                       init_freq = init_freq - (freq_step * i);
 
-                       direction *= -1;
+               direction *= -1;
+               stv0900_write_reg(intp, DMDISTATE, 0x5C);
+               stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff);
+               stv0900_write_reg(intp, CFRINIT0, init_freq  & 0xff);
+               stv0900_write_reg(intp, DMDISTATE, 0x58);
+               msleep(10);
+               agc2level = 0;
 
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (init_freq >> 8) & 0xff);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT0, init_freq  & 0xff);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x58);
+               for (j = 0; j < 10; j++)
+                       agc2level += (stv0900_read_reg(intp, AGC2I1) << 8)
+                                       | stv0900_read_reg(intp, AGC2I0);
 
-                       msleep(10);
-                       agc2level = 0;
-                       for (j = 0; j < 10; j++)
-                               agc2level += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
-                                               | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
+               agc2level /= 10;
 
-                       agc2level /= 10;
+               if (agc2level < minagc2level)
+                       minagc2level = agc2level;
 
-                       if (agc2level < minagc2level)
-                               minagc2level = agc2level;
-               }
-               break;
        }
 
        return (u16)minagc2level;
@@ -1927,336 +1380,192 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
 static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       int timingLock = FALSE;
+       int timing_lck = FALSE;
        s32 i, timingcpt = 0,
                direction = 1,
                nb_steps,
                current_step = 0,
                tuner_freq;
+       u32 agc2_th,
+               coarse_srate = 0,
+               agc2_integr = 0,
+               currier_step = 1200;
 
-       u32 coarse_srate = 0, agc2_integr = 0, currier_step = 1200;
-
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1F);
-               stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0x12);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xf0);
-               stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xe0);
-               stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1);
-               stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1);
-               stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83);
-               stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0);
-               stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82);
-               stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0);
-               stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0);
-               stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x50);
-
-               if (i_params->chip_id >= 0x20) {
-                       stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x6a);
-                       stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x95);
-               } else {
-                       stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
-                       stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x73);
-               }
+       if (intp->chip_id >= 0x30)
+               agc2_th = 0x2e00;
+       else
+               agc2_th = 0x1f00;
+
+       stv0900_write_bits(intp, DEMOD_MODE, 0x1f);
+       stv0900_write_reg(intp, TMGCFG, 0x12);
+       stv0900_write_reg(intp, TMGTHRISE, 0xf0);
+       stv0900_write_reg(intp, TMGTHFALL, 0xe0);
+       stv0900_write_bits(intp, SCAN_ENABLE, 1);
+       stv0900_write_bits(intp, CFR_AUTOSCAN, 1);
+       stv0900_write_reg(intp, SFRUP1, 0x83);
+       stv0900_write_reg(intp, SFRUP0, 0xc0);
+       stv0900_write_reg(intp, SFRLOW1, 0x82);
+       stv0900_write_reg(intp, SFRLOW0, 0xa0);
+       stv0900_write_reg(intp, DMDT0M, 0x0);
+       stv0900_write_reg(intp, AGC2REF, 0x50);
+
+       if (intp->chip_id >= 0x30) {
+               stv0900_write_reg(intp, CARFREQ, 0x99);
+               stv0900_write_reg(intp, SFRSTEP, 0x98);
+       } else if (intp->chip_id >= 0x20) {
+               stv0900_write_reg(intp, CARFREQ, 0x6a);
+               stv0900_write_reg(intp, SFRSTEP, 0x95);
+       } else {
+               stv0900_write_reg(intp, CARFREQ, 0xed);
+               stv0900_write_reg(intp, SFRSTEP, 0x73);
+       }
 
-               if (i_params->dmd1_symbol_rate <= 2000000)
-                       currier_step = 1000;
-               else if (i_params->dmd1_symbol_rate <= 5000000)
-                       currier_step = 2000;
-               else if (i_params->dmd1_symbol_rate <= 12000000)
-                       currier_step = 3000;
-               else
+       if (intp->symbol_rate[demod] <= 2000000)
+               currier_step = 1000;
+       else if (intp->symbol_rate[demod] <= 5000000)
+               currier_step = 2000;
+       else if (intp->symbol_rate[demod] <= 12000000)
+               currier_step = 3000;
+       else
                        currier_step = 5000;
 
-               nb_steps = -1 + ((i_params->dmd1_srch_range / 1000) / currier_step);
-               nb_steps /= 2;
-               nb_steps = (2 * nb_steps) + 1;
-
-               if (nb_steps < 0)
-                       nb_steps = 1;
-
-               else if (nb_steps > 10) {
-                       nb_steps = 11;
-                       currier_step = (i_params->dmd1_srch_range / 1000) / 10;
-               }
-
-               current_step = 0;
+       nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step);
+       nb_steps /= 2;
+       nb_steps = (2 * nb_steps) + 1;
 
-               direction = 1;
-               tuner_freq = i_params->tuner1_freq;
-
-               while ((timingLock == FALSE) && (current_step < nb_steps)) {
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5F);
-                       stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x0);
-
-                       msleep(50);
-
-                       for (i = 0; i < 10; i++) {
-                               if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2)
-                                       timingcpt++;
-
-                               agc2_integr += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
-
-                       }
-
-                       agc2_integr /= 10;
-                       coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-                       current_step++;
-                       direction *= -1;
-
-                       dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started. tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", tuner_freq, agc2_integr, coarse_srate, timingcpt);
-
-                       if ((timingcpt >= 5) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) {
-                               timingLock = TRUE;
-                       }
-
-                       else if (current_step < nb_steps) {
-                               if (direction > 0)
-                                       tuner_freq += (current_step * currier_step);
-                               else
-                                       tuner_freq -= (current_step * currier_step);
+       if (nb_steps < 0)
+               nb_steps = 1;
+       else if (nb_steps > 10) {
+               nb_steps = 11;
+               currier_step = (intp->srch_range[demod] / 1000) / 10;
+       }
 
-                               stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw);
-                       }
-               }
+       current_step = 0;
+       direction = 1;
 
-               if (timingLock == FALSE)
-                       coarse_srate = 0;
-               else
-                       coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-               break;
-       case STV0900_DEMOD_2:
-               stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1F);
-               stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0x12);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xf0);
-               stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xe0);
-               stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1);
-               stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1);
-               stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83);
-               stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0);
-               stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82);
-               stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0);
-               stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0);
-               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x50);
-
-               if (i_params->chip_id >= 0x20) {
-                       stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x6a);
-                       stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x95);
-               } else {
-                       stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
-                       stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x73);
-               }
+       tuner_freq = intp->freq[demod];
 
-               if (i_params->dmd2_symbol_rate <= 2000000)
-                       currier_step = 1000;
-               else if (i_params->dmd2_symbol_rate <= 5000000)
-                       currier_step = 2000;
-               else if (i_params->dmd2_symbol_rate <= 12000000)
-                       currier_step = 3000;
-               else
-                       currier_step = 5000;
+       while ((timing_lck == FALSE) && (current_step < nb_steps)) {
+               stv0900_write_reg(intp, DMDISTATE, 0x5f);
+               stv0900_write_bits(intp, DEMOD_MODE, 0);
 
+               msleep(50);
 
-               nb_steps = -1 + ((i_params->dmd2_srch_range / 1000) / currier_step);
-               nb_steps /= 2;
-               nb_steps = (2 * nb_steps) + 1;
+               for (i = 0; i < 10; i++) {
+                       if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2)
+                               timingcpt++;
 
-               if (nb_steps < 0)
-                       nb_steps = 1;
-               else if (nb_steps > 10) {
-                       nb_steps = 11;
-                       currier_step = (i_params->dmd2_srch_range / 1000) / 10;
+                       agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) |
+                                       stv0900_read_reg(intp, AGC2I0);
                }
 
-               current_step = 0;
-               direction = 1;
-               tuner_freq = i_params->tuner2_freq;
-
-               while ((timingLock == FALSE) && (current_step < nb_steps)) {
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5F);
-                       stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x0);
-
-                       msleep(50);
-                       timingcpt = 0;
-
-                       for (i = 0; i < 20; i++) {
-                               if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2)
-                                       timingcpt++;
-                               agc2_integr += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
-                                                               | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
-                       }
-
-                       agc2_integr /= 20;
-                       coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-                       if ((timingcpt >= 10) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000))
-                               timingLock = TRUE;
-                       else {
-                               current_step++;
-                               direction *= -1;
-
-                               if (direction > 0)
-                                       tuner_freq += (current_step * currier_step);
-                               else
-                                       tuner_freq -= (current_step * currier_step);
+               agc2_integr /= 10;
+               coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+               current_step++;
+               direction *= -1;
+
+               dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started."
+                       " tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n",
+                       tuner_freq, agc2_integr, coarse_srate, timingcpt);
+
+               if ((timingcpt >= 5) &&
+                               (agc2_integr < agc2_th) &&
+                               (coarse_srate < 55000000) &&
+                               (coarse_srate > 850000))
+                       timing_lck = TRUE;
+               else if (current_step < nb_steps) {
+                       if (direction > 0)
+                               tuner_freq += (current_step * currier_step);
+                       else
+                               tuner_freq -= (current_step * currier_step);
 
-                               stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw);
-                       }
+                       stv0900_set_tuner(fe, tuner_freq, intp->bw[demod]);
                }
-
-               if (timingLock == FALSE)
-                       coarse_srate = 0;
-               else
-                       coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
-               break;
        }
 
+       if (timing_lck == FALSE)
+               coarse_srate = 0;
+       else
+               coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+
        return coarse_srate;
 }
 
 static u32 stv0900_search_srate_fine(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       u32 coarse_srate,
-       coarse_freq,
-       symb;
+       u32     coarse_srate,
+               coarse_freq,
+               symb,
+               symbmax,
+               symbmin,
+               symbcomp;
+
+       coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod);
+
+       if (coarse_srate > 3000000) {
+               symbmax = 13 * (coarse_srate / 10);
+               symbmax = (symbmax / 1000) * 65536;
+               symbmax /= (intp->mclk / 1000);
+
+               symbmin = 10 * (coarse_srate / 13);
+               symbmin = (symbmin / 1000)*65536;
+               symbmin /= (intp->mclk / 1000);
+
+               symb = (coarse_srate / 1000) * 65536;
+               symb /= (intp->mclk / 1000);
+       } else {
+               symbmax = 13 * (coarse_srate / 10);
+               symbmax = (symbmax / 100) * 65536;
+               symbmax /= (intp->mclk / 100);
 
-       coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
+               symbmin = 10 * (coarse_srate / 14);
+               symbmin = (symbmin / 100) * 65536;
+               symbmin /= (intp->mclk / 100);
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               coarse_freq = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8)
-                                               | stv0900_read_reg(i_params, R0900_P1_CFR1);
-               symb = 13 * (coarse_srate / 10);
-
-               if (symb < i_params->dmd1_symbol_rate)
-                       coarse_srate = 0;
-               else {
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
-                       stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
-                       stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20);
-                       stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x00);
-                       stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2);
-                       stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
-
-                       if (i_params->chip_id >= 0x20)
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49);
-                       else
-                               stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
-
-                       if (coarse_srate > 3000000) {
-                               symb = 13 * (coarse_srate / 10);
-                               symb = (symb / 1000) * 65536;
-                               symb /= (i_params->mclk / 1000);
-                               stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF));
-
-                               symb = 10 * (coarse_srate / 13);
-                               symb = (symb / 1000) * 65536;
-                               symb /= (i_params->mclk / 1000);
-
-                               stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF));
-
-                               symb = (coarse_srate / 1000) * 65536;
-                               symb /= (i_params->mclk / 1000);
-                               stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF);
-                               stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF));
-                       } else {
-                               symb = 13 * (coarse_srate / 10);
-                               symb = (symb / 100) * 65536;
-                               symb /= (i_params->mclk / 100);
-                               stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF));
-
-                               symb = 10 * (coarse_srate / 14);
-                               symb = (symb / 100) * 65536;
-                               symb /= (i_params->mclk / 100);
-                               stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF));
-
-                               symb = (coarse_srate / 100) * 65536;
-                               symb /= (i_params->mclk / 100);
-                               stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF);
-                               stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF));
-                       }
+               symb = (coarse_srate / 100) * 65536;
+               symb /= (intp->mclk / 100);
+       }
 
-                       stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (coarse_freq >> 8) & 0xff);
-                       stv0900_write_reg(i_params, R0900_P1_CFRINIT0, coarse_freq  & 0xff);
-                       stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
-               }
-               break;
-       case STV0900_DEMOD_2:
-               coarse_freq = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8)
-                                               | stv0900_read_reg(i_params, R0900_P2_CFR1);
-
-               symb = 13 * (coarse_srate / 10);
-
-               if (symb < i_params->dmd2_symbol_rate)
-                       coarse_srate = 0;
-               else {
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
-                       stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
-                       stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20);
-                       stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0x00);
-                       stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2);
-                       stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
-
-                       if (i_params->chip_id >= 0x20)
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49);
-                       else
-                               stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
-
-                       if (coarse_srate > 3000000) {
-                               symb = 13 * (coarse_srate / 10);
-                               symb = (symb / 1000) * 65536;
-                               symb /= (i_params->mclk / 1000);
-                               stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF));
-
-                               symb = 10 * (coarse_srate / 13);
-                               symb = (symb / 1000) * 65536;
-                               symb /= (i_params->mclk / 1000);
-
-                               stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF));
-
-                               symb = (coarse_srate / 1000) * 65536;
-                               symb /= (i_params->mclk / 1000);
-                               stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF);
-                               stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF));
-                       } else {
-                               symb = 13 * (coarse_srate / 10);
-                               symb = (symb / 100) * 65536;
-                               symb /= (i_params->mclk / 100);
-                               stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF));
-
-                               symb = 10 * (coarse_srate / 14);
-                               symb = (symb / 100) * 65536;
-                               symb /= (i_params->mclk / 100);
-                               stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F);
-                               stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF));
-
-                               symb = (coarse_srate / 100) * 65536;
-                               symb /= (i_params->mclk / 100);
-                               stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF);
-                               stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF));
-                       }
+       symbcomp = 13 * (coarse_srate / 10);
+               coarse_freq = (stv0900_read_reg(intp, CFR2) << 8)
+                                       | stv0900_read_reg(intp, CFR1);
+
+       if (symbcomp < intp->symbol_rate[demod])
+               coarse_srate = 0;
+       else {
+               stv0900_write_reg(intp, DMDISTATE, 0x1f);
+               stv0900_write_reg(intp, TMGCFG2, 0xc1);
+               stv0900_write_reg(intp, TMGTHRISE, 0x20);
+               stv0900_write_reg(intp, TMGTHFALL, 0x00);
+               stv0900_write_reg(intp, TMGCFG, 0xd2);
+               stv0900_write_bits(intp, CFR_AUTOSCAN, 0);
+               stv0900_write_reg(intp, AGC2REF, 0x38);
+
+               if (intp->chip_id >= 0x30)
+                       stv0900_write_reg(intp, CARFREQ, 0x79);
+               else if (intp->chip_id >= 0x20)
+                       stv0900_write_reg(intp, CARFREQ, 0x49);
+               else
+                       stv0900_write_reg(intp, CARFREQ, 0xed);
 
-                       stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (coarse_freq >> 8) & 0xff);
-                       stv0900_write_reg(i_params, R0900_P2_CFRINIT0, coarse_freq  & 0xff);
-                       stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
-               }
+               stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f);
+               stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff));
 
-               break;
+               stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f);
+               stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff));
+
+               stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff);
+               stv0900_write_reg(intp, SFRINIT0, (symb & 0xff));
+
+               stv0900_write_reg(intp, DMDT0M, 0x20);
+               stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff);
+               stv0900_write_reg(intp, CFRINIT0, coarse_freq  & 0xff);
+               stv0900_write_reg(intp, DMDISTATE, 0x15);
        }
 
        return coarse_srate;
@@ -2265,163 +1574,135 @@ static u32 stv0900_search_srate_fine(struct dvb_frontend *fe)
 static int stv0900_blind_search_algo(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
-       u8 k_ref_tmg, k_ref_tmg_max, k_ref_tmg_min;
-       u32 coarse_srate;
-       int lock = FALSE, coarse_fail = FALSE;
-       s32 demod_timeout = 500, fec_timeout = 50, kref_tmg_reg, fail_cpt, i, agc2_overflow;
-       u16 agc2_integr;
-       u8 dstatus2;
-
-       dprintk(KERN_INFO "%s\n", __func__);
-
-       if (i_params->chip_id < 0x20) {
+       u8      k_ref_tmg,
+               k_ref_tmg_max,
+               k_ref_tmg_min;
+       u32     coarse_srate,
+               agc2_th;
+       int     lock = FALSE,
+               coarse_fail = FALSE;
+       s32     demod_timeout = 500,
+               fec_timeout = 50,
+               fail_cpt,
+               i,
+               agc2_overflow;
+       u16     agc2_int;
+       u8      dstatus2;
+
+       dprintk("%s\n", __func__);
+
+       if (intp->chip_id < 0x20) {
                k_ref_tmg_max = 233;
                k_ref_tmg_min = 143;
        } else {
-               k_ref_tmg_max = 120;
-               k_ref_tmg_min = 30;
+               k_ref_tmg_max = 110;
+               k_ref_tmg_min = 10;
        }
 
-       agc2_integr = stv0900_blind_check_agc2_min_level(i_params, demod);
-
-       if (agc2_integr > STV0900_BLIND_SEARCH_AGC2_TH) {
-               lock = FALSE;
-
-       } else {
-               switch (demod) {
-               case STV0900_DEMOD_1:
-               default:
-                       if (i_params->chip_id == 0x10)
-                               stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xAA);
-
-                       if (i_params->chip_id < 0x20)
-                               stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55);
-
-                       stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xC4);
-                       stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44);
-
-                       if (i_params->chip_id >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41);
-                               stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41);
-                               stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82);
-                               stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0);
-                       }
-
-                       kref_tmg_reg = R0900_P1_KREFTMG;
-                       break;
-               case STV0900_DEMOD_2:
-                       if (i_params->chip_id == 0x10)
-                               stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xAA);
-
-                       if (i_params->chip_id < 0x20)
-                               stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55);
+       if (intp->chip_id <= 0x20)
+               agc2_th = STV0900_BLIND_SEARCH_AGC2_TH;
+       else
+               agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30;
 
-                       stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xC4);
-                       stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44);
+       agc2_int = stv0900_blind_check_agc2_min_level(intp, demod);
 
-                       if (i_params->chip_id >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41);
-                               stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41);
-                               stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82);
-                               stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0);
-                       }
+       if (agc2_int > STV0900_BLIND_SEARCH_AGC2_TH)
+               return FALSE;
 
-                       kref_tmg_reg = R0900_P2_KREFTMG;
-                       break;
-               }
+       if (intp->chip_id == 0x10)
+               stv0900_write_reg(intp, CORRELEXP, 0xaa);
 
-               k_ref_tmg = k_ref_tmg_max;
+       if (intp->chip_id < 0x20)
+               stv0900_write_reg(intp, CARHDR, 0x55);
+       else
+               stv0900_write_reg(intp, CARHDR, 0x20);
 
-               do {
-                       stv0900_write_reg(i_params, kref_tmg_reg, k_ref_tmg);
-                       if (stv0900_search_srate_coarse(fe) != 0) {
-                               coarse_srate = stv0900_search_srate_fine(fe);
+       if (intp->chip_id <= 0x20)
+               stv0900_write_reg(intp, CARCFG, 0xc4);
+       else
+               stv0900_write_reg(intp, CARCFG, 0x6);
 
-                               if (coarse_srate != 0) {
-                                       stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, coarse_srate, STV0900_BLIND_SEARCH);
-                                       lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
-                               } else
-                                       lock = FALSE;
-                       } else {
-                               fail_cpt = 0;
-                               agc2_overflow = 0;
+       stv0900_write_reg(intp, RTCS2, 0x44);
 
-                               switch (demod) {
-                               case STV0900_DEMOD_1:
-                               default:
-                                       for (i = 0; i < 10; i++) {
-                                               agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
-                                                               | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
+       if (intp->chip_id >= 0x20) {
+               stv0900_write_reg(intp, EQUALCFG, 0x41);
+               stv0900_write_reg(intp, FFECFG, 0x41);
+               stv0900_write_reg(intp, VITSCALE, 0x82);
+               stv0900_write_reg(intp, VAVSRVIT, 0x0);
+       }
 
-                                               if (agc2_integr >= 0xff00)
-                                                       agc2_overflow++;
+       k_ref_tmg = k_ref_tmg_max;
 
-                                               dstatus2 = stv0900_read_reg(i_params, R0900_P1_DSTATUS2);
+       do {
+               stv0900_write_reg(intp, KREFTMG, k_ref_tmg);
+               if (stv0900_search_srate_coarse(fe) != 0) {
+                       coarse_srate = stv0900_search_srate_fine(fe);
+
+                       if (coarse_srate != 0) {
+                               stv0900_get_lock_timeout(&demod_timeout,
+                                                       &fec_timeout,
+                                                       coarse_srate,
+                                                       STV0900_BLIND_SEARCH);
+                               lock = stv0900_get_demod_lock(intp,
+                                                       demod,
+                                                       demod_timeout);
+                       } else
+                               lock = FALSE;
+               } else {
+                       fail_cpt = 0;
+                       agc2_overflow = 0;
 
-                                               if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1))
-                                                       fail_cpt++;
-                                       }
-                                       break;
-                               case STV0900_DEMOD_2:
-                                       for (i = 0; i < 10; i++) {
-                                               agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
-                                                               | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
+                       for (i = 0; i < 10; i++) {
+                               agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8)
+                                       | stv0900_read_reg(intp, AGC2I0);
 
-                                               if (agc2_integr >= 0xff00)
-                                                       agc2_overflow++;
+                               if (agc2_int >= 0xff00)
+                                       agc2_overflow++;
 
-                                               dstatus2 = stv0900_read_reg(i_params, R0900_P2_DSTATUS2);
+                               dstatus2 = stv0900_read_reg(intp, DSTATUS2);
 
-                                               if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1))
-                                                       fail_cpt++;
-                                       }
-                                       break;
-                               }
+                               if (((dstatus2 & 0x1) == 0x1) &&
+                                               ((dstatus2 >> 7) == 1))
+                                       fail_cpt++;
+                       }
 
-                               if ((fail_cpt > 7) || (agc2_overflow > 7))
-                                       coarse_fail = TRUE;
+                       if ((fail_cpt > 7) || (agc2_overflow > 7))
+                               coarse_fail = TRUE;
 
-                               lock = FALSE;
-                       }
-                       k_ref_tmg -= 30;
-               } while ((k_ref_tmg >= k_ref_tmg_min) && (lock == FALSE) && (coarse_fail == FALSE));
-       }
+                       lock = FALSE;
+               }
+               k_ref_tmg -= 30;
+       } while ((k_ref_tmg >= k_ref_tmg_min) &&
+                               (lock == FALSE) &&
+                               (coarse_fail == FALSE));
 
        return lock;
 }
 
-static void stv0900_set_viterbi_acq(struct stv0900_internal *i_params,
+static void stv0900_set_viterbi_acq(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
-       s32 vth_reg;
-
-       dprintk(KERN_INFO "%s\n", __func__);
+       s32 vth_reg = VTH12;
 
-       dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12);
+       dprintk("%s\n", __func__);
 
-       stv0900_write_reg(i_params, vth_reg++, 0x96);
-       stv0900_write_reg(i_params, vth_reg++, 0x64);
-       stv0900_write_reg(i_params, vth_reg++, 0x36);
-       stv0900_write_reg(i_params, vth_reg++, 0x23);
-       stv0900_write_reg(i_params, vth_reg++, 0x1E);
-       stv0900_write_reg(i_params, vth_reg++, 0x19);
+       stv0900_write_reg(intp, vth_reg++, 0x96);
+       stv0900_write_reg(intp, vth_reg++, 0x64);
+       stv0900_write_reg(intp, vth_reg++, 0x36);
+       stv0900_write_reg(intp, vth_reg++, 0x23);
+       stv0900_write_reg(intp, vth_reg++, 0x1e);
+       stv0900_write_reg(intp, vth_reg++, 0x19);
 }
 
-static void stv0900_set_search_standard(struct stv0900_internal *i_params,
+static void stv0900_set_search_standard(struct stv0900_internal *intp,
                                        enum fe_stv0900_demod_num demod)
 {
 
-       int sstndrd;
-
-       dprintk(KERN_INFO "%s\n", __func__);
-
-       sstndrd = i_params->dmd1_srch_standard;
-       if (demod == 1)
-               sstndrd = i_params->dmd2_srch_stndrd;
+       dprintk("%s\n", __func__);
 
-       switch (sstndrd) {
+       switch (intp->srch_standard[demod]) {
        case STV0900_SEARCH_DVBS1:
                dprintk("Search Standard = DVBS1\n");
                break;
@@ -2436,129 +1717,74 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params,
                break;
        }
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               switch (i_params->dmd1_srch_standard) {
-               case STV0900_SEARCH_DVBS1:
-               case STV0900_SEARCH_DSS:
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-
-                       stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0);
-                       stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
-                       stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
-                       stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x22);
-
-                       stv0900_set_viterbi_acq(i_params, demod);
-                       stv0900_set_viterbi_standard(i_params,
-                                               i_params->dmd1_srch_standard,
-                                               i_params->dmd1_fec, demod);
-
-                       break;
-               case STV0900_SEARCH_DVBS2:
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 1);
-                       stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
-                       stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
-                       stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26);
-                       if (i_params->demod_mode != STV0900_SINGLE) {
-                               if (i_params->chip_id <= 0x11)
-                                       stv0900_stop_all_s2_modcod(i_params, demod);
-                               else
-                                       stv0900_activate_s2_modcode(i_params, demod);
-
-                       } else
-                               stv0900_activate_s2_modcode_single(i_params, demod);
-
-                       stv0900_set_viterbi_tracq(i_params, demod);
-
-                       break;
-               case STV0900_AUTO_SEARCH:
-               default:
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0);
-                       stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
-                       stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
-                       stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26);
-                       if (i_params->demod_mode != STV0900_SINGLE) {
-                               if (i_params->chip_id <= 0x11)
-                                       stv0900_stop_all_s2_modcod(i_params, demod);
-                               else
-                                       stv0900_activate_s2_modcode(i_params, demod);
+       switch (intp->srch_standard[demod]) {
+       case STV0900_SEARCH_DVBS1:
+       case STV0900_SEARCH_DSS:
+               stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+               stv0900_write_bits(intp, DVBS2_ENABLE, 0);
+               stv0900_write_bits(intp, STOP_CLKVIT, 0);
+               stv0900_set_dvbs1_track_car_loop(intp,
+                                               demod,
+                                               intp->symbol_rate[demod]);
+               stv0900_write_reg(intp, CAR2CFG, 0x22);
+
+               stv0900_set_viterbi_acq(intp, demod);
+               stv0900_set_viterbi_standard(intp,
+                                       intp->srch_standard[demod],
+                                       intp->fec[demod], demod);
 
-                       } else
-                               stv0900_activate_s2_modcode_single(i_params, demod);
+               break;
+       case STV0900_SEARCH_DVBS2:
+               stv0900_write_bits(intp, DVBS1_ENABLE, 0);
+               stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+               stv0900_write_bits(intp, STOP_CLKVIT, 1);
+               stv0900_write_reg(intp, ACLC, 0x1a);
+               stv0900_write_reg(intp, BCLC, 0x09);
+               if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/
+                       stv0900_write_reg(intp, CAR2CFG, 0x26);
+               else
+                       stv0900_write_reg(intp, CAR2CFG, 0x66);
 
-                       if (i_params->dmd1_symbol_rate >= 2000000)
-                               stv0900_set_viterbi_acq(i_params, demod);
+               if (intp->demod_mode != STV0900_SINGLE) {
+                       if (intp->chip_id <= 0x11)
+                               stv0900_stop_all_s2_modcod(intp, demod);
                        else
-                               stv0900_set_viterbi_tracq(i_params, demod);
+                               stv0900_activate_s2_modcod(intp, demod);
 
-                       stv0900_set_viterbi_standard(i_params, i_params->dmd1_srch_standard, i_params->dmd1_fec, demod);
+               } else
+                       stv0900_activate_s2_modcod_single(intp, demod);
 
-                       break;
-               }
-               break;
-       case STV0900_DEMOD_2:
-               switch (i_params->dmd2_srch_stndrd) {
-               case STV0900_SEARCH_DVBS1:
-               case STV0900_SEARCH_DSS:
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0);
-                       stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
-                       stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
-                       stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x22);
-                       stv0900_set_viterbi_acq(i_params, demod);
-                       stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod);
-                       break;
-               case STV0900_SEARCH_DVBS2:
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 1);
-                       stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
-                       stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
-                       stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26);
-                       if (i_params->demod_mode != STV0900_SINGLE)
-                               stv0900_activate_s2_modcode(i_params, demod);
-                       else
-                               stv0900_activate_s2_modcode_single(i_params, demod);
+               stv0900_set_viterbi_tracq(intp, demod);
 
-                       stv0900_set_viterbi_tracq(i_params, demod);
-                       break;
-               case STV0900_AUTO_SEARCH:
-               default:
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
-                       stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0);
-                       stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
-                       stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
-                       stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26);
-                       if (i_params->demod_mode != STV0900_SINGLE)
-                               stv0900_activate_s2_modcode(i_params, demod);
-                       else
-                               stv0900_activate_s2_modcode_single(i_params, demod);
+               break;
+       case STV0900_AUTO_SEARCH:
+       default:
+               stv0900_write_bits(intp, DVBS1_ENABLE, 1);
+               stv0900_write_bits(intp, DVBS2_ENABLE, 1);
+               stv0900_write_bits(intp, STOP_CLKVIT, 0);
+               stv0900_write_reg(intp, ACLC, 0x1a);
+               stv0900_write_reg(intp, BCLC, 0x09);
+               stv0900_set_dvbs1_track_car_loop(intp,
+                                               demod,
+                                               intp->symbol_rate[demod]);
+               if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/
+                       stv0900_write_reg(intp, CAR2CFG, 0x26);
+               else
+                       stv0900_write_reg(intp, CAR2CFG, 0x66);
 
-                       if (i_params->dmd2_symbol_rate >= 2000000)
-                               stv0900_set_viterbi_acq(i_params, demod);
+               if (intp->demod_mode != STV0900_SINGLE) {
+                       if (intp->chip_id <= 0x11)
+                               stv0900_stop_all_s2_modcod(intp, demod);
                        else
-                               stv0900_set_viterbi_tracq(i_params, demod);
+                               stv0900_activate_s2_modcod(intp, demod);
 
-                       stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod);
+               } else
+                       stv0900_activate_s2_modcod_single(intp, demod);
 
-                       break;
-               }
+               stv0900_set_viterbi_tracq(intp, demod);
+               stv0900_set_viterbi_standard(intp,
+                                               intp->srch_standard[demod],
+                                               intp->fec[demod], demod);
 
                break;
        }
@@ -2567,10 +1793,11 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params,
 enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
 {
        struct stv0900_state *state = fe->demodulator_priv;
-       struct stv0900_internal *i_params = state->internal;
+       struct stv0900_internal *intp = state->internal;
        enum fe_stv0900_demod_num demod = state->demod;
 
-       s32 demod_timeout = 500, fec_timeout = 50, stream_merger_field;
+       s32 demod_timeout = 500, fec_timeout = 50;
+       s32 aq_power, agc1_power, i;
 
        int lock = FALSE, low_sr = FALSE;
 
@@ -2578,157 +1805,117 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
        enum fe_stv0900_search_algo algo;
        int no_signal = FALSE;
 
-       dprintk(KERN_INFO "%s\n", __func__);
+       dprintk("%s\n", __func__);
 
-       switch (demod) {
-       case STV0900_DEMOD_1:
-       default:
-               algo = i_params->dmd1_srch_algo;
-
-               stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1);
-               stream_merger_field = F0900_P1_RST_HWARE;
-
-               stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C);
-
-               if (i_params->chip_id >= 0x20)
-                       stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x9e);
+       algo = intp->srch_algo[demod];
+       stv0900_write_bits(intp, RST_HWARE, 1);
+       stv0900_write_reg(intp, DMDISTATE, 0x5c);
+       if (intp->chip_id >= 0x20) {
+               if (intp->symbol_rate[demod] > 5000000)
+                       stv0900_write_reg(intp, CORRELABS, 0x9e);
                else
-                       stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x88);
-
-               stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd1_symbol_rate, i_params->dmd1_srch_algo);
-
-               if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
-                       i_params->tuner1_bw = 2 * 36000000;
-
-                       stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x00);
-                       stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70);
-
-                       stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
-               } else {
-                       stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20);
-                       stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2);
-
-                       if (i_params->dmd1_symbol_rate < 2000000)
-                               stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x63);
-                       else
-                               stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70);
-
-                       stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
-                       if (i_params->chip_id >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0x5a);
-
-                               if (i_params->dmd1_srch_algo == STV0900_COLD_START)
-                                       i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10;
-                               else if (i_params->dmd1_srch_algo == STV0900_WARM_START)
-                                       i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000;
-                       } else {
-                               stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0xc1);
-                               i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10;
-                       }
-
-                       stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
-
-                       stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
-                       stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
-                       stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
-                       if (i_params->dmd1_symbol_rate >= 10000000)
-                               low_sr = FALSE;
-                       else
-                               low_sr = TRUE;
-
-               }
-
-               stv0900_set_tuner(fe, i_params->tuner1_freq, i_params->tuner1_bw);
-
-               stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, i_params->dmd1_srch_iq_inv);
-               stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
+                       stv0900_write_reg(intp, CORRELABS, 0x82);
+       } else
+               stv0900_write_reg(intp, CORRELABS, 0x88);
 
-               stv0900_set_search_standard(i_params, demod);
+       stv0900_get_lock_timeout(&demod_timeout, &fec_timeout,
+                               intp->symbol_rate[demod],
+                               intp->srch_algo[demod]);
 
-               if (i_params->dmd1_srch_algo != STV0900_BLIND_SEARCH)
-                       stv0900_start_search(i_params, demod);
-               break;
-       case STV0900_DEMOD_2:
-               algo = i_params->dmd2_srch_algo;
+       if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) {
+               intp->bw[demod] = 2 * 36000000;
 
-               stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1);
+               stv0900_write_reg(intp, TMGCFG2, 0xc0);
+               stv0900_write_reg(intp, CORRELMANT, 0x70);
 
-               stream_merger_field = F0900_P2_RST_HWARE;
-
-               stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C);
+               stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod);
+       } else {
+               stv0900_write_reg(intp, DMDT0M, 0x20);
+               stv0900_write_reg(intp, TMGCFG, 0xd2);
 
-               if (i_params->chip_id >= 0x20)
-                       stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x9e);
+               if (intp->symbol_rate[demod] < 2000000)
+                       stv0900_write_reg(intp, CORRELMANT, 0x63);
                else
-                       stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x88);
+                       stv0900_write_reg(intp, CORRELMANT, 0x70);
 
-               stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd2_symbol_rate, i_params->dmd2_srch_algo);
+               stv0900_write_reg(intp, AGC2REF, 0x38);
 
-               if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
-                       i_params->tuner2_bw = 2 * 36000000;
+               intp->bw[demod] =
+                               stv0900_carrier_width(intp->symbol_rate[demod],
+                                                               intp->rolloff);
+               if (intp->chip_id >= 0x20) {
+                       stv0900_write_reg(intp, KREFTMG, 0x5a);
 
-                       stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x00);
-                       stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70);
+                       if (intp->srch_algo[demod] == STV0900_COLD_START) {
+                               intp->bw[demod] += 10000000;
+                               intp->bw[demod] *= 15;
+                               intp->bw[demod] /= 10;
+                       } else if (intp->srch_algo[demod] == STV0900_WARM_START)
+                               intp->bw[demod] += 10000000;
 
-                       stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
                } else {
-                       stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20);
-                       stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2);
+                       stv0900_write_reg(intp, KREFTMG, 0xc1);
+                       intp->bw[demod] += 10000000;
+                       intp->bw[demod] *= 15;
+                       intp->bw[demod] /= 10;
+               }
 
-                       if (i_params->dmd2_symbol_rate < 2000000)
-                               stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x63);
-                       else
-                               stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70);
+               stv0900_write_reg(intp, TMGCFG2, 0xc1);
 
-                       if (i_params->dmd2_symbol_rate >= 10000000)
-                               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
-                       else
-                               stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x60);
+               stv0900_set_symbol_rate(intp, intp->mclk,
+                                       intp->symbol_rate[demod], demod);
+               stv0900_set_max_symbol_rate(intp, intp->mclk,
+                                       intp->symbol_rate[demod], demod);
+               stv0900_set_min_symbol_rate(intp, intp->mclk,
+                                       intp->symbol_rate[demod], demod);
+               if (intp->symbol_rate[demod] >= 10000000)
+                       low_sr = FALSE;
+               else
+                       low_sr = TRUE;
 
-                       if (i_params->chip_id >= 0x20) {
-                               stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0x5a);
+       }
 
-                               if (i_params->dmd2_srch_algo == STV0900_COLD_START)
-                                       i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate,
-                                                       i_params->rolloff) + 10000000)) / 10;
-                               else if (i_params->dmd2_srch_algo == STV0900_WARM_START)
-                                       i_params->tuner2_bw = stv0900_carrier_width(i_params->dmd2_symbol_rate,
-                                                       i_params->rolloff) + 10000000;
-                       } else {
-                               stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0xc1);
-                               i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate,
-                                                                       i_params->rolloff) + 10000000)) / 10;
-                       }
+       stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]);
 
-                       stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
+       agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1),
+                               stv0900_get_bits(intp, AGCIQ_VALUE0));
 
-                       stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
-                       stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
-                       stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
-                       if (i_params->dmd2_symbol_rate >= 10000000)
-                               low_sr = FALSE;
-                       else
-                               low_sr = TRUE;
+       aq_power = 0;
 
-               }
+       if (agc1_power == 0) {
+               for (i = 0; i < 5; i++)
+                       aq_power += (stv0900_get_bits(intp, POWER_I) +
+                                       stv0900_get_bits(intp, POWER_Q)) / 2;
 
-               stv0900_set_tuner(fe, i_params->tuner2_freq, i_params->tuner2_bw);
+               aq_power /= 5;
+       }
 
-               stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, i_params->dmd2_srch_iq_inv);
-               stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
+       if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) {
+               intp->result[demod].locked = FALSE;
+               signal_type = STV0900_NOAGC1;
+               dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__);
+       } else {
+               stv0900_write_bits(intp, SPECINV_CONTROL,
+                                       intp->srch_iq_inv[demod]);
+               if (intp->chip_id <= 0x20) /*cut 2.0*/
+                       stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1);
+               else /*cut 3.0*/
+                       stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1);
 
-               stv0900_set_search_standard(i_params, demod);
+               stv0900_set_search_standard(intp, demod);
 
-               if (i_params->dmd2_srch_algo != STV0900_BLIND_SEARCH)
-                       stv0900_start_search(i_params, demod);
-               break;
+               if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH)
+                       stv0900_start_search(intp, demod);
        }
 
-       if (i_params->chip_id == 0x12) {
-               stv0900_write_bits(i_params, stream_merger_field, 0);
+       if (signal_type == STV0900_NOAGC1)
+               return signal_type;
+
+       if (intp->chip_id == 0x12) {
+               stv0900_write_bits(intp, RST_HWARE, 0);
                msleep(3);
-               stv0900_write_bits(i_params, stream_merger_field, 1);
-               stv0900_write_bits(i_params, stream_merger_field, 0);
+               stv0900_write_bits(intp, RST_HWARE, 1);
+               stv0900_write_bits(intp, RST_HWARE, 0);
        }
 
        if (algo == STV0900_BLIND_SEARCH)
@@ -2736,12 +1923,12 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
        else if (algo == STV0900_COLD_START)
                lock = stv0900_get_demod_cold_lock(fe, demod_timeout);
        else if (algo == STV0900_WARM_START)
-               lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
+               lock = stv0900_get_demod_lock(intp, demod, demod_timeout);
 
        if ((lock == FALSE) && (algo == STV0900_COLD_START)) {
                if (low_sr == FALSE) {
-                       if (stv0900_check_timing_lock(i_params, demod) == TRUE)
-                               lock = stv0900_sw_algo(i_params, demod);
+                       if (stv0900_check_timing_lock(intp, demod) == TRUE)
+                               lock = stv0900_sw_algo(intp, demod);
                }
        }
 
@@ -2750,98 +1937,64 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
 
        if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) {
                stv0900_track_optimization(fe);
-               if (i_params->chip_id <= 0x11) {
-                       if ((stv0900_get_standard(fe, STV0900_DEMOD_1) == STV0900_DVBS1_STANDARD) && (stv0900_get_standard(fe, STV0900_DEMOD_2) == STV0900_DVBS1_STANDARD)) {
+               if (intp->chip_id <= 0x11) {
+                       if ((stv0900_get_standard(fe, 0) ==
+                                               STV0900_DVBS1_STANDARD) &&
+                          (stv0900_get_standard(fe, 1) ==
+                                               STV0900_DVBS1_STANDARD)) {
                                msleep(20);
-                               stv0900_write_bits(i_params, stream_merger_field, 0);
+                               stv0900_write_bits(intp, RST_HWARE, 0);
                        } else {
-                               stv0900_write_bits(i_params, stream_merger_field, 0);
+                               stv0900_write_bits(intp, RST_HWARE, 0);
                                msleep(3);
-                               stv0900_write_bits(i_params, stream_merger_field, 1);
-                               stv0900_write_bits(i_params, stream_merger_field, 0);
+                               stv0900_write_bits(intp, RST_HWARE, 1);
+                               stv0900_write_bits(intp, RST_HWARE, 0);
                        }
-               } else if (i_params->chip_id == 0x20) {
-                       stv0900_write_bits(i_params, stream_merger_field, 0);
+
+               } else if (intp->chip_id >= 0x20) {
+                       stv0900_write_bits(intp, RST_HWARE, 0);
                        msleep(3);
-                       stv0900_write_bits(i_params, stream_merger_field, 1);
-                       stv0900_write_bits(i_params, stream_merger_field, 0);
+                       stv0900_write_bits(intp, RST_HWARE, 1);
+                       stv0900_write_bits(intp, RST_HWARE, 0);
                }
 
-               if (stv0900_wait_for_lock(i_params, demod, fec_timeout, fec_timeout) == TRUE) {
+               if (stv0900_wait_for_lock(intp, demod,
+                                       fec_timeout, fec_timeout) == TRUE) {
                        lock = TRUE;
-                       switch (demod) {
-                       case STV0900_DEMOD_1:
-                       default:
-                               i_params->dmd1_rslts.locked = TRUE;
-                               if (i_params->dmd1_rslts.standard == STV0900_DVBS2_STANDARD) {
-                                       stv0900_set_dvbs2_rolloff(i_params, demod);
-                                       stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0x40);
-                                       stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0);
-                                       stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67);
-                               } else {
-                                       stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
-                               }
-
-                               stv0900_write_reg(i_params, R0900_P1_FBERCPT4, 0);
-                               stv0900_write_reg(i_params, R0900_P1_ERRCTRL2, 0xc1);
-                               break;
-                       case STV0900_DEMOD_2:
-                               i_params->dmd2_rslts.locked = TRUE;
-
-                               if (i_params->dmd2_rslts.standard == STV0900_DVBS2_STANDARD) {
-                                       stv0900_set_dvbs2_rolloff(i_params, demod);
-                                       stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x60);
-                                       stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x20);
-                                       stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67);
-                               } else {
-                                       stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
-                               }
-
-                               stv0900_write_reg(i_params, R0900_P2_FBERCPT4, 0);
-
-                               stv0900_write_reg(i_params, R0900_P2_ERRCTRL2, 0xc1);
-                               break;
+                       intp->result[demod].locked = TRUE;
+                       if (intp->result[demod].standard ==
+                                               STV0900_DVBS2_STANDARD) {
+                               stv0900_set_dvbs2_rolloff(intp, demod);
+                               stv0900_write_bits(intp, RESET_UPKO_COUNT, 1);
+                               stv0900_write_bits(intp, RESET_UPKO_COUNT, 0);
+                               stv0900_write_reg(intp, ERRCTRL1, 0x67);
+                       } else {
+                               stv0900_write_reg(intp, ERRCTRL1, 0x75);
                        }
+
+                       stv0900_write_reg(intp, FBERCPT4, 0);
+                       stv0900_write_reg(intp, ERRCTRL2, 0xc1);
                } else {
                        lock = FALSE;
                        signal_type = STV0900_NODATA;
-                       no_signal = stv0900_check_signal_presence(i_params, demod);
-
-                       switch (demod) {
-                       case STV0900_DEMOD_1:
-                       default:
-                               i_params->dmd1_rslts.locked = FALSE;
-                               break;
-                       case STV0900_DEMOD_2:
-                               i_params->dmd2_rslts.locked = FALSE;
-                               break;
-                       }
+                       no_signal = stv0900_check_signal_presence(intp, demod);
+
+                               intp->result[demod].locked = FALSE;
                }
        }
 
-       if ((signal_type == STV0900_NODATA) && (no_signal == FALSE)) {
-               switch (demod) {
-               case STV0900_DEMOD_1:
-               default:
-                       if (i_params->chip_id <= 0x11) {
-                               if ((stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) &&
-                                               (i_params->dmd1_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST))
-                                       signal_type = stv0900_dvbs1_acq_workaround(fe);
-                       } else
-                               i_params->dmd1_rslts.locked = FALSE;
+       if ((signal_type != STV0900_NODATA) || (no_signal != FALSE))
+               return signal_type;
 
-                       break;
-               case STV0900_DEMOD_2:
-                       if (i_params->chip_id <= 0x11) {
-                               if ((stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) &&
-                                               (i_params->dmd2_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST))
-                                       signal_type = stv0900_dvbs1_acq_workaround(fe);
-                       } else
-                               i_params->dmd2_rslts.locked = FALSE;
-                       break;
-               }
+       if (intp->chip_id > 0x11) {
+               intp->result[demod].locked = FALSE;
+               return signal_type;
        }
 
+       if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) &&
+          (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST))
+               signal_type = stv0900_dvbs1_acq_workaround(fe);
+
        return signal_type;
 }
 
index 488bdfb..48edd54 100644 (file)
@@ -825,7 +825,7 @@ static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate
                sym /= (state->mclk >> 7);
        }
 
-       if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0xff)) < 0) /* MSB */
+       if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */
                goto err;
        if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */
                goto err;
@@ -1238,6 +1238,8 @@ static int stv090x_delivery_search(struct stv090x_state *state)
                                goto err;
                }
 
+               if (stv090x_set_vit_thtracq(state) < 0)
+                       goto err;
                break;
 
        case STV090x_SEARCH_AUTO:
@@ -1278,17 +1280,8 @@ static int stv090x_delivery_search(struct stv090x_state *state)
                                goto err;
                }
 
-               if (state->srate >= 2000000) {
-                       /* Srate >= 2MSPS, Viterbi threshold to acquire */
-                       if (stv090x_set_vit_thacq(state) < 0)
-                               goto err;
-               } else {
-                       /* Srate < 2MSPS, Reset Viterbi thresholdto track
-                        * and then re-acquire
-                        */
-                       if (stv090x_set_vit_thtracq(state) < 0)
-                               goto err;
-               }
+               if (stv090x_set_vit_thacq(state) < 0)
+                       goto err;
 
                if (stv090x_set_viterbi(state) < 0)
                        goto err;
@@ -1317,7 +1310,7 @@ static int stv090x_start_search(struct stv090x_state *state)
                                goto err;
                        if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0)
                                goto err;
-                       if (STV090x_WRITE_DEMOD(state, CFRUP1, 0xff) < 0)
+                       if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0)
                                goto err;
                        if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0)
                                goto err;
@@ -1371,7 +1364,7 @@ static int stv090x_start_search(struct stv090x_state *state)
 
                if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0)
                        goto err;
-               if (STV090x_WRITE_DEMOD(state, CFRUP1, LSB(freq)) < 0)
+               if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0)
                        goto err;
 
                freq *= -1;
@@ -1422,6 +1415,9 @@ static int stv090x_start_search(struct stv090x_state *state)
        if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0)
                goto err;
 
+       if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0)
+               goto err;
+
        if (state->dev_ver >= 0x20) {
                /*Frequency offset detector setting*/
                if (state->srate < 2000000) {
@@ -1430,20 +1426,22 @@ static int stv090x_start_search(struct stv090x_state *state)
                                if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0)
                                        goto err;
                        } else {
-                               /* Cut 2 */
+                               /* Cut 3 */
                                if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0)
                                        goto err;
                        }
                        if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0)
                                goto err;
-               }
-
-               if (state->srate < 10000000) {
+               } else if (state->srate < 10000000) {
                        if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0)
                                goto err;
+                       if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
+                               goto err;
                } else {
                        if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0)
                                goto err;
+                       if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
+                               goto err;
                }
        } else {
                if (state->srate < 10000000) {
@@ -1485,14 +1483,14 @@ err:
 
 static int stv090x_get_agc2_min_level(struct stv090x_state *state)
 {
-       u32 agc2_min = 0, agc2 = 0, freq_init, freq_step, reg;
+       u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg;
        s32 i, j, steps, dir;
 
        if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
                goto err;
        reg = STV090x_READ_DEMOD(state, DMDCFGMD);
-       STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1);
-       STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1);
+       STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0);
+       STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
        if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
                goto err;
 
@@ -1509,10 +1507,8 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
        if (stv090x_set_srate(state, 1000000) < 0)
                goto err;
 
-       steps  = -1 + state->search_range / 1000000;
-       steps /= 2;
-       steps  = (2 * steps) + 1;
-       if (steps < 0)
+       steps  = state->search_range / 1000000;
+       if (steps <= 0)
                steps = 1;
 
        dir = 1;
@@ -1525,7 +1521,7 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
                else
                        freq_init = freq_init - (freq_step * i);
 
-               dir = -1;
+               dir *= -1;
 
                if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */
                        goto err;
@@ -1536,13 +1532,14 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state)
                if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */
                        goto err;
                msleep(10);
+
+               agc2 = 0;
                for (j = 0; j < 10; j++) {
-                       agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8;
-                       agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+                       agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+                               STV090x_READ_DEMOD(state, AGC2I0);
                }
                agc2 /= 10;
-               agc2_min = 0xffff;
-               if (agc2 < 0xffff)
+               if (agc2 < agc2_min)
                        agc2_min = agc2;
        }
 
@@ -1584,6 +1581,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
        int tmg_lock = 0, i;
        s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq;
        u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg;
+       u32 agc2th;
+
+       if (state->dev_ver >= 0x30)
+               agc2th = 0x2e00;
+       else
+               agc2th = 0x1f00;
 
        reg = STV090x_READ_DEMOD(state, DMDISTATE);
        STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */
@@ -1591,13 +1594,15 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
                goto err;
        if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0)
                goto err;
+       if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0)
+               goto err;
        if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0)
                goto err;
        if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0)
                goto err;
        reg = STV090x_READ_DEMOD(state, DMDCFGMD);
        STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1);
-       STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1);
+       STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
        if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
                goto err;
 
@@ -1611,13 +1616,13 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
                goto err;
        if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0)
                goto err;
-       if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x60) < 0)
+       if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0)
                goto err;
 
        if (state->dev_ver >= 0x30) {
                if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0)
                        goto err;
-               if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0)
+               if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0)
                        goto err;
 
        } else if (state->dev_ver >= 0x20) {
@@ -1652,23 +1657,31 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
        while ((!tmg_lock) && (cur_step < steps)) {
                if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */
                        goto err;
-               reg = STV090x_READ_DEMOD(state, DMDISTATE);
-               STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x00); /* trigger acquisition */
-               if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
+               if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
+                       goto err;
+               if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
+                       goto err;
+               if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0)
+                       goto err;
+               if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0)
+                       goto err;
+               /* trigger acquisition */
+               if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0)
                        goto err;
                msleep(50);
                for (i = 0; i < 10; i++) {
                        reg = STV090x_READ_DEMOD(state, DSTATUS);
                        if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2)
                                tmg_cpt++;
-                       agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8;
-                       agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+                       agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+                               STV090x_READ_DEMOD(state, AGC2I0);
                }
                agc2 /= 10;
                srate_coarse = stv090x_get_srate(state, state->mclk);
                cur_step++;
                dir *= -1;
-               if ((tmg_cpt >= 5) && (agc2 < 0x1f00) && (srate_coarse < 55000000) && (srate_coarse > 850000))
+               if ((tmg_cpt >= 5) && (agc2 < agc2th) &&
+                   (srate_coarse < 50000000) && (srate_coarse > 850000))
                        tmg_lock = 1;
                else if (cur_step < steps) {
                        if (dir > 0)
@@ -1681,7 +1694,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
                                goto err;
 
                        if (state->config->tuner_set_frequency) {
-                               if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
+                               if (state->config->tuner_set_frequency(fe, freq) < 0)
                                        goto err;
                        }
 
@@ -1738,7 +1751,7 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
        else {
                if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */
                        goto err;
-               if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0x01) < 0)
+               if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0)
                        goto err;
                if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0)
                        goto err;
@@ -1751,6 +1764,9 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
                if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
                        goto err;
 
+               if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+                       goto err;
+
                if (state->dev_ver >= 0x30) {
                        if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0)
                                goto err;
@@ -1856,12 +1872,12 @@ static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout)
 static int stv090x_blind_search(struct stv090x_state *state)
 {
        u32 agc2, reg, srate_coarse;
-       s32 timeout_dmd = 500, cpt_fail, agc2_ovflw, i;
+       s32 cpt_fail, agc2_ovflw, i;
        u8 k_ref, k_max, k_min;
        int coarse_fail, lock;
 
-       k_max = 120;
-       k_min = 30;
+       k_max = 110;
+       k_min = 10;
 
        agc2 = stv090x_get_agc2_min_level(state);
 
@@ -1900,7 +1916,8 @@ static int stv090x_blind_search(struct stv090x_state *state)
                                srate_coarse = stv090x_srate_srch_fine(state);
                                if (srate_coarse != 0) {
                                        stv090x_get_lock_tmg(state);
-                                       lock = stv090x_get_dmdlock(state, timeout_dmd);
+                                       lock = stv090x_get_dmdlock(state,
+                                                       state->DemodTimeout);
                                } else {
                                        lock = 0;
                                }
@@ -1908,8 +1925,8 @@ static int stv090x_blind_search(struct stv090x_state *state)
                                cpt_fail = 0;
                                agc2_ovflw = 0;
                                for (i = 0; i < 10; i++) {
-                                       agc2  = STV090x_READ_DEMOD(state, AGC2I1) << 8;
-                                       agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+                                       agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+                                               STV090x_READ_DEMOD(state, AGC2I0);
                                        if (agc2 >= 0xff00)
                                                agc2_ovflw++;
                                        reg = STV090x_READ_DEMOD(state, DSTATUS2);
@@ -1923,7 +1940,7 @@ static int stv090x_blind_search(struct stv090x_state *state)
 
                                lock = 0;
                        }
-                       k_ref -= 30;
+                       k_ref -= 20;
                } while ((k_ref >= k_min) && (!lock) && (!coarse_fail));
        }
 
@@ -2062,7 +2079,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
                                                goto err;
 
                                        if (state->config->tuner_set_frequency) {
-                                               if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
+                                               if (state->config->tuner_set_frequency(fe, freq) < 0)
                                                        goto err;
                                        }
 
@@ -2093,17 +2110,6 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
                                                goto err;
 
                                        STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c);
-                                       if (state->delsys == STV090x_DVBS2) {
-                                               reg = STV090x_READ_DEMOD(state, DMDCFGMD);
-                                               STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
-                                               STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
-                                               if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
-                                                       goto err;
-                                               STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
-                                               STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
-                                               if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
-                                                       goto err;
-                                       }
                                        if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
                                                goto err;
                                        if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
@@ -2425,7 +2431,7 @@ static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk)
 
        derot = (int_1 * int_2) +
                ((int_1 * tmp_2) >> 12) +
-               ((int_1 * tmp_1) >> 12);
+               ((int_2 * tmp_1) >> 12);
 
        return derot;
 }
@@ -2732,7 +2738,7 @@ static int stv090x_optimize_track(struct stv090x_state *state)
        switch (state->delsys) {
        case STV090x_DVBS1:
        case STV090x_DSS:
-               if (state->algo == STV090x_SEARCH_AUTO) {
+               if (state->search_mode == STV090x_SEARCH_AUTO) {
                        reg = STV090x_READ_DEMOD(state, DMDCFGMD);
                        STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
                        STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
@@ -2857,6 +2863,9 @@ static int stv090x_optimize_track(struct stv090x_state *state)
                if (stv090x_set_srate(state, srate) < 0)
                        goto err;
                blind_tune = 1;
+
+               if (stv090x_dvbs_track_crl(state) < 0)
+                       goto err;
        }
 
        if (state->dev_ver >= 0x20) {
@@ -3042,7 +3051,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
        struct dvb_frontend *fe = &state->frontend;
        enum stv090x_signal_state signal_state = STV090x_NOCARRIER;
        u32 reg;
-       s32 timeout_dmd = 500, timeout_fec = 50, agc1_power, power_iq = 0, i;
+       s32 agc1_power, power_iq = 0, i;
        int lock = 0, low_sr = 0, no_signal = 0;
 
        reg = STV090x_READ_DEMOD(state, TSCFGH);
@@ -3054,8 +3063,13 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
                goto err;
 
        if (state->dev_ver >= 0x20) {
-               if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) /* cut 2.0 */
-                       goto err;
+               if (state->srate > 5000000) {
+                       if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
+                               goto err;
+               } else {
+                       if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0)
+                               goto err;
+               }
        }
 
        stv090x_get_lock_tmg(state);
@@ -3175,7 +3189,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
        if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) {
                dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq);
                lock = 0;
-
+               signal_state = STV090x_NOAGC1;
        } else {
                reg = STV090x_READ_DEMOD(state, DEMOD);
                STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion);
@@ -3199,18 +3213,17 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
                }
        }
 
-       /* need to check for AGC1 state */
-
-
+       if (signal_state == STV090x_NOAGC1)
+               return signal_state;
 
        if (state->algo == STV090x_BLIND_SEARCH)
                lock = stv090x_blind_search(state);
 
        else if (state->algo == STV090x_COLD_SEARCH)
-               lock = stv090x_get_coldlock(state, timeout_dmd);
+               lock = stv090x_get_coldlock(state, state->DemodTimeout);
 
        else if (state->algo == STV090x_WARM_SEARCH)
-               lock = stv090x_get_dmdlock(state, timeout_dmd);
+               lock = stv090x_get_dmdlock(state, state->DemodTimeout);
 
        if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) {
                if (!low_sr) {
@@ -3245,8 +3258,9 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
                                goto err;
                }
 
-               if (stv090x_get_lock(state, timeout_fec, timeout_fec)) {
-                       lock = 1;
+               lock = stv090x_get_lock(state, state->FecTimeout,
+                               state->FecTimeout);
+               if (lock) {
                        if (state->delsys == STV090x_DVBS2) {
                                stv090x_set_s2rolloff(state);
 
@@ -3273,7 +3287,6 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
                        if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0)
                                goto err;
                } else {
-                       lock = 0;
                        signal_state = STV090x_NODATA;
                        no_signal = stv090x_chk_signal(state);
                }
@@ -3296,7 +3309,13 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
        state->search_mode = STV090x_SEARCH_AUTO;
        state->algo = STV090x_COLD_SEARCH;
        state->fec = STV090x_PRERR;
-       state->search_range = 2000000;
+       if (state->srate > 10000000) {
+               dprintk(FE_DEBUG, 1, "Search range: 10 MHz");
+               state->search_range = 10000000;
+       } else {
+               dprintk(FE_DEBUG, 1, "Search range: 5 MHz");
+               state->search_range = 5000000;
+       }
 
        if (stv090x_algo(state) == STV090x_RANGEOK) {
                dprintk(FE_DEBUG, 1, "Search success!");
@@ -3309,7 +3328,6 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron
        return DVBFE_ALGO_SEARCH_ERROR;
 }
 
-/* FIXME! */
 static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
 {
        struct stv090x_state *state = fe->demodulator_priv;
@@ -3331,9 +3349,15 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
                dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2");
                reg = STV090x_READ_DEMOD(state, DSTATUS);
                if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) {
-                       reg = STV090x_READ_DEMOD(state, TSSTATUS);
-                       if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
-                               *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+                       reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
+                       if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) {
+                               reg = STV090x_READ_DEMOD(state, TSSTATUS);
+                               if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) {
+                                       *status = FE_HAS_CARRIER |
+                                                 FE_HAS_VITERBI |
+                                                 FE_HAS_SYNC |
+                                                 FE_HAS_LOCK;
+                               }
                        }
                }
                break;
@@ -3412,14 +3436,12 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val)
        int res = 0;
        int min = 0, med;
 
-       if (val < tab[min].read)
-               res = tab[min].real;
-       else if (val >= tab[max].read)
-               res = tab[max].real;
-       else {
+       if ((val >= tab[min].read && val < tab[max].read) ||
+           (val >= tab[max].read && val < tab[min].read)) {
                while ((max - min) > 1) {
                        med = (max + min) / 2;
-                       if (val >= tab[min].read && val < tab[med].read)
+                       if ((val >= tab[min].read && val < tab[med].read) ||
+                           (val >= tab[med].read && val < tab[min].read))
                                max = med;
                        else
                                min = med;
@@ -3428,6 +3450,18 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val)
                       (tab[max].real - tab[min].real) /
                       (tab[max].read - tab[min].read)) +
                        tab[min].real;
+       } else {
+               if (tab[min].read < tab[max].read) {
+                       if (val < tab[min].read)
+                               res = tab[min].real;
+                       else if (val >= tab[max].read)
+                               res = tab[max].real;
+               } else {
+                       if (val >= tab[min].read)
+                               res = tab[min].real;
+                       else if (val < tab[max].read)
+                               res = tab[max].real;
+               }
        }
 
        return res;
@@ -3437,16 +3471,22 @@ static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
 {
        struct stv090x_state *state = fe->demodulator_priv;
        u32 reg;
-       s32 agc;
+       s32 agc_0, agc_1, agc;
+       s32 str;
 
        reg = STV090x_READ_DEMOD(state, AGCIQIN1);
-       agc = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+       agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+       reg = STV090x_READ_DEMOD(state, AGCIQIN0);
+       agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+       agc = MAKEWORD16(agc_1, agc_0);
 
-       *strength = stv090x_table_lookup(stv090x_rf_tab, ARRAY_SIZE(stv090x_rf_tab) - 1, agc);
+       str = stv090x_table_lookup(stv090x_rf_tab,
+               ARRAY_SIZE(stv090x_rf_tab) - 1, agc);
        if (agc > stv090x_rf_tab[0].read)
-               *strength = 5;
+               str = 0;
        else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read)
-               *strength = -100;
+               str = -100;
+       *strength = (str + 100) * 0xFFFF / 100;
 
        return 0;
 }
@@ -3457,6 +3497,8 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
        u32 reg_0, reg_1, reg, i;
        s32 val_0, val_1, val = 0;
        u8 lock_f;
+       s32 div;
+       u32 last;
 
        switch (state->delsys) {
        case STV090x_DVBS2:
@@ -3468,14 +3510,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
                                reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1);
                                val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD);
                                reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0);
-                               val_0 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD);
+                               val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD);
                                val  += MAKEWORD16(val_1, val_0);
                                msleep(1);
                        }
                        val /= 16;
-                       *cnr = stv090x_table_lookup(stv090x_s2cn_tab, ARRAY_SIZE(stv090x_s2cn_tab) - 1, val);
-                       if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s2cn_tab) - 1].read)
-                               *cnr = 1000;
+                       last = ARRAY_SIZE(stv090x_s2cn_tab) - 1;
+                       div = stv090x_s2cn_tab[0].read -
+                             stv090x_s2cn_tab[last].read;
+                       *cnr = 0xFFFF - ((val * 0xFFFF) / div);
                }
                break;
 
@@ -3489,14 +3532,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
                                reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1);
                                val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD);
                                reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0);
-                               val_0 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD);
+                               val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD);
                                val  += MAKEWORD16(val_1, val_0);
                                msleep(1);
                        }
                        val /= 16;
-                       *cnr = stv090x_table_lookup(stv090x_s1cn_tab, ARRAY_SIZE(stv090x_s1cn_tab) - 1, val);
-                       if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s1cn_tab) - 1].read)
-                               *cnr = 1000;
+                       last = ARRAY_SIZE(stv090x_s1cn_tab) - 1;
+                       div = stv090x_s1cn_tab[0].read -
+                             stv090x_s1cn_tab[last].read;
+                       *cnr = 0xFFFF - ((val * 0xFFFF) / div);
                }
                break;
        default:
@@ -3732,6 +3776,8 @@ static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc
 {
        u32 reg = 0;
 
+       reg = stv090x_read_reg(state, STV090x_GENCFG);
+
        switch (ldpc_mode) {
        case STV090x_DUAL:
        default:
index 5a4a017..5921a8d 100644 (file)
@@ -83,7 +83,7 @@
 
 #define STV090x_IQPOWER_THRESHOLD        30
 #define STV090x_SEARCH_AGC2_TH_CUT20    700
-#define STV090x_SEARCH_AGC2_TH_CUT30   1200
+#define STV090x_SEARCH_AGC2_TH_CUT30   1400
 
 #define STV090x_SEARCH_AGC2_TH(__ver)  \
        ((__ver <= 0x20) ?              \
@@ -91,6 +91,7 @@
        STV090x_SEARCH_AGC2_TH_CUT30)
 
 enum stv090x_signal_state {
+       STV090x_NOAGC1,
        STV090x_NOCARRIER,
        STV090x_NODATA,
        STV090x_DATAOK,
index 57b6abb..2502855 100644 (file)
@@ -44,7 +44,7 @@
 #define STV090x_OFFST_OUTSERRS2_HZ_FIELD       5
 #define STV090x_WIDTH_OUTSERRS2_HZ_FIELD       1
 #define STV090x_OFFST_OUTSERRS3_HZ_FIELD       4
-#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD       1
+#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD       1
 #define STV090x_OFFST_OUTPARRS3_HZ_FIELD       3
 #define STV090x_WIDTH_OUTPARRS3_HZ_FIELD       1
 
 #define STV090x_IRQMASK3                       0xf124
 #define STV090x_OFFST_MPLL_LOCK_FIELD          5
 #define STV090x_WIDTH_MPLL_LOCK_FIELD          1
-#define STV090x_OFFST_MSTREAM_LCK_3_FIELD      2
-#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD      3
-#define STV090x_OFFST_MSTREAM_LCK_2_FIELD      2
-#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD      3
+#define STV090x_OFFST_MSTREAM_LCK_3_FIELD      4
+#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD      1
+#define STV090x_OFFST_MSTREAM_LCK_2_FIELD      3
+#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD      1
 #define STV090x_OFFST_MSTREAM_LCK_1_FIELD      2
-#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD      3
+#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD      1
 #define STV090x_OFFST_MDVBS1_PRF_2_FIELD       1
 #define STV090x_WIDTH_MDVBS1_PRF_2_FIELD       1
 #define STV090x_OFFST_MDVBS1_PRF_1_FIELD       0
 #define STV090x_WIDTH_MDVBS1_PRF_1_FIELD       1
 
 #define STV090x_IRQMASK2                       0xf125
-#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD      5
-#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD      3
-#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD      5
-#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD      3
+#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD      7
+#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD      1
+#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD      6
+#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD      1
 #define STV090x_OFFST_MSPY_ENDSIM_1_FIELD      5
-#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD      3
+#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD      1
 #define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD    4
 #define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD    1
 #define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD    3
 #define STV090x_OFFST_SELX1RATIO_FIELD         5
 #define STV090x_WIDTH_SELX1RATIO_FIELD         1
 #define STV090x_OFFST_STOP_PLL_FIELD           3
-#define STV090x_WIDTH_SELX1RATIO_FIELD         1
+#define STV090x_WIDTH_STOP_PLL_FIELD           1
 #define STV090x_OFFST_BYPASSPLLFSK_FIELD       2
 #define STV090x_WIDTH_BYPASSPLLFSK_FIELD       1
 #define STV090x_OFFST_SELOSCI_FIELD            1
 #define STV090x_OFFST_Px_CONT_TONE_FIELD       4
 #define STV090x_WIDTH_Px_CONT_TONE_FIELD       1
 #define STV090x_OFFST_Px_FIFO_4BREADY_FIELD    3
-#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD    2
+#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD    1
 #define STV090x_OFFST_Px_FIFO_EMPTY_FIELD      2
 #define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD      1
 #define STV090x_OFFST_Px_ABORT_DISRX_FIELD     0
 #define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD    1
 #define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD    6
 #define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD    1
-#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD    5 /* check */
-#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD    1
-#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD     4 /* check */
+#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD     4
 #define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD     1
-#define STV090x_OFFST_Px_TUN_AUTOSCAN_FIELD    3
-#define STV090x_WIDTH_Px_TUN_AUTOSCAN_FIELD    1
+#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD    3
+#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD    1
 #define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD  2
 #define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD  1
 #define STV090x_OFFST_Px_TUN_RNG_FIELD         0
 #define STV090x_P2_DMDFLYW                     STV090x_Px_DMDFLYW(2)
 #define STV090x_OFFST_Px_I2C_IRQVAL_FIELD      4
 #define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD      4
-#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD    0 /* check */
+#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD    0
 #define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD    4
 
 #define STV090x_Px_DSTATUS3(__x)               (0xF41D - (__x - 1) * 0x200)
 #define STV090x_P1_CFRINC1                     STV090x_Px_CFRINC1(1)
 #define STV090x_P2_CFRINC1                     STV090x_Px_CFRINC1(2)
 #define STV090x_OFFST_Px_CFR_INC1_FIELD                0
-#define STV090x_WIDTH_Px_CFR_INC1_FIELD                7
+#define STV090x_WIDTH_Px_CFR_INC1_FIELD                7 /* check */
 
 #define STV090x_Px_CFRINC0(__x)                        (0xF44B - (__x - 1) * 0x200)
 #define STV090x_P1_CFRINC0                     STV090x_Px_CFRINC0(1)
 #define STV090x_P2_CFRINC0                     STV090x_Px_CFRINC0(2)
-#define STV090x_OFFST_Px_CFR_INC0_FIELD                4
+#define STV090x_OFFST_Px_CFR_INC0_FIELD                4 /* check */
 #define STV090x_WIDTH_Px_CFR_INC0_FIELD                4
 
 #define STV090x_Pn_CFRy(__x, __y)              (0xF44E - (__x - 1) * 0x200 - __y * 0x1)
 #define STV090x_Px_SFRINIT1(__x)               (0xF45E - (__x - 1) * 0x200)
 #define STV090x_P1_SFRINIT1                    STV090x_Px_SFRINIT1(1)
 #define STV090x_P2_SFRINIT1                    STV090x_Px_SFRINIT1(2)
-#define STV090x_OFFST_Px_SFR_INIT_FIELD                0
-#define STV090x_WIDTH_Px_SFR_INIT_FIELD                8
+#define STV090x_OFFST_Px_SFR_INIT1_FIELD       0
+#define STV090x_WIDTH_Px_SFR_INIT1_FIELD       7
 
 #define STV090x_Px_SFRINIT0(__x)               (0xF45F - (__x - 1) * 0x200)
 #define STV090x_P1_SFRINIT0                    STV090x_Px_SFRINIT0(1)
 #define STV090x_P2_SFRINIT0                    STV090x_Px_SFRINIT0(2)
-#define STV090x_OFFST_Px_SFR_INIT_FIELD                0
-#define STV090x_WIDTH_Px_SFR_INIT_FIELD                8
+#define STV090x_OFFST_Px_SFR_INIT0_FIELD       0
+#define STV090x_WIDTH_Px_SFR_INIT0_FIELD       8
 
 #define STV090x_Px_SFRUP1(__x)                 (0xF460 - (__x - 1) * 0x200)
 #define STV090x_P1_SFRUP1                      STV090x_Px_SFRUP1(1)
 #define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD  0
 #define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD  8
 
-#define STV090x_Px_SFRy(__x, __y)              (0xF464 - (__x-1) * 0x200 + (3 - __y))
+#define STV090x_Px_SFRy(__x, __y)              (0xF467 - (__x-1) * 0x200 - __y)
 #define STV090x_P1_SFR0                                STV090x_Px_SFRy(1, 0)
 #define STV090x_P1_SFR1                                STV090x_Px_SFRy(1, 1)
 #define STV090x_P1_SFR2                                STV090x_Px_SFRy(1, 2)
 #define STV090x_P2_SFR2                                STV090x_Px_SFRy(2, 2)
 #define STV090x_P2_SFR3                                STV090x_Px_SFRy(2, 3)
 #define STV090x_OFFST_Px_SYMB_FREQ_FIELD       0
-#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD       32
+#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD       8
 
 #define STV090x_Px_TMGREG2(__x)                        (0xF468 - (__x - 1) * 0x200)
 #define STV090x_P1_TMGREG2                     STV090x_Px_TMGREG2(1)
 
 #define STV090x_Px_TMGREG1(__x)                        (0xF469 - (__x - 1) * 0x200)
 #define STV090x_P1_TMGREG1                     STV090x_Px_TMGREG1(1)
-#define STV090x_P2_TMGREG1                             STV090x_Px_TMGREG1(2)
+#define STV090x_P2_TMGREG1                     STV090x_Px_TMGREG1(2)
 #define STV090x_OFFST_Px_TMGREG_FIELD          0
 #define STV090x_WIDTH_Px_TMGREG_FIELD          8
 
 #define STV090x_OFFST_Px_MU_EQUALDFE_FIELD     0
 #define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD     3
 
-#define STV090x_Px_EQUAIy(__x, __y)            (0xf470 - (__x - 1) * 0x200 + (__y - 1))
+#define STV090x_Px_EQUAIy(__x, __y)    (0xf470 - (__x-1) * 0x200 + 2 * (__y-1))
 #define STV090x_P1_EQUAI1                      STV090x_Px_EQUAIy(1, 1)
 #define STV090x_P1_EQUAI2                      STV090x_Px_EQUAIy(1, 2)
 #define STV090x_P1_EQUAI3                      STV090x_Px_EQUAIy(1, 3)
 #define STV090x_OFFST_Px_EQUA_ACCIy_FIELD      0
 #define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD      8
 
-#define STV090x_Px_EQUAQy(__x, __y)            (0xf471 - (__x - 1) * 0x200 + (__y - 1))
+#define STV090x_Px_EQUAQy(__x, __y)    (0xf471 - (__x-1) * 0x200 + 2 * (__y-1))
 #define STV090x_P1_EQUAQ1                      STV090x_Px_EQUAQy(1, 1)
 #define STV090x_P1_EQUAQ2                      STV090x_Px_EQUAQy(1, 2)
 #define STV090x_P1_EQUAQ3                      STV090x_Px_EQUAQy(1, 3)
 #define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD       0
 #define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD       4
 
-#define STV090x_Px_ACLC2S232A(__x)             (0xf499 - (__x - 1) * 0x200)
+#define STV090x_Px_ACLC2S232A(__x)             (0xf49A - (__x - 1) * 0x200)
 #define STV090x_P1_ACLC2S232A                  STV090x_Px_ACLC2S232A(1)
 #define STV090x_P2_ACLC2S232A                  STV090x_Px_ACLC2S232A(2)
 #define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD       4
 #define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0
 #define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4
 
-#define STV090x_Px_BCLC2S216A(__x)             (0xf49d - (__x - 1) * 0x200)
+#define STV090x_Px_BCLC2S216A(__x)             (0xf49e - (__x - 1) * 0x200)
 #define STV090x_P1_BCLC2S216A                  STV090x_Px_BCLC2S216A(1)
 #define STV090x_P2_BCLC2S216A                  STV090x_Px_BCLC2S216A(1)
 #define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD       4
 #define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD       0
 #define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD       4
 
-#define STV090x_Px_BCLC2S232A(__x)             (0xf49d - (__x - 1) * 0x200)
+#define STV090x_Px_BCLC2S232A(__x)             (0xf49f - (__x - 1) * 0x200)
 #define STV090x_P1_BCLC2S232A                  STV090x_Px_BCLC2S232A(1)
 #define STV090x_P2_BCLC2S232A                  STV090x_Px_BCLC2S232A(1)
 #define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD       4
 #define STV090x_P1_MODCODLST1                  STV090x_Px_MODCODLST1(1)
 #define STV090x_P2_MODCODLST1                  STV090x_Px_MODCODLST1(2)
 #define STV090x_OFFST_Px_DIS_MODCOD29_FIELD    4
-#define STV090x_WIDTH_Px_DIS_MODCOD29T_FIELD   4
+#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD    4
 #define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD  0
 #define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD  4
 
 #define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD       1
 #define STV090x_OFFST_Px_DILXX_RESET_FIELD             5
 #define STV090x_WIDTH_Px_DILXX_RESET_FIELD             1
-#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD          5
+#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD          4
 #define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD          1
 #define STV090x_OFFST_Px_SCRAMBDETECT_FIELD            1
 #define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD            1
 #define STV090x_P1_TSBITRATE1                          STV090x_Px_TSBITRATEy(1, 1)
 #define STV090x_P2_TSBITRATE0                          STV090x_Px_TSBITRATEy(2, 0)
 #define STV090x_P2_TSBITRATE1                          STV090x_Px_TSBITRATEy(2, 1)
-#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD          7
+#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD          0
 #define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD          8
 
 #define STV090x_Px_ERRCTRL1(__x)                       (0xF598 - (__x - 1) * 0x200)
index dcf1b21..bef0cc8 100644 (file)
@@ -37,6 +37,7 @@ struct stv6110_priv {
 
        u32 mclk;
        u8 clk_div;
+       u8 gain;
        u8 regs[8];
 };
 
@@ -255,7 +256,7 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency)
        u8 ret = 0x04;
        u32 divider, ref, p, presc, i, result_freq, vco_freq;
        s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val;
-       s32 srate; u8 gain;
+       s32 srate;
 
        dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__,
                                                frequency, priv->mclk);
@@ -273,15 +274,8 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency)
        } else
                srate = 15000000;
 
-       if (srate >= 15000000)
-               gain = 3; /* +6 dB */
-       else if (srate >= 5000000)
-               gain = 3; /* +6 dB */
-       else
-               gain = 3; /* +6 dB */
-
        priv->regs[RSTV6110_CTRL2] &= ~0x0f;
-       priv->regs[RSTV6110_CTRL2] |= (gain & 0x0f);
+       priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f);
 
        if (frequency <= 1023000) {
                p = 1;
@@ -436,6 +430,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
        priv->i2c = i2c;
        priv->mclk = config->mclk;
        priv->clk_div = config->clk_div;
+       priv->gain = config->gain;
 
        memcpy(&priv->regs, &reg0[1], 8);
 
index 9db2402..fe71bba 100644 (file)
@@ -41,6 +41,7 @@
 struct stv6110_config {
        u8 i2c_address;
        u32 mclk;
+       u8 gain;
        u8 clk_div;     /* divisor value for the output clock */
 };
 
index 3d8a2e0..bcfcb65 100644 (file)
@@ -95,7 +95,7 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
 {
        struct stv6110x_state *stv6110x = fe->tuner_priv;
        u32 rDiv, divider;
-       s32 pVal, pCalc, rDivOpt = 0;
+       s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000;
        u8 i;
 
        STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16));
@@ -121,8 +121,10 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency)
        for (rDiv = 0; rDiv <= 3; rDiv++) {
                pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv);
 
-               if ((abs((s32)(pCalc - pVal))) < (abs((s32)(1000 - pVal))))
+               if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal))))
                        rDivOpt = rDiv;
+
+               pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt);
        }
 
        divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz;
index 1fd8306..81e623a 100644 (file)
@@ -27,7 +27,6 @@
 #include <linux/pci.h>
 #include <linux/kthread.h>
 #include <linux/freezer.h>
-#include <linux/vmalloc.h>
 
 #include "dvbdev.h"
 #include "dvb_demux.h"
index 2db940f..fc65949 100644 (file)
@@ -48,6 +48,60 @@ struct va1j5jf8007s_state {
        enum va1j5jf8007s_tune_state tune_state;
 };
 
+static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       struct va1j5jf8007s_state *state;
+       u8 addr;
+       int i;
+       u8 write_buf[1], read_buf[1];
+       struct i2c_msg msgs[2];
+       s32 word, x1, x2, x3, x4, x5, y;
+
+       state = fe->demodulator_priv;
+       addr = state->config->demod_address;
+
+       word = 0;
+       for (i = 0; i < 2; i++) {
+               write_buf[0] = 0xbc + i;
+
+               msgs[0].addr = addr;
+               msgs[0].flags = 0;
+               msgs[0].len = sizeof(write_buf);
+               msgs[0].buf = write_buf;
+
+               msgs[1].addr = addr;
+               msgs[1].flags = I2C_M_RD;
+               msgs[1].len = sizeof(read_buf);
+               msgs[1].buf = read_buf;
+
+               if (i2c_transfer(state->adap, msgs, 2) != 2)
+                       return -EREMOTEIO;
+
+               word <<= 8;
+               word |= read_buf[0];
+       }
+
+       word -= 3000;
+       if (word < 0)
+               word = 0;
+
+       x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000);
+       x2 = (s64)x1 * x1 >> 31;
+       x3 = (s64)x2 * x1 >> 31;
+       x4 = (s64)x2 * x2 >> 31;
+       x5 = (s64)x4 * x1 >> 31;
+
+       y = (58857ll << 23) / 1000;
+       y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30;
+       y += (s64)x2 * ((88977ll << 24) / 1000) >> 28;
+       y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27;
+       y += (s64)x4 * ((14341ll << 27) / 1000) >> 27;
+       y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28;
+
+       *snr = y < 0 ? 0 : y >> 15;
+       return 0;
+}
+
 static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
 {
        return DVBFE_ALGO_HW;
@@ -337,7 +391,7 @@ va1j5jf8007s_tune(struct dvb_frontend *fe,
 {
        struct va1j5jf8007s_state *state;
        int ret;
-       int lock;
+       int lock = 0;
 
        state = fe->demodulator_priv;
 
@@ -536,6 +590,7 @@ static struct dvb_frontend_ops va1j5jf8007s_ops = {
                        FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
        },
 
+       .read_snr = va1j5jf8007s_read_snr,
        .get_frontend_algo = va1j5jf8007s_get_frontend_algo,
        .read_status = va1j5jf8007s_read_status,
        .tune = va1j5jf8007s_tune,
index 71117f4..3db4f3e 100644 (file)
@@ -46,6 +46,52 @@ struct va1j5jf8007t_state {
        enum va1j5jf8007t_tune_state tune_state;
 };
 
+static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+       struct va1j5jf8007t_state *state;
+       u8 addr;
+       int i;
+       u8 write_buf[1], read_buf[1];
+       struct i2c_msg msgs[2];
+       s32 word, x, y;
+
+       state = fe->demodulator_priv;
+       addr = state->config->demod_address;
+
+       word = 0;
+       for (i = 0; i < 3; i++) {
+               write_buf[0] = 0x8b + i;
+
+               msgs[0].addr = addr;
+               msgs[0].flags = 0;
+               msgs[0].len = sizeof(write_buf);
+               msgs[0].buf = write_buf;
+
+               msgs[1].addr = addr;
+               msgs[1].flags = I2C_M_RD;
+               msgs[1].len = sizeof(read_buf);
+               msgs[1].buf = read_buf;
+
+               if (i2c_transfer(state->adap, msgs, 2) != 2)
+                       return -EREMOTEIO;
+
+               word <<= 8;
+               word |= read_buf[0];
+       }
+
+       if (!word)
+               return -EIO;
+
+       x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24));
+       y = (24ll << 46) / 1000000;
+       y = ((s64)y * x >> 30) - (16ll << 40) / 10000;
+       y = ((s64)y * x >> 29) + (398ll << 35) / 10000;
+       y = ((s64)y * x >> 30) + (5491ll << 29) / 10000;
+       y = ((s64)y * x >> 30) + (30965ll << 23) / 10000;
+       *snr = y >> 15;
+       return 0;
+}
+
 static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe)
 {
        return DVBFE_ALGO_HW;
@@ -224,7 +270,7 @@ va1j5jf8007t_tune(struct dvb_frontend *fe,
 {
        struct va1j5jf8007t_state *state;
        int ret;
-       int lock, retry;
+       int lock = 0, retry = 0;
 
        state = fe->demodulator_priv;
 
@@ -393,6 +439,7 @@ static struct dvb_frontend_ops va1j5jf8007t_ops = {
                        FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
        },
 
+       .read_snr = va1j5jf8007t_read_snr,
        .get_frontend_algo = va1j5jf8007t_get_frontend_algo,
        .read_status = va1j5jf8007t_read_status,
        .tune = va1j5jf8007t_tune,
index 0420e28..1067b22 100644 (file)
@@ -97,7 +97,7 @@ static struct sms_board sms_boards[] = {
        },
 };
 
-struct sms_board *sms_get_board(int id)
+struct sms_board *sms_get_board(unsigned id)
 {
        BUG_ON(id >= ARRAY_SIZE(sms_boards));
 
@@ -294,6 +294,8 @@ int sms_board_load_modules(int id)
        case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A:
        case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B:
        case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
+       case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+       case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
                request_module("smsdvb");
                break;
        default:
index 38f062f..8f19fc0 100644 (file)
@@ -81,7 +81,7 @@ struct sms_board {
        int led_power, led_hi, led_lo, lna_ctrl, rf_switch;
 };
 
-struct sms_board *sms_get_board(int id);
+struct sms_board *sms_get_board(unsigned id);
 
 extern struct smscore_device_t *coredev;
 
index fa6a623..ca758bc 100644 (file)
@@ -1373,7 +1373,7 @@ static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum,
 
        *pGroupCfg = 1;
 
-       if (PinNum >= 0 && PinNum <= 1) {
+       if (PinNum <= 1)        {
                *pTranslatedPinNum = 0;
                *pGroupNum = 9;
                *pGroupCfg = 2;
index d1d652e..24206cb 100644 (file)
@@ -78,7 +78,7 @@ struct smssdio_device {
 
 static int smssdio_sendrequest(void *context, void *buffer, size_t size)
 {
-       int ret;
+       int ret = 0;
        struct smssdio_device *smsdev;
 
        smsdev = context;
index 8d65c65..baf3159 100644 (file)
@@ -2425,7 +2425,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
                 * use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
                 */
                saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
-               /* set event counter 1 treshold to maximum allowed value        (rEC p55) */
+               /* set event counter 1 threshold to maximum allowed value        (rEC p55) */
                saa7146_write(dev, ECT1R,  0x3fff );
 #endif
                /* Set RPS1 Address register to point to RPS code               (r108 p42) */
@@ -2559,7 +2559,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
                 * use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
                 */
                saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
-               /* set event counter 1 treshold to maximum allowed value        (rEC p55) */
+               /* set event counter 1 threshold to maximum allowed value        (rEC p55) */
                saa7146_write(dev, ECT1R,  0x3fff );
 #endif
                /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */
index 8ea9152..983672a 100644 (file)
@@ -1055,7 +1055,7 @@ static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = {
        { STB0899_TSCFGH                , 0x0c },
        { STB0899_TSCFGM                , 0x00 },
        { STB0899_TSCFGL                , 0x0c },
-       { STB0899_TSOUT                 , 0x0d }, /* 0x0d for CAM */
+       { STB0899_TSOUT                 , 0x4d }, /* 0x0d for CAM */
        { STB0899_RSSYNCDEL             , 0x00 },
        { STB0899_TSINHDELH             , 0x02 },
        { STB0899_TSINHDELM             , 0x00 },
index b5c6813..7d193eb 100644 (file)
@@ -178,7 +178,7 @@ static void msp430_ir_interrupt(unsigned long data)
        if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) {
                ir_input_nokey(dev, &budget_ci->ir.state);
                ir_input_keydown(dev, &budget_ci->ir.state,
-                                budget_ci->ir.ir_key, raw);
+                                budget_ci->ir.ir_key);
                budget_ci->ir.last_raw = raw;
        }
 
@@ -224,8 +224,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
        case 0x1011:
        case 0x1012:
                /* The hauppauge keymap is a superset of these remotes */
-               ir_input_init(input_dev, &budget_ci->ir.state,
+               error = ir_input_init(input_dev, &budget_ci->ir.state,
                              IR_TYPE_RC5, &ir_codes_hauppauge_new_table);
+               if (error < 0)
+                       goto out2;
 
                if (rc5_device < 0)
                        budget_ci->ir.rc5_device = 0x1f;
@@ -236,8 +238,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
        case 0x1017:
        case 0x101a:
                /* for the Technotrend 1500 bundled remote */
-               ir_input_init(input_dev, &budget_ci->ir.state,
+               error = ir_input_init(input_dev, &budget_ci->ir.state,
                              IR_TYPE_RC5, &ir_codes_tt_1500_table);
+               if (error < 0)
+                       goto out2;
 
                if (rc5_device < 0)
                        budget_ci->ir.rc5_device = IR_DEVICE_ANY;
@@ -246,8 +250,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
                break;
        default:
                /* unknown remote */
-               ir_input_init(input_dev, &budget_ci->ir.state,
+               error = ir_input_init(input_dev, &budget_ci->ir.state,
                              IR_TYPE_RC5, &ir_codes_budget_ci_old_table);
+               if (error < 0)
+                       goto out2;
 
                if (rc5_device < 0)
                        budget_ci->ir.rc5_device = IR_DEVICE_ANY;
@@ -280,6 +286,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
        return 0;
 
 out2:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
 out1:
        return error;
@@ -297,6 +304,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci)
        del_timer_sync(&dev->timer);
        ir_input_nokey(dev, &budget_ci->ir.state);
 
+       ir_input_free(dev);
        input_unregister_device(dev);
 }
 
@@ -1248,7 +1256,7 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
        { STB0899_TSCFGH                , 0x0c },
        { STB0899_TSCFGM                , 0x00 },
        { STB0899_TSCFGL                , 0x0c },
-       { STB0899_TSOUT                 , 0x0d }, /* 0x0d for CAM */
+       { STB0899_TSOUT                 , 0x4d }, /* 0x0d for CAM */
        { STB0899_RSSYNCDEL             , 0x00 },
        { STB0899_TSINHDELH             , 0x02 },
        { STB0899_TSINHDELM             , 0x00 },
index 6013668..9c92f9d 100644 (file)
@@ -456,7 +456,7 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte
        // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled
        // use 0x15 to track VPE  interrupts - increase by 1 every vpeirq() is called
        saa7146_write(dev, EC1SSR, (0x03<<2) | 3 );
-       // set event counter 1 treshold to maximum allowed value        (rEC p55)
+       // set event counter 1 threshold to maximum allowed value        (rEC p55)
        saa7146_write(dev, ECT1R,  0x3fff );
 #endif
        // Fix VSYNC level
index d91e063..53baccb 100644 (file)
@@ -1198,7 +1198,7 @@ static int ttusb_init_rc( struct ttusb_dec *dec)
        int err;
 
        usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys));
-       strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys));
+       strlcat(dec->rc_phys, "/input0", sizeof(dec->rc_phys));
 
        input_dev = input_allocate_device();
        if (!input_dev)
index b134553..4c2b8a2 100644 (file)
@@ -197,7 +197,8 @@ config RADIO_MAESTRO
 
 config RADIO_MIROPCM20
        tristate "miroSOUND PCM20 radio"
-       depends on ISA && VIDEO_V4L2
+       depends on ISA && VIDEO_V4L2 && SND
+       select SND_ISA
        select SND_MIRO
        ---help---
          Choose Y here if you have this FM radio card. You also need to enable
@@ -419,4 +420,16 @@ config RADIO_TEA5764_XTAL
          Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N
          here if TEA5764 reference frequency is connected in FREQIN.
 
+config RADIO_TEF6862
+       tristate "TEF6862 Car Radio Enhanced Selectivity Tuner"
+       depends on I2C && VIDEO_V4L2
+       ---help---
+         Say Y here if you want to use the TEF6862 Car Radio Enhanced
+         Selectivity Tuner, found for instance on the Russellville development
+         board. On the russellville the device is connected to internal
+         timberdale I2C bus.
+
+         To compile this driver as a module, choose M here: the
+         module will be called TEF6862.
+
 endif # RADIO_ADAPTERS
index 8a63d54..01922ad 100644 (file)
@@ -23,5 +23,6 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o
 obj-$(CONFIG_RADIO_SI470X) += si470x/
 obj-$(CONFIG_USB_MR800) += radio-mr800.o
 obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
+obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
 
 EXTRA_CFLAGS += -Isound
index a123908..949f605 100644 (file)
@@ -28,7 +28,7 @@
  * http://av-usbradio.sourceforge.net/index.php
  * http://sourceforge.net/projects/av-usbradio/
  * Latest release of theirs project was in 2005.
- * Probably, this driver could be improved trough using their
+ * Probably, this driver could be improved through using their
  * achievements (specifications given).
  * Also, Faidon Liambotis <paravoid@debian.org> wrote nice driver for this radio
  * in 2007. He allowed to use his driver to improve current mr800 radio driver.
@@ -85,6 +85,9 @@ MODULE_LICENSE("GPL");
 #define amradio_dev_warn(dev, fmt, arg...)                             \
                dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
 
+#define amradio_dev_err(dev, fmt, arg...) \
+               dev_err(dev, MR800_DRIVER_NAME " - " fmt, ##arg)
+
 /* Probably USB_TIMEOUT should be modified in module parameter */
 #define BUFFER_LENGTH 8
 #define USB_TIMEOUT 500
@@ -129,18 +132,20 @@ static int usb_amradio_resume(struct usb_interface *intf);
 struct amradio_device {
        /* reference to USB and video device */
        struct usb_device *usbdev;
-       struct video_device *videodev;
+       struct usb_interface *intf;
+       struct video_device videodev;
        struct v4l2_device v4l2_dev;
 
        unsigned char *buffer;
        struct mutex lock;      /* buffer locking */
        int curfreq;
        int stereo;
-       int users;
-       int removed;
        int muted;
+       int initialized;
 };
 
+#define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev)
+
 /* USB Device ID List */
 static struct usb_device_id usb_amradio_device_table[] = {
        {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
@@ -159,7 +164,7 @@ static struct usb_driver usb_amradio_driver = {
        .resume                 = usb_amradio_resume,
        .reset_resume           = usb_amradio_resume,
        .id_table               = usb_amradio_device_table,
-       .supports_autosuspend   = 0,
+       .supports_autosuspend   = 1,
 };
 
 /* switch on/off the radio. Send 8 bytes to device */
@@ -168,11 +173,7 @@ static int amradio_set_mute(struct amradio_device *radio, char argument)
        int retval;
        int size;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
-       mutex_lock(&radio->lock);
+       BUG_ON(!mutex_is_locked(&radio->lock));
 
        radio->buffer[0] = 0x00;
        radio->buffer[1] = 0x55;
@@ -187,14 +188,12 @@ static int amradio_set_mute(struct amradio_device *radio, char argument)
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
        if (retval < 0 || size != BUFFER_LENGTH) {
-               mutex_unlock(&radio->lock);
+               amradio_dev_warn(&radio->videodev.dev, "set mute failed\n");
                return retval;
        }
 
        radio->muted = argument;
 
-       mutex_unlock(&radio->lock);
-
        return retval;
 }
 
@@ -205,11 +204,7 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
        int size;
        unsigned short freq_send = 0x10 + (freq >> 3) / 25;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
-       mutex_lock(&radio->lock);
+       BUG_ON(!mutex_is_locked(&radio->lock));
 
        radio->buffer[0] = 0x00;
        radio->buffer[1] = 0x55;
@@ -223,10 +218,8 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
        retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
-       if (retval < 0 || size != BUFFER_LENGTH) {
-               mutex_unlock(&radio->lock);
-               return retval;
-       }
+       if (retval < 0 || size != BUFFER_LENGTH)
+               goto out_err;
 
        /* frequency is calculated from freq_send and placed in first 2 bytes */
        radio->buffer[0] = (freq_send >> 8) & 0xff;
@@ -240,13 +233,15 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
        retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
-       if (retval < 0 || size != BUFFER_LENGTH) {
-               mutex_unlock(&radio->lock);
-               return retval;
-       }
+       if (retval < 0 || size != BUFFER_LENGTH)
+               goto out_err;
 
-       mutex_unlock(&radio->lock);
+       radio->curfreq = freq;
+       goto out;
 
+out_err:
+       amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n");
+out:
        return retval;
 }
 
@@ -255,11 +250,7 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument)
        int retval;
        int size;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
-       mutex_lock(&radio->lock);
+       BUG_ON(!mutex_is_locked(&radio->lock));
 
        radio->buffer[0] = 0x00;
        radio->buffer[1] = 0x55;
@@ -274,14 +265,14 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument)
                (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
 
        if (retval < 0 || size != BUFFER_LENGTH) {
-               radio->stereo = -1;
-               mutex_unlock(&radio->lock);
+               amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n");
                return retval;
        }
 
-       radio->stereo = 1;
-
-       mutex_unlock(&radio->lock);
+       if (argument == WANT_STEREO)
+               radio->stereo = 1;
+       else
+               radio->stereo = 0;
 
        return retval;
 }
@@ -296,19 +287,19 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
        struct amradio_device *radio = usb_get_intfdata(intf);
 
        mutex_lock(&radio->lock);
-       radio->removed = 1;
+       radio->usbdev = NULL;
        mutex_unlock(&radio->lock);
 
        usb_set_intfdata(intf, NULL);
-       video_unregister_device(radio->videodev);
        v4l2_device_disconnect(&radio->v4l2_dev);
+       video_unregister_device(&radio->videodev);
 }
 
 /* vidioc_querycap - query device capabilities */
 static int vidioc_querycap(struct file *file, void *priv,
                                        struct v4l2_capability *v)
 {
-       struct amradio_device *radio = video_drvdata(file);
+       struct amradio_device *radio = file->private_data;
 
        strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
        strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
@@ -322,13 +313,9 @@ static int vidioc_querycap(struct file *file, void *priv,
 static int vidioc_g_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
+       struct amradio_device *radio = file->private_data;
        int retval;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
        if (v->index > 0)
                return -EINVAL;
 
@@ -341,9 +328,6 @@ static int vidioc_g_tuner(struct file *file, void *priv,
  * amradio_set_stereo shouldn't be here
  */
        retval = amradio_set_stereo(radio, WANT_STEREO);
-       if (retval < 0)
-               amradio_dev_warn(&radio->videodev->dev,
-                       "set stereo failed\n");
 
        strcpy(v->name, "FM");
        v->type = V4L2_TUNER_RADIO;
@@ -357,19 +341,16 @@ static int vidioc_g_tuner(struct file *file, void *priv,
                v->audmode = V4L2_TUNER_MODE_MONO;
        v->signal = 0xffff;     /* Can't get the signal strength, sad.. */
        v->afc = 0; /* Don't know what is this */
-       return 0;
+
+       return retval;
 }
 
 /* vidioc_s_tuner - set tuner attributes */
 static int vidioc_s_tuner(struct file *file, void *priv,
                                struct v4l2_tuner *v)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
-
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
+       struct amradio_device *radio = file->private_data;
+       int retval = -EINVAL;
 
        if (v->index > 0)
                return -EINVAL;
@@ -378,57 +359,33 @@ static int vidioc_s_tuner(struct file *file, void *priv,
        switch (v->audmode) {
        case V4L2_TUNER_MODE_MONO:
                retval = amradio_set_stereo(radio, WANT_MONO);
-               if (retval < 0)
-                       amradio_dev_warn(&radio->videodev->dev,
-                               "set mono failed\n");
                break;
        case V4L2_TUNER_MODE_STEREO:
                retval = amradio_set_stereo(radio, WANT_STEREO);
-               if (retval < 0)
-                       amradio_dev_warn(&radio->videodev->dev,
-                               "set stereo failed\n");
                break;
-       default:
-               return -EINVAL;
        }
 
-       return 0;
+       return retval;
 }
 
 /* vidioc_s_frequency - set tuner radio frequency */
 static int vidioc_s_frequency(struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
+       struct amradio_device *radio = file->private_data;
 
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
-
-       mutex_lock(&radio->lock);
-       radio->curfreq = f->frequency;
-       mutex_unlock(&radio->lock);
-
-       retval = amradio_setfreq(radio, radio->curfreq);
-       if (retval < 0)
-               amradio_dev_warn(&radio->videodev->dev,
-                       "set frequency failed\n");
-       return 0;
+       return amradio_setfreq(radio, f->frequency);
 }
 
 /* vidioc_g_frequency - get tuner radio frequency */
 static int vidioc_g_frequency(struct file *file, void *priv,
                                struct v4l2_frequency *f)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
+       struct amradio_device *radio = file->private_data;
 
        f->type = V4L2_TUNER_RADIO;
        f->frequency = radio->curfreq;
+
        return 0;
 }
 
@@ -448,17 +405,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
 static int vidioc_g_ctrl(struct file *file, void *priv,
                                struct v4l2_control *ctrl)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
+       struct amradio_device *radio = file->private_data;
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
                ctrl->value = radio->muted;
                return 0;
        }
+
        return -EINVAL;
 }
 
@@ -466,33 +420,20 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
 static int vidioc_s_ctrl(struct file *file, void *priv,
                                struct v4l2_control *ctrl)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
-
-       /* safety check */
-       if (radio->removed)
-               return -EIO;
+       struct amradio_device *radio = file->private_data;
+       int retval = -EINVAL;
 
        switch (ctrl->id) {
        case V4L2_CID_AUDIO_MUTE:
-               if (ctrl->value) {
+               if (ctrl->value)
                        retval = amradio_set_mute(radio, AMRADIO_STOP);
-                       if (retval < 0) {
-                               amradio_dev_warn(&radio->videodev->dev,
-                                       "amradio_stop failed\n");
-                               return -1;
-                       }
-               } else {
+               else
                        retval = amradio_set_mute(radio, AMRADIO_START);
-                       if (retval < 0) {
-                               amradio_dev_warn(&radio->videodev->dev,
-                                       "amradio_start failed\n");
-                               return -1;
-                       }
-               }
-               return 0;
+
+               break;
        }
-       return -EINVAL;
+
+       return retval;
 }
 
 /* vidioc_g_audio - get audio attributes */
@@ -531,75 +472,108 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
        return 0;
 }
 
-/* open device - amradio_start() and amradio_setfreq() */
-static int usb_amradio_open(struct file *file)
+static int usb_amradio_init(struct amradio_device *radio)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
        int retval;
 
-       lock_kernel();
+       retval = amradio_set_mute(radio, AMRADIO_STOP);
+       if (retval)
+               goto out_err;
 
-       radio->users = 1;
-       radio->muted = 1;
+       retval = amradio_set_stereo(radio, WANT_STEREO);
+       if (retval)
+               goto out_err;
 
-       retval = amradio_set_mute(radio, AMRADIO_START);
-       if (retval < 0) {
-               amradio_dev_warn(&radio->videodev->dev,
-                       "radio did not start up properly\n");
-               radio->users = 0;
-               unlock_kernel();
-               return -EIO;
+       radio->initialized = 1;
+       goto out;
+
+out_err:
+       amradio_dev_err(&radio->videodev.dev, "initialization failed\n");
+out:
+       return retval;
+}
+
+/* open device - amradio_start() and amradio_setfreq() */
+static int usb_amradio_open(struct file *file)
+{
+       struct amradio_device *radio = vdev_to_amradio(video_devdata(file));
+       int retval = 0;
+
+       mutex_lock(&radio->lock);
+
+       if (!radio->usbdev) {
+               retval = -EIO;
+               goto unlock;
        }
 
-       retval = amradio_set_stereo(radio, WANT_STEREO);
-       if (retval < 0)
-               amradio_dev_warn(&radio->videodev->dev,
-                       "set stereo failed\n");
+       file->private_data = radio;
+       retval = usb_autopm_get_interface(radio->intf);
+       if (retval)
+               goto unlock;
 
-       retval = amradio_setfreq(radio, radio->curfreq);
-       if (retval < 0)
-               amradio_dev_warn(&radio->videodev->dev,
-                       "set frequency failed\n");
+       if (unlikely(!radio->initialized)) {
+               retval = usb_amradio_init(radio);
+               if (retval)
+                       usb_autopm_put_interface(radio->intf);
+       }
 
-       unlock_kernel();
-       return 0;
+unlock:
+       mutex_unlock(&radio->lock);
+       return retval;
 }
 
 /*close device */
 static int usb_amradio_close(struct file *file)
 {
-       struct amradio_device *radio = video_get_drvdata(video_devdata(file));
-       int retval;
-
-       if (!radio)
-               return -ENODEV;
+       struct amradio_device *radio = file->private_data;
+       int retval = 0;
 
        mutex_lock(&radio->lock);
-       radio->users = 0;
+
+       if (!radio->usbdev)
+               retval = -EIO;
+       else
+               usb_autopm_put_interface(radio->intf);
+
        mutex_unlock(&radio->lock);
+       return retval;
+}
 
-       if (!radio->removed) {
-               retval = amradio_set_mute(radio, AMRADIO_STOP);
-               if (retval < 0)
-                       amradio_dev_warn(&radio->videodev->dev,
-                               "amradio_stop failed\n");
+static long usb_amradio_ioctl(struct file *file, unsigned int cmd,
+                               unsigned long arg)
+{
+       struct amradio_device *radio = file->private_data;
+       long retval = 0;
+
+       mutex_lock(&radio->lock);
+
+       if (!radio->usbdev) {
+               retval = -EIO;
+               goto unlock;
        }
 
-       return 0;
+       retval = video_ioctl2(file, cmd, arg);
+
+unlock:
+       mutex_unlock(&radio->lock);
+       return retval;
 }
 
 /* Suspend device - stop device. Need to be checked and fixed */
 static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct amradio_device *radio = usb_get_intfdata(intf);
-       int retval;
 
-       retval = amradio_set_mute(radio, AMRADIO_STOP);
-       if (retval < 0)
-               dev_warn(&intf->dev, "amradio_stop failed\n");
+       mutex_lock(&radio->lock);
+
+       if (!radio->muted && radio->initialized) {
+               amradio_set_mute(radio, AMRADIO_STOP);
+               radio->muted = 0;
+       }
 
        dev_info(&intf->dev, "going into suspend..\n");
 
+       mutex_unlock(&radio->lock);
        return 0;
 }
 
@@ -607,14 +581,26 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
 static int usb_amradio_resume(struct usb_interface *intf)
 {
        struct amradio_device *radio = usb_get_intfdata(intf);
-       int retval;
 
-       retval = amradio_set_mute(radio, AMRADIO_START);
-       if (retval < 0)
-               dev_warn(&intf->dev, "amradio_start failed\n");
+       mutex_lock(&radio->lock);
+
+       if (unlikely(!radio->initialized))
+               goto unlock;
+
+       if (radio->stereo)
+               amradio_set_stereo(radio, WANT_STEREO);
+       else
+               amradio_set_stereo(radio, WANT_MONO);
+
+       amradio_setfreq(radio, radio->curfreq);
 
+       if (!radio->muted)
+               amradio_set_mute(radio, AMRADIO_START);
+
+unlock:
        dev_info(&intf->dev, "coming out of suspend..\n");
 
+       mutex_unlock(&radio->lock);
        return 0;
 }
 
@@ -623,7 +609,7 @@ static const struct v4l2_file_operations usb_amradio_fops = {
        .owner          = THIS_MODULE,
        .open           = usb_amradio_open,
        .release        = usb_amradio_close,
-       .ioctl          = video_ioctl2,
+       .ioctl          = usb_amradio_ioctl,
 };
 
 static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
@@ -643,10 +629,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
 
 static void usb_amradio_video_device_release(struct video_device *videodev)
 {
-       struct amradio_device *radio = video_get_drvdata(videodev);
-
-       /* we call v4l to free radio->videodev */
-       video_device_release(videodev);
+       struct amradio_device *radio = vdev_to_amradio(videodev);
 
        v4l2_device_unregister(&radio->v4l2_dev);
 
@@ -660,70 +643,63 @@ static int usb_amradio_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
        struct amradio_device *radio;
-       struct v4l2_device *v4l2_dev;
-       int retval;
+       int retval = 0;
 
        radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL);
 
        if (!radio) {
                dev_err(&intf->dev, "kmalloc for amradio_device failed\n");
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err;
        }
 
        radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
 
        if (!radio->buffer) {
                dev_err(&intf->dev, "kmalloc for radio->buffer failed\n");
-               kfree(radio);
-               return -ENOMEM;
+               retval = -ENOMEM;
+               goto err_nobuf;
        }
 
-       v4l2_dev = &radio->v4l2_dev;
-       retval = v4l2_device_register(&intf->dev, v4l2_dev);
+       retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
        if (retval < 0) {
                dev_err(&intf->dev, "couldn't register v4l2_device\n");
-               kfree(radio->buffer);
-               kfree(radio);
-               return retval;
+               goto err_v4l2;
        }
 
-       radio->videodev = video_device_alloc();
-
-       if (!radio->videodev) {
-               dev_err(&intf->dev, "video_device_alloc failed\n");
-               kfree(radio->buffer);
-               kfree(radio);
-               return -ENOMEM;
-       }
+       strlcpy(radio->videodev.name, radio->v4l2_dev.name,
+               sizeof(radio->videodev.name));
+       radio->videodev.v4l2_dev = &radio->v4l2_dev;
+       radio->videodev.fops = &usb_amradio_fops;
+       radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops;
+       radio->videodev.release = usb_amradio_video_device_release;
 
-       strlcpy(radio->videodev->name, v4l2_dev->name, sizeof(radio->videodev->name));
-       radio->videodev->v4l2_dev = v4l2_dev;
-       radio->videodev->fops = &usb_amradio_fops;
-       radio->videodev->ioctl_ops = &usb_amradio_ioctl_ops;
-       radio->videodev->release = usb_amradio_video_device_release;
-
-       radio->removed = 0;
-       radio->users = 0;
        radio->usbdev = interface_to_usbdev(intf);
+       radio->intf = intf;
        radio->curfreq = 95.16 * FREQ_MUL;
-       radio->stereo = -1;
 
        mutex_init(&radio->lock);
 
-       video_set_drvdata(radio->videodev, radio);
+       video_set_drvdata(&radio->videodev, radio);
 
-       retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
+       retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
+                                       radio_nr);
        if (retval < 0) {
                dev_err(&intf->dev, "could not register video device\n");
-               video_device_release(radio->videodev);
-               v4l2_device_unregister(v4l2_dev);
-               kfree(radio->buffer);
-               kfree(radio);
-               return -EIO;
+               goto err_vdev;
        }
 
        usb_set_intfdata(intf, radio);
        return 0;
+
+err_vdev:
+       v4l2_device_unregister(&radio->v4l2_dev);
+err_v4l2:
+       kfree(radio->buffer);
+err_nobuf:
+       kfree(radio);
+err:
+       return retval;
 }
 
 static int __init amradio_init(void)
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
new file mode 100644 (file)
index 0000000..6e607ff
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#define DRIVER_NAME "tef6862"
+
+#define FREQ_MUL 16000
+
+#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10)
+#define TEF6862_HI_FREQ (108 * FREQ_MUL)
+
+/* Write mode sub addresses */
+#define WM_SUB_BANDWIDTH       0x0
+#define WM_SUB_PLLM            0x1
+#define WM_SUB_PLLL            0x2
+#define WM_SUB_DAA             0x3
+#define WM_SUB_AGC             0x4
+#define WM_SUB_BAND            0x5
+#define WM_SUB_CONTROL         0x6
+#define WM_SUB_LEVEL           0x7
+#define WM_SUB_IFCF            0x8
+#define WM_SUB_IFCAP           0x9
+#define WM_SUB_ACD             0xA
+#define WM_SUB_TEST            0xF
+
+/* Different modes of the MSA register */
+#define MODE_BUFFER            0x0
+#define MODE_PRESET            0x1
+#define MODE_SEARCH            0x2
+#define MODE_AF_UPDATE         0x3
+#define MODE_JUMP              0x4
+#define MODE_CHECK             0x5
+#define MODE_LOAD              0x6
+#define MODE_END               0x7
+#define MODE_SHIFT             5
+
+struct tef6862_state {
+       struct v4l2_subdev sd;
+       unsigned long freq;
+};
+
+static inline struct tef6862_state *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct tef6862_state, sd);
+}
+
+static u16 tef6862_sigstr(struct i2c_client *client)
+{
+       u8 buf[4];
+       int err = i2c_master_recv(client, buf, sizeof(buf));
+       if (err == sizeof(buf))
+               return buf[3] << 8;
+       return 0;
+}
+
+static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
+{
+       if (v->index > 0)
+               return -EINVAL;
+
+       /* only support FM for now */
+       strlcpy(v->name, "FM", sizeof(v->name));
+       v->type = V4L2_TUNER_RADIO;
+       v->rangelow = TEF6862_LO_FREQ;
+       v->rangehigh = TEF6862_HI_FREQ;
+       v->rxsubchans = V4L2_TUNER_SUB_MONO;
+       v->capability = V4L2_TUNER_CAP_LOW;
+       v->audmode = V4L2_TUNER_MODE_STEREO;
+       v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd));
+
+       return 0;
+}
+
+static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v)
+{
+       return v->index ? -EINVAL : 0;
+}
+
+static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+       struct tef6862_state *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       u16 pll;
+       u8 i2cmsg[3];
+       int err;
+
+       if (f->tuner != 0)
+               return -EINVAL;
+
+       pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL;
+       i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM;
+       i2cmsg[1] = (pll >> 8) & 0xff;
+       i2cmsg[2] = pll & 0xff;
+
+       err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg));
+       if (!err)
+               state->freq = f->frequency;
+       return err;
+}
+
+static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+{
+       struct tef6862_state *state = to_state(sd);
+
+       if (f->tuner != 0)
+               return -EINVAL;
+       f->type = V4L2_TUNER_RADIO;
+       f->frequency = state->freq;
+       return 0;
+}
+
+static int tef6862_g_chip_ident(struct v4l2_subdev *sd,
+       struct v4l2_dbg_chip_ident *chip)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0);
+}
+
+static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = {
+       .g_tuner = tef6862_g_tuner,
+       .s_tuner = tef6862_s_tuner,
+       .s_frequency = tef6862_s_frequency,
+       .g_frequency = tef6862_g_frequency,
+};
+
+static const struct v4l2_subdev_core_ops tef6862_core_ops = {
+       .g_chip_ident = tef6862_g_chip_ident,
+};
+
+static const struct v4l2_subdev_ops tef6862_ops = {
+       .core = &tef6862_core_ops,
+       .tuner = &tef6862_tuner_ops,
+};
+
+/*
+ * Generic i2c probe
+ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+ */
+
+static int __devinit tef6862_probe(struct i2c_client *client,
+                       const struct i2c_device_id *id)
+{
+       struct tef6862_state *state;
+       struct v4l2_subdev *sd;
+
+       /* Check if the adapter supports the needed features */
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -EIO;
+
+       v4l_info(client, "chip found @ 0x%02x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+
+       state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL);
+       if (state == NULL)
+               return -ENOMEM;
+       state->freq = TEF6862_LO_FREQ;
+
+       sd = &state->sd;
+       v4l2_i2c_subdev_init(sd, client, &tef6862_ops);
+
+       return 0;
+}
+
+static int __devexit tef6862_remove(struct i2c_client *client)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+       v4l2_device_unregister_subdev(sd);
+       kfree(to_state(sd));
+       return 0;
+}
+
+static const struct i2c_device_id tef6862_id[] = {
+       {DRIVER_NAME, 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tef6862_id);
+
+static struct i2c_driver tef6862_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = DRIVER_NAME,
+       },
+       .probe          = tef6862_probe,
+       .remove         = tef6862_remove,
+       .id_table       = tef6862_id,
+};
+
+static __init int tef6862_init(void)
+{
+       return i2c_add_driver(&tef6862_driver);
+}
+
+static __exit void tef6862_exit(void)
+{
+       i2c_del_driver(&tef6862_driver);
+}
+
+module_init(tef6862_init);
+module_exit(tef6862_exit);
+
+MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
+MODULE_AUTHOR("Mocean Laboratories");
+MODULE_LICENSE("GPL v2");
+
index e6186b3..9dc74c9 100644 (file)
@@ -600,7 +600,7 @@ source "drivers/media/video/bt8xx/Kconfig"
 
 config VIDEO_PMS
        tristate "Mediavision Pro Movie Studio Video For Linux"
-       depends on ISA && VIDEO_V4L1
+       depends on ISA && VIDEO_V4L2
        help
          Say Y if you have such a thing.
 
@@ -847,6 +847,12 @@ config SOC_CAMERA_MT9V022
        help
          This driver supports MT9V022 cameras from Micron
 
+config SOC_CAMERA_RJ54N1
+       tristate "rj54n1cb0c support"
+       depends on SOC_CAMERA && I2C
+       help
+         This is a rj54n1cb0c video driver
+
 config SOC_CAMERA_TW9910
        tristate "tw9910 support"
        depends on SOC_CAMERA && I2C
@@ -865,6 +871,12 @@ config SOC_CAMERA_OV772X
        help
          This is a ov772x camera driver
 
+config SOC_CAMERA_OV9640
+       tristate "ov9640 camera support"
+       depends on SOC_CAMERA && I2C
+       help
+         This is a ov9640 camera driver
+
 config MX1_VIDEO
        bool
 
@@ -939,9 +951,14 @@ source "drivers/media/video/usbvideo/Kconfig"
 source "drivers/media/video/et61x251/Kconfig"
 
 config VIDEO_OVCAMCHIP
-       tristate "OmniVision Camera Chip support"
+       tristate "OmniVision Camera Chip support (DEPRECATED)"
        depends on I2C && VIDEO_V4L1
        ---help---
+         This driver is DEPRECATED please use the gspca ov519 module
+         instead. Note that for the ov511 / ov518 support of the gspca module
+         you need atleast version 0.6.0 of libv4l and for the w9968cf
+         atleast version 0.6.3 of libv4l.
+
          Support for the OmniVision OV6xxx and OV7xxx series of camera chips.
          This driver is intended to be used with the ov511 and w9968cf USB
          camera drivers.
@@ -950,9 +967,13 @@ config VIDEO_OVCAMCHIP
          module will be called ovcamchip.
 
 config USB_W9968CF
-       tristate "USB W996[87]CF JPEG Dual Mode Camera support"
+       tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)"
        depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP
        ---help---
+         This driver is DEPRECATED please use the gspca ov519 module
+         instead. Note that for the w9968cf support of the gspca module
+         you need atleast version 0.6.3 of libv4l.
+
          Say Y here if you want support for cameras based on OV681 or
          Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
 
@@ -995,9 +1016,13 @@ config USB_SE401
 source "drivers/media/video/sn9c102/Kconfig"
 
 config USB_STV680
-       tristate "USB STV680 (Pencam) Camera support"
+       tristate "USB STV680 (Pencam) Camera support (DEPRECATED)"
        depends on VIDEO_V4L1
        ---help---
+         This driver is DEPRECATED please use the gspca stv0680 module
+         instead. Note that for the gspca stv0680 module you need
+         atleast version 0.6.3 of libv4l.
+
          Say Y here if you want to connect this type of camera to your
          computer's USB port. This includes the Pencam line of cameras.
          See <file:Documentation/video4linux/stv680.txt> for more information
index e541932..7a2dcc3 100644 (file)
@@ -77,6 +77,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111)      += mt9m111.o
 obj-$(CONFIG_SOC_CAMERA_MT9T031)       += mt9t031.o
 obj-$(CONFIG_SOC_CAMERA_MT9V022)       += mt9v022.o
 obj-$(CONFIG_SOC_CAMERA_OV772X)                += ov772x.o
+obj-$(CONFIG_SOC_CAMERA_OV9640)                += ov9640.o
+obj-$(CONFIG_SOC_CAMERA_RJ54N1)                += rj54n1cb0c.o
 obj-$(CONFIG_SOC_CAMERA_TW9910)                += tw9910.o
 
 # And now the v4l2 drivers:
index 1b3cbd0..0826f0d 100644 (file)
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-chip-ident.h>
+#include <linux/mutex.h>
 
 #define DRIVER_NAME "adv7180"
 
-#define ADV7180_INPUT_CONTROL_REG      0x00
-#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM      0x00
-#define ADV7180_AUTODETECT_ENABLE_REG  0x07
-#define ADV7180_AUTODETECT_DEFAULT     0x7f
-
-
-#define ADV7180_STATUS1_REG 0x10
-#define ADV7180_STATUS1_AUTOD_MASK 0x70
+#define ADV7180_INPUT_CONTROL_REG                      0x00
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM   0x00
+#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10
+#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM    0x20
+#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM    0x30
+#define ADV7180_INPUT_CONTROL_NTSC_J                   0x40
+#define ADV7180_INPUT_CONTROL_NTSC_M                   0x50
+#define ADV7180_INPUT_CONTROL_PAL60                    0x60
+#define ADV7180_INPUT_CONTROL_NTSC_443                 0x70
+#define ADV7180_INPUT_CONTROL_PAL_BG                   0x80
+#define ADV7180_INPUT_CONTROL_PAL_N                    0x90
+#define ADV7180_INPUT_CONTROL_PAL_M                    0xa0
+#define ADV7180_INPUT_CONTROL_PAL_M_PED                        0xb0
+#define ADV7180_INPUT_CONTROL_PAL_COMB_N               0xc0
+#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED           0xd0
+#define ADV7180_INPUT_CONTROL_PAL_SECAM                        0xe0
+#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED            0xf0
+
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG            0x04
+#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS                0xC5
+
+#define ADV7180_AUTODETECT_ENABLE_REG                  0x07
+#define ADV7180_AUTODETECT_DEFAULT                     0x7f
+
+#define ADV7180_ADI_CTRL_REG                           0x0e
+#define ADV7180_ADI_CTRL_IRQ_SPACE                     0x20
+
+#define ADV7180_STATUS1_REG                            0x10
+#define ADV7180_STATUS1_IN_LOCK                0x01
+#define ADV7180_STATUS1_AUTOD_MASK     0x70
 #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00
 #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
 #define ADV7180_STATUS1_AUTOD_PAL_M    0x20
 #define ADV7180_IDENT_REG 0x11
 #define ADV7180_ID_7180 0x18
 
+#define ADV7180_ICONF1_ADI             0x40
+#define ADV7180_ICONF1_ACTIVE_LOW      0x01
+#define ADV7180_ICONF1_PSYNC_ONLY      0x10
+#define ADV7180_ICONF1_ACTIVE_TO_CLR   0xC0
+
+#define ADV7180_IRQ1_LOCK      0x01
+#define ADV7180_IRQ1_UNLOCK    0x02
+#define ADV7180_ISR1_ADI       0x42
+#define ADV7180_ICR1_ADI       0x43
+#define ADV7180_IMR1_ADI       0x44
+#define ADV7180_IMR2_ADI       0x48
+#define ADV7180_IRQ3_AD_CHANGE 0x08
+#define ADV7180_ISR3_ADI       0x4A
+#define ADV7180_ICR3_ADI       0x4B
+#define ADV7180_IMR3_ADI       0x4C
+#define ADV7180_IMR4_ADI       0x50
 
 struct adv7180_state {
-       struct v4l2_subdev sd;
+       struct v4l2_subdev      sd;
+       struct work_struct      work;
+       struct mutex            mutex; /* mutual excl. when accessing chip */
+       int                     irq;
+       v4l2_std_id             curr_norm;
+       bool                    autodetect;
 };
 
-static v4l2_std_id determine_norm(struct i2c_client *client)
+static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
 {
-       u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
-
        switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
        case ADV7180_STATUS1_AUTOD_NTSM_M_J:
-               return V4L2_STD_NTSC_M_JP;
+               return V4L2_STD_NTSC;
        case ADV7180_STATUS1_AUTOD_NTSC_4_43:
                return V4L2_STD_NTSC_443;
        case ADV7180_STATUS1_AUTOD_PAL_M:
@@ -81,6 +123,53 @@ static v4l2_std_id determine_norm(struct i2c_client *client)
        }
 }
 
+static int v4l2_std_to_adv7180(v4l2_std_id std)
+{
+       if (std == V4L2_STD_PAL_60)
+               return ADV7180_INPUT_CONTROL_PAL60;
+       if (std == V4L2_STD_NTSC_443)
+               return ADV7180_INPUT_CONTROL_NTSC_443;
+       if (std == V4L2_STD_PAL_N)
+               return ADV7180_INPUT_CONTROL_PAL_N;
+       if (std == V4L2_STD_PAL_M)
+               return ADV7180_INPUT_CONTROL_PAL_M;
+       if (std == V4L2_STD_PAL_Nc)
+               return ADV7180_INPUT_CONTROL_PAL_COMB_N;
+
+       if (std & V4L2_STD_PAL)
+               return ADV7180_INPUT_CONTROL_PAL_BG;
+       if (std & V4L2_STD_NTSC)
+               return ADV7180_INPUT_CONTROL_NTSC_M;
+       if (std & V4L2_STD_SECAM)
+               return ADV7180_INPUT_CONTROL_PAL_SECAM;
+
+       return -EINVAL;
+}
+
+static u32 adv7180_status_to_v4l2(u8 status1)
+{
+       if (!(status1 & ADV7180_STATUS1_IN_LOCK))
+               return V4L2_IN_ST_NO_SIGNAL;
+
+       return 0;
+}
+
+static int __adv7180_status(struct i2c_client *client, u32 *status,
+       v4l2_std_id *std)
+{
+       int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
+
+       if (status1 < 0)
+               return status1;
+
+       if (status)
+               *status = adv7180_status_to_v4l2(status1);
+       if (std)
+               *std = adv7180_std_to_v4l2(status1);
+
+       return 0;
+}
+
 static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
 {
        return container_of(sd, struct adv7180_state, sd);
@@ -88,10 +177,31 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
 
 static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
 {
-       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       struct adv7180_state *state = to_state(sd);
+       int err = mutex_lock_interruptible(&state->mutex);
+       if (err)
+               return err;
+
+       /* when we are interrupt driven we know the state */
+       if (!state->autodetect || state->irq > 0)
+               *std = state->curr_norm;
+       else
+               err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std);
+
+       mutex_unlock(&state->mutex);
+       return err;
+}
 
-       *std = determine_norm(client);
-       return 0;
+static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+       struct adv7180_state *state = to_state(sd);
+       int ret = mutex_lock_interruptible(&state->mutex);
+       if (ret)
+               return ret;
+
+       ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL);
+       mutex_unlock(&state->mutex);
+       return ret;
 }
 
 static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
@@ -102,12 +212,51 @@ static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
        return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0);
 }
 
+static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+       struct adv7180_state *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret = mutex_lock_interruptible(&state->mutex);
+       if (ret)
+               return ret;
+
+       /* all standards -> autodetect */
+       if (std == V4L2_STD_ALL) {
+               ret = i2c_smbus_write_byte_data(client,
+                       ADV7180_INPUT_CONTROL_REG,
+                       ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+               if (ret < 0)
+                       goto out;
+
+               __adv7180_status(client, NULL, &state->curr_norm);
+               state->autodetect = true;
+       } else {
+               ret = v4l2_std_to_adv7180(std);
+               if (ret < 0)
+                       goto out;
+
+               ret = i2c_smbus_write_byte_data(client,
+                       ADV7180_INPUT_CONTROL_REG, ret);
+               if (ret < 0)
+                       goto out;
+
+               state->curr_norm = std;
+               state->autodetect = false;
+       }
+       ret = 0;
+out:
+       mutex_unlock(&state->mutex);
+       return ret;
+}
+
 static const struct v4l2_subdev_video_ops adv7180_video_ops = {
        .querystd = adv7180_querystd,
+       .g_input_status = adv7180_g_input_status,
 };
 
 static const struct v4l2_subdev_core_ops adv7180_core_ops = {
        .g_chip_ident = adv7180_g_chip_ident,
+       .s_std = adv7180_s_std,
 };
 
 static const struct v4l2_subdev_ops adv7180_ops = {
@@ -115,12 +264,45 @@ static const struct v4l2_subdev_ops adv7180_ops = {
        .video = &adv7180_video_ops,
 };
 
+static void adv7180_work(struct work_struct *work)
+{
+       struct adv7180_state *state = container_of(work, struct adv7180_state,
+               work);
+       struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+       u8 isr3;
+
+       mutex_lock(&state->mutex);
+       i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+               ADV7180_ADI_CTRL_IRQ_SPACE);
+       isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
+       /* clear */
+       i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
+       i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0);
+
+       if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
+               __adv7180_status(client, NULL, &state->curr_norm);
+       mutex_unlock(&state->mutex);
+
+       enable_irq(state->irq);
+}
+
+static irqreturn_t adv7180_irq(int irq, void *devid)
+{
+       struct adv7180_state *state = devid;
+
+       schedule_work(&state->work);
+
+       disable_irq_nosync(state->irq);
+
+       return IRQ_HANDLED;
+}
+
 /*
  * Generic i2c probe
  * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
  */
 
-static int adv7180_probe(struct i2c_client *client,
+static __devinit int adv7180_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
        struct adv7180_state *state;
@@ -135,32 +317,111 @@ static int adv7180_probe(struct i2c_client *client,
                        client->addr << 1, client->adapter->name);
 
        state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
-       if (state == NULL)
-               return -ENOMEM;
+       if (state == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       state->irq = client->irq;
+       INIT_WORK(&state->work, adv7180_work);
+       mutex_init(&state->mutex);
+       state->autodetect = true;
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
 
        /* Initialize adv7180 */
-       /* enable autodetection */
+       /* Enable autodetection */
        ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
-               ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM);
-       if (ret > 0)
-               ret = i2c_smbus_write_byte_data(client,
-                       ADV7180_AUTODETECT_ENABLE_REG,
-                       ADV7180_AUTODETECT_DEFAULT);
-       if (ret < 0) {
-               printk(KERN_ERR DRIVER_NAME
-                       ": Failed to communicate to chip: %d\n", ret);
-               return ret;
+               ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+       if (ret < 0)
+               goto err_unreg_subdev;
+
+       ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG,
+               ADV7180_AUTODETECT_DEFAULT);
+       if (ret < 0)
+               goto err_unreg_subdev;
+
+       /* ITU-R BT.656-4 compatible */
+       ret = i2c_smbus_write_byte_data(client,
+               ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+               ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+       if (ret < 0)
+               goto err_unreg_subdev;
+
+       /* read current norm */
+       __adv7180_status(client, NULL, &state->curr_norm);
+
+       /* register for interrupts */
+       if (state->irq > 0) {
+               ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
+                       state);
+               if (ret)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+                       ADV7180_ADI_CTRL_IRQ_SPACE);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               /* config the Interrupt pin to be active low */
+               ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
+                       ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               /* enable AD change interrupts interrupts */
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
+                       ADV7180_IRQ3_AD_CHANGE);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
+
+               ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
+                       0);
+               if (ret < 0)
+                       goto err_unreg_subdev;
        }
 
        return 0;
+
+err_unreg_subdev:
+       mutex_destroy(&state->mutex);
+       v4l2_device_unregister_subdev(sd);
+       kfree(state);
+err:
+       printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret);
+       return ret;
 }
 
-static int adv7180_remove(struct i2c_client *client)
+static __devexit int adv7180_remove(struct i2c_client *client)
 {
        struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct adv7180_state *state = to_state(sd);
+
+       if (state->irq > 0) {
+               free_irq(client->irq, state);
+               if (cancel_work_sync(&state->work)) {
+                       /*
+                        * Work was pending, therefore we need to enable
+                        * IRQ here to balance the disable_irq() done in the
+                        * interrupt handler.
+                        */
+                       enable_irq(state->irq);
+               }
+       }
 
+       mutex_destroy(&state->mutex);
        v4l2_device_unregister_subdev(sd);
        kfree(to_state(sd));
        return 0;
@@ -179,7 +440,7 @@ static struct i2c_driver adv7180_driver = {
                .name   = DRIVER_NAME,
        },
        .probe          = adv7180_probe,
-       .remove         = adv7180_remove,
+       .remove         = __devexit_p(adv7180_remove),
        .id_table       = adv7180_id,
 };
 
index 51527d7..1485aee 100644 (file)
@@ -830,7 +830,7 @@ static int au0828_v4l2_close(struct file *filp)
                au0828_uninit_isoc(dev);
 
                /* Save some power by putting tuner to sleep */
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
 
                /* When close the device, set the usb intf0 into alt0 to free
                   USB bandwidth */
index f9330e3..5bb0f9e 100644 (file)
@@ -299,7 +299,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd,
 
        v4l2_dbg(1, debug, sd, "set input %x\n", input);
 
-       if (input < 0 || input > 7)
+       if (input > 7)
                return -EINVAL;
 
        if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
index ebd51af..84a957e 100644 (file)
@@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv)
 
        if ((ir->mask_keydown  &&  (0 != (gpio & ir->mask_keydown))) ||
            (ir->mask_keyup    &&  (0 == (gpio & ir->mask_keyup)))) {
-               ir_input_keydown(ir->dev,&ir->ir,data,data);
+               ir_input_keydown(ir->dev, &ir->ir, data);
        } else {
                /* HACK: Probably, ir->mask_keydown is missing
                   for this board */
                if (btv->c.type == BTTV_BOARD_WINFAST2000)
-                       ir_input_keydown(ir->dev, &ir->ir, data, data);
+                       ir_input_keydown(ir->dev, &ir->ir, data);
 
                ir_input_nokey(ir->dev,&ir->ir);
        }
@@ -104,7 +104,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
                        gpio, data,
                        (gpio & ir->mask_keyup) ? " up" : "up/down");
 
-               ir_input_keydown(ir->dev, &ir->ir, data, data);
+               ir_input_keydown(ir->dev, &ir->ir, data);
                if (keyup)
                        ir_input_nokey(ir->dev, &ir->ir);
        } else {
@@ -118,7 +118,7 @@ static void ir_enltv_handle_key(struct bttv *btv)
                if (keyup)
                        ir_input_nokey(ir->dev, &ir->ir);
                else
-                       ir_input_keydown(ir->dev, &ir->ir, data, data);
+                       ir_input_keydown(ir->dev, &ir->ir, data);
        }
 
        ir->last_gpio = data | keyup;
@@ -368,7 +368,10 @@ int bttv_input_init(struct bttv *btv)
        snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
                 pci_name(btv->c.pci));
 
-       ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       if (err < 0)
+               goto err_out_free;
+
        input_dev->name = ir->name;
        input_dev->phys = ir->phys;
        input_dev->id.bustype = BUS_PCI;
@@ -400,6 +403,7 @@ int bttv_input_init(struct bttv *btv)
        bttv_ir_stop(btv);
        btv->remote = NULL;
  err_out_free:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
        kfree(ir);
        return err;
@@ -411,6 +415,7 @@ void bttv_input_fini(struct bttv *btv)
                return;
 
        bttv_ir_stop(btv);
+       ir_input_free(btv->remote->dev);
        input_unregister_device(btv->remote->dev);
        kfree(btv->remote);
        btv->remote = NULL;
index 536dedb..4392c76 100644 (file)
@@ -99,10 +99,8 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
                             or_value);
 }
 
-static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
+static void cx18_av_init(struct cx18 *cx)
 {
-       struct cx18 *cx = v4l2_get_subdevdata(sd);
-
        /*
         * The crystal freq used in calculations in this driver will be
         * 28.636360 MHz.
@@ -125,7 +123,6 @@ static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
 
        /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
        cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
-       return 0;
 }
 
 static void cx18_av_initialize(struct v4l2_subdev *sd)
@@ -198,7 +195,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd)
        cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000);
 
        /* Setup the Video and and Aux/Audio PLLs */
-       cx18_av_init(sd, 0);
+       cx18_av_init(cx);
 
        /* set video to auto-detect */
        /* Clear bits 11-12 to enable slow locking mode.  Set autodetect mode */
@@ -1355,7 +1352,6 @@ static int cx18_av_s_register(struct v4l2_subdev *sd,
 static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
        .g_chip_ident = cx18_av_g_chip_ident,
        .log_status = cx18_av_log_status,
-       .init = cx18_av_init,
        .load_fw = cx18_av_load_fw,
        .reset = cx18_av_reset,
        .queryctrl = cx18_av_queryctrl,
@@ -1399,6 +1395,7 @@ int cx18_av_probe(struct cx18 *cx)
 {
        struct cx18_av_state *state = &cx->av_state;
        struct v4l2_subdev *sd;
+       int err;
 
        state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
        state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
@@ -1417,5 +1414,8 @@ int cx18_av_probe(struct cx18 *cx)
        snprintf(sd->name, sizeof(sd->name),
                 "%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
        sd->grp_id = CX18_HW_418_AV;
-       return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+       err = v4l2_device_register_subdev(&cx->v4l2_dev, sd);
+       if (!err)
+               cx18_av_init(cx);
+       return err;
 }
index 9b84a0c..cafb7e9 100644 (file)
@@ -294,7 +294,7 @@ struct cx18_av_state {
 #define CXADEC_QAM_CONST_DEC       0x924
 #define CXADEC_QAM_ROTATOR_FREQ    0x948
 
-/* Bit defintions / settings used in Mako Audio */
+/* Bit definitions / settings used in Mako Audio */
 #define CXADEC_PREF_MODE_MONO_LANGA        0
 #define CXADEC_PREF_MODE_MONO_LANGB        1
 #define CXADEC_PREF_MODE_MONO_LANGC        2
index 444e3c7..af3d716 100644 (file)
@@ -34,6 +34,9 @@
 #define CX18_HW_Z8F0811_IR_HAUP        (CX18_HW_Z8F0811_IR_RX_HAUP | \
                                 CX18_HW_Z8F0811_IR_TX_HAUP)
 
+#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \
+                       CX18_HW_Z8F0811_IR_TX_HAUP)
+
 /* video inputs */
 #define        CX18_CARD_INPUT_VID_TUNER       1
 #define        CX18_CARD_INPUT_SVIDEO1         2
index 6dd51e2..7f65a47 100644 (file)
@@ -87,7 +87,6 @@ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
 static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
 static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
 static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
-/* VBI bufsize based on standards supported by card tuner for now */
 static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
 
 static int enc_ts_bufs = -1;
@@ -128,7 +127,6 @@ module_param(enc_ts_bufsize, int, 0644);
 module_param(enc_mpg_bufsize, int, 0644);
 module_param(enc_idx_bufsize, int, 0644);
 module_param(enc_yuv_bufsize, int, 0644);
-/* VBI bufsize based on standards supported by card tuner for now */
 module_param(enc_pcm_bufsize, int, 0644);
 
 module_param(enc_ts_bufs, int, 0644);
@@ -211,7 +209,9 @@ MODULE_PARM_DESC(enc_yuv_buffers,
                 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
 MODULE_PARM_DESC(enc_yuv_bufsize,
                 "Size of an encoder YUV buffer (kB)\n"
-                "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE));
+                "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+                "\t\t\t(multiples of size required for 32 screen lines)\n"
+                "\t\t\tDefault: 102");
 MODULE_PARM_DESC(enc_yuv_bufs,
                 "Number of encoder YUV buffers\n"
                 "\t\t\tDefault is computed from other enc_yuv_* parameters");
@@ -220,7 +220,7 @@ MODULE_PARM_DESC(enc_vbi_buffers,
                 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
 MODULE_PARM_DESC(enc_vbi_bufs,
                 "Number of encoder VBI buffers\n"
-                "\t\t\tDefault is computed from enc_vbi_buffers & tuner std");
+                "\t\t\tDefault is computed from enc_vbi_buffers");
 MODULE_PARM_DESC(enc_pcm_buffers,
                 "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
                 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
@@ -499,10 +499,27 @@ static void cx18_process_options(struct cx18 *cx)
                        continue;
                }
                /*
+                * YUV is a special case where the stream_buf_size needs to be
+                * an integral multiple of 33.75 kB (storage for 32 screens
+                * lines to maintain alignment in case of lost buffers
+                */
+               if (i == CX18_ENC_STREAM_TYPE_YUV) {
+                       cx->stream_buf_size[i] *= 1024;
+                       cx->stream_buf_size[i] -=
+                          (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+
+                       if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+                               cx->stream_buf_size[i] =
+                                               CX18_UNIT_ENC_YUV_BUFSIZE;
+               }
+               /*
+                * YUV is a special case where the stream_buf_size is
+                * now in bytes.
                 * VBI is a special case where the stream_buf_size is fixed
                 * and already in bytes
                 */
-               if (i == CX18_ENC_STREAM_TYPE_VBI) {
+               if (i == CX18_ENC_STREAM_TYPE_VBI ||
+                   i == CX18_ENC_STREAM_TYPE_YUV) {
                        if (cx->stream_buffers[i] < 0) {
                                cx->stream_buffers[i] =
                                        cx->options.megabytes[i] * 1024 * 1024
@@ -513,18 +530,24 @@ static void cx18_process_options(struct cx18 *cx)
                                        cx->stream_buffers[i]
                                        * cx->stream_buf_size[i]/(1024 * 1024);
                        }
-                       continue;
-               }
-               /* All other streams have stream_buf_size in kB at this point */
-               if (cx->stream_buffers[i] < 0) {
-                       cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
-                                               / cx->stream_buf_size[i];
                } else {
-                       /* N.B. This might round down to 0 */
-                       cx->options.megabytes[i] =
-                         cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024;
+                       /* All other streams have stream_buf_size in kB here */
+                       if (cx->stream_buffers[i] < 0) {
+                               cx->stream_buffers[i] =
+                                               cx->options.megabytes[i] * 1024
+                                               / cx->stream_buf_size[i];
+                       } else {
+                               /* N.B. This might round down to 0 */
+                               cx->options.megabytes[i] =
+                                               cx->stream_buffers[i]
+                                               * cx->stream_buf_size[i] / 1024;
+                       }
+                       /* convert from kB to bytes */
+                       cx->stream_buf_size[i] *= 1024;
                }
-               cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
+               CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+                               "%d bytes\n", i, cx->options.megabytes[i],
+                               cx->stream_buffers[i], cx->stream_buf_size[i]);
        }
 
        cx->options.cardtype = cardtype[cx->instance];
@@ -669,6 +692,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
        cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
        cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
 
+       /* IVTV style VBI insertion into MPEG streams */
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+       INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+       list_add(&cx->vbi.sliced_mpeg_buf.list,
+                &cx->vbi.sliced_mpeg_mdl.buf_list);
        return 0;
 }
 
@@ -883,7 +912,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
                CX18_ERR("Could not register A/V decoder subdevice\n");
                goto free_map;
        }
-       cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0);
 
        /* Initialize GPIO Reset Controller to do chip resets during i2c init */
        if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
@@ -1200,7 +1228,7 @@ static struct pci_driver cx18_pci_driver = {
       .remove =   cx18_remove,
 };
 
-static int module_start(void)
+static int __init module_start(void)
 {
        printk(KERN_INFO "cx18:  Start initialization, version %s\n", CX18_VERSION);
 
@@ -1224,7 +1252,7 @@ static int module_start(void)
        return 0;
 }
 
-static void module_cleanup(void)
+static void __exit module_cleanup(void)
 {
        pci_unregister_driver(&cx18_pci_driver);
 }
index c6a1e90..e3f7911 100644 (file)
@@ -50,6 +50,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
 #include <media/tuner.h>
+#include <media/ir-kbd-i2c.h>
 #include "cx18-mailbox.h"
 #include "cx18-av-core.h"
 #include "cx23418.h"
 /* Maximum firmware DMA buffers per stream */
 #define CX18_MAX_FW_MDLS_PER_STREAM 63
 
+/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */
+#define CX18_UNIT_ENC_YUV_BUFSIZE      (720 *  32 * 3 / 2) /* bytes */
+#define CX18_625_LINE_ENC_YUV_BUFSIZE  (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32)
+#define CX18_525_LINE_ENC_YUV_BUFSIZE  (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32)
+
 /* DMA buffer, default size in kB allocated */
 #define CX18_DEFAULT_ENC_TS_BUFSIZE   32
 #define CX18_DEFAULT_ENC_MPG_BUFSIZE  32
 #define CX18_DEFAULT_ENC_IDX_BUFSIZE  32
-#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128
-/* Default VBI bufsize based on standards supported by card tuner for now */
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE  (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1)
 #define CX18_DEFAULT_ENC_PCM_BUFSIZE   4
 
 /* i2c stuff */
@@ -246,8 +251,8 @@ struct cx18_options {
        int radio;              /* enable/disable radio */
 };
 
-/* per-buffer bit flags */
-#define CX18_F_B_NEED_BUF_SWAP  0      /* this buffer should be byte swapped */
+/* per-mdl bit flags */
+#define CX18_F_M_NEED_SWAP  0  /* mdl buffer data must be endianess swapped */
 
 /* per-stream, s_flags */
 #define CX18_F_S_CLAIMED       3       /* this stream is claimed */
@@ -274,18 +279,29 @@ struct cx18_options {
 struct cx18_buffer {
        struct list_head list;
        dma_addr_t dma_handle;
-       u32 id;
-       unsigned long b_flags;
-       unsigned skipped;
        char *buf;
 
        u32 bytesused;
        u32 readpos;
 };
 
+struct cx18_mdl {
+       struct list_head list;
+       u32 id;         /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */
+
+       unsigned int skipped;
+       unsigned long m_flags;
+
+       struct list_head buf_list;
+       struct cx18_buffer *curr_buf; /* current buffer in list for reading */
+
+       u32 bytesused;
+       u32 readpos;
+};
+
 struct cx18_queue {
        struct list_head list;
-       atomic_t buffers;
+       atomic_t depth;
        u32 bytesused;
        spinlock_t lock;
 };
@@ -337,7 +353,7 @@ struct cx18_stream {
        const char *name;               /* name of the stream */
        int type;                       /* stream type */
        u32 handle;                     /* task handle */
-       unsigned mdl_offset;
+       unsigned int mdl_base_idx;
 
        u32 id;
        unsigned long s_flags;  /* status flags, see above */
@@ -346,14 +362,20 @@ struct cx18_stream {
                                   PCI_DMA_NONE */
        wait_queue_head_t waitq;
 
-       /* Buffer Stats */
-       u32 buffers;
-       u32 buf_size;
+       /* Buffers */
+       struct list_head buf_pool;      /* buffers not attached to an MDL */
+       u32 buffers;                    /* total buffers owned by this stream */
+       u32 buf_size;                   /* size in bytes of a single buffer */
+
+       /* MDL sizes - all stream MDLs are the same size */
+       u32 bufs_per_mdl;
+       u32 mdl_size;           /* total bytes in all buffers in a mdl */
 
-       /* Buffer Queues */
-       struct cx18_queue q_free;       /* free buffers */
-       struct cx18_queue q_busy;       /* busy buffers - in use by firmware */
-       struct cx18_queue q_full;       /* full buffers - data for user apps */
+       /* MDL Queues */
+       struct cx18_queue q_free;       /* free - in rotation, not committed */
+       struct cx18_queue q_busy;       /* busy - in use by firmware */
+       struct cx18_queue q_full;       /* full - data for user apps */
+       struct cx18_queue q_idle;       /* idle - not in rotation */
 
        struct work_struct out_work_order;
 
@@ -481,10 +503,11 @@ struct vbi_info {
        u32 inserted_frame;
 
        /*
-        * A dummy driver stream transfer buffer with a copy of the next
+        * A dummy driver stream transfer mdl & buffer with a copy of the next
         * sliced_mpeg_data[] buffer for output to userland apps.
         * Only used in cx18-fileops.c, but its state needs to persist at times.
         */
+       struct cx18_mdl sliced_mpeg_mdl;
        struct cx18_buffer sliced_mpeg_buf;
 };
 
@@ -511,10 +534,9 @@ struct cx18 {
        u8 is_60hz;
        u8 nof_inputs;          /* number of video inputs */
        u8 nof_audio_inputs;    /* number of audio inputs */
-       u16 buffer_id;          /* buffer ID counter */
        u32 v4l2_cap;           /* V4L2 capabilities of card */
        u32 hw_flags;           /* Hardware description of the board */
-       unsigned mdl_offset;
+       unsigned int free_mdl_idx;
        struct cx18_scb __iomem *scb; /* pointer to SCB */
        struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/
        struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/
@@ -585,6 +607,8 @@ struct cx18 {
        struct i2c_algo_bit_data i2c_algo[2];
        struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
 
+       struct IR_i2c_init_data ir_i2c_init_data;
+
        /* gpio */
        u32 gpio_dir;
        u32 gpio_val;
index 51a0c33..71ad2d1 100644 (file)
@@ -61,6 +61,7 @@ static struct mxl5005s_config hauppauge_hvr1600_tuner = {
        .top             = MXL5005S_TOP_25P2,
        .mod_mode        = MXL_DIGITAL_MODE,
        .if_mode         = MXL_ZERO_IF,
+       .qam_gain        = 0x02,
        .AgcMasterByte   = 0x00,
 };
 
@@ -71,7 +72,8 @@ static struct s5h1409_config hauppauge_hvr1600_config = {
        .qam_if        = 44000,
        .inversion     = S5H1409_INVERSION_OFF,
        .status_mode   = S5H1409_DEMODLOCKING,
-       .mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+       .mpeg_timing   = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+       .hvr1600_opt   = S5H1409_HVR1600_OPTIMIZE
 };
 
 /*
@@ -360,9 +362,10 @@ int cx18_dvb_register(struct cx18_stream *stream)
        dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
 
        CX18_INFO("DVB Frontend registered\n");
-       CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n",
+       CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n",
                  stream->dvb.dvb_adapter.num, stream->name,
-                 stream->buffers, stream->buf_size/1024);
+                 stream->buffers, stream->buf_size/1024,
+                 (stream->buf_size * 100 / 1024) % 100);
 
        mutex_init(&dvb->feedlock);
        dvb->enabled = 1;
index 04d9c25..4e278db 100644 (file)
@@ -166,11 +166,12 @@ static void cx18_dualwatch(struct cx18 *cx)
 }
 
 
-static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err)
+static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
+                                    int *err)
 {
        struct cx18 *cx = s->cx;
        struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
-       struct cx18_buffer *buf;
+       struct cx18_mdl *mdl;
        DEFINE_WAIT(wait);
 
        *err = 0;
@@ -185,32 +186,33 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
                        }
                        if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
                            !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
-                               while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
+                               while ((mdl = cx18_dequeue(s_vbi,
+                                                          &s_vbi->q_full))) {
                                        /* byteswap and process VBI data */
-                                       cx18_process_vbi_data(cx, buf,
+                                       cx18_process_vbi_data(cx, mdl,
                                                              s_vbi->type);
-                                       cx18_stream_put_buf_fw(s_vbi, buf);
+                                       cx18_stream_put_mdl_fw(s_vbi, mdl);
                                }
                        }
-                       buf = &cx->vbi.sliced_mpeg_buf;
-                       if (buf->readpos != buf->bytesused)
-                               return buf;
+                       mdl = &cx->vbi.sliced_mpeg_mdl;
+                       if (mdl->readpos != mdl->bytesused)
+                               return mdl;
                }
 
                /* do we have new data? */
-               buf = cx18_dequeue(s, &s->q_full);
-               if (buf) {
-                       if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP,
-                                               &buf->b_flags))
-                               return buf;
+               mdl = cx18_dequeue(s, &s->q_full);
+               if (mdl) {
+                       if (!test_and_clear_bit(CX18_F_M_NEED_SWAP,
+                                               &mdl->m_flags))
+                               return mdl;
                        if (s->type == CX18_ENC_STREAM_TYPE_MPG)
                                /* byteswap MPG data */
-                               cx18_buf_swap(buf);
+                               cx18_mdl_swap(mdl);
                        else {
                                /* byteswap and process VBI data */
-                               cx18_process_vbi_data(cx, buf, s->type);
+                               cx18_process_vbi_data(cx, mdl, s->type);
                        }
-                       return buf;
+                       return mdl;
                }
 
                /* return if end of stream */
@@ -229,7 +231,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
                prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
                /* New buffers might have become available before we were added
                   to the waitqueue */
-               if (!atomic_read(&s->q_full.buffers))
+               if (!atomic_read(&s->q_full.depth))
                        schedule();
                finish_wait(&s->waitq, &wait);
                if (signal_pending(current)) {
@@ -241,21 +243,28 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
        }
 }
 
-static void cx18_setup_sliced_vbi_buf(struct cx18 *cx)
+static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx)
 {
+       struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl;
+       struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf;
        int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
 
-       cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];
-       cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];
-       cx->vbi.sliced_mpeg_buf.readpos = 0;
+       buf->buf = cx->vbi.sliced_mpeg_data[idx];
+       buf->bytesused = cx->vbi.sliced_mpeg_size[idx];
+       buf->readpos = 0;
+
+       mdl->curr_buf = NULL;
+       mdl->bytesused = cx->vbi.sliced_mpeg_size[idx];
+       mdl->readpos = 0;
 }
 
 static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
-               struct cx18_buffer *buf, char __user *ubuf, size_t ucount)
+       struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop)
 {
        struct cx18 *cx = s->cx;
        size_t len = buf->bytesused - buf->readpos;
 
+       *stop = false;
        if (len > ucount)
                len = ucount;
        if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
@@ -335,7 +344,8 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
                                /* We declare we actually found a Program Pack*/
                                cx->search_pack_header = 0; /* expect vid PES */
                                len = (char *)q - start;
-                               cx18_setup_sliced_vbi_buf(cx);
+                               cx18_setup_sliced_vbi_mdl(cx);
+                               *stop = true;
                                break;
                        }
                }
@@ -352,6 +362,60 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
        return len;
 }
 
+/**
+ * list_entry_is_past_end - check if a previous loop cursor is off list end
+ * @pos:       the type * previously used as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Check if the entry's list_head is the head of the list, thus it's not a
+ * real entry but was the loop cursor that walked past the end
+ */
+#define list_entry_is_past_end(pos, head, member) \
+       (&pos->member == (head))
+
+static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
+               struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
+{
+       size_t tot_written = 0;
+       int rc;
+       bool stop = false;
+
+       if (mdl->curr_buf == NULL)
+               mdl->curr_buf = list_first_entry(&mdl->buf_list,
+                                                struct cx18_buffer, list);
+
+       if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+               /*
+                * For some reason we've exhausted the buffers, but the MDL
+                * object still said some data was unread.
+                * Fix that and bail out.
+                */
+               mdl->readpos = mdl->bytesused;
+               return 0;
+       }
+
+       list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+               if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+                       continue;
+
+               rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written,
+                                          ucount - tot_written, &stop);
+               if (rc < 0)
+                       return rc;
+               mdl->readpos += rc;
+               tot_written += rc;
+
+               if (stop ||     /* Forced stopping point for VBI insertion */
+                   tot_written >= ucount ||    /* Reader request statisfied */
+                   mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+                   mdl->readpos >= mdl->bytesused) /* MDL buffers drained */
+                       break;
+       }
+       return tot_written;
+}
+
 static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
                size_t tot_count, int non_block)
 {
@@ -373,12 +437,12 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
                single_frame = 1;
 
        for (;;) {
-               struct cx18_buffer *buf;
+               struct cx18_mdl *mdl;
                int rc;
 
-               buf = cx18_get_buffer(s, non_block, &rc);
+               mdl = cx18_get_mdl(s, non_block, &rc);
                /* if there is no data available... */
-               if (buf == NULL) {
+               if (mdl == NULL) {
                        /* if we got data, then return that regardless */
                        if (tot_written)
                                break;
@@ -392,20 +456,20 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
                        return rc;
                }
 
-               rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written,
+               rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written,
                                tot_count - tot_written);
 
-               if (buf != &cx->vbi.sliced_mpeg_buf) {
-                       if (buf->readpos == buf->bytesused)
-                               cx18_stream_put_buf_fw(s, buf);
+               if (mdl != &cx->vbi.sliced_mpeg_mdl) {
+                       if (mdl->readpos == mdl->bytesused)
+                               cx18_stream_put_mdl_fw(s, mdl);
                        else
-                               cx18_push(s, buf, &s->q_full);
-               } else if (buf->readpos == buf->bytesused) {
+                               cx18_push(s, mdl, &s->q_full);
+               } else if (mdl->readpos == mdl->bytesused) {
                        int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
 
                        cx->vbi.sliced_mpeg_size[idx] = 0;
                        cx->vbi.inserted_frame++;
-                       cx->vbi_data_inserted += buf->bytesused;
+                       cx->vbi_data_inserted += mdl->bytesused;
                }
                if (rc < 0)
                        return rc;
@@ -543,7 +607,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
        CX18_DEBUG_HI_FILE("Encoder poll\n");
        poll_wait(filp, &s->waitq, wait);
 
-       if (atomic_read(&s->q_full.buffers))
+       if (atomic_read(&s->q_full.depth))
                return POLLIN | POLLRDNORM;
        if (eof)
                return POLLHUP;
index 2477461..eecf29a 100644 (file)
@@ -28,7 +28,6 @@
 #include "cx18-gpio.h"
 #include "cx18-i2c.h"
 #include "cx18-irq.h"
-#include <media/ir-kbd-i2c.h>
 
 #define CX18_REG_I2C_1_WR   0xf15000
 #define CX18_REG_I2C_1_RD   0xf15008
@@ -97,17 +96,11 @@ static const char * const hw_devicenames[] = {
        "ir_rx_z8f0811_haup",
 };
 
-static const struct IR_i2c_init_data z8f0811_ir_init_data = {
-       .ir_codes = &ir_codes_hauppauge_new_table,
-       .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR,
-       .type = IR_TYPE_RC5,
-       .name = "CX23418 Z8F0811 Hauppauge",
-};
-
-static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type,
-                          u8 addr)
+static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw,
+                          const char *type, u8 addr)
 {
        struct i2c_board_info info;
+       struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data;
        unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
 
        memset(&info, 0, sizeof(struct i2c_board_info));
@@ -116,9 +109,11 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type,
        /* Our default information for ir-kbd-i2c.c to use */
        switch (hw) {
        case CX18_HW_Z8F0811_IR_RX_HAUP:
-               info.platform_data = (void *) &z8f0811_ir_init_data;
-               break;
-       default:
+               init_data->ir_codes = &ir_codes_hauppauge_new_table;
+               init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+               init_data->type = IR_TYPE_RC5;
+               init_data->name = cx->card_name;
+               info.platform_data = init_data;
                break;
        }
 
@@ -154,8 +149,8 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
                return sd != NULL ? 0 : -1;
        }
 
-       if (hw & CX18_HW_Z8F0811_IR_HAUP)
-               return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]);
+       if (hw & CX18_HW_IR_ANY)
+               return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]);
 
        /* Is it not an I2C device or one we do not wish to register? */
        if (!hw_addrs[idx])
index fc76e4d..3e4fc19 100644 (file)
@@ -910,7 +910,8 @@ static int cx18_log_status(struct file *file, void *fh)
                        continue;
                CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
                          s->name, s->s_flags,
-                         atomic_read(&s->q_full.buffers) * 100 / s->buffers,
+                         atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
+                          / s->buffers,
                          (s->buffers * s->buf_size) / 1024, s->buffers);
        }
        CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
index afe46c3..f231dd0 100644 (file)
@@ -131,13 +131,39 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
  * Functions that run in a work_queue work handling context
  */
 
+static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+       struct cx18_buffer *buf;
+
+       if (!s->dvb.enabled || mdl->bytesused == 0)
+               return;
+
+       /* We ignore mdl and buf readpos accounting here - it doesn't matter */
+
+       /* The likely case */
+       if (list_is_singular(&mdl->buf_list)) {
+               buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+                                      list);
+               if (buf->bytesused)
+                       dvb_dmx_swfilter(&s->dvb.demux,
+                                        buf->buf, buf->bytesused);
+               return;
+       }
+
+       list_for_each_entry(buf, &mdl->buf_list, list) {
+               if (buf->bytesused == 0)
+                       break;
+               dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused);
+       }
+}
+
 static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
 {
        u32 handle, mdl_ack_count, id;
        struct cx18_mailbox *mb;
        struct cx18_mdl_ack *mdl_ack;
        struct cx18_stream *s;
-       struct cx18_buffer *buf;
+       struct cx18_mdl *mdl;
        int i;
 
        mb = &order->mb;
@@ -158,7 +184,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
                id = mdl_ack->id;
                /*
                 * Simple integrity check for processing a stale (and possibly
-                * inconsistent mailbox): make sure the buffer id is in the
+                * inconsistent mailbox): make sure the MDL id is in the
                 * valid range for the stream.
                 *
                 * We go through the trouble of dealing with stale mailboxes
@@ -169,44 +195,42 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
                 * There are occasions when we get a half changed mailbox,
                 * which this check catches for a handle & id mismatch.  If the
                 * handle and id do correspond, the worst case is that we
-                * completely lost the old buffer, but pick up the new buffer
+                * completely lost the old MDL, but pick up the new MDL
                 * early (but the new mdl_ack is guaranteed to be good in this
                 * case as the firmware wouldn't point us to a new mdl_ack until
                 * it's filled in).
                 *
-                * cx18_queue_get buf() will detect the lost buffers
+                * cx18_queue_get_mdl() will detect the lost MDLs
                 * and send them back to q_free for fw rotation eventually.
                 */
                if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
-                   !(id >= s->mdl_offset &&
-                     id < (s->mdl_offset + s->buffers))) {
+                   !(id >= s->mdl_base_idx &&
+                     id < (s->mdl_base_idx + s->buffers))) {
                        CX18_WARN("Fell behind! Ignoring stale mailbox with "
-                                 " inconsistent data. Lost buffer for mailbox "
+                                 " inconsistent data. Lost MDL for mailbox "
                                  "seq no %d\n", mb->request);
                        break;
                }
-               buf = cx18_queue_get_buf(s, id, mdl_ack->data_used);
+               mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used);
 
-               CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
-               if (buf == NULL) {
-                       CX18_WARN("Could not find buf %d for stream %s\n",
+               CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id);
+               if (mdl == NULL) {
+                       CX18_WARN("Could not find MDL %d for stream %s\n",
                                  id, s->name);
                        continue;
                }
 
                CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
-                                 s->name, buf->bytesused);
+                                 s->name, mdl->bytesused);
 
                if (s->type != CX18_ENC_STREAM_TYPE_TS)
-                       cx18_enqueue(s, buf, &s->q_full);
+                       cx18_enqueue(s, mdl, &s->q_full);
                else {
-                       if (s->dvb.enabled)
-                               dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
-                                                buf->bytesused);
-                       cx18_enqueue(s, buf, &s->q_free);
+                       cx18_mdl_send_to_dvb(s, mdl);
+                       cx18_enqueue(s, mdl, &s->q_free);
                }
        }
-       /* Put as many buffers as possible back into fw use */
+       /* Put as many MDLs as possible back into fw use */
        cx18_stream_load_fw_queue(s);
 
        wake_up(&cx->dma_waitq);
@@ -616,7 +640,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
 
        /*
         * Wait for XPU to perform extra actions for the caller in some cases.
-        * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers
+        * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs
         * back in a burst shortly thereafter
         */
        if (info->flags & API_SLOW)
index e23aaac..33a3491 100644 (file)
 struct cx18;
 
 /*
- * This structure is used by CPU to provide completed buffers information
- * Its structure is dictrated by the layout of the SCB, required by the
- * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
+ * This structure is used by CPU to provide completed MDL & buffers information.
+ * Its structure is dictated by the layout of the SCB, required by the
+ * firmware, but its definition needs to be here, instead of in cx18-scb.h,
  * for mailbox work order scheduling
  */
 struct cx18_mdl_ack {
     u32 id;        /* ID of a completed MDL */
-    u32 data_used; /* Total data filled in the MDL for buffer 'id' */
+    u32 data_used; /* Total data filled in the MDL with 'id' */
 };
 
 /* The cx18_mailbox struct is the mailbox structure which is used for passing
index fa1ed78..6330482 100644 (file)
@@ -26,6 +26,7 @@
 #include "cx18-queue.h"
 #include "cx18-streams.h"
 #include "cx18-scb.h"
+#include "cx18-io.h"
 
 void cx18_buf_swap(struct cx18_buffer *buf)
 {
@@ -35,151 +36,312 @@ void cx18_buf_swap(struct cx18_buffer *buf)
                swab32s((u32 *)(buf->buf + i));
 }
 
+void _cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+       struct cx18_buffer *buf;
+
+       list_for_each_entry(buf, &mdl->buf_list, list) {
+               if (buf->bytesused == 0)
+                       break;
+               cx18_buf_swap(buf);
+       }
+}
+
 void cx18_queue_init(struct cx18_queue *q)
 {
        INIT_LIST_HEAD(&q->list);
-       atomic_set(&q->buffers, 0);
+       atomic_set(&q->depth, 0);
        q->bytesused = 0;
 }
 
-struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
                                 struct cx18_queue *q, int to_front)
 {
-       /* clear the buffer if it is not to be enqueued to the full queue */
+       /* clear the mdl if it is not to be enqueued to the full queue */
        if (q != &s->q_full) {
-               buf->bytesused = 0;
-               buf->readpos = 0;
-               buf->b_flags = 0;
-               buf->skipped = 0;
+               mdl->bytesused = 0;
+               mdl->readpos = 0;
+               mdl->m_flags = 0;
+               mdl->skipped = 0;
+               mdl->curr_buf = NULL;
        }
 
        /* q_busy is restricted to a max buffer count imposed by firmware */
        if (q == &s->q_busy &&
-           atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+           atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
                q = &s->q_free;
 
        spin_lock(&q->lock);
 
        if (to_front)
-               list_add(&buf->list, &q->list); /* LIFO */
+               list_add(&mdl->list, &q->list); /* LIFO */
        else
-               list_add_tail(&buf->list, &q->list); /* FIFO */
-       q->bytesused += buf->bytesused - buf->readpos;
-       atomic_inc(&q->buffers);
+               list_add_tail(&mdl->list, &q->list); /* FIFO */
+       q->bytesused += mdl->bytesused - mdl->readpos;
+       atomic_inc(&q->depth);
 
        spin_unlock(&q->lock);
        return q;
 }
 
-struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
 {
-       struct cx18_buffer *buf = NULL;
+       struct cx18_mdl *mdl = NULL;
 
        spin_lock(&q->lock);
        if (!list_empty(&q->list)) {
-               buf = list_first_entry(&q->list, struct cx18_buffer, list);
-               list_del_init(&buf->list);
-               q->bytesused -= buf->bytesused - buf->readpos;
-               buf->skipped = 0;
-               atomic_dec(&q->buffers);
+               mdl = list_first_entry(&q->list, struct cx18_mdl, list);
+               list_del_init(&mdl->list);
+               q->bytesused -= mdl->bytesused - mdl->readpos;
+               mdl->skipped = 0;
+               atomic_dec(&q->depth);
        }
        spin_unlock(&q->lock);
-       return buf;
+       return mdl;
+}
+
+static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+                                         struct cx18_mdl *mdl)
+{
+       struct cx18_buffer *buf;
+       u32 buf_size = s->buf_size;
+       u32 bytesused = mdl->bytesused;
+
+       list_for_each_entry(buf, &mdl->buf_list, list) {
+               buf->readpos = 0;
+               if (bytesused >= buf_size) {
+                       buf->bytesused = buf_size;
+                       bytesused -= buf_size;
+               } else {
+                       buf->bytesused = bytesused;
+                       bytesused = 0;
+               }
+               cx18_buf_sync_for_cpu(s, buf);
+       }
+}
+
+static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
+                                               struct cx18_mdl *mdl)
+{
+       struct cx18_buffer *buf;
+
+       if (list_is_singular(&mdl->buf_list)) {
+               buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
+                                      list);
+               buf->bytesused = mdl->bytesused;
+               buf->readpos = 0;
+               cx18_buf_sync_for_cpu(s, buf);
+       } else {
+               _cx18_mdl_update_bufs_for_cpu(s, mdl);
+       }
 }
 
-struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
        u32 bytesused)
 {
        struct cx18 *cx = s->cx;
-       struct cx18_buffer *buf;
-       struct cx18_buffer *tmp;
-       struct cx18_buffer *ret = NULL;
+       struct cx18_mdl *mdl;
+       struct cx18_mdl *tmp;
+       struct cx18_mdl *ret = NULL;
        LIST_HEAD(sweep_up);
 
        /*
         * We don't have to acquire multiple q locks here, because we are
         * serialized by the single threaded work handler.
-        * Buffers from the firmware will thus remain in order as
+        * MDLs from the firmware will thus remain in order as
         * they are moved from q_busy to q_full or to the dvb ring buffer.
         */
        spin_lock(&s->q_busy.lock);
-       list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
+       list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) {
                /*
                 * We should find what the firmware told us is done,
                 * right at the front of the queue.  If we don't, we likely have
-                * missed a buffer done message from the firmware.
-                * Once we skip a buffer repeatedly, relative to the size of
+                * missed an mdl done message from the firmware.
+                * Once we skip an mdl repeatedly, relative to the size of
                 * q_busy, we have high confidence we've missed it.
                 */
-               if (buf->id != id) {
-                       buf->skipped++;
-                       if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
-                               /* buffer must have fallen out of rotation */
-                               CX18_WARN("Skipped %s, buffer %d, %d "
+               if (mdl->id != id) {
+                       mdl->skipped++;
+                       if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) {
+                               /* mdl must have fallen out of rotation */
+                               CX18_WARN("Skipped %s, MDL %d, %d "
                                          "times - it must have dropped out of "
-                                         "rotation\n", s->name, buf->id,
-                                         buf->skipped);
+                                         "rotation\n", s->name, mdl->id,
+                                         mdl->skipped);
                                /* Sweep it up to put it back into rotation */
-                               list_move_tail(&buf->list, &sweep_up);
-                               atomic_dec(&s->q_busy.buffers);
+                               list_move_tail(&mdl->list, &sweep_up);
+                               atomic_dec(&s->q_busy.depth);
                        }
                        continue;
                }
                /*
-                * We pull the desired buffer off of the queue here.  Something
+                * We pull the desired mdl off of the queue here.  Something
                 * will have to put it back on a queue later.
                 */
-               list_del_init(&buf->list);
-               atomic_dec(&s->q_busy.buffers);
-               ret = buf;
+               list_del_init(&mdl->list);
+               atomic_dec(&s->q_busy.depth);
+               ret = mdl;
                break;
        }
        spin_unlock(&s->q_busy.lock);
 
        /*
-        * We found the buffer for which we were looking.  Get it ready for
+        * We found the mdl for which we were looking.  Get it ready for
         * the caller to put on q_full or in the dvb ring buffer.
         */
        if (ret != NULL) {
                ret->bytesused = bytesused;
                ret->skipped = 0;
-               /* readpos and b_flags were 0'ed when the buf went on q_busy */
-               cx18_buf_sync_for_cpu(s, ret);
+               /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */
+               cx18_mdl_update_bufs_for_cpu(s, ret);
                if (s->type != CX18_ENC_STREAM_TYPE_TS)
-                       set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags);
+                       set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags);
        }
 
-       /* Put any buffers the firmware is ignoring back into normal rotation */
-       list_for_each_entry_safe(buf, tmp, &sweep_up, list) {
-               list_del_init(&buf->list);
-               cx18_enqueue(s, buf, &s->q_free);
+       /* Put any mdls the firmware is ignoring back into normal rotation */
+       list_for_each_entry_safe(mdl, tmp, &sweep_up, list) {
+               list_del_init(&mdl->list);
+               cx18_enqueue(s, mdl, &s->q_free);
        }
        return ret;
 }
 
-/* Move all buffers of a queue to q_free, while flushing the buffers */
-static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
+/* Move all mdls of a queue, while flushing the mdl */
+static void cx18_queue_flush(struct cx18_stream *s,
+                            struct cx18_queue *q_src, struct cx18_queue *q_dst)
 {
-       struct cx18_buffer *buf;
+       struct cx18_mdl *mdl;
 
-       if (q == &s->q_free)
+       /* It only makes sense to flush to q_free or q_idle */
+       if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy)
                return;
 
-       spin_lock(&q->lock);
-       while (!list_empty(&q->list)) {
-               buf = list_first_entry(&q->list, struct cx18_buffer, list);
-               list_move_tail(&buf->list, &s->q_free.list);
-               buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
-               atomic_inc(&s->q_free.buffers);
+       spin_lock(&q_src->lock);
+       spin_lock(&q_dst->lock);
+       while (!list_empty(&q_src->list)) {
+               mdl = list_first_entry(&q_src->list, struct cx18_mdl, list);
+               list_move_tail(&mdl->list, &q_dst->list);
+               mdl->bytesused = 0;
+               mdl->readpos = 0;
+               mdl->m_flags = 0;
+               mdl->skipped = 0;
+               mdl->curr_buf = NULL;
+               atomic_inc(&q_dst->depth);
        }
-       cx18_queue_init(q);
-       spin_unlock(&q->lock);
+       cx18_queue_init(q_src);
+       spin_unlock(&q_src->lock);
+       spin_unlock(&q_dst->lock);
 }
 
 void cx18_flush_queues(struct cx18_stream *s)
 {
-       cx18_queue_flush(s, &s->q_busy);
-       cx18_queue_flush(s, &s->q_full);
+       cx18_queue_flush(s, &s->q_busy, &s->q_free);
+       cx18_queue_flush(s, &s->q_full, &s->q_free);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_unload_queues(struct cx18_stream *s)
+{
+       struct cx18_queue *q_idle = &s->q_idle;
+       struct cx18_mdl *mdl;
+       struct cx18_buffer *buf;
+
+       /* Move all MDLS to q_idle */
+       cx18_queue_flush(s, &s->q_busy, q_idle);
+       cx18_queue_flush(s, &s->q_full, q_idle);
+       cx18_queue_flush(s, &s->q_free, q_idle);
+
+       /* Reset MDL id's and move all buffers back to the stream's buf_pool */
+       spin_lock(&q_idle->lock);
+       list_for_each_entry(mdl, &q_idle->list, list) {
+               while (!list_empty(&mdl->buf_list)) {
+                       buf = list_first_entry(&mdl->buf_list,
+                                              struct cx18_buffer, list);
+                       list_move_tail(&buf->list, &s->buf_pool);
+                       buf->bytesused = 0;
+                       buf->readpos = 0;
+               }
+               mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */
+               /* all other mdl fields were cleared by cx18_queue_flush() */
+       }
+       spin_unlock(&q_idle->lock);
+}
+
+/*
+ * Note, s->buf_pool is not protected by a lock,
+ * the stream better not have *anything* going on when calling this
+ */
+void cx18_load_queues(struct cx18_stream *s)
+{
+       struct cx18 *cx = s->cx;
+       struct cx18_mdl *mdl;
+       struct cx18_buffer *buf;
+       int mdl_id;
+       int i;
+       u32 partial_buf_size;
+
+       /*
+        * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free
+        * Excess MDLs are left on q_idle
+        * Excess buffers are left in buf_pool and/or on an MDL in q_idle
+        */
+       mdl_id = s->mdl_base_idx;
+       for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl;
+            mdl != NULL && i == s->bufs_per_mdl;
+            mdl = cx18_dequeue(s, &s->q_idle)) {
+
+               mdl->id = mdl_id;
+
+               for (i = 0; i < s->bufs_per_mdl; i++) {
+                       if (list_empty(&s->buf_pool))
+                               break;
+
+                       buf = list_first_entry(&s->buf_pool, struct cx18_buffer,
+                                              list);
+                       list_move_tail(&buf->list, &mdl->buf_list);
+
+                       /* update the firmware's MDL array with this buffer */
+                       cx18_writel(cx, buf->dma_handle,
+                                   &cx->scb->cpu_mdl[mdl_id + i].paddr);
+                       cx18_writel(cx, s->buf_size,
+                                   &cx->scb->cpu_mdl[mdl_id + i].length);
+               }
+
+               if (i == s->bufs_per_mdl) {
+                       /*
+                        * The encoder doesn't honor s->mdl_size.  So in the
+                        * case of a non-integral number of buffers to meet
+                        * mdl_size, we lie about the size of the last buffer
+                        * in the MDL to get the encoder to really only send
+                        * us mdl_size bytes per MDL transfer.
+                        */
+                       partial_buf_size = s->mdl_size % s->buf_size;
+                       if (partial_buf_size) {
+                               cx18_writel(cx, partial_buf_size,
+                                     &cx->scb->cpu_mdl[mdl_id + i - 1].length);
+                       }
+                       cx18_enqueue(s, mdl, &s->q_free);
+               } else {
+                       /* Not enough buffers for this MDL; we won't use it */
+                       cx18_push(s, mdl, &s->q_idle);
+               }
+               mdl_id += i;
+       }
+}
+
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl)
+{
+       int dma = s->dma;
+       u32 buf_size = s->buf_size;
+       struct pci_dev *pci_dev = s->cx->pci_dev;
+       struct cx18_buffer *buf;
+
+       list_for_each_entry(buf, &mdl->buf_list, list)
+               pci_dma_sync_single_for_device(pci_dev, buf->dma_handle,
+                                              buf_size, dma);
 }
 
 int cx18_stream_alloc(struct cx18_stream *s)
@@ -190,44 +352,62 @@ int cx18_stream_alloc(struct cx18_stream *s)
        if (s->buffers == 0)
                return 0;
 
-       CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
+       CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers "
+                       "(%d.%02d kB total)\n",
                s->name, s->buffers, s->buf_size,
-               s->buffers * s->buf_size / 1024);
+               s->buffers * s->buf_size / 1024,
+               (s->buffers * s->buf_size * 100 / 1024) % 100);
 
-       if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
+       if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] -
                                (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) {
                unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE -
                                        ((char __iomem *)cx->scb->cpu_mdl));
 
                CX18_ERR("Too many buffers, cannot fit in SCB area\n");
                CX18_ERR("Max buffers = %zd\n",
-                       bufsz / sizeof(struct cx18_mdl));
+                       bufsz / sizeof(struct cx18_mdl_ent));
                return -ENOMEM;
        }
 
-       s->mdl_offset = cx->mdl_offset;
+       s->mdl_base_idx = cx->free_mdl_idx;
 
-       /* allocate stream buffers. Initially all buffers are in q_free. */
+       /* allocate stream buffers and MDLs */
        for (i = 0; i < s->buffers; i++) {
-               struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
-                                               GFP_KERNEL|__GFP_NOWARN);
+               struct cx18_mdl *mdl;
+               struct cx18_buffer *buf;
 
-               if (buf == NULL)
+               /* 1 MDL per buffer to handle the worst & also default case */
+               mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN);
+               if (mdl == NULL)
                        break;
+
+               buf = kzalloc(sizeof(struct cx18_buffer),
+                               GFP_KERNEL|__GFP_NOWARN);
+               if (buf == NULL) {
+                       kfree(mdl);
+                       break;
+               }
+
                buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
                if (buf->buf == NULL) {
+                       kfree(mdl);
                        kfree(buf);
                        break;
                }
-               buf->id = cx->buffer_id++;
+
+               INIT_LIST_HEAD(&mdl->list);
+               INIT_LIST_HEAD(&mdl->buf_list);
+               mdl->id = s->mdl_base_idx; /* a somewhat safe value */
+               cx18_enqueue(s, mdl, &s->q_idle);
+
                INIT_LIST_HEAD(&buf->list);
                buf->dma_handle = pci_map_single(s->cx->pci_dev,
                                buf->buf, s->buf_size, s->dma);
                cx18_buf_sync_for_cpu(s, buf);
-               cx18_enqueue(s, buf, &s->q_free);
+               list_add_tail(&buf->list, &s->buf_pool);
        }
        if (i == s->buffers) {
-               cx->mdl_offset += s->buffers;
+               cx->free_mdl_idx += s->buffers;
                return 0;
        }
        CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
@@ -237,13 +417,21 @@ int cx18_stream_alloc(struct cx18_stream *s)
 
 void cx18_stream_free(struct cx18_stream *s)
 {
+       struct cx18_mdl *mdl;
        struct cx18_buffer *buf;
 
-       /* move all buffers to q_free */
-       cx18_flush_queues(s);
+       /* move all buffers to buf_pool and all MDLs to q_idle */
+       cx18_unload_queues(s);
+
+       /* empty q_idle */
+       while ((mdl = cx18_dequeue(s, &s->q_idle)))
+               kfree(mdl);
+
+       /* empty buf_pool */
+       while (!list_empty(&s->buf_pool)) {
+               buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list);
+               list_del_init(&buf->list);
 
-       /* empty q_free */
-       while ((buf = cx18_dequeue(s, &s->q_free))) {
                pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
                                s->buf_size, s->dma);
                kfree(buf->buf);
index 4de0626..88a6d34 100644 (file)
@@ -40,32 +40,59 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
                                s->buf_size, s->dma);
 }
 
+void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_sync_for_device(struct cx18_stream *s,
+                                           struct cx18_mdl *mdl)
+{
+       if (list_is_singular(&mdl->buf_list))
+               cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list,
+                                                            struct cx18_buffer,
+                                                            list));
+       else
+               _cx18_mdl_sync_for_device(s, mdl);
+}
+
 void cx18_buf_swap(struct cx18_buffer *buf);
+void _cx18_mdl_swap(struct cx18_mdl *mdl);
+
+static inline void cx18_mdl_swap(struct cx18_mdl *mdl)
+{
+       if (list_is_singular(&mdl->buf_list))
+               cx18_buf_swap(list_first_entry(&mdl->buf_list,
+                                              struct cx18_buffer, list));
+       else
+               _cx18_mdl_swap(mdl);
+}
 
 /* cx18_queue utility functions */
-struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
                                 struct cx18_queue *q, int to_front);
 
 static inline
-struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
                                struct cx18_queue *q)
 {
-       return _cx18_enqueue(s, buf, q, 0); /* FIFO */
+       return _cx18_enqueue(s, mdl, q, 0); /* FIFO */
 }
 
 static inline
-struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf,
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl,
                             struct cx18_queue *q)
 {
-       return _cx18_enqueue(s, buf, q, 1); /* LIFO */
+       return _cx18_enqueue(s, mdl, q, 1); /* LIFO */
 }
 
 void cx18_queue_init(struct cx18_queue *q);
-struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
-struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
+struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
+struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
        u32 bytesused);
 void cx18_flush_queues(struct cx18_stream *s);
 
+/* queue MDL reconfiguration helpers */
+void cx18_unload_queues(struct cx18_stream *s);
+void cx18_load_queues(struct cx18_stream *s);
+
 /* cx18_stream utility functions */
 int cx18_stream_alloc(struct cx18_stream *s);
 void cx18_stream_free(struct cx18_stream *s);
index 1dc1c43..368f23d 100644 (file)
@@ -81,7 +81,7 @@
 
 
 /* This structure is used by EPU to provide memory descriptors in its memory */
-struct cx18_mdl {
+struct cx18_mdl_ent {
     u32 paddr;  /* Physical address of a buffer segment */
     u32 length; /* Length of the buffer segment */
 };
@@ -272,7 +272,7 @@ struct cx18_scb {
        struct cx18_mailbox  ppu2epu_mb;
 
        struct cx18_mdl_ack  cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS];
-       struct cx18_mdl      cpu_mdl[1];
+       struct cx18_mdl_ent  cpu_mdl[1];
 };
 
 void cx18_init_scb(struct cx18 *cx);
index 7df513a..c398651 100644 (file)
@@ -115,6 +115,9 @@ static void cx18_stream_init(struct cx18 *cx, int type)
        s->dma = cx18_stream_info[type].dma;
        s->buffers = cx->stream_buffers[type];
        s->buf_size = cx->stream_buf_size[type];
+       INIT_LIST_HEAD(&s->buf_pool);
+       s->bufs_per_mdl = 1;
+       s->mdl_size = s->buf_size * s->bufs_per_mdl;
 
        init_waitqueue_head(&s->waitq);
        s->id = -1;
@@ -124,6 +127,8 @@ static void cx18_stream_init(struct cx18 *cx, int type)
        cx18_queue_init(&s->q_busy);
        spin_lock_init(&s->q_full.lock);
        cx18_queue_init(&s->q_full);
+       spin_lock_init(&s->q_idle.lock);
+       cx18_queue_init(&s->q_idle);
 
        INIT_WORK(&s->out_work_order, cx18_out_work_handler);
 }
@@ -257,9 +262,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
 
        switch (vfl_type) {
        case VFL_TYPE_GRABBER:
-               CX18_INFO("Registered device video%d for %s (%d x %d kB)\n",
+               CX18_INFO("Registered device video%d for %s "
+                         "(%d x %d.%02d kB)\n",
                          num, s->name, cx->stream_buffers[type],
-                         cx->stream_buf_size[type]/1024);
+                         cx->stream_buf_size[type] / 1024,
+                         (cx->stream_buf_size[type] * 100 / 1024) % 100);
                break;
 
        case VFL_TYPE_RADIO:
@@ -441,8 +448,8 @@ static void cx18_vbi_setup(struct cx18_stream *s)
 }
 
 static
-struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s,
-                                          struct cx18_buffer *buf)
+struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
+                                          struct cx18_mdl *mdl)
 {
        struct cx18 *cx = s->cx;
        struct cx18_queue *q;
@@ -451,16 +458,16 @@ struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s,
        if (s->handle == CX18_INVALID_TASK_HANDLE ||
            test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
            !test_bit(CX18_F_S_STREAMING, &s->s_flags))
-               return cx18_enqueue(s, buf, &s->q_free);
+               return cx18_enqueue(s, mdl, &s->q_free);
 
-       q = cx18_enqueue(s, buf, &s->q_busy);
+       q = cx18_enqueue(s, mdl, &s->q_busy);
        if (q != &s->q_busy)
-               return q; /* The firmware has the max buffers it can handle */
+               return q; /* The firmware has the max MDLs it can handle */
 
-       cx18_buf_sync_for_device(s, buf);
+       cx18_mdl_sync_for_device(s, mdl);
        cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
-                 (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
-                 1, buf->id, s->buf_size);
+                 (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
+                 s->bufs_per_mdl, mdl->id, s->mdl_size);
        return q;
 }
 
@@ -468,19 +475,19 @@ static
 void _cx18_stream_load_fw_queue(struct cx18_stream *s)
 {
        struct cx18_queue *q;
-       struct cx18_buffer *buf;
+       struct cx18_mdl *mdl;
 
-       if (atomic_read(&s->q_free.buffers) == 0 ||
-           atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+       if (atomic_read(&s->q_free.depth) == 0 ||
+           atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
                return;
 
        /* Move from q_free to q_busy notifying the firmware, until the limit */
        do {
-               buf = cx18_dequeue(s, &s->q_free);
-               if (buf == NULL)
+               mdl = cx18_dequeue(s, &s->q_free);
+               if (mdl == NULL)
                        break;
-               q = _cx18_stream_put_buf_fw(s, buf);
-       } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM
+               q = _cx18_stream_put_mdl_fw(s, mdl);
+       } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
                 && q == &s->q_busy);
 }
 
@@ -492,11 +499,51 @@ void cx18_out_work_handler(struct work_struct *work)
        _cx18_stream_load_fw_queue(s);
 }
 
+static void cx18_stream_configure_mdls(struct cx18_stream *s)
+{
+       cx18_unload_queues(s);
+
+       switch (s->type) {
+       case CX18_ENC_STREAM_TYPE_YUV:
+               /*
+                * Height should be a multiple of 32 lines.
+                * Set the MDL size to the exact size needed for one frame.
+                * Use enough buffers per MDL to cover the MDL size
+                */
+               s->mdl_size = 720 * s->cx->params.height * 3 / 2;
+               s->bufs_per_mdl = s->mdl_size / s->buf_size;
+               if (s->mdl_size % s->buf_size)
+                       s->bufs_per_mdl++;
+               break;
+       case CX18_ENC_STREAM_TYPE_VBI:
+               s->bufs_per_mdl = 1;
+               if  (cx18_raw_vbi(s->cx)) {
+                       s->mdl_size = (s->cx->is_60hz ? 12 : 18)
+                                                      * 2 * vbi_active_samples;
+               } else {
+                       /*
+                        * See comment in cx18_vbi_setup() below about the
+                        * extra lines we capture in sliced VBI mode due to
+                        * the lines on which EAV RP codes toggle.
+                       */
+                       s->mdl_size = s->cx->is_60hz
+                                  ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz
+                                  : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz;
+               }
+               break;
+       default:
+               s->bufs_per_mdl = 1;
+               s->mdl_size = s->buf_size * s->bufs_per_mdl;
+               break;
+       }
+
+       cx18_load_queues(s);
+}
+
 int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
 {
        u32 data[MAX_MB_ARGUMENTS];
        struct cx18 *cx = s->cx;
-       struct cx18_buffer *buf;
        int captype = 0;
        struct cx18_api_func_private priv;
 
@@ -619,14 +666,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
                (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
 
        /* Init all the cpu_mdls for this stream */
-       cx18_flush_queues(s);
-       spin_lock(&s->q_free.lock);
-       list_for_each_entry(buf, &s->q_free.list, list) {
-               cx18_writel(cx, buf->dma_handle,
-                                       &cx->scb->cpu_mdl[buf->id].paddr);
-               cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
-       }
-       spin_unlock(&s->q_free.lock);
+       cx18_stream_configure_mdls(s);
        _cx18_stream_load_fw_queue(s);
 
        /* begin_capture */
index 1afc3fd..4a01db5 100644 (file)
@@ -28,18 +28,18 @@ int cx18_streams_setup(struct cx18 *cx);
 int cx18_streams_register(struct cx18 *cx);
 void cx18_streams_cleanup(struct cx18 *cx, int unregister);
 
-/* Related to submission of buffers to firmware */
+/* Related to submission of mdls to firmware */
 static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
 {
        struct cx18 *cx = s->cx;
        queue_work(cx->out_work_queue, &s->out_work_order);
 }
 
-static inline void cx18_stream_put_buf_fw(struct cx18_stream *s,
-                                         struct cx18_buffer *buf)
+static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s,
+                                         struct cx18_mdl *mdl)
 {
-       /* Put buf on q_free; the out work handler will move buf(s) to q_busy */
-       cx18_enqueue(s, buf, &s->q_free);
+       /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */
+       cx18_enqueue(s, mdl, &s->q_free);
        cx18_stream_load_fw_queue(s);
 }
 
index c2aef4a..574c1c6 100644 (file)
@@ -105,6 +105,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
 
 /* Compress raw VBI format, removes leading SAV codes and surplus space
    after the frame.  Returns new compressed size. */
+/* FIXME - this function ignores the input size. */
 static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
 {
        u32 line_size = vbi_active_samples;
@@ -185,8 +186,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
        return line;
 }
 
-void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
-                          int streamtype)
+static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
 {
        /*
         * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
@@ -203,9 +203,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
        u32 pts;
        int lines;
 
-       if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
-               return;
-
        /*
         * The CX23418 sends us data that is 32 bit little-endian swapped,
         * but we want the raw VBI bytes in the order they were in the raster
@@ -250,3 +247,31 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
                copy_vbi_data(cx, lines, pts);
        cx->vbi.frame++;
 }
+
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
+                          int streamtype)
+{
+       struct cx18_buffer *buf;
+       u32 orig_used;
+
+       if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+               return;
+
+       /*
+        * Big assumption here:
+        * Every buffer hooked to the MDL's buf_list is a complete VBI frame
+        * that ends at the end of the buffer.
+        *
+        * To assume anything else would make the code in this file
+        * more complex, or require extra memcpy()'s to make the
+        * buffers satisfy the above assumption.  It's just simpler to set
+        * up the encoder buffer transfers to make the assumption true.
+        */
+       list_for_each_entry(buf, &mdl->buf_list, list) {
+               orig_used = buf->bytesused;
+               if (orig_used == 0)
+                       break;
+               _cx18_process_vbi_data(cx, buf);
+               mdl->bytesused -= (orig_used - buf->bytesused);
+       }
+}
index e7e1ae4..b365cf4 100644 (file)
@@ -21,6 +21,6 @@
  *  02111-1307  USA
  */
 
-void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
                           int streamtype);
 int cx18_used_line(struct cx18 *cx, int line, int field);
index 45494b0..9c0b5bb 100644 (file)
@@ -24,7 +24,7 @@
 
 #define CX18_DRIVER_NAME "cx18"
 #define CX18_DRIVER_VERSION_MAJOR 1
-#define CX18_DRIVER_VERSION_MINOR 2
+#define CX18_DRIVER_VERSION_MINOR 3
 #define CX18_DRIVER_VERSION_PATCHLEVEL 0
 
 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
index 9956abf..868806e 100644 (file)
 /* Description: This command provides the offset to a Memory Descriptor List
    IN[0] - Task handle. Handle of the task to start
    IN[1] - Offset of the MDL from the beginning of the local DDR.
-   IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1]
+   IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1]
    IN[3] - Buffer ID
    IN[4] - Total buffer length
    ReturnCode - One of the ERR_DE_... */
index 28f48f4..c217441 100644 (file)
@@ -2414,9 +2414,11 @@ int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev)
                cx231xx_info("No ACK after %d msec -GPIO I2C failed!",
                             nInit * 10);
 
-       /* readAck
-          throuth clock stretch ,slave has given a SCL signal,
-          so the SDA data can be directly read.  */
+       /*
+        * readAck
+        * through clock stretch, slave has given a SCL signal,
+        * so the SDA data can be directly read.
+        */
        status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
 
        if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) {
index 48f22fa..cd135f0 100644 (file)
@@ -126,8 +126,7 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
 
        if (do_sendkey) {
                dprintk("sending keypress\n");
-               ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
-                                poll_result.rc_data[0]);
+               ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]);
                ir_input_nokey(ir->input, &ir->ir);
        }
 
@@ -198,7 +197,11 @@ int cx231xx_ir_init(struct cx231xx *dev)
        usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
        strlcat(ir->phys, "/input0", sizeof(ir->phys));
 
-       ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
+       err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER,
+                           dev->board.ir_codes);
+       if (err < 0)
+               goto err_out_free;
+
        input_dev->name = ir->name;
        input_dev->phys = ir->phys;
        input_dev->id.bustype = BUS_USB;
@@ -223,6 +226,7 @@ err_out_stop:
        cx231xx_ir_stop(ir);
        dev->ir = NULL;
 err_out_free:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
        kfree(ir);
        return err;
@@ -237,6 +241,7 @@ int cx231xx_ir_fini(struct cx231xx *dev)
                return 0;
 
        cx231xx_ir_stop(ir);
+       ir_input_free(ir->input);
        input_unregister_device(ir->input);
        kfree(ir);
 
index 3650372..d095aa0 100644 (file)
@@ -2106,7 +2106,7 @@ static int cx231xx_v4l2_close(struct file *filp)
                }
 
                /* Save some power by putting tuner to sleep */
-               call_all(dev, tuner, s_standby);
+               call_all(dev, core, s_power, 0);
 
                /* do this before setting alternate! */
                cx231xx_uninit_isoc(dev);
index fd3fc3e..bcdda9a 100644 (file)
@@ -18,7 +18,9 @@ config VIDEO_CX23885
        select DVB_TDA10048 if !DVB_FE_CUSTOMISE
        select DVB_LNBP21 if !DVB_FE_CUSTOMISE
        select DVB_STV6110 if !DVB_FE_CUSTOMISE
+       select DVB_CX24116 if !DVB_FE_CUSTOMISE
        select DVB_STV0900 if !DVB_FE_CUSTOMISE
+       select DVB_DS3000 if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
        select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
index ab8ea35..5787ae2 100644 (file)
@@ -1,6 +1,7 @@
 cx23885-objs   := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
                    cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
-                   netup-init.o cimax2.o netup-eeprom.o
+                   cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \
+                   netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o
 
 obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
 
index 6c3b51c..0eed852 100644 (file)
@@ -37,6 +37,7 @@
 #include <media/cx2341x.h>
 
 #include "cx23885.h"
+#include "cx23885-ioctl.h"
 
 #define CX23885_FIRM_IMAGE_SIZE 376836
 #define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
@@ -318,7 +319,7 @@ static int mc417_wait_ready(struct cx23885_dev *dev)
        }
 }
 
-static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
+int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
 {
        u32 regval;
 
@@ -382,7 +383,7 @@ static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
        return mc417_wait_ready(dev);
 }
 
-static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
+int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
 {
        int retval;
        u32 regval;
@@ -1724,6 +1725,11 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
        .vidioc_log_status       = vidioc_log_status,
        .vidioc_querymenu        = vidioc_querymenu,
        .vidioc_queryctrl        = vidioc_queryctrl,
+       .vidioc_g_chip_ident     = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .vidioc_g_register       = cx23885_g_register,
+       .vidioc_s_register       = cx23885_s_register,
+#endif
 };
 
 static struct video_device cx23885_mpeg_template = {
index bfdf79f..1ec4816 100644 (file)
@@ -28,6 +28,7 @@
 #include "cx23885.h"
 #include "tuner-xc2028.h"
 #include "netup-init.h"
+#include "cx23888-ir.h"
 
 /* ------------------------------------------------------------------ */
 /* board config info                                                  */
@@ -199,11 +200,61 @@ struct cx23885_board cx23885_boards[] = {
        },
        [CX23885_BOARD_MYGICA_X8506] = {
                .name           = "Mygica X8506 DMB-TH",
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
+               .porta          = CX23885_ANALOG_VIDEO,
                .portb          = CX23885_MPEG_DVB,
+               .input          = {
+                       {
+                               .type   = CX23885_VMUX_TELEVISION,
+                               .vmux   = CX25840_COMPOSITE2,
+                       },
+                       {
+                               .type   = CX23885_VMUX_COMPOSITE1,
+                               .vmux   = CX25840_COMPOSITE8,
+                       },
+                       {
+                               .type   = CX23885_VMUX_SVIDEO,
+                               .vmux   = CX25840_SVIDEO_LUMA3 |
+                                               CX25840_SVIDEO_CHROMA4,
+                       },
+                       {
+                               .type   = CX23885_VMUX_COMPONENT,
+                               .vmux   = CX25840_COMPONENT_ON |
+                                       CX25840_VIN1_CH1 |
+                                       CX25840_VIN6_CH2 |
+                                       CX25840_VIN7_CH3,
+                       },
+               },
        },
        [CX23885_BOARD_MAGICPRO_PROHDTVE2] = {
                .name           = "Magic-Pro ProHDTV Extreme 2",
+               .tuner_type = TUNER_XC5000,
+               .tuner_addr = 0x61,
+               .porta          = CX23885_ANALOG_VIDEO,
                .portb          = CX23885_MPEG_DVB,
+               .input          = {
+                       {
+                               .type   = CX23885_VMUX_TELEVISION,
+                               .vmux   = CX25840_COMPOSITE2,
+                       },
+                       {
+                               .type   = CX23885_VMUX_COMPOSITE1,
+                               .vmux   = CX25840_COMPOSITE8,
+                       },
+                       {
+                               .type   = CX23885_VMUX_SVIDEO,
+                               .vmux   = CX25840_SVIDEO_LUMA3 |
+                                               CX25840_SVIDEO_CHROMA4,
+                       },
+                       {
+                               .type   = CX23885_VMUX_COMPONENT,
+                               .vmux   = CX25840_COMPONENT_ON |
+                                       CX25840_VIN1_CH1 |
+                                       CX25840_VIN6_CH2 |
+                                       CX25840_VIN7_CH3,
+                       },
+               },
        },
        [CX23885_BOARD_HAUPPAUGE_HVR1850] = {
                .name           = "Hauppauge WinTV-HVR1850",
@@ -214,6 +265,15 @@ struct cx23885_board cx23885_boards[] = {
                .name           = "Compro VideoMate E800",
                .portc          = CX23885_MPEG_DVB,
        },
+       [CX23885_BOARD_HAUPPAUGE_HVR1290] = {
+               .name           = "Hauppauge WinTV-HVR1290",
+               .portc          = CX23885_MPEG_DVB,
+       },
+       [CX23885_BOARD_MYGICA_X8558PRO] = {
+               .name           = "Mygica X8558 PRO DMB-TH",
+               .portb          = CX23885_MPEG_DVB,
+               .portc          = CX23885_MPEG_DVB,
+       },
 };
 const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
 
@@ -349,6 +409,14 @@ struct cx23885_subid cx23885_subids[] = {
                .subvendor = 0x1858,
                .subdevice = 0xe800,
                .card      = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
+       }, {
+               .subvendor = 0x0070,
+               .subdevice = 0x8551,
+               .card      = CX23885_BOARD_HAUPPAUGE_HVR1290,
+       }, {
+               .subvendor = 0x14f1,
+               .subdevice = 0x8578,
+               .card      = CX23885_BOARD_MYGICA_X8558PRO,
        },
 };
 const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -509,9 +577,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
                 * DVB-T and MPEG2 HW Encoder */
                break;
        case 85021:
-               /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM,
+               /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM,
                        Dual channel ATSC and MPEG2 HW Encoder */
                break;
+       case 85721:
+               /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
+                       Dual channel ATSC and Basic analog */
+               break;
        default:
                printk(KERN_WARNING "%s: warning: "
                        "unknown hauppauge model #%d\n",
@@ -710,10 +782,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
                cx_set(GP0_IO, 0x00040004);
                break;
        case CX23885_BOARD_TBS_6920:
-       case CX23885_BOARD_TEVII_S470:
                cx_write(MC417_CTL, 0x00000036);
                cx_write(MC417_OEN, 0x00001000);
-               cx_write(MC417_RWD, 0x00001800);
+               cx_set(MC417_RWD, 0x00000002);
+               mdelay(200);
+               cx_clear(MC417_RWD, 0x00000800);
+               mdelay(200);
+               cx_set(MC417_RWD, 0x00000800);
+               mdelay(200);
                break;
        case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
                /* GPIO-0 INTA from CiMax1
@@ -758,15 +834,26 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
                break;
        case CX23885_BOARD_MYGICA_X8506:
        case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+               /* GPIO-0 (0)Analog / (1)Digital TV */
                /* GPIO-1 reset XC5000 */
                /* GPIO-2 reset LGS8GL5 / LGS8G75 */
-               cx_set(GP0_IO, 0x00060000);
-               cx_clear(GP0_IO, 0x00000006);
+               cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
+               cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
                mdelay(100);
-               cx_set(GP0_IO, 0x00060006);
+               cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2);
+               mdelay(100);
+               break;
+       case CX23885_BOARD_MYGICA_X8558PRO:
+               /* GPIO-0 reset first ATBM8830 */
+               /* GPIO-1 reset second ATBM8830 */
+               cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
+               cx23885_gpio_clear(dev, GPIO_0 | GPIO_1);
+               mdelay(100);
+               cx23885_gpio_set(dev, GPIO_0 | GPIO_1);
                mdelay(100);
                break;
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
                /* GPIO-0 656_CLK */
                /* GPIO-1 656_D0 */
                /* GPIO-2 Wake# */
@@ -801,6 +888,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
 
 int cx23885_ir_init(struct cx23885_dev *dev)
 {
+       int ret = 0;
        switch (dev->board) {
        case CX23885_BOARD_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_HAUPPAUGE_HVR1500:
@@ -812,15 +900,46 @@ int cx23885_ir_init(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1275:
        case CX23885_BOARD_HAUPPAUGE_HVR1255:
        case CX23885_BOARD_HAUPPAUGE_HVR1210:
-       case CX23885_BOARD_HAUPPAUGE_HVR1850:
                /* FIXME: Implement me */
                break;
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               ret = cx23888_ir_probe(dev);
+               if (ret)
+                       break;
+               dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+               dev->pci_irqmask |= PCI_MSK_IR;
+               break;
        case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
                request_module("ir-kbd-i2c");
                break;
        }
 
-       return 0;
+       return ret;
+}
+
+void cx23885_ir_fini(struct cx23885_dev *dev)
+{
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               dev->pci_irqmask &= ~PCI_MSK_IR;
+               cx_clear(PCI_INT_MSK, PCI_MSK_IR);
+               cx23888_ir_remove(dev);
+               dev->sd_ir = NULL;
+               break;
+       }
+}
+
+void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
+{
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR))
+                       cx_set(PCI_INT_MSK, PCI_MSK_IR);
+               break;
+       }
 }
 
 void cx23885_card_setup(struct cx23885_dev *dev)
@@ -853,6 +972,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1255:
        case CX23885_BOARD_HAUPPAUGE_HVR1210:
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
                if (dev->i2c_bus[0].i2c_rc == 0)
                        hauppauge_eeprom(dev, eeprom+0xc0);
                break;
@@ -886,8 +1006,12 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
                ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
                break;
-       case CX23885_BOARD_TEVII_S470:
        case CX23885_BOARD_TBS_6920:
+               ts1->gen_ctrl_val  = 0x4; /* Parallel */
+               ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+               ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+               break;
+       case CX23885_BOARD_TEVII_S470:
        case CX23885_BOARD_DVBWORLD_2005:
                ts1->gen_ctrl_val  = 0x5; /* Parallel */
                ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -907,6 +1031,14 @@ void cx23885_card_setup(struct cx23885_dev *dev)
                ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
                ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
                break;
+       case CX23885_BOARD_MYGICA_X8558PRO:
+               ts1->gen_ctrl_val  = 0x5; /* Parallel */
+               ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+               ts1->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+               ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
+               ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+               ts2->src_sel_val   = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+               break;
        case CX23885_BOARD_HAUPPAUGE_HVR1250:
        case CX23885_BOARD_HAUPPAUGE_HVR1500:
        case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
@@ -922,6 +1054,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_HAUPPAUGE_HVR1210:
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
        default:
                ts2->gen_ctrl_val  = 0xc; /* Serial bus + punctured clock */
                ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -939,6 +1072,10 @@ void cx23885_card_setup(struct cx23885_dev *dev)
        case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
        case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
        case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_MYGICA_X8506:
+       case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
                dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
                                &dev->i2c_bus[2].i2c_adap,
                                "cx25840", "cx25840", 0x88 >> 1, NULL);
index c31284b..04b12d2 100644 (file)
@@ -32,6 +32,9 @@
 
 #include "cx23885.h"
 #include "cimax2.h"
+#include "cx23888-ir.h"
+#include "cx23885-ir.h"
+#include "cx23885-input.h"
 
 MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
 MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -753,6 +756,23 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
                        __func__, dev->hwrevision);
 }
 
+/* Find the first v4l2_subdev member of the group id in hw */
+struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw)
+{
+       struct v4l2_subdev *result = NULL;
+       struct v4l2_subdev *sd;
+
+       spin_lock(&dev->v4l2_dev.lock);
+       v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) {
+               if (sd->grp_id == hw) {
+                       result = sd;
+                       break;
+               }
+       }
+       spin_unlock(&dev->v4l2_dev.lock);
+       return result;
+}
+
 static int cx23885_dev_setup(struct cx23885_dev *dev)
 {
        int i;
@@ -899,7 +919,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
        cx23885_i2c_register(&dev->i2c_bus[1]);
        cx23885_i2c_register(&dev->i2c_bus[2]);
        cx23885_card_setup(dev);
-       call_all(dev, tuner, s_standby);
+       call_all(dev, core, s_power, 0);
        cx23885_ir_init(dev);
 
        if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
@@ -1637,6 +1657,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        u32 ts1_status, ts1_mask;
        u32 ts2_status, ts2_mask;
        int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
+       bool ir_handled = false;
 
        pci_status = cx_read(PCI_INT_STAT);
        pci_mask = cx_read(PCI_INT_MSK);
@@ -1662,18 +1683,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        dprintk(7, "ts2_status: 0x%08x  ts2_mask: 0x%08x count: 0x%x\n",
                ts2_status, ts2_mask, ts2_count);
 
-       if ((pci_status & PCI_MSK_RISC_RD) ||
-           (pci_status & PCI_MSK_RISC_WR) ||
-           (pci_status & PCI_MSK_AL_RD) ||
-           (pci_status & PCI_MSK_AL_WR) ||
-           (pci_status & PCI_MSK_APB_DMA) ||
-           (pci_status & PCI_MSK_VID_C) ||
-           (pci_status & PCI_MSK_VID_B) ||
-           (pci_status & PCI_MSK_VID_A) ||
-           (pci_status & PCI_MSK_AUD_INT) ||
-           (pci_status & PCI_MSK_AUD_EXT) ||
-           (pci_status & PCI_MSK_GPIO0) ||
-           (pci_status & PCI_MSK_GPIO1)) {
+       if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR |
+                         PCI_MSK_AL_RD   | PCI_MSK_AL_WR   | PCI_MSK_APB_DMA |
+                         PCI_MSK_VID_C   | PCI_MSK_VID_B   | PCI_MSK_VID_A   |
+                         PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT |
+                         PCI_MSK_GPIO0   | PCI_MSK_GPIO1   |
+                         PCI_MSK_IR)) {
 
                if (pci_status & PCI_MSK_RISC_RD)
                        dprintk(7, " (PCI_MSK_RISC_RD   0x%08x)\n",
@@ -1722,6 +1737,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
                if (pci_status & PCI_MSK_GPIO1)
                        dprintk(7, " (PCI_MSK_GPIO1     0x%08x)\n",
                                PCI_MSK_GPIO1);
+
+               if (pci_status & PCI_MSK_IR)
+                       dprintk(7, " (PCI_MSK_IR        0x%08x)\n",
+                               PCI_MSK_IR);
        }
 
        if (cx23885_boards[dev->board].cimax > 0 &&
@@ -1752,12 +1771,48 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
        if (vida_status)
                handled += cx23885_video_irq(dev, vida_status);
 
+       if (pci_status & PCI_MSK_IR) {
+               v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine,
+                                pci_status, &ir_handled);
+               if (ir_handled)
+                       handled++;
+       }
+
        if (handled)
                cx_write(PCI_INT_STAT, pci_status);
 out:
        return IRQ_RETVAL(handled);
 }
 
+static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd,
+                                   unsigned int notification, void *arg)
+{
+       struct cx23885_dev *dev;
+
+       if (sd == NULL)
+               return;
+
+       dev = to_cx23885(sd->v4l2_dev);
+
+       switch (notification) {
+       case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */
+               if (sd == dev->sd_ir)
+                       cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg);
+               break;
+       case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */
+               if (sd == dev->sd_ir)
+                       cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg);
+               break;
+       }
+}
+
+static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev)
+{
+       INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler);
+       INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler);
+       dev->v4l2_dev.notify = cx23885_v4l2_dev_notify;
+}
+
 static inline int encoder_on_portb(struct cx23885_dev *dev)
 {
        return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER;
@@ -1816,6 +1871,26 @@ void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask)
                printk(KERN_INFO "%s: Unsupported\n", dev->name);
 }
 
+u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask)
+{
+       if (mask & 0x00000007)
+               return (cx_read(GP0_IO) >> 8) & mask & 0x7;
+
+       if (mask & 0x0007fff8) {
+               if (encoder_on_portb(dev) || encoder_on_portc(dev))
+                       printk(KERN_ERR
+                               "%s: Reading GPIO moving on encoder ports\n",
+                               dev->name);
+               return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3;
+       }
+
+       /* TODO: 23-19 */
+       if (mask & 0x00f80000)
+               printk(KERN_INFO "%s: Unsupported\n", dev->name);
+
+       return 0;
+}
+
 void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
 {
        if ((mask & 0x00000007) && asoutput)
@@ -1854,6 +1929,9 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
        if (err < 0)
                goto fail_free;
 
+       /* Prepare to handle notifications from subdevices */
+       cx23885_v4l2_dev_notify_init(dev);
+
        /* pci init */
        dev->pci = pci_dev;
        if (pci_enable_device(pci_dev)) {
@@ -1896,6 +1974,14 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
                break;
        }
 
+       /*
+        * The CX2388[58] IR controller can start firing interrupts when
+        * enabled, so these have to take place after the cx23885_irq() handler
+        * is hooked up by the call to request_irq() above.
+        */
+       cx23885_ir_pci_int_enable(dev);
+       cx23885_input_init(dev);
+
        return 0;
 
 fail_irq:
@@ -1912,6 +1998,9 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
        struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
        struct cx23885_dev *dev = to_cx23885(v4l2_dev);
 
+       cx23885_input_fini(dev);
+       cx23885_ir_fini(dev);
+
        cx23885_shutdown(dev);
 
        pci_disable_device(pci_dev);
@@ -1957,7 +2046,7 @@ static struct pci_driver cx23885_pci_driver = {
        .resume   = NULL,
 };
 
-static int cx23885_init(void)
+static int __init cx23885_init(void)
 {
        printk(KERN_INFO "cx23885 driver version %d.%d.%d loaded\n",
               (CX23885_VERSION_CODE >> 16) & 0xff,
@@ -1970,7 +2059,7 @@ static int cx23885_init(void)
        return pci_register_driver(&cx23885_pci_driver);
 }
 
-static void cx23885_fini(void)
+static void __exit cx23885_fini(void)
 {
        pci_unregister_driver(&cx23885_pci_driver);
 }
index 45e13ee..e45d2df 100644 (file)
@@ -38,6 +38,7 @@
 #include "tda18271.h"
 #include "lgdt330x.h"
 #include "xc5000.h"
+#include "max2165.h"
 #include "tda10048.h"
 #include "tuner-xc2028.h"
 #include "tuner-simple.h"
@@ -54,6 +55,9 @@
 #include "netup-eeprom.h"
 #include "netup-init.h"
 #include "lgdt3305.h"
+#include "atbm8830.h"
+#include "ds3000.h"
+#include "cx23885-f300.h"
 
 static unsigned int debug;
 
@@ -400,6 +404,7 @@ static struct stv0900_reg stv0900_ts_regs[] = {
 
 static struct stv0900_config netup_stv0900_config = {
        .demod_address = 0x68,
+       .demod_mode = 1, /* dual */
        .xtal = 8000000,
        .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
        .diseqc_mode = 2,/* 2/3 PWM */
@@ -414,34 +419,22 @@ static struct stv6110_config netup_stv6110_tunerconfig_a = {
        .i2c_address = 0x60,
        .mclk = 16000000,
        .clk_div = 1,
+       .gain = 8, /* +16 dB  - maximum gain */
 };
 
 static struct stv6110_config netup_stv6110_tunerconfig_b = {
        .i2c_address = 0x63,
        .mclk = 16000000,
        .clk_div = 1,
+       .gain = 8, /* +16 dB  - maximum gain */
 };
 
-static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
-{
-       struct cx23885_tsport *port = fe->dvb->priv;
-       struct cx23885_dev *dev = port->dev;
-
-       if (voltage == SEC_VOLTAGE_18)
-               cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */
-       else if (voltage == SEC_VOLTAGE_13)
-               cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */
-       else
-               cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */
-       return 0;
-}
-
 static struct cx24116_config tbs_cx24116_config = {
-       .demod_address = 0x05,
+       .demod_address = 0x55,
 };
 
-static struct cx24116_config tevii_cx24116_config = {
-       .demod_address = 0x55,
+static struct ds3000_config tevii_ds3000_config = {
+       .demod_address = 0x68,
 };
 
 static struct cx24116_config dvbworld_cx24116_config = {
@@ -486,11 +479,40 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe,
                        break;
                }
                break;
+       case CX23885_BOARD_MYGICA_X8506:
+       case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+               /* Select Digital TV */
+               cx23885_gpio_set(dev, GPIO_0);
+               break;
        }
-       return (port->set_frontend_save) ?
-               port->set_frontend_save(fe, param) : -ENODEV;
+       return 0;
 }
 
+static int cx23885_dvb_fe_ioctl_override(struct dvb_frontend *fe,
+                                        unsigned int cmd, void *parg,
+                                        unsigned int stage)
+{
+       int err = 0;
+
+       switch (stage) {
+       case DVB_FE_IOCTL_PRE:
+
+               switch (cmd) {
+               case FE_SET_FRONTEND:
+                       err = cx23885_dvb_set_frontend(fe,
+                               (struct dvb_frontend_parameters *) parg);
+                       break;
+               }
+               break;
+
+       case DVB_FE_IOCTL_POST:
+               /* no post-ioctl handling required */
+               break;
+       }
+       return err;
+};
+
+
 static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
        .prod = LGS8GXX_PROD_LGS8G75,
        .demod_address = 0x19,
@@ -511,6 +533,38 @@ static struct xc5000_config magicpro_prohdtve2_xc5000_config = {
        .if_khz = 6500,
 };
 
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
+       .prod = ATBM8830_PROD_8830,
+       .demod_address = 0x44,
+       .serial_ts = 0,
+       .ts_sampling_edge = 1,
+       .ts_clk_gated = 0,
+       .osc_clk_freq = 30400, /* in kHz */
+       .if_freq = 0, /* zero IF */
+       .zif_swap_iq = 1,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
+       .i2c_address = 0x60,
+       .osc_clk = 20
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
+       .prod = ATBM8830_PROD_8830,
+       .demod_address = 0x44,
+       .serial_ts = 1,
+       .ts_sampling_edge = 1,
+       .ts_clk_gated = 0,
+       .osc_clk_freq = 30400, /* in kHz */
+       .if_freq = 0, /* zero IF */
+       .zif_swap_iq = 1,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
+       .i2c_address = 0x60,
+       .osc_clk = 20
+};
+
 static int dvb_register(struct cx23885_tsport *port)
 {
        struct cx23885_dev *dev = port->dev;
@@ -550,12 +604,6 @@ static int dvb_register(struct cx23885_tsport *port)
                                   0x60, &dev->i2c_bus[1].i2c_adap,
                                   &hauppauge_hvr127x_config);
                }
-
-               /* FIXME: temporary hack */
-               /* define bridge override to set_frontend */
-               port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend;
-               fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend;
-
                break;
        case CX23885_BOARD_HAUPPAUGE_HVR1255:
                i2c_bus = &dev->i2c_bus[0];
@@ -772,23 +820,23 @@ static int dvb_register(struct cx23885_tsport *port)
                }
                break;
        case CX23885_BOARD_TBS_6920:
-               i2c_bus = &dev->i2c_bus[0];
+               i2c_bus = &dev->i2c_bus[1];
 
                fe0->dvb.frontend = dvb_attach(cx24116_attach,
-                       &tbs_cx24116_config,
-                       &i2c_bus->i2c_adap);
+                                       &tbs_cx24116_config,
+                                       &i2c_bus->i2c_adap);
                if (fe0->dvb.frontend != NULL)
-                       fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage;
+                       fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
 
                break;
        case CX23885_BOARD_TEVII_S470:
                i2c_bus = &dev->i2c_bus[1];
 
-               fe0->dvb.frontend = dvb_attach(cx24116_attach,
-                       &tevii_cx24116_config,
-                       &i2c_bus->i2c_adap);
+               fe0->dvb.frontend = dvb_attach(ds3000_attach,
+                                       &tevii_ds3000_config,
+                                       &i2c_bus->i2c_adap);
                if (fe0->dvb.frontend != NULL)
-                       fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage;
+                       fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
 
                break;
        case CX23885_BOARD_DVBWORLD_2005:
@@ -814,8 +862,8 @@ static int dvb_register(struct cx23885_tsport *port)
                                        if (!dvb_attach(lnbh24_attach,
                                                        fe0->dvb.frontend,
                                                        &i2c_bus->i2c_adap,
-                                                       LNBH24_PCL,
-                                                       LNBH24_TTX, 0x09))
+                                                       LNBH24_PCL | LNBH24_TTX,
+                                                       LNBH24_TEN, 0x09))
                                                printk(KERN_ERR
                                                        "No LNBH24 found!\n");
 
@@ -835,8 +883,8 @@ static int dvb_register(struct cx23885_tsport *port)
                                        if (!dvb_attach(lnbh24_attach,
                                                        fe0->dvb.frontend,
                                                        &i2c_bus->i2c_adap,
-                                                       LNBH24_PCL,
-                                                       LNBH24_TTX, 0x0a))
+                                                       LNBH24_PCL | LNBH24_TTX,
+                                                       LNBH24_TEN, 0x0a))
                                                printk(KERN_ERR
                                                        "No LNBH24 found!\n");
 
@@ -872,6 +920,7 @@ static int dvb_register(struct cx23885_tsport *port)
                }
                break;
        case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
                i2c_bus = &dev->i2c_bus[0];
                fe0->dvb.frontend = dvb_attach(s5h1411_attach,
                        &hcw_s5h1411_config,
@@ -881,6 +930,36 @@ static int dvb_register(struct cx23885_tsport *port)
                                0x60, &dev->i2c_bus[0].i2c_adap,
                                &hauppauge_tda18271_config);
                break;
+       case CX23885_BOARD_MYGICA_X8558PRO:
+               switch (port->nr) {
+               /* port B */
+               case 1:
+                       i2c_bus = &dev->i2c_bus[0];
+                       fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+                               &mygica_x8558pro_atbm8830_cfg1,
+                               &i2c_bus->i2c_adap);
+                       if (fe0->dvb.frontend != NULL) {
+                               dvb_attach(max2165_attach,
+                                       fe0->dvb.frontend,
+                                       &i2c_bus->i2c_adap,
+                                       &mygic_x8558pro_max2165_cfg1);
+                       }
+                       break;
+               /* port C */
+               case 2:
+                       i2c_bus = &dev->i2c_bus[1];
+                       fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+                               &mygica_x8558pro_atbm8830_cfg2,
+                               &i2c_bus->i2c_adap);
+                       if (fe0->dvb.frontend != NULL) {
+                               dvb_attach(max2165_attach,
+                                       fe0->dvb.frontend,
+                                       &i2c_bus->i2c_adap,
+                                       &mygic_x8558pro_max2165_cfg2);
+                       }
+                       break;
+               }
+               break;
 
        default:
                printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
@@ -897,14 +976,15 @@ static int dvb_register(struct cx23885_tsport *port)
        fe0->dvb.frontend->callback = cx23885_tuner_callback;
 
        /* Put the analog decoder in standby to keep it quiet */
-       call_all(dev, tuner, s_standby);
+       call_all(dev, core, s_power, 0);
 
        if (fe0->dvb.frontend->ops.analog_ops.standby)
                fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
 
        /* register everything */
        ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
-               &dev->pci->dev, adapter_nr, 0);
+                                       &dev->pci->dev, adapter_nr, 0,
+                                       cx23885_dvb_fe_ioctl_override);
 
        /* init CI & MAC */
        switch (dev->board) {
@@ -940,7 +1020,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port)
        int err, i;
 
        /* Here we need to allocate the correct number of frontends,
-        * as reflected in the cards struct. The reality is that currrently
+        * as reflected in the cards struct. The reality is that currently
         * no cx23885 boards support this - yet. But, if we don't modify this
         * code then the second frontend would never be allocated (later)
         * and fail with error before the attach in dvb_register().
diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c
new file mode 100644 (file)
index 0000000..93998f2
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Driver for Silicon Labs C8051F300 microcontroller.
+ *
+ * It is used for LNB power control in TeVii S470,
+ * TBS 6920 PCIe DVB-S2 cards.
+ *
+ * Microcontroller connected to cx23885 GPIO pins:
+ * GPIO0 - data                - P0.3 F300
+ * GPIO1 - reset       - P0.2 F300
+ * GPIO2 - clk         - P0.1 F300
+ * GPIO3 - busy                - P0.0 F300
+ *
+ * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+
+#define F300_DATA      GPIO_0
+#define F300_RESET     GPIO_1
+#define F300_CLK       GPIO_2
+#define F300_BUSY      GPIO_3
+
+static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl)
+{
+       cx23885_gpio_enable(dev, line, 1);
+       if (lvl == 1)
+               cx23885_gpio_set(dev, line);
+       else
+               cx23885_gpio_clear(dev, line);
+}
+
+static u8 f300_get_line(struct cx23885_dev *dev, u32 line)
+{
+       cx23885_gpio_enable(dev, line, 0);
+
+       return cx23885_gpio_get(dev, line);
+}
+
+static void f300_send_byte(struct cx23885_dev *dev, u8 dta)
+{
+       u8 i;
+
+       for (i = 0; i < 8; i++) {
+               f300_set_line(dev, F300_CLK, 0);
+               udelay(30);
+               f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */
+               udelay(30);
+               dta <<= 1;
+               f300_set_line(dev, F300_CLK, 1);
+               udelay(30);
+       }
+}
+
+static u8 f300_get_byte(struct cx23885_dev *dev)
+{
+       u8 i, dta = 0;
+
+       for (i = 0; i < 8; i++) {
+               f300_set_line(dev, F300_CLK, 0);
+               udelay(30);
+               dta <<= 1;
+               f300_set_line(dev, F300_CLK, 1);
+               udelay(30);
+               dta |= f300_get_line(dev, F300_DATA);/* msb first */
+
+       }
+
+       return dta;
+}
+
+static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf)
+{
+       struct cx23885_tsport *port = fe->dvb->priv;
+       struct cx23885_dev *dev = port->dev;
+       u8 i, temp, ret = 0;
+
+       temp = buf[0];
+       for (i = 0; i < buf[0]; i++)
+               temp += buf[i + 1];
+       temp = (~temp + 1);/* get check sum */
+       buf[1 + buf[0]] = temp;
+
+       f300_set_line(dev, F300_RESET, 1);
+       f300_set_line(dev, F300_CLK, 1);
+       udelay(30);
+       f300_set_line(dev, F300_DATA, 1);
+       msleep(1);
+
+       /* question: */
+       f300_set_line(dev, F300_RESET, 0);/* begin to send data */
+       msleep(1);
+
+       f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */
+       msleep(1);
+
+       temp = buf[0];
+       temp += 2;
+       for (i = 0; i < temp; i++)
+               f300_send_byte(dev, buf[i]);
+
+       f300_set_line(dev, F300_RESET, 1);/* sent data over */
+       f300_set_line(dev, F300_DATA, 1);
+
+       /* answer: */
+       temp = 0;
+       for (i = 0; ((i < 8) & (temp == 0)); i++) {
+               msleep(1);
+               if (f300_get_line(dev, F300_BUSY) == 0)
+                       temp = 1;
+       }
+
+       if (i > 7) {
+               printk(KERN_ERR "%s: timeout, the slave no response\n",
+                                                               __func__);
+               ret = 1; /* timeout, the slave no response */
+       } else { /* the slave not busy, prepare for getting data */
+               f300_set_line(dev, F300_RESET, 0);/*ready...*/
+               msleep(1);
+               f300_send_byte(dev, 0xe1);/* 0xe1 is Read */
+               msleep(1);
+               temp = f300_get_byte(dev);/*get the data length */
+               if (temp > 14)
+                       temp = 14;
+
+               for (i = 0; i < (temp + 1); i++)
+                       f300_get_byte(dev);/* get data to empty buffer */
+
+               f300_set_line(dev, F300_RESET, 1);/* received data over */
+               f300_set_line(dev, F300_DATA, 1);
+       }
+
+       return ret;
+}
+
+int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+       u8 buf[16];
+
+       buf[0] = 0x05;
+       buf[1] = 0x38;/* write port */
+       buf[2] = 0x01;/* A port, lnb power */
+
+       switch (voltage) {
+       case SEC_VOLTAGE_13:
+               buf[3] = 0x01;/* power on */
+               buf[4] = 0x02;/* B port, H/V */
+               buf[5] = 0x00;/*13V v*/
+               break;
+       case SEC_VOLTAGE_18:
+               buf[3] = 0x01;
+               buf[4] = 0x02;
+               buf[5] = 0x01;/* 18V h*/
+               break;
+       case SEC_VOLTAGE_OFF:
+               buf[3] = 0x00;/* power off */
+               buf[4] = 0x00;
+               buf[5] = 0x00;
+               break;
+       }
+
+       return f300_xfer(fe, buf);
+}
diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h
new file mode 100644 (file)
index 0000000..e73344c
--- /dev/null
@@ -0,0 +1,2 @@
+extern int f300_set_voltage(struct dvb_frontend *fe,
+                               fe_sec_voltage_t voltage);
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
new file mode 100644 (file)
index 0000000..469e083
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared remote control input device
+ *
+ *  Most of this file is
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.net>
+ *
+ *  However, the cx23885_input_{init,fini} functions contained herein are
+ *  derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+ *
+ *  Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ *  Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ *                    Markus Rechberger <mrechberger@gmail.com>
+ *                    Mauro Carvalho Chehab <mchehab@infradead.org>
+ *                    Sascha Sommer <saschasommer@freenet.de>
+ *  Copyright (C) 2004, 2005 Chris Pascoe
+ *  Copyright (C) 2003, 2004 Gerd Knorr
+ *  Copyright (C) 2003 Pavel Machek
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/input.h>
+#include <media/ir-common.h>
+#include <media/v4l2-subdev.h>
+
+#include "cx23885.h"
+
+#define RC5_BITS               14
+#define RC5_HALF_BITS          (2*RC5_BITS)
+#define RC5_HALF_BITS_MASK     ((1 << RC5_HALF_BITS) - 1)
+
+#define RC5_START_BITS_NORMAL  0x3 /* Command range  0 -  63 */
+#define RC5_START_BITS_EXTENDED        0x2 /* Command range 64 - 127 */
+
+#define RC5_EXTENDED_COMMAND_OFFSET    64
+
+static inline unsigned int rc5_command(u32 rc5_baseband)
+{
+       return RC5_INSTR(rc5_baseband) +
+               ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED)
+                       ? RC5_EXTENDED_COMMAND_OFFSET : 0);
+}
+
+static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev)
+{
+       struct card_ir *ir_input = dev->ir_input;
+       unsigned int code, command;
+       u32 rc5;
+
+       /* Ignore codes that are too short to be valid RC-5 */
+       if (ir_input->last_bit < (RC5_HALF_BITS - 1))
+               return;
+
+       /* The library has the manchester coding backwards; XOR to adapt. */
+       code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK;
+       rc5 = ir_rc5_decode(code);
+
+       switch (RC5_START(rc5)) {
+       case RC5_START_BITS_NORMAL:
+               break;
+       case RC5_START_BITS_EXTENDED:
+               /* Don't allow if the remote only emits standard commands */
+               if (ir_input->start == RC5_START_BITS_NORMAL)
+                       return;
+               break;
+       default:
+               return;
+       }
+
+       if (ir_input->addr != RC5_ADDR(rc5))
+               return;
+
+       /* Don't generate a keypress for RC-5 auto-repeated keypresses */
+       command = rc5_command(rc5);
+       if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) ||
+           command != rc5_command(ir_input->last_rc5) ||
+           /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */
+           RC5_START(ir_input->last_rc5) == 0) {
+               /* This keypress is differnet: not an auto repeat */
+               ir_input_nokey(ir_input->dev, &ir_input->ir);
+               ir_input_keydown(ir_input->dev, &ir_input->ir, command);
+       }
+       ir_input->last_rc5 = rc5;
+
+       /* Schedule when we should do the key up event: ir_input_nokey() */
+       mod_timer(&ir_input->timer_keyup,
+                 jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout));
+}
+
+static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev,
+                                              u32 ns_pulse)
+{
+       const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */
+       struct card_ir *ir_input = dev->ir_input;
+       int i, level, quarterbits, halfbits;
+
+       if (!ir_input->active) {
+               ir_input->active = 1;
+               /* assume an initial space that we may not detect or measure */
+               ir_input->code = 0;
+               ir_input->last_bit = 0;
+       }
+
+       if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
+               ir_input->last_bit++; /* Account for the final space */
+               ir_input->active = 0;
+               cx23885_input_process_raw_rc5(dev);
+               return;
+       }
+
+       level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0;
+
+       /* Skip any leading space to sync to the start bit */
+       if (ir_input->last_bit == 0 && level == 0)
+               return;
+
+       /*
+        * With valid RC-5 we can get up to two consecutive half-bits in a
+        * single pulse measurment.  Experiments have shown that the duration
+        * of a half-bit can vary.  Make sure we always end up with an even
+        * number of quarter bits at the same level (mark or space).
+        */
+       ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+       quarterbits = ns_pulse / rc5_quarterbit_ns;
+       if (quarterbits & 1)
+               quarterbits++;
+       halfbits = quarterbits / 2;
+
+       for (i = 0; i < halfbits; i++) {
+               ir_input->last_bit++;
+               ir_input->code |= (level << ir_input->last_bit);
+
+               if (ir_input->last_bit >= RC5_HALF_BITS-1) {
+                       ir_input->active = 0;
+                       cx23885_input_process_raw_rc5(dev);
+                       /*
+                        * If level is 1, a leading mark is invalid for RC5.
+                        * If level is 0, we scan past extra intial space.
+                        * Either way we don't want to reactivate collecting
+                        * marks or spaces here with any left over half-bits.
+                        */
+                       break;
+               }
+       }
+}
+
+static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev,
+                                                  bool add_eom)
+{
+       struct card_ir *ir_input = dev->ir_input;
+       struct ir_input_state *ir_input_state = &ir_input->ir;
+
+       u32 ns_pulse[RC5_HALF_BITS+1];
+       ssize_t num = 0;
+       int count, i;
+
+       do {
+               v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse,
+                                sizeof(ns_pulse), &num);
+
+               count = num / sizeof(u32);
+
+               /* Append an end of Rx seq, if the caller requested */
+               if (add_eom && count < ARRAY_SIZE(ns_pulse)) {
+                       ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+                       count++;
+               }
+
+               /* Just drain the Rx FIFO, if we're called, but not RC-5 */
+               if (ir_input_state->ir_type != IR_TYPE_RC5)
+                       continue;
+
+               for (i = 0; i < count; i++)
+                       cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]);
+       } while (num != 0);
+}
+
+void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+{
+       struct v4l2_subdev_ir_parameters params;
+       int overrun, data_available;
+
+       if (dev->sd_ir == NULL || events == 0)
+               return;
+
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               /*
+                * The only board we handle right now.  However other boards
+                * using the CX2388x integrated IR controller should be similar
+                */
+               break;
+       default:
+               return;
+       }
+
+       overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+                           V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+
+       data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+                                  V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+
+       if (overrun) {
+               /* If there was a FIFO overrun, stop the device */
+               v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+               params.enable = false;
+               /* Mitigate race with cx23885_input_ir_stop() */
+               params.shutdown = atomic_read(&dev->ir_input_stopping);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+       }
+
+       if (data_available)
+               cx23885_input_process_pulse_widths_rc5(dev, overrun);
+
+       if (overrun) {
+               /* If there was a FIFO overrun, clear & restart the device */
+               params.enable = true;
+               /* Mitigate race with cx23885_input_ir_stop() */
+               params.shutdown = atomic_read(&dev->ir_input_stopping);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+       }
+}
+
+static void cx23885_input_ir_start(struct cx23885_dev *dev)
+{
+       struct card_ir *ir_input = dev->ir_input;
+       struct ir_input_state *ir_input_state = &ir_input->ir;
+       struct v4l2_subdev_ir_parameters params;
+
+       if (dev->sd_ir == NULL)
+               return;
+
+       atomic_set(&dev->ir_input_stopping, 0);
+
+       /* keyup timer set up, if needed */
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               setup_timer(&ir_input->timer_keyup,
+                           ir_rc5_timer_keyup, /* Not actually RC-5 specific */
+                           (unsigned long) ir_input);
+               if (ir_input_state->ir_type == IR_TYPE_RC5) {
+                       /*
+                        * RC-5 repeats a held key every
+                        * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms
+                        */
+                       ir_input->rc5_key_timeout = 115;
+               }
+               break;
+       }
+
+       v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               /*
+                * The IR controller on this board only returns pulse widths.
+                * Any other mode setting will fail to set up the device.
+               */
+               params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+               params.enable = true;
+               params.interrupt_enable = true;
+               params.shutdown = false;
+
+               /* Setup for baseband compatible with both RC-5 and RC-6A */
+               params.modulation = false;
+               /* RC-5:  2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+               /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+               params.max_pulse_width = 3333333; /* ns */
+               /* RC-5:    666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+               /* RC-6A:   333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+               params.noise_filter_min_width = 333333; /* ns */
+               /*
+                * This board has inverted receive sense:
+                * mark is received as low logic level;
+                * falling edges are detected as rising edges; etc.
+                */
+               params.invert = true;
+               break;
+       }
+       v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+}
+
+static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+{
+       struct card_ir *ir_input = dev->ir_input;
+       struct v4l2_subdev_ir_parameters params;
+
+       if (dev->sd_ir == NULL)
+               return;
+
+       /*
+        * Stop the sd_ir subdevice from generating notifications and
+        * scheduling work.
+        * It is shutdown this way in order to mitigate a race with
+        * cx23885_input_rx_work_handler() in the overrun case, which could
+        * re-enable the subdevice.
+        */
+       atomic_set(&dev->ir_input_stopping, 1);
+       v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+       while (params.shutdown == false) {
+               params.enable = false;
+               params.interrupt_enable = false;
+               params.shutdown = true;
+               v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+               v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+       }
+
+       flush_scheduled_work();
+
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               del_timer_sync(&ir_input->timer_keyup);
+               break;
+       }
+}
+
+int cx23885_input_init(struct cx23885_dev *dev)
+{
+       struct card_ir *ir;
+       struct input_dev *input_dev;
+       struct ir_scancode_table *ir_codes = NULL;
+       int ir_type, ir_addr, ir_start;
+       int ret;
+
+       /*
+        * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+        * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+        */
+       if (dev->sd_ir == NULL)
+               return -ENODEV;
+
+       switch (dev->board) {
+       case CX23885_BOARD_HAUPPAUGE_HVR1850:
+       case CX23885_BOARD_HAUPPAUGE_HVR1290:
+               /* Parameters for the grey Hauppauge remote for the HVR-1850 */
+               ir_codes = &ir_codes_hauppauge_new_table;
+               ir_type = IR_TYPE_RC5;
+               ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */
+               ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */
+               break;
+       }
+       if (ir_codes == NULL)
+               return -ENODEV;
+
+       ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!ir || !input_dev) {
+               ret = -ENOMEM;
+               goto err_out_free;
+       }
+
+       ir->dev = input_dev;
+       ir->addr = ir_addr;
+       ir->start = ir_start;
+
+       /* init input device */
+       snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)",
+                cx23885_boards[dev->board].name);
+       snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci));
+
+       ret = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       if (ret < 0)
+               goto err_out_free;
+
+       input_dev->name = ir->name;
+       input_dev->phys = ir->phys;
+       input_dev->id.bustype = BUS_PCI;
+       input_dev->id.version = 1;
+       if (dev->pci->subsystem_vendor) {
+               input_dev->id.vendor  = dev->pci->subsystem_vendor;
+               input_dev->id.product = dev->pci->subsystem_device;
+       } else {
+               input_dev->id.vendor  = dev->pci->vendor;
+               input_dev->id.product = dev->pci->device;
+       }
+       input_dev->dev.parent = &dev->pci->dev;
+
+       dev->ir_input = ir;
+       cx23885_input_ir_start(dev);
+
+       ret = input_register_device(ir->dev);
+       if (ret)
+               goto err_out_stop;
+
+       return 0;
+
+err_out_stop:
+       cx23885_input_ir_stop(dev);
+       dev->ir_input = NULL;
+err_out_free:
+       ir_input_free(input_dev);
+       input_free_device(input_dev);
+       kfree(ir);
+       return ret;
+}
+
+void cx23885_input_fini(struct cx23885_dev *dev)
+{
+       /* Always stop the IR hardware from generating interrupts */
+       cx23885_input_ir_stop(dev);
+
+       if (dev->ir_input == NULL)
+               return;
+       ir_input_free(dev->ir_input->dev);
+       input_unregister_device(dev->ir_input->dev);
+       kfree(dev->ir_input);
+       dev->ir_input = NULL;
+}
diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/video/cx23885/cx23885-input.h
new file mode 100644 (file)
index 0000000..3572cb1
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared remote control input device
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_INPUT_H_
+#define _CX23885_INPUT_H_
+int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events);
+
+int cx23885_input_init(struct cx23885_dev *dev);
+void cx23885_input_fini(struct cx23885_dev *dev);
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/video/cx23885/cx23885-ioctl.c
new file mode 100644 (file)
index 0000000..dfb4627
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Various common ioctl() support functions
+ *
+ *  Copyright (c) 2009 Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx23885.h"
+#include <media/v4l2-chip-ident.h>
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+                        struct v4l2_dbg_chip_ident *chip)
+{
+       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+       int err = 0;
+       u8 rev;
+
+       chip->ident = V4L2_IDENT_NONE;
+       chip->revision = 0;
+       switch (chip->match.type) {
+       case V4L2_CHIP_MATCH_HOST:
+               switch (chip->match.addr) {
+               case 0:
+                       rev = cx_read(RDR_CFG2) & 0xff;
+                       switch (dev->pci->device) {
+                       case 0x8852:
+                               /* rev 0x04 could be '885 or '888. Pick '888. */
+                               if (rev == 0x04)
+                                       chip->ident = V4L2_IDENT_CX23888;
+                               else
+                                       chip->ident = V4L2_IDENT_CX23885;
+                               break;
+                       case 0x8880:
+                               if (rev == 0x0e || rev == 0x0f)
+                                       chip->ident = V4L2_IDENT_CX23887;
+                               else
+                                       chip->ident = V4L2_IDENT_CX23888;
+                               break;
+                       default:
+                               chip->ident = V4L2_IDENT_UNKNOWN;
+                               break;
+                       }
+                       chip->revision = (dev->pci->device << 16) | (rev << 8) |
+                                        (dev->hwrevision & 0xff);
+                       break;
+               case 1:
+                       if (dev->v4l_device != NULL) {
+                               chip->ident = V4L2_IDENT_CX23417;
+                               chip->revision = 0;
+                       }
+                       break;
+               case 2:
+                       /*
+                        * The integrated IR controller on the CX23888 is
+                        * host chip 2.  It may not be used/initialized or sd_ir
+                        * may be pointing at the cx25840 subdevice for the
+                        * IR controller on the CX23885.  Thus we find it
+                        * without using the dev->sd_ir pointer.
+                        */
+                       call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
+                               chip);
+                       break;
+               default:
+                       err = -EINVAL; /* per V4L2 spec */
+                       break;
+               }
+               break;
+       case V4L2_CHIP_MATCH_I2C_DRIVER:
+               /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+               call_all(dev, core, g_chip_ident, chip);
+               break;
+       case V4L2_CHIP_MATCH_I2C_ADDR:
+               /*
+                * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+                * to look if a chip is at the address with no driver.  That's a
+                * dangerous thing to do with EEPROMs anyway.
+                */
+               call_all(dev, core, g_chip_ident, chip);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23885_g_host_register(struct cx23885_dev *dev,
+                                  struct v4l2_dbg_register *reg)
+{
+       if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+               return -EINVAL;
+
+       reg->size = 4;
+       reg->val = cx_read(reg->reg);
+       return 0;
+}
+
+static int cx23417_g_register(struct cx23885_dev *dev,
+                             struct v4l2_dbg_register *reg)
+{
+       u32 value;
+
+       if (dev->v4l_device == NULL)
+               return -EINVAL;
+
+       if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+               return -EINVAL;
+
+       if (mc417_register_read(dev, (u16) reg->reg, &value))
+               return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+       reg->size = 4;
+       reg->val = value;
+       return 0;
+}
+
+int cx23885_g_register(struct file *file, void *fh,
+                      struct v4l2_dbg_register *reg)
+{
+       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+               switch (reg->match.addr) {
+               case 0:
+                       return cx23885_g_host_register(dev, reg);
+               case 1:
+                       return cx23417_g_register(dev, reg);
+               default:
+                       break;
+               }
+       }
+
+       /* FIXME - any error returns should not be ignored */
+       call_all(dev, core, g_register, reg);
+       return 0;
+}
+
+static int cx23885_s_host_register(struct cx23885_dev *dev,
+                                  struct v4l2_dbg_register *reg)
+{
+       if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
+               return -EINVAL;
+
+       reg->size = 4;
+       cx_write(reg->reg, reg->val);
+       return 0;
+}
+
+static int cx23417_s_register(struct cx23885_dev *dev,
+                             struct v4l2_dbg_register *reg)
+{
+       if (dev->v4l_device == NULL)
+               return -EINVAL;
+
+       if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
+               return -EINVAL;
+
+       if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val))
+               return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
+
+       reg->size = 4;
+       return 0;
+}
+
+int cx23885_s_register(struct file *file, void *fh,
+                      struct v4l2_dbg_register *reg)
+{
+       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
+               switch (reg->match.addr) {
+               case 0:
+                       return cx23885_s_host_register(dev, reg);
+               case 1:
+                       return cx23417_s_register(dev, reg);
+               default:
+                       break;
+               }
+       }
+
+       /* FIXME - any error returns should not be ignored */
+       call_all(dev, core, s_register, reg);
+       return 0;
+}
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/video/cx23885/cx23885-ioctl.h
new file mode 100644 (file)
index 0000000..80b0f49
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Various common ioctl() support functions
+ *
+ *  Copyright (c) 2009 Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _CX23885_IOCTL_H_
+#define _CX23885_IOCTL_H_
+
+int cx23885_g_chip_ident(struct file *file, void *fh,
+                        struct v4l2_dbg_chip_ident *chip);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx23885_g_register(struct file *file, void *fh,
+                      struct v4l2_dbg_register *reg);
+
+
+int cx23885_s_register(struct file *file, void *fh,
+                      struct v4l2_dbg_register *reg);
+
+#endif
+#endif
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
new file mode 100644 (file)
index 0000000..6ae982c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <media/v4l2-device.h>
+
+#include "cx23885.h"
+#include "cx23885-input.h"
+
+#define CX23885_IR_RX_FIFO_SERVICE_REQ         0
+#define CX23885_IR_RX_END_OF_RX_DETECTED       1
+#define CX23885_IR_RX_HW_FIFO_OVERRUN          2
+#define CX23885_IR_RX_SW_FIFO_OVERRUN          3
+
+#define CX23885_IR_TX_FIFO_SERVICE_REQ         0
+
+
+void cx23885_ir_rx_work_handler(struct work_struct *work)
+{
+       struct cx23885_dev *dev =
+                            container_of(work, struct cx23885_dev, ir_rx_work);
+       u32 events = 0;
+       unsigned long *notifications = &dev->ir_rx_notifications;
+
+       if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications))
+               events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+       if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications))
+               events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+       if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications))
+               events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+       if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications))
+               events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+       if (events == 0)
+               return;
+
+       if (dev->ir_input)
+               cx23885_input_rx_work_handler(dev, events);
+}
+
+void cx23885_ir_tx_work_handler(struct work_struct *work)
+{
+       struct cx23885_dev *dev =
+                            container_of(work, struct cx23885_dev, ir_tx_work);
+       u32 events = 0;
+       unsigned long *notifications = &dev->ir_tx_notifications;
+
+       if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications))
+               events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+
+       if (events == 0)
+               return;
+
+}
+
+/* Called in an IRQ context */
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+       struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+       unsigned long *notifications = &dev->ir_rx_notifications;
+
+       if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ)
+               set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications);
+       if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED)
+               set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications);
+       if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN)
+               set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
+       if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
+               set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
+       schedule_work(&dev->ir_rx_work);
+}
+
+/* Called in an IRQ context */
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
+{
+       struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
+       unsigned long *notifications = &dev->ir_tx_notifications;
+
+       if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
+               set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
+       schedule_work(&dev->ir_tx_work);
+}
diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h
new file mode 100644 (file)
index 0000000..9b8a6d5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  Infrared device support routines - non-input, non-vl42_subdev routines
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23885_IR_H_
+#define _CX23885_IR_H_
+void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events);
+
+void cx23885_ir_rx_work_handler(struct work_struct *work);
+void cx23885_ir_tx_work_handler(struct work_struct *work);
+#endif
index eafbe52..c0bc9a0 100644 (file)
@@ -212,8 +212,9 @@ Channel manager Data Structure entry = 20 DWORD
 
 #define DEV_CNTRL2     0x00040000
 
-#define PCI_MSK_GPIO1   (1 << 24)
-#define PCI_MSK_GPIO0   (1 << 23)
+#define PCI_MSK_IR        (1 << 28)
+#define PCI_MSK_GPIO1     (1 << 24)
+#define PCI_MSK_GPIO0     (1 << 23)
 #define PCI_MSK_APB_DMA   (1 << 12)
 #define PCI_MSK_AL_WR     (1 << 11)
 #define PCI_MSK_AL_RD     (1 << 10)
index 654cc25..8b372b4 100644 (file)
@@ -35,6 +35,7 @@
 #include "cx23885.h"
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include "cx23885-ioctl.h"
 
 MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
 MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
@@ -401,6 +402,13 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
                INPUT(input)->gpio2, INPUT(input)->gpio3);
        dev->input = input;
 
+       if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
+               dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2) {
+               /* Select Analog TV */
+               if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
+                       cx23885_gpio_clear(dev, GPIO_0);
+       }
+
        /* Tell the internal A/V decoder */
        v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
                        INPUT(input)->vmux, 0, 0);
@@ -1144,6 +1152,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
                [CX23885_VMUX_COMPOSITE3] = "Composite3",
                [CX23885_VMUX_COMPOSITE4] = "Composite4",
                [CX23885_VMUX_SVIDEO]     = "S-Video",
+               [CX23885_VMUX_COMPONENT]  = "Component",
                [CX23885_VMUX_TELEVISION] = "Television",
                [CX23885_VMUX_CABLE]      = "Cable TV",
                [CX23885_VMUX_DVB]        = "DVB",
@@ -1312,34 +1321,6 @@ static int vidioc_s_frequency(struct file *file, void *priv,
                cx23885_set_freq(dev, f);
 }
 
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register(struct file *file, void *fh,
-                               struct v4l2_dbg_register *reg)
-{
-       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
-
-       if (!v4l2_chip_match_host(&reg->match))
-               return -EINVAL;
-
-       call_all(dev, core, g_register, reg);
-
-       return 0;
-}
-
-static int vidioc_s_register(struct file *file, void *fh,
-                               struct v4l2_dbg_register *reg)
-{
-       struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
-
-       if (!v4l2_chip_match_host(&reg->match))
-               return -EINVAL;
-
-       call_all(dev, core, s_register, reg);
-
-       return 0;
-}
-#endif
-
 /* ----------------------------------------------------------- */
 
 static void cx23885_vid_timeout(unsigned long data)
@@ -1449,9 +1430,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
        .vidioc_s_tuner       = vidioc_s_tuner,
        .vidioc_g_frequency   = vidioc_g_frequency,
        .vidioc_s_frequency   = vidioc_s_frequency,
+       .vidioc_g_chip_ident  = cx23885_g_chip_ident,
 #ifdef CONFIG_VIDEO_ADV_DEBUG
-       .vidioc_g_register    = vidioc_g_register,
-       .vidioc_s_register    = vidioc_s_register,
+       .vidioc_g_register    = cx23885_g_register,
+       .vidioc_s_register    = cx23885_s_register,
 #endif
 };
 
@@ -1529,9 +1511,11 @@ int cx23885_video_register(struct cx23885_dev *dev)
                if (sd) {
                        struct tuner_setup tun_setup;
 
+                       memset(&tun_setup, 0, sizeof(tun_setup));
                        tun_setup.mode_mask = T_ANALOG_TV;
                        tun_setup.type = dev->tuner_type;
                        tun_setup.addr = v4l2_i2c_subdev_addr(sd);
+                       tun_setup.tuner_callback = cx23885_tuner_callback;
 
                        v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
                }
index cc7a165..fa74476 100644 (file)
@@ -79,6 +79,8 @@
 #define CX23885_BOARD_MAGICPRO_PROHDTVE2       23
 #define CX23885_BOARD_HAUPPAUGE_HVR1850        24
 #define CX23885_BOARD_COMPRO_VIDEOMATE_E800    25
+#define CX23885_BOARD_HAUPPAUGE_HVR1290        26
+#define CX23885_BOARD_MYGICA_X8558PRO          27
 
 #define GPIO_0 0x00000001
 #define GPIO_1 0x00000002
@@ -157,6 +159,7 @@ enum cx23885_itype {
        CX23885_VMUX_COMPOSITE3,
        CX23885_VMUX_COMPOSITE4,
        CX23885_VMUX_SVIDEO,
+       CX23885_VMUX_COMPONENT,
        CX23885_VMUX_TELEVISION,
        CX23885_VMUX_CABLE,
        CX23885_VMUX_DVB,
@@ -297,10 +300,6 @@ struct cx23885_tsport {
        /* Allow a single tsport to have multiple frontends */
        u32                        num_frontends;
        void                       *port_priv;
-
-       /* FIXME: temporary hack */
-       int (*set_frontend_save) (struct dvb_frontend *,
-                                 struct dvb_frontend_parameters *);
 };
 
 struct cx23885_dev {
@@ -356,6 +355,16 @@ struct cx23885_dev {
        unsigned int               has_radio;
        struct v4l2_subdev         *sd_cx25840;
 
+       /* Infrared */
+       struct v4l2_subdev         *sd_ir;
+       struct work_struct         ir_rx_work;
+       unsigned long              ir_rx_notifications;
+       struct work_struct         ir_tx_work;
+       unsigned long              ir_tx_notifications;
+
+       struct card_ir             *ir_input;
+       atomic_t                   ir_input_stopping;
+
        /* V4l */
        u32                        freq;
        struct video_device        *video_dev;
@@ -383,6 +392,13 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
 #define call_all(dev, o, f, args...) \
        v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
 
+#define CX23885_HW_888_IR (1 << 0)
+
+#define call_hw(dev, grpid, o, f, args...) \
+       v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
+
+extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
+
 extern struct list_head cx23885_devlist;
 
 #define SRAM_CH01  0 /* Video A */
@@ -455,6 +471,7 @@ extern void cx23885_wakeup(struct cx23885_tsport *port,
 
 extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
 extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
 extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
        int asoutput);
 
@@ -471,6 +488,8 @@ extern int cx23885_tuner_callback(void *priv, int component,
        int command, int arg);
 extern void cx23885_card_list(struct cx23885_dev *dev);
 extern int  cx23885_ir_init(struct cx23885_dev *dev);
+extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev);
+extern void cx23885_ir_fini(struct cx23885_dev *dev);
 extern void cx23885_gpio_setup(struct cx23885_dev *dev);
 extern void cx23885_card_setup(struct cx23885_dev *dev);
 extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
@@ -515,6 +534,10 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
 extern void cx23885_mc417_init(struct cx23885_dev *dev);
 extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
 extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
+extern int mc417_register_read(struct cx23885_dev *dev,
+                               u16 address, u32 *value);
+extern int mc417_register_write(struct cx23885_dev *dev,
+                               u16 address, u32 value);
 extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
 extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
 extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
new file mode 100644 (file)
index 0000000..3ccc8af
--- /dev/null
@@ -0,0 +1,1239 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  CX23888 Integrated Consumer Infrared Controller
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#include <linux/kfifo.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "cx23885.h"
+
+static unsigned int ir_888_debug;
+module_param(ir_888_debug, int, 0644);
+MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
+
+#define CX23888_IR_REG_BASE    0x170000
+/*
+ * These CX23888 register offsets have a straightforward one to one mapping
+ * to the CX23885 register offsets of 0x200 through 0x218
+ */
+#define CX23888_IR_CNTRL_REG   0x170000
+#define CNTRL_WIN_3_3  0x00000000
+#define CNTRL_WIN_4_3  0x00000001
+#define CNTRL_WIN_3_4  0x00000002
+#define CNTRL_WIN_4_4  0x00000003
+#define CNTRL_WIN      0x00000003
+#define CNTRL_EDG_NONE 0x00000000
+#define CNTRL_EDG_FALL 0x00000004
+#define CNTRL_EDG_RISE 0x00000008
+#define CNTRL_EDG_BOTH 0x0000000C
+#define CNTRL_EDG      0x0000000C
+#define CNTRL_DMD      0x00000010
+#define CNTRL_MOD      0x00000020
+#define CNTRL_RFE      0x00000040
+#define CNTRL_TFE      0x00000080
+#define CNTRL_RXE      0x00000100
+#define CNTRL_TXE      0x00000200
+#define CNTRL_RIC      0x00000400
+#define CNTRL_TIC      0x00000800
+#define CNTRL_CPL      0x00001000
+#define CNTRL_LBM      0x00002000
+#define CNTRL_R                0x00004000
+
+#define CX23888_IR_TXCLK_REG   0x170004
+#define TXCLK_TCD      0x0000FFFF
+
+#define CX23888_IR_RXCLK_REG   0x170008
+#define RXCLK_RCD      0x0000FFFF
+
+#define CX23888_IR_CDUTY_REG   0x17000C
+#define CDUTY_CDC      0x0000000F
+
+#define CX23888_IR_STATS_REG   0x170010
+#define STATS_RTO      0x00000001
+#define STATS_ROR      0x00000002
+#define STATS_RBY      0x00000004
+#define STATS_TBY      0x00000008
+#define STATS_RSR      0x00000010
+#define STATS_TSR      0x00000020
+
+#define CX23888_IR_IRQEN_REG   0x170014
+#define IRQEN_RTE      0x00000001
+#define IRQEN_ROE      0x00000002
+#define IRQEN_RSE      0x00000010
+#define IRQEN_TSE      0x00000020
+
+#define CX23888_IR_FILTR_REG   0x170018
+#define FILTR_LPF      0x0000FFFF
+
+/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */
+#define CX23888_IR_FIFO_REG    0x170040
+#define FIFO_RXTX      0x0000FFFF
+#define FIFO_RXTX_LVL  0x00010000
+#define FIFO_RXTX_RTO  0x0001FFFF
+#define FIFO_RX_NDV    0x00020000
+#define FIFO_RX_DEPTH  8
+#define FIFO_TX_DEPTH  8
+
+/* CX23888 unique registers */
+#define CX23888_IR_SEEDP_REG   0x17001C
+#define CX23888_IR_TIMOL_REG   0x170020
+#define CX23888_IR_WAKE0_REG   0x170024
+#define CX23888_IR_WAKE1_REG   0x170028
+#define CX23888_IR_WAKE2_REG   0x17002C
+#define CX23888_IR_MASK0_REG   0x170030
+#define CX23888_IR_MASK1_REG   0x170034
+#define CX23888_IR_MAKS2_REG   0x170038
+#define CX23888_IR_DPIPG_REG   0x17003C
+#define CX23888_IR_LEARN_REG   0x170044
+
+#define CX23888_VIDCLK_FREQ    108000000 /* 108 MHz, BT.656 */
+#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2)
+
+#define CX23888_IR_RX_KFIFO_SIZE       (512 * sizeof(u32))
+#define CX23888_IR_TX_KFIFO_SIZE       (512 * sizeof(u32))
+
+struct cx23888_ir_state {
+       struct v4l2_subdev sd;
+       struct cx23885_dev *dev;
+       u32 id;
+       u32 rev;
+
+       struct v4l2_subdev_ir_parameters rx_params;
+       struct mutex rx_params_lock;
+       atomic_t rxclk_divider;
+       atomic_t rx_invert;
+
+       struct kfifo *rx_kfifo;
+       spinlock_t rx_kfifo_lock;
+
+       struct v4l2_subdev_ir_parameters tx_params;
+       struct mutex tx_params_lock;
+       atomic_t txclk_divider;
+
+       struct kfifo *tx_kfifo;
+       spinlock_t tx_kfifo_lock;
+};
+
+static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd)
+{
+       return v4l2_get_subdevdata(sd);
+}
+
+/*
+ * IR register block read and write functions
+ */
+static
+inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value)
+{
+       cx_write(addr, value);
+       return 0;
+}
+
+static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr)
+{
+       return cx_read(addr);
+}
+
+static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr,
+                                    u32 and_mask, u32 or_value)
+{
+       cx_andor(addr, ~and_mask, or_value);
+       return 0;
+}
+
+/*
+ * Rx and Tx Clock Divider register computations
+ *
+ * Note the largest clock divider value of 0xffff corresponds to:
+ *     (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_clock_divider(unsigned int d)
+{
+       if (d > RXCLK_RCD + 1)
+               d = RXCLK_RCD;
+       else if (d < 2)
+               d = 1;
+       else
+               d--;
+       return (u16) d;
+}
+
+static inline u16 ns_to_clock_divider(unsigned int ns)
+{
+       return count_to_clock_divider(
+               DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int clock_divider_to_ns(unsigned int divider)
+{
+       /* Period of the Rx or Tx clock in ns */
+       return DIV_ROUND_CLOSEST((divider + 1) * 1000,
+                                CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline u16 carrier_freq_to_clock_divider(unsigned int freq)
+{
+       return count_to_clock_divider(
+                         DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16));
+}
+
+static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
+{
+       return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16);
+}
+
+static inline u16 freq_to_clock_divider(unsigned int freq,
+                                       unsigned int rollovers)
+{
+       return count_to_clock_divider(
+                  DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers));
+}
+
+static inline unsigned int clock_divider_to_freq(unsigned int divider,
+                                                unsigned int rollovers)
+{
+       return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ,
+                                (divider + 1) * rollovers);
+}
+
+/*
+ * Low Pass Filter register calculations
+ *
+ * Note the largest count value of 0xffff corresponds to:
+ *     0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
+ * which fits in 21 bits, so we'll use unsigned int for time arguments.
+ */
+static inline u16 count_to_lpf_count(unsigned int d)
+{
+       if (d > FILTR_LPF)
+               d = FILTR_LPF;
+       else if (d < 4)
+               d = 0;
+       return (u16) d;
+}
+
+static inline u16 ns_to_lpf_count(unsigned int ns)
+{
+       return count_to_lpf_count(
+               DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000));
+}
+
+static inline unsigned int lpf_count_to_ns(unsigned int count)
+{
+       /* Duration of the Low Pass Filter rejection window in ns */
+       return DIV_ROUND_CLOSEST(count * 1000,
+                                CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static inline unsigned int lpf_count_to_us(unsigned int count)
+{
+       /* Duration of the Low Pass Filter rejection window in us */
+       return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+/*
+ * FIFO register pulse width count compuations
+ */
+static u32 clock_divider_to_resolution(u16 divider)
+{
+       /*
+        * Resolution is the duration of 1 tick of the readable portion of
+        * of the pulse width counter as read from the FIFO.  The two lsb's are
+        * not readable, hence the << 2.  This function returns ns.
+        */
+       return DIV_ROUND_CLOSEST((1 << 2)  * ((u32) divider + 1) * 1000,
+                                CX23888_IR_REFCLK_FREQ / 1000000);
+}
+
+static u64 pulse_width_count_to_ns(u16 count, u16 divider)
+{
+       u64 n;
+       u32 rem;
+
+       /*
+        * The 2 lsb's of the pulse width timer count are not readable, hence
+        * the (count << 2) | 0x3
+        */
+       n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
+       rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000);     /* / MHz => ns */
+       if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+               n++;
+       return n;
+}
+
+static unsigned int pulse_width_count_to_us(u16 count, u16 divider)
+{
+       u64 n;
+       u32 rem;
+
+       /*
+        * The 2 lsb's of the pulse width timer count are not readable, hence
+        * the (count << 2) | 0x3
+        */
+       n = (((u64) count << 2) | 0x3) * (divider + 1);    /* cycles      */
+       rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
+       if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2)
+               n++;
+       return (unsigned int) n;
+}
+
+/*
+ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
+ *
+ * The total pulse clock count is an 18 bit pulse width timer count as the most
+ * significant part and (up to) 16 bit clock divider count as a modulus.
+ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
+ * width timer count's least significant bit.
+ */
+static u64 ns_to_pulse_clocks(u32 ns)
+{
+       u64 clocks;
+       u32 rem;
+       clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles  */
+       rem = do_div(clocks, 1000);                         /* /1000 = cycles */
+       if (rem >= 1000 / 2)
+               clocks++;
+       return clocks;
+}
+
+static u16 pulse_clocks_to_clock_divider(u64 count)
+{
+       u32 rem;
+
+       rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+
+       /* net result needs to be rounded down and decremented by 1 */
+       if (count > RXCLK_RCD + 1)
+               count = RXCLK_RCD;
+       else if (count < 2)
+               count = 1;
+       else
+               count--;
+       return (u16) count;
+}
+
+/*
+ * IR Control Register helpers
+ */
+enum tx_fifo_watermark {
+       TX_FIFO_HALF_EMPTY = 0,
+       TX_FIFO_EMPTY      = CNTRL_TIC,
+};
+
+enum rx_fifo_watermark {
+       RX_FIFO_HALF_FULL = 0,
+       RX_FIFO_NOT_EMPTY = CNTRL_RIC,
+};
+
+static inline void control_tx_irq_watermark(struct cx23885_dev *dev,
+                                           enum tx_fifo_watermark level)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level);
+}
+
+static inline void control_rx_irq_watermark(struct cx23885_dev *dev,
+                                           enum rx_fifo_watermark level)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level);
+}
+
+static inline void control_tx_enable(struct cx23885_dev *dev, bool enable)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
+                          enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
+}
+
+static inline void control_rx_enable(struct cx23885_dev *dev, bool enable)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
+                          enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
+}
+
+static inline void control_tx_modulation_enable(struct cx23885_dev *dev,
+                                               bool enable)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD,
+                          enable ? CNTRL_MOD : 0);
+}
+
+static inline void control_rx_demodulation_enable(struct cx23885_dev *dev,
+                                                 bool enable)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD,
+                          enable ? CNTRL_DMD : 0);
+}
+
+static inline void control_rx_s_edge_detection(struct cx23885_dev *dev,
+                                              u32 edge_types)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
+                          edge_types & CNTRL_EDG_BOTH);
+}
+
+static void control_rx_s_carrier_window(struct cx23885_dev *dev,
+                                       unsigned int carrier,
+                                       unsigned int *carrier_range_low,
+                                       unsigned int *carrier_range_high)
+{
+       u32 v;
+       unsigned int c16 = carrier * 16;
+
+       if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
+               v = CNTRL_WIN_3_4;
+               *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
+       } else {
+               v = CNTRL_WIN_3_3;
+               *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
+       }
+
+       if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
+               v |= CNTRL_WIN_4_3;
+               *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
+       } else {
+               v |= CNTRL_WIN_3_3;
+               *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
+       }
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v);
+}
+
+static inline void control_tx_polarity_invert(struct cx23885_dev *dev,
+                                             bool invert)
+{
+       cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL,
+                          invert ? CNTRL_CPL : 0);
+}
+
+/*
+ * IR Rx & Tx Clock Register helpers
+ */
+static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev,
+                                      unsigned int freq,
+                                      u16 *divider)
+{
+       *divider = carrier_freq_to_clock_divider(freq);
+       cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+       return clock_divider_to_carrier_freq(*divider);
+}
+
+static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev,
+                                      unsigned int freq,
+                                      u16 *divider)
+{
+       *divider = carrier_freq_to_clock_divider(freq);
+       cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+       return clock_divider_to_carrier_freq(*divider);
+}
+
+static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+                                     u16 *divider)
+{
+       u64 pulse_clocks;
+
+       if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+               ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+       pulse_clocks = ns_to_pulse_clocks(ns);
+       *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+       cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider);
+       return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns,
+                                     u16 *divider)
+{
+       u64 pulse_clocks;
+
+       if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+               ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+       pulse_clocks = ns_to_pulse_clocks(ns);
+       *divider = pulse_clocks_to_clock_divider(pulse_clocks);
+       cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider);
+       return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
+}
+
+/*
+ * IR Tx Carrier Duty Cycle register helpers
+ */
+static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev,
+                                         unsigned int duty_cycle)
+{
+       u32 n;
+       n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
+       if (n != 0)
+               n--;
+       if (n > 15)
+               n = 15;
+       cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n);
+       return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
+}
+
+/*
+ * IR Filter Register helpers
+ */
+static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns)
+{
+       u32 count = ns_to_lpf_count(min_width_ns);
+       cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count);
+       return lpf_count_to_ns(count);
+}
+
+/*
+ * IR IRQ Enable Register helpers
+ */
+static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask)
+{
+       mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
+       cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG,
+                          ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
+}
+
+static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask)
+{
+       mask &= IRQEN_TSE;
+       cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask);
+}
+
+/*
+ * V4L2 Subdevice IR Ops
+ */
+static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status,
+                                 bool *handled)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+
+       u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+       u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+       u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+
+       u32 rx_data[FIFO_RX_DEPTH];
+       int i, j, k;
+       u32 events, v;
+       int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
+
+       tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
+       rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
+       rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
+       ror = stats & STATS_ROR; /* Rx FIFO Over Run */
+
+       tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
+       rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */
+       rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
+       roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
+
+       *handled = false;
+       v4l2_dbg(2, ir_888_debug, sd, "IRQ Status:  %s %s %s %s %s %s\n",
+                tsr ? "tsr" : "   ", rsr ? "rsr" : "   ",
+                rto ? "rto" : "   ", ror ? "ror" : "   ",
+                stats & STATS_TBY ? "tby" : "   ",
+                stats & STATS_RBY ? "rby" : "   ");
+
+       v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n",
+                tse ? "tse" : "   ", rse ? "rse" : "   ",
+                rte ? "rte" : "   ", roe ? "roe" : "   ");
+
+       /*
+        * Transmitter interrupt service
+        */
+       if (tse && tsr) {
+               /*
+                * TODO:
+                * Check the watermark threshold setting
+                * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
+                * Push the data to the hardware FIFO.
+                * If there was nothing more to send in the tx_kfifo, disable
+                *      the TSR IRQ and notify the v4l2_device.
+                * If there was something in the tx_kfifo, check the tx_kfifo
+                *      level and notify the v4l2_device, if it is low.
+                */
+               /* For now, inhibit TSR interrupt until Tx is implemented */
+               irqenable_tx(dev, 0);
+               events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
+               v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
+               *handled = true;
+       }
+
+       /*
+        * Receiver interrupt service
+        */
+       kror = 0;
+       if ((rse && rsr) || (rte && rto)) {
+               /*
+                * Receive data on RSR to clear the STATS_RSR.
+                * Receive data on RTO, since we may not have yet hit the RSR
+                * watermark when we receive the RTO.
+                */
+               for (i = 0, v = FIFO_RX_NDV;
+                    (v & FIFO_RX_NDV) && !kror; i = 0) {
+                       for (j = 0;
+                            (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
+                               v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG);
+                               rx_data[i++] = v & ~FIFO_RX_NDV;
+                       }
+                       if (i == 0)
+                               break;
+                       j = i * sizeof(u32);
+                       k = kfifo_put(state->rx_kfifo,
+                                     (unsigned char *) rx_data, j);
+                       if (k != j)
+                               kror++; /* rx_kfifo over run */
+               }
+               *handled = true;
+       }
+
+       events = 0;
+       v = 0;
+       if (kror) {
+               events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
+               v4l2_err(sd, "IR receiver software FIFO overrun\n");
+       }
+       if (roe && ror) {
+               /*
+                * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
+                * the Rx FIFO Over Run status (STATS_ROR)
+                */
+               v |= CNTRL_RFE;
+               events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
+               v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
+       }
+       if (rte && rto) {
+               /*
+                * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
+                * the Rx Pulse Width Timer Time Out (STATS_RTO)
+                */
+               v |= CNTRL_RXE;
+               events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
+       }
+       if (v) {
+               /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */
+               cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v);
+               cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl);
+               *handled = true;
+       }
+       if (kfifo_len(state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2)
+               events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
+
+       if (events)
+               v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
+       return 0;
+}
+
+/* Receiver */
+static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
+                             ssize_t *num)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       bool invert = (bool) atomic_read(&state->rx_invert);
+       u16 divider = (u16) atomic_read(&state->rxclk_divider);
+
+       unsigned int i, n;
+       u32 *p;
+       u32 u, v;
+
+       n = count / sizeof(u32) * sizeof(u32);
+       if (n == 0) {
+               *num = 0;
+               return 0;
+       }
+
+       n = kfifo_get(state->rx_kfifo, buf, n);
+
+       n /= sizeof(u32);
+       *num = n * sizeof(u32);
+
+       for (p = (u32 *) buf, i = 0; i < n; p++, i++) {
+               if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
+                       *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+                       v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n");
+                       continue;
+               }
+
+               u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0;
+               if (invert)
+                       u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK;
+
+               v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX),
+                                                 divider);
+               if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS)
+                       v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1;
+
+               *p = u | v;
+
+               v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns  %s\n",
+                        v, u ? "mark" : "space");
+       }
+       return 0;
+}
+
+static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       mutex_lock(&state->rx_params_lock);
+       memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters));
+       mutex_unlock(&state->rx_params_lock);
+       return 0;
+}
+
+static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+
+       mutex_lock(&state->rx_params_lock);
+
+       /* Disable or slow down all IR Rx circuits and counters */
+       irqenable_rx(dev, 0);
+       control_rx_enable(dev, false);
+       control_rx_demodulation_enable(dev, false);
+       control_rx_s_edge_detection(dev, CNTRL_EDG_NONE);
+       filter_rx_s_min_width(dev, 0);
+       cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD);
+
+       state->rx_params.shutdown = true;
+
+       mutex_unlock(&state->rx_params_lock);
+       return 0;
+}
+
+static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+       struct v4l2_subdev_ir_parameters *o = &state->rx_params;
+       u16 rxclk_divider;
+
+       if (p->shutdown)
+               return cx23888_ir_rx_shutdown(sd);
+
+       if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+               return -ENOSYS;
+
+       mutex_lock(&state->rx_params_lock);
+
+       o->shutdown = p->shutdown;
+
+       o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+       o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+
+       /* Before we tweak the hardware, we have to disable the receiver */
+       irqenable_rx(dev, 0);
+       control_rx_enable(dev, false);
+
+       control_rx_demodulation_enable(dev, p->modulation);
+       o->modulation = p->modulation;
+
+       if (p->modulation) {
+               p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq,
+                                                    &rxclk_divider);
+
+               o->carrier_freq = p->carrier_freq;
+
+               o->duty_cycle = p->duty_cycle = 50;
+
+               control_rx_s_carrier_window(dev, p->carrier_freq,
+                                           &p->carrier_range_lower,
+                                           &p->carrier_range_upper);
+               o->carrier_range_lower = p->carrier_range_lower;
+               o->carrier_range_upper = p->carrier_range_upper;
+       } else {
+               p->max_pulse_width =
+                           rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width,
+                                                      &rxclk_divider);
+               o->max_pulse_width = p->max_pulse_width;
+       }
+       atomic_set(&state->rxclk_divider, rxclk_divider);
+
+       p->noise_filter_min_width =
+                         filter_rx_s_min_width(dev, p->noise_filter_min_width);
+       o->noise_filter_min_width = p->noise_filter_min_width;
+
+       p->resolution = clock_divider_to_resolution(rxclk_divider);
+       o->resolution = p->resolution;
+
+       /* FIXME - make this dependent on resolution for better performance */
+       control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL);
+
+       control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH);
+
+       o->invert = p->invert;
+       atomic_set(&state->rx_invert, p->invert);
+
+       o->interrupt_enable = p->interrupt_enable;
+       o->enable = p->enable;
+       if (p->enable) {
+               kfifo_reset(state->rx_kfifo);
+               if (p->interrupt_enable)
+                       irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
+               control_rx_enable(dev, p->enable);
+       }
+
+       mutex_unlock(&state->rx_params_lock);
+       return 0;
+}
+
+/* Transmitter */
+static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
+                              ssize_t *num)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+       /* For now enable the Tx FIFO Service interrupt & pretend we did work */
+       irqenable_tx(dev, IRQEN_TSE);
+       *num = count;
+       return 0;
+}
+
+static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       mutex_lock(&state->tx_params_lock);
+       memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters));
+       mutex_unlock(&state->tx_params_lock);
+       return 0;
+}
+
+static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+
+       mutex_lock(&state->tx_params_lock);
+
+       /* Disable or slow down all IR Tx circuits and counters */
+       irqenable_tx(dev, 0);
+       control_tx_enable(dev, false);
+       control_tx_modulation_enable(dev, false);
+       cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD);
+
+       state->tx_params.shutdown = true;
+
+       mutex_unlock(&state->tx_params_lock);
+       return 0;
+}
+
+static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd,
+                                     struct v4l2_subdev_ir_parameters *p)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+       struct v4l2_subdev_ir_parameters *o = &state->tx_params;
+       u16 txclk_divider;
+
+       if (p->shutdown)
+               return cx23888_ir_tx_shutdown(sd);
+
+       if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
+               return -ENOSYS;
+
+       mutex_lock(&state->tx_params_lock);
+
+       o->shutdown = p->shutdown;
+
+       o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+
+       o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32);
+
+       /* Before we tweak the hardware, we have to disable the transmitter */
+       irqenable_tx(dev, 0);
+       control_tx_enable(dev, false);
+
+       control_tx_modulation_enable(dev, p->modulation);
+       o->modulation = p->modulation;
+
+       if (p->modulation) {
+               p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq,
+                                                    &txclk_divider);
+               o->carrier_freq = p->carrier_freq;
+
+               p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle);
+               o->duty_cycle = p->duty_cycle;
+       } else {
+               p->max_pulse_width =
+                           txclk_tx_s_max_pulse_width(dev, p->max_pulse_width,
+                                                      &txclk_divider);
+               o->max_pulse_width = p->max_pulse_width;
+       }
+       atomic_set(&state->txclk_divider, txclk_divider);
+
+       p->resolution = clock_divider_to_resolution(txclk_divider);
+       o->resolution = p->resolution;
+
+       /* FIXME - make this dependent on resolution for better performance */
+       control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY);
+
+       control_tx_polarity_invert(dev, p->invert);
+       o->invert = p->invert;
+
+       o->interrupt_enable = p->interrupt_enable;
+       o->enable = p->enable;
+       if (p->enable) {
+               kfifo_reset(state->tx_kfifo);
+               if (p->interrupt_enable)
+                       irqenable_tx(dev, IRQEN_TSE);
+               control_tx_enable(dev, p->enable);
+       }
+
+       mutex_unlock(&state->tx_params_lock);
+       return 0;
+}
+
+
+/*
+ * V4L2 Subdevice Core Ops
+ */
+static int cx23888_ir_log_status(struct v4l2_subdev *sd)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       struct cx23885_dev *dev = state->dev;
+       char *s;
+       int i, j;
+
+       u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG);
+       u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD;
+       u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD;
+       u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC;
+       u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG);
+       u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG);
+       u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF;
+
+       v4l2_info(sd, "IR Receiver:\n");
+       v4l2_info(sd, "\tEnabled:                           %s\n",
+                 cntrl & CNTRL_RXE ? "yes" : "no");
+       v4l2_info(sd, "\tDemodulation from a carrier:       %s\n",
+                 cntrl & CNTRL_DMD ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO:                              %s\n",
+                 cntrl & CNTRL_RFE ? "enabled" : "disabled");
+       switch (cntrl & CNTRL_EDG) {
+       case CNTRL_EDG_NONE:
+               s = "disabled";
+               break;
+       case CNTRL_EDG_FALL:
+               s = "falling edge";
+               break;
+       case CNTRL_EDG_RISE:
+               s = "rising edge";
+               break;
+       case CNTRL_EDG_BOTH:
+               s = "rising & falling edges";
+               break;
+       default:
+               s = "??? edge";
+               break;
+       }
+       v4l2_info(sd, "\tPulse timers' start/stop trigger:  %s\n", s);
+       v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
+                 cntrl & CNTRL_R ? "not loaded" : "overflow marker");
+       v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
+                 cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
+       v4l2_info(sd, "\tLoopback mode:                     %s\n",
+                 cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
+       if (cntrl & CNTRL_DMD) {
+               v4l2_info(sd, "\tExpected carrier (16 clocks):      %u Hz\n",
+                         clock_divider_to_carrier_freq(rxclk));
+               switch (cntrl & CNTRL_WIN) {
+               case CNTRL_WIN_3_3:
+                       i = 3;
+                       j = 3;
+                       break;
+               case CNTRL_WIN_4_3:
+                       i = 4;
+                       j = 3;
+                       break;
+               case CNTRL_WIN_3_4:
+                       i = 3;
+                       j = 4;
+                       break;
+               case CNTRL_WIN_4_4:
+                       i = 4;
+                       j = 4;
+                       break;
+               default:
+                       i = 0;
+                       j = 0;
+                       break;
+               }
+               v4l2_info(sd, "\tNext carrier edge window:          16 clocks "
+                         "-%1d/+%1d, %u to %u Hz\n", i, j,
+                         clock_divider_to_freq(rxclk, 16 + j),
+                         clock_divider_to_freq(rxclk, 16 - i));
+       } else {
+               v4l2_info(sd, "\tMax measurable pulse width:        %u us, "
+                         "%llu ns\n",
+                         pulse_width_count_to_us(FIFO_RXTX, rxclk),
+                         pulse_width_count_to_ns(FIFO_RXTX, rxclk));
+       }
+       v4l2_info(sd, "\tLow pass filter:                   %s\n",
+                 filtr ? "enabled" : "disabled");
+       if (filtr)
+               v4l2_info(sd, "\tMin acceptable pulse width (LPF):  %u us, "
+                         "%u ns\n",
+                         lpf_count_to_us(filtr),
+                         lpf_count_to_ns(filtr));
+       v4l2_info(sd, "\tPulse width timer timed-out:       %s\n",
+                 stats & STATS_RTO ? "yes" : "no");
+       v4l2_info(sd, "\tPulse width timer time-out intr:   %s\n",
+                 irqen & IRQEN_RTE ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO overrun:                      %s\n",
+                 stats & STATS_ROR ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO overrun interrupt:            %s\n",
+                 irqen & IRQEN_ROE ? "enabled" : "disabled");
+       v4l2_info(sd, "\tBusy:                              %s\n",
+                 stats & STATS_RBY ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service requested:            %s\n",
+                 stats & STATS_RSR ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
+                 irqen & IRQEN_RSE ? "enabled" : "disabled");
+
+       v4l2_info(sd, "IR Transmitter:\n");
+       v4l2_info(sd, "\tEnabled:                           %s\n",
+                 cntrl & CNTRL_TXE ? "yes" : "no");
+       v4l2_info(sd, "\tModulation onto a carrier:         %s\n",
+                 cntrl & CNTRL_MOD ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO:                              %s\n",
+                 cntrl & CNTRL_TFE ? "enabled" : "disabled");
+       v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
+                 cntrl & CNTRL_TIC ? "not empty" : "half full or less");
+       v4l2_info(sd, "\tSignal polarity:                   %s\n",
+                 cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark");
+       if (cntrl & CNTRL_MOD) {
+               v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
+                         clock_divider_to_carrier_freq(txclk));
+               v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
+                         cduty + 1);
+       } else {
+               v4l2_info(sd, "\tMax pulse width:                   %u us, "
+                         "%llu ns\n",
+                         pulse_width_count_to_us(FIFO_RXTX, txclk),
+                         pulse_width_count_to_ns(FIFO_RXTX, txclk));
+       }
+       v4l2_info(sd, "\tBusy:                              %s\n",
+                 stats & STATS_TBY ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service requested:            %s\n",
+                 stats & STATS_TSR ? "yes" : "no");
+       v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
+                 irqen & IRQEN_TSE ? "enabled" : "disabled");
+
+       return 0;
+}
+
+static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match)
+{
+       return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2;
+}
+
+static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd,
+                                  struct v4l2_dbg_chip_ident *chip)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+
+       if (cx23888_ir_dbg_match(&chip->match)) {
+               chip->ident = state->id;
+               chip->revision = state->rev;
+       }
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx23888_ir_g_register(struct v4l2_subdev *sd,
+                                struct v4l2_dbg_register *reg)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+       if (!cx23888_ir_dbg_match(&reg->match))
+               return -EINVAL;
+       if ((addr & 0x3) != 0)
+               return -EINVAL;
+       if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+               return -EINVAL;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       reg->size = 4;
+       reg->val = cx23888_ir_read4(state->dev, addr);
+       return 0;
+}
+
+static int cx23888_ir_s_register(struct v4l2_subdev *sd,
+                                struct v4l2_dbg_register *reg)
+{
+       struct cx23888_ir_state *state = to_state(sd);
+       u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg;
+
+       if (!cx23888_ir_dbg_match(&reg->match))
+               return -EINVAL;
+       if ((addr & 0x3) != 0)
+               return -EINVAL;
+       if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG)
+               return -EINVAL;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       cx23888_ir_write4(state->dev, addr, reg->val);
+       return 0;
+}
+#endif
+
+static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = {
+       .g_chip_ident = cx23888_ir_g_chip_ident,
+       .log_status = cx23888_ir_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .g_register = cx23888_ir_g_register,
+       .s_register = cx23888_ir_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = {
+       .interrupt_service_routine = cx23888_ir_irq_handler,
+
+       .rx_read = cx23888_ir_rx_read,
+       .rx_g_parameters = cx23888_ir_rx_g_parameters,
+       .rx_s_parameters = cx23888_ir_rx_s_parameters,
+
+       .tx_write = cx23888_ir_tx_write,
+       .tx_g_parameters = cx23888_ir_tx_g_parameters,
+       .tx_s_parameters = cx23888_ir_tx_s_parameters,
+};
+
+static const struct v4l2_subdev_ops cx23888_ir_controller_ops = {
+       .core = &cx23888_ir_core_ops,
+       .ir = &cx23888_ir_ir_ops,
+};
+
+static const struct v4l2_subdev_ir_parameters default_rx_params = {
+       .bytes_per_data_element = sizeof(u32),
+       .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+       .enable = false,
+       .interrupt_enable = false,
+       .shutdown = true,
+
+       .modulation = true,
+       .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */
+
+       /* RC-5:    666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+       /* RC-6A:   333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+       .noise_filter_min_width = 333333, /* ns */
+       .carrier_range_lower = 35000,
+       .carrier_range_upper = 37000,
+       .invert = false,
+};
+
+static const struct v4l2_subdev_ir_parameters default_tx_params = {
+       .bytes_per_data_element = sizeof(u32),
+       .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
+
+       .enable = false,
+       .interrupt_enable = false,
+       .shutdown = true,
+
+       .modulation = true,
+       .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
+       .duty_cycle = 25,      /* 25 %   - RC-5 carrier */
+       .invert = false,
+};
+
+int cx23888_ir_probe(struct cx23885_dev *dev)
+{
+       struct cx23888_ir_state *state;
+       struct v4l2_subdev *sd;
+       struct v4l2_subdev_ir_parameters default_params;
+       int ret;
+
+       state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL);
+       if (state == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&state->rx_kfifo_lock);
+       state->rx_kfifo = kfifo_alloc(CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL,
+                                     &state->rx_kfifo_lock);
+       if (state->rx_kfifo == NULL)
+               return -ENOMEM;
+
+       spin_lock_init(&state->tx_kfifo_lock);
+       state->tx_kfifo = kfifo_alloc(CX23888_IR_TX_KFIFO_SIZE, GFP_KERNEL,
+                                     &state->tx_kfifo_lock);
+       if (state->tx_kfifo == NULL) {
+               kfifo_free(state->rx_kfifo);
+               return -ENOMEM;
+       }
+
+       state->dev = dev;
+       state->id = V4L2_IDENT_CX23888_IR;
+       state->rev = 0;
+       sd = &state->sd;
+
+       v4l2_subdev_init(sd, &cx23888_ir_controller_ops);
+       v4l2_set_subdevdata(sd, state);
+       /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */
+       snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name);
+       sd->grp_id = CX23885_HW_888_IR;
+
+       ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd);
+       if (ret == 0) {
+               /*
+                * Ensure no interrupts arrive from '888 specific conditions,
+                * since we ignore them in this driver to have commonality with
+                * similar IR controller cores.
+                */
+               cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0);
+
+               mutex_init(&state->rx_params_lock);
+               memcpy(&default_params, &default_rx_params,
+                      sizeof(struct v4l2_subdev_ir_parameters));
+               v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
+
+               mutex_init(&state->tx_params_lock);
+               memcpy(&default_params, &default_tx_params,
+                      sizeof(struct v4l2_subdev_ir_parameters));
+               v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
+       } else {
+               kfifo_free(state->rx_kfifo);
+               kfifo_free(state->tx_kfifo);
+       }
+       return ret;
+}
+
+int cx23888_ir_remove(struct cx23885_dev *dev)
+{
+       struct v4l2_subdev *sd;
+       struct cx23888_ir_state *state;
+
+       sd = cx23885_find_hw(dev, CX23885_HW_888_IR);
+       if (sd == NULL)
+               return -ENODEV;
+
+       cx23888_ir_rx_shutdown(sd);
+       cx23888_ir_tx_shutdown(sd);
+
+       state = to_state(sd);
+       v4l2_device_unregister_subdev(sd);
+       kfifo_free(state->rx_kfifo);
+       kfifo_free(state->tx_kfifo);
+       kfree(state);
+       /* Nothing more to free() as state held the actual v4l2_subdev object */
+       return 0;
+}
diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/video/cx23885/cx23888-ir.h
new file mode 100644 (file)
index 0000000..3d446f9
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *  Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ *  CX23888 Integrated Consumer Infrared Controller
+ *
+ *  Copyright (C) 2009  Andy Walls <awalls@radix.net>
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#ifndef _CX23888_IR_H_
+#define _CX23888_IR_H_
+int cx23888_ir_probe(struct cx23885_dev *dev);
+int cx23888_ir_remove(struct cx23885_dev *dev);
+#endif
index 2f846f5..45608d5 100644 (file)
 
 #include "cx25840-core.h"
 
-static int set_audclk_freq(struct i2c_client *client, u32 freq)
+/*
+ * Note: The PLL and SRC parameters are based on a reference frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * However, it's not the exact reference frequency that matters, only that the
+ * firmware and modules that comprise the driver for a particular board all
+ * use the same value (close to the ideal value).
+ *
+ * Comments below will note which reference frequency is assumed for various
+ * parameters.  They will usually be one of
+ *
+ *     ref_freq = 28.636360 MHz
+ *             or
+ *     ref_freq = 28.636363 MHz
+ */
+
+static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq)
 {
        struct cx25840_state *state = to_state(i2c_get_clientdata(client));
 
-       if (freq != 32000 && freq != 44100 && freq != 48000)
-               return -EINVAL;
-
-       /* common for all inputs and rates */
-       /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
-       if (!state->is_cx23885 && !state->is_cx231xx)
-               cx25840_write(client, 0x127, 0x50);
-
        if (state->aud_input != CX25840_AUDIO_SERIAL) {
                switch (freq) {
                case 32000:
-                       if (state->is_cx23885) {
-                               /* We don't have register values
-                                * so avoid destroying registers. */
-                               break;
-                       }
-
-                       if (!state->is_cx231xx) {
-                               /* VID_PLL and AUX_PLL */
-                               cx25840_write4(client, 0x108, 0x1006040f);
-
-                               /* AUX_PLL_FRAC */
-                               cx25840_write4(client, 0x110, 0x01bb39ee);
-                       }
-
-                       if (state->is_cx25836)
+                       /*
+                        * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+                        * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10
+                        */
+                       cx25840_write4(client, 0x108, 0x1006040f);
+
+                       /*
+                        * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+                        * 28636360 * 0xf.15f17f0/4 = 108 MHz
+                        * 432 MHz pre-postdivide
+                        */
+
+                       /*
+                        * AUX_PLL Fraction = 0x1bb39ee
+                        * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384
+                        * 196.6 MHz pre-postdivide
+                        * FIXME < 200 MHz is out of specified valid range
+                        * FIXME 28636363 ref_freq doesn't match VID PLL ref
+                        */
+                       cx25840_write4(client, 0x110, 0x01bb39ee);
+
+                       /*
+                        * SA_MCLK_SEL = 1
+                        * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+                        */
+                       cx25840_write(client, 0x127, 0x50);
+
+                       if (is_cx2583x(state))
                                break;
 
-                       /* src3/4/6_ctl = 0x0801f77f */
+                       /* src3/4/6_ctl */
+                       /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
                        cx25840_write4(client, 0x900, 0x0801f77f);
                        cx25840_write4(client, 0x904, 0x0801f77f);
                        cx25840_write4(client, 0x90c, 0x0801f77f);
                        break;
 
                case 44100:
-                       if (state->is_cx23885) {
-                               /* We don't have register values
-                                * so avoid destroying registers. */
+                       /*
+                        * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+                        * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10
+                        */
+                       cx25840_write4(client, 0x108, 0x1009040f);
+
+                       /*
+                        * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+                        * 28636360 * 0xf.15f17f0/4 = 108 MHz
+                        * 432 MHz pre-postdivide
+                        */
+
+                       /*
+                        * AUX_PLL Fraction = 0x0ec6bd6
+                        * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384
+                        * 271 MHz pre-postdivide
+                        * FIXME 28636363 ref_freq doesn't match VID PLL ref
+                        */
+                       cx25840_write4(client, 0x110, 0x00ec6bd6);
+
+                       /*
+                        * SA_MCLK_SEL = 1
+                        * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+                        */
+                       cx25840_write(client, 0x127, 0x50);
+
+                       if (is_cx2583x(state))
                                break;
-                       }
-
-                       if (!state->is_cx231xx) {
-                               /* VID_PLL and AUX_PLL */
-                               cx25840_write4(client, 0x108, 0x1009040f);
-
-                               /* AUX_PLL_FRAC */
-                               cx25840_write4(client, 0x110, 0x00ec6bd6);
-                       }
 
-                       if (state->is_cx25836)
-                               break;
-
-                       /* src3/4/6_ctl = 0x08016d59 */
+                       /* src3/4/6_ctl */
+                       /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
                        cx25840_write4(client, 0x900, 0x08016d59);
                        cx25840_write4(client, 0x904, 0x08016d59);
                        cx25840_write4(client, 0x90c, 0x08016d59);
                        break;
 
                case 48000:
-                       if (state->is_cx23885) {
-                               /* We don't have register values
-                                * so avoid destroying registers. */
-                               break;
-                       }
-
-                       if (!state->is_cx231xx) {
-                               /* VID_PLL and AUX_PLL */
-                               cx25840_write4(client, 0x108, 0x100a040f);
-
-                               /* AUX_PLL_FRAC */
-                               cx25840_write4(client, 0x110, 0x0098d6e5);
-                       }
-
-                       if (state->is_cx25836)
+                       /*
+                        * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+                        * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10
+                        */
+                       cx25840_write4(client, 0x108, 0x100a040f);
+
+                       /*
+                        * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+                        * 28636360 * 0xf.15f17f0/4 = 108 MHz
+                        * 432 MHz pre-postdivide
+                        */
+
+                       /*
+                        * AUX_PLL Fraction = 0x098d6e5
+                        * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384
+                        * 295 MHz pre-postdivide
+                        * FIXME 28636363 ref_freq doesn't match VID PLL ref
+                        */
+                       cx25840_write4(client, 0x110, 0x0098d6e5);
+
+                       /*
+                        * SA_MCLK_SEL = 1
+                        * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider
+                        */
+                       cx25840_write(client, 0x127, 0x50);
+
+                       if (is_cx2583x(state))
                                break;
 
-                       /* src3/4/6_ctl = 0x08014faa */
+                       /* src3/4/6_ctl */
+                       /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
                        cx25840_write4(client, 0x900, 0x08014faa);
                        cx25840_write4(client, 0x904, 0x08014faa);
                        cx25840_write4(client, 0x90c, 0x08014faa);
@@ -112,91 +162,249 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
        } else {
                switch (freq) {
                case 32000:
-                       if (state->is_cx23885) {
-                               /* We don't have register values
-                                * so avoid destroying registers. */
-                               break;
-                       }
-
-                       if (!state->is_cx231xx) {
-                               /* VID_PLL and AUX_PLL */
-                               cx25840_write4(client, 0x108, 0x1e08040f);
-
-                               /* AUX_PLL_FRAC */
-                               cx25840_write4(client, 0x110, 0x012a0869);
-                       }
+                       /*
+                        * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+                        * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e
+                        */
+                       cx25840_write4(client, 0x108, 0x1e08040f);
+
+                       /*
+                        * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+                        * 28636360 * 0xf.15f17f0/4 = 108 MHz
+                        * 432 MHz pre-postdivide
+                        */
+
+                       /*
+                        * AUX_PLL Fraction = 0x12a0869
+                        * 28636363 * 0x8.9504348/0x1e = 32000 * 256
+                        * 246 MHz pre-postdivide
+                        * FIXME 28636363 ref_freq doesn't match VID PLL ref
+                        */
+                       cx25840_write4(client, 0x110, 0x012a0869);
+
+                       /*
+                        * SA_MCLK_SEL = 1
+                        * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider
+                        */
+                       cx25840_write(client, 0x127, 0x54);
 
-                       if (state->is_cx25836)
+                       if (is_cx2583x(state))
                                break;
 
-                       /* src1_ctl = 0x08010000 */
+                       /* src1_ctl */
+                       /* 0x1.0000 = 32000/32000 */
                        cx25840_write4(client, 0x8f8, 0x08010000);
 
-                       /* src3/4/6_ctl = 0x08020000 */
+                       /* src3/4/6_ctl */
+                       /* 0x2.0000 = 2 * (32000/32000) */
                        cx25840_write4(client, 0x900, 0x08020000);
                        cx25840_write4(client, 0x904, 0x08020000);
                        cx25840_write4(client, 0x90c, 0x08020000);
-
-                       /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
-                       cx25840_write(client, 0x127, 0x54);
                        break;
 
                case 44100:
-                       if (state->is_cx23885) {
-                               /* We don't have register values
-                                * so avoid destroying registers. */
-                               break;
-                       }
-
-
-                       if (!state->is_cx231xx) {
-                               /* VID_PLL and AUX_PLL */
-                               cx25840_write4(client, 0x108, 0x1809040f);
-
-                               /* AUX_PLL_FRAC */
-                               cx25840_write4(client, 0x110, 0x00ec6bd6);
-                       }
-
-                       if (state->is_cx25836)
+                       /*
+                        * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+                        * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18
+                        */
+                       cx25840_write4(client, 0x108, 0x1809040f);
+
+                       /*
+                        * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+                        * 28636360 * 0xf.15f17f0/4 = 108 MHz
+                        * 432 MHz pre-postdivide
+                        */
+
+                       /*
+                        * AUX_PLL Fraction = 0x0ec6bd6
+                        * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256
+                        * 271 MHz pre-postdivide
+                        * FIXME 28636363 ref_freq doesn't match VID PLL ref
+                        */
+                       cx25840_write4(client, 0x110, 0x00ec6bd6);
+
+                       /*
+                        * SA_MCLK_SEL = 1
+                        * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
+                        */
+                       cx25840_write(client, 0x127, 0x50);
+
+                       if (is_cx2583x(state))
                                break;
 
-                       /* src1_ctl = 0x08010000 */
+                       /* src1_ctl */
+                       /* 0x1.60cd = 44100/32000 */
                        cx25840_write4(client, 0x8f8, 0x080160cd);
 
-                       /* src3/4/6_ctl = 0x08020000 */
+                       /* src3/4/6_ctl */
+                       /* 0x1.7385 = 2 * (32000/44100) */
                        cx25840_write4(client, 0x900, 0x08017385);
                        cx25840_write4(client, 0x904, 0x08017385);
                        cx25840_write4(client, 0x90c, 0x08017385);
                        break;
 
                case 48000:
-                       if (!state->is_cx23885 && !state->is_cx231xx) {
-                               /* VID_PLL and AUX_PLL */
-                               cx25840_write4(client, 0x108, 0x180a040f);
+                       /*
+                        * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04
+                        * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18
+                        */
+                       cx25840_write4(client, 0x108, 0x180a040f);
+
+                       /*
+                        * VID_PLL Fraction (register 0x10c) = 0x2be2fe
+                        * 28636360 * 0xf.15f17f0/4 = 108 MHz
+                        * 432 MHz pre-postdivide
+                        */
+
+                       /*
+                        * AUX_PLL Fraction = 0x098d6e5
+                        * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256
+                        * 295 MHz pre-postdivide
+                        * FIXME 28636363 ref_freq doesn't match VID PLL ref
+                        */
+                       cx25840_write4(client, 0x110, 0x0098d6e5);
+
+                       /*
+                        * SA_MCLK_SEL = 1
+                        * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider
+                        */
+                       cx25840_write(client, 0x127, 0x50);
+
+                       if (is_cx2583x(state))
+                               break;
 
-                               /* AUX_PLL_FRAC */
-                               cx25840_write4(client, 0x110, 0x0098d6e5);
-                       }
+                       /* src1_ctl */
+                       /* 0x1.8000 = 48000/32000 */
+                       cx25840_write4(client, 0x8f8, 0x08018000);
 
-                       if (state->is_cx25836)
-                               break;
+                       /* src3/4/6_ctl */
+                       /* 0x1.5555 = 2 * (32000/48000) */
+                       cx25840_write4(client, 0x900, 0x08015555);
+                       cx25840_write4(client, 0x904, 0x08015555);
+                       cx25840_write4(client, 0x90c, 0x08015555);
+                       break;
+               }
+       }
+
+       state->audclk_freq = freq;
+
+       return 0;
+}
+
+static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+       return cx25840_set_audclk_freq(client, freq);
+}
+
+static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+       if (state->aud_input != CX25840_AUDIO_SERIAL) {
+               switch (freq) {
+               case 32000:
+               case 44100:
+               case 48000:
+                       /* We don't have register values
+                        * so avoid destroying registers. */
+                       /* FIXME return -EINVAL; */
+                       break;
+               }
+       } else {
+               switch (freq) {
+               case 32000:
+               case 44100:
+                       /* We don't have register values
+                        * so avoid destroying registers. */
+                       /* FIXME return -EINVAL; */
+                       break;
+
+               case 48000:
+                       /* src1_ctl */
+                       /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
+                       cx25840_write4(client, 0x8f8, 0x0801867c);
+
+                       /* src3/4/6_ctl */
+                       /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+                       cx25840_write4(client, 0x900, 0x08014faa);
+                       cx25840_write4(client, 0x904, 0x08014faa);
+                       cx25840_write4(client, 0x90c, 0x08014faa);
+                       break;
+               }
+       }
+
+       state->audclk_freq = freq;
+
+       return 0;
+}
+
+static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+       if (state->aud_input != CX25840_AUDIO_SERIAL) {
+               switch (freq) {
+               case 32000:
+                       /* src3/4/6_ctl */
+                       /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */
+                       cx25840_write4(client, 0x900, 0x0801f77f);
+                       cx25840_write4(client, 0x904, 0x0801f77f);
+                       cx25840_write4(client, 0x90c, 0x0801f77f);
+                       break;
+
+               case 44100:
+                       /* src3/4/6_ctl */
+                       /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */
+                       cx25840_write4(client, 0x900, 0x08016d59);
+                       cx25840_write4(client, 0x904, 0x08016d59);
+                       cx25840_write4(client, 0x90c, 0x08016d59);
+                       break;
+
+               case 48000:
+                       /* src3/4/6_ctl */
+                       /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+                       cx25840_write4(client, 0x900, 0x08014faa);
+                       cx25840_write4(client, 0x904, 0x08014faa);
+                       cx25840_write4(client, 0x90c, 0x08014faa);
+                       break;
+               }
+       } else {
+               switch (freq) {
+               /* FIXME These cases make different assumptions about audclk */
+               case 32000:
+                       /* src1_ctl */
+                       /* 0x1.0000 = 32000/32000 */
+                       cx25840_write4(client, 0x8f8, 0x08010000);
 
-                       if (!state->is_cx23885 && !state->is_cx231xx) {
-                               /* src1_ctl */
-                               cx25840_write4(client, 0x8f8, 0x08018000);
+                       /* src3/4/6_ctl */
+                       /* 0x2.0000 = 2 * (32000/32000) */
+                       cx25840_write4(client, 0x900, 0x08020000);
+                       cx25840_write4(client, 0x904, 0x08020000);
+                       cx25840_write4(client, 0x90c, 0x08020000);
+                       break;
 
-                               /* src3/4/6_ctl */
-                               cx25840_write4(client, 0x900, 0x08015555);
-                               cx25840_write4(client, 0x904, 0x08015555);
-                               cx25840_write4(client, 0x90c, 0x08015555);
-                       } else {
+               case 44100:
+                       /* src1_ctl */
+                       /* 0x1.60cd = 44100/32000 */
+                       cx25840_write4(client, 0x8f8, 0x080160cd);
 
-                               cx25840_write4(client, 0x8f8, 0x0801867c);
+                       /* src3/4/6_ctl */
+                       /* 0x1.7385 = 2 * (32000/44100) */
+                       cx25840_write4(client, 0x900, 0x08017385);
+                       cx25840_write4(client, 0x904, 0x08017385);
+                       cx25840_write4(client, 0x90c, 0x08017385);
+                       break;
 
-                               cx25840_write4(client, 0x900, 0x08014faa);
-                               cx25840_write4(client, 0x904, 0x08014faa);
-                               cx25840_write4(client, 0x90c, 0x08014faa);
-                       }
+               case 48000:
+                       /* src1_ctl */
+                       /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */
+                       cx25840_write4(client, 0x8f8, 0x0801867c);
+
+                       /* src3/4/6_ctl */
+                       /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */
+                       cx25840_write4(client, 0x900, 0x08014faa);
+                       cx25840_write4(client, 0x904, 0x08014faa);
+                       cx25840_write4(client, 0x90c, 0x08014faa);
                        break;
                }
        }
@@ -206,6 +414,25 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
        return 0;
 }
 
+static int set_audclk_freq(struct i2c_client *client, u32 freq)
+{
+       struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+       if (freq != 32000 && freq != 44100 && freq != 48000)
+               return -EINVAL;
+
+       if (is_cx231xx(state))
+               return cx231xx_set_audclk_freq(client, freq);
+
+       if (is_cx2388x(state))
+               return cx23885_set_audclk_freq(client, freq);
+
+       if (is_cx2583x(state))
+               return cx25836_set_audclk_freq(client, freq);
+
+       return cx25840_set_audclk_freq(client, freq);
+}
+
 void cx25840_audio_set_path(struct i2c_client *client)
 {
        struct cx25840_state *state = to_state(i2c_get_clientdata(client));
@@ -243,7 +470,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
        cx25840_and_or(client, 0x810, ~0x1, 0x00);
 
        /* Ensure the controller is running when we exit */
-       if (state->is_cx23885 || state->is_cx231xx)
+       if (is_cx2388x(state) || is_cx231xx(state))
                cx25840_and_or(client, 0x803, ~0x10, 0x10);
 }
 
@@ -383,7 +610,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
        struct cx25840_state *state = to_state(sd);
        int retval;
 
-       if (!state->is_cx25836)
+       if (!is_cx2583x(state))
                cx25840_and_or(client, 0x810, ~0x1, 1);
        if (state->aud_input != CX25840_AUDIO_SERIAL) {
                cx25840_and_or(client, 0x803, ~0x10, 0);
@@ -392,7 +619,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
        retval = set_audclk_freq(client, freq);
        if (state->aud_input != CX25840_AUDIO_SERIAL)
                cx25840_and_or(client, 0x803, ~0x10, 0x10);
-       if (!state->is_cx25836)
+       if (!is_cx2583x(state))
                cx25840_and_or(client, 0x810, ~0x1, 0);
        return retval;
 }
index 1aeaf18..385ecd5 100644 (file)
@@ -259,6 +259,13 @@ static void cx23885_initialize(struct i2c_client *client)
        struct cx25840_state *state = to_state(i2c_get_clientdata(client));
        struct workqueue_struct *q;
 
+       /*
+        * Come out of digital power down
+        * The CX23888, at least, needs this, otherwise registers aside from
+        * 0x0-0x2 can't be read or written.
+        */
+       cx25840_write(client, 0x000, 0);
+
        /* Internal Reset */
        cx25840_and_or(client, 0x102, ~0x01, 0x01);
        cx25840_and_or(client, 0x102, ~0x01, 0x00);
@@ -269,18 +276,45 @@ static void cx23885_initialize(struct i2c_client *client)
        /* DIF in reset? */
        cx25840_write(client, 0x398, 0);
 
-       /* Trust the default xtal, no division */
-       /* This changes for the cx23888 products */
+       /*
+        * Trust the default xtal, no division
+        * '885: 28.636363... MHz
+        * '887: 25.000000 MHz
+        * '888: 50.000000 MHz
+        */
        cx25840_write(client, 0x2, 0x76);
 
-       /* Bring down the regulator for AUX clk */
+       /* Power up all the PLL's and DLL */
        cx25840_write(client, 0x1, 0x40);
 
-       /* Sys PLL frac */
-       cx25840_write4(client, 0x11c, 0x01d1744c);
-
-       /* Sys PLL int */
-       cx25840_write4(client, 0x118, 0x00000416);
+       /* Sys PLL */
+       switch (state->id) {
+       case V4L2_IDENT_CX23888_AV:
+               /*
+                * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz
+                * 572.73 MHz before post divide
+                */
+               cx25840_write4(client, 0x11c, 0x00e8ba26);
+               cx25840_write4(client, 0x118, 0x0000040b);
+               break;
+       case V4L2_IDENT_CX23887_AV:
+               /*
+                * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz
+                * 572.73 MHz before post divide
+                */
+               cx25840_write4(client, 0x11c, 0x01d1744c);
+               cx25840_write4(client, 0x118, 0x00000416);
+               break;
+       case V4L2_IDENT_CX23885_AV:
+       default:
+               /*
+                * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz
+                * 572.73 MHz before post divide
+                */
+               cx25840_write4(client, 0x11c, 0x00000000);
+               cx25840_write4(client, 0x118, 0x00000414);
+               break;
+       }
 
        /* Disable DIF bypass */
        cx25840_write4(client, 0x33c, 0x00000001);
@@ -288,11 +322,15 @@ static void cx23885_initialize(struct i2c_client *client)
        /* DIF Src phase inc */
        cx25840_write4(client, 0x340, 0x0df7df83);
 
-       /* Vid PLL frac */
-       cx25840_write4(client, 0x10c, 0x01b6db7b);
-
-       /* Vid PLL int */
-       cx25840_write4(client, 0x108, 0x00000512);
+       /*
+        * Vid PLL
+        * Setup for a BT.656 pixel clock of 13.5 Mpixels/second
+        *
+        * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz
+        * 432.0 MHz before post divide
+        */
+       cx25840_write4(client, 0x10c, 0x002be2c9);
+       cx25840_write4(client, 0x108, 0x0000040f);
 
        /* Luma */
        cx25840_write4(client, 0x414, 0x00107d12);
@@ -300,11 +338,43 @@ static void cx23885_initialize(struct i2c_client *client)
        /* Chroma */
        cx25840_write4(client, 0x420, 0x3d008282);
 
-       /* Aux PLL frac */
-       cx25840_write4(client, 0x114, 0x017dbf48);
-
-       /* Aux PLL int */
-       cx25840_write4(client, 0x110, 0x000a030e);
+       /*
+        * Aux PLL
+        * Initial setup for audio sample clock:
+        * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz
+        * Intial I2S output/master clock(?):
+        * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz
+        */
+       switch (state->id) {
+       case V4L2_IDENT_CX23888_AV:
+               /*
+                * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz
+                * 368.64 MHz before post divide
+                * 122.88 MHz / 0xa = 12.288 MHz
+                */
+               cx25840_write4(client, 0x114, 0x00bedfa4);
+               cx25840_write4(client, 0x110, 0x000a0307);
+               break;
+       case V4L2_IDENT_CX23887_AV:
+               /*
+                * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz
+                * 368.64 MHz before post divide
+                * 122.88 MHz / 0xa = 12.288 MHz
+                */
+               cx25840_write4(client, 0x114, 0x017dbf48);
+               cx25840_write4(client, 0x110, 0x000a030e);
+               break;
+       case V4L2_IDENT_CX23885_AV:
+       default:
+               /*
+                * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz
+                * 368.64 MHz before post divide
+                * 122.88 MHz / 0xa = 12.288 MHz
+                */
+               cx25840_write4(client, 0x114, 0x01bf0c9e);
+               cx25840_write4(client, 0x110, 0x000a030c);
+               break;
+       };
 
        /* ADC2 input select */
        cx25840_write(client, 0x102, 0x10);
@@ -494,7 +564,7 @@ void cx25840_std_setup(struct i2c_client *client)
        }
 
        /* DEBUG: Displays configured PLL frequency */
-       if (!state->is_cx231xx) {
+       if (!is_cx231xx(state)) {
                pll_int = cx25840_read(client, 0x108);
                pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
                pll_post = cx25840_read(client, 0x109);
@@ -615,13 +685,30 @@ static void input_change(struct i2c_client *client)
                }
                cx25840_write(client, 0x80b, 0x00);
        } else if (std & V4L2_STD_PAL) {
-               /* Follow tuner change procedure for PAL */
+               /* Autodetect audio standard and audio system */
                cx25840_write(client, 0x808, 0xff);
-               cx25840_write(client, 0x80b, 0x10);
+               /* Since system PAL-L is pretty much non-existant and
+                  not used by any public broadcast network, force
+                  6.5 MHz carrier to be interpreted as System DK,
+                  this avoids DK audio detection instability */
+              cx25840_write(client, 0x80b, 0x00);
        } else if (std & V4L2_STD_SECAM) {
-               /* Select autodetect for SECAM */
+               /* Autodetect audio standard and audio system */
                cx25840_write(client, 0x808, 0xff);
-               cx25840_write(client, 0x80b, 0x10);
+               /* If only one of SECAM-DK / SECAM-L is required, then force
+                 6.5MHz carrier, else autodetect it */
+               if ((std & V4L2_STD_SECAM_DK) &&
+                   !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+                       /* 6.5 MHz carrier to be interpreted as System DK */
+                       cx25840_write(client, 0x80b, 0x00);
+              } else if (!(std & V4L2_STD_SECAM_DK) &&
+                         (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) {
+                       /* 6.5 MHz carrier to be interpreted as System L */
+                       cx25840_write(client, 0x80b, 0x08);
+              } else {
+                       /* 6.5 MHz carrier to be autodetected */
+                       cx25840_write(client, 0x80b, 0x10);
+              }
        }
 
        cx25840_and_or(client, 0x810, ~0x01, 0);
@@ -633,6 +720,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
        struct cx25840_state *state = to_state(i2c_get_clientdata(client));
        u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
                           vid_input <= CX25840_COMPOSITE8);
+       u8 is_component = (vid_input & CX25840_COMPONENT_ON) ==
+                       CX25840_COMPONENT_ON;
+       int luma = vid_input & 0xf0;
+       int chroma = vid_input & 0xf00;
        u8 reg;
 
        v4l_dbg(1, cx25840_debug, client,
@@ -645,18 +736,14 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
                reg = vid_input & 0xff;
                if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON)
                        is_composite = 0;
-               else
+               else if ((vid_input & CX25840_COMPONENT_ON) == 0)
                        is_composite = 1;
 
                v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
                        reg, is_composite);
-       } else
-       if (is_composite) {
+       } else if (is_composite) {
                reg = 0xf0 + (vid_input - CX25840_COMPOSITE1);
        } else {
-               int luma = vid_input & 0xf0;
-               int chroma = vid_input & 0xf00;
-
                if ((vid_input & ~0xff0) ||
                    luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 ||
                    chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
@@ -678,7 +765,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
         * configuration in reg (for the cx23885) so we have no
         * need to attempt to flip bits for earlier av decoders.
         */
-       if (!state->is_cx23885 && !state->is_cx231xx) {
+       if (!is_cx2388x(state) && !is_cx231xx(state)) {
                switch (aud_input) {
                case CX25840_AUDIO_SERIAL:
                        /* do nothing, use serial audio input */
@@ -698,10 +785,13 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
 
        cx25840_write(client, 0x103, reg);
 
-       /* Set INPUT_MODE to Composite (0) or S-Video (1) */
-       cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
+       /* Set INPUT_MODE to Composite, S-Video or Component */
+       if (is_component)
+               cx25840_and_or(client, 0x401, ~0x6, 0x6);
+       else
+               cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
 
-       if (!state->is_cx23885 && !state->is_cx231xx) {
+       if (!is_cx2388x(state) && !is_cx231xx(state)) {
                /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
                cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
                /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
@@ -710,22 +800,31 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
                else
                        cx25840_and_or(client, 0x102, ~0x4, 0);
        } else {
-               if (is_composite)
+               /* Set DUAL_MODE_ADC2 to 1 if component*/
+               cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0);
+               if (is_composite) {
                        /* ADC2 input select channel 2 */
                        cx25840_and_or(client, 0x102, ~0x2, 0);
-               else
-                       /* ADC2 input select channel 3 */
-                       cx25840_and_or(client, 0x102, ~0x2, 2);
+               } else if (!is_component) {
+                       /* S-Video */
+                       if (chroma >= CX25840_SVIDEO_CHROMA7) {
+                               /* ADC2 input select channel 3 */
+                               cx25840_and_or(client, 0x102, ~0x2, 2);
+                       } else {
+                               /* ADC2 input select channel 2 */
+                               cx25840_and_or(client, 0x102, ~0x2, 0);
+                       }
+               }
        }
 
        state->vid_input = vid_input;
        state->aud_input = aud_input;
-       if (!state->is_cx25836) {
+       if (!is_cx2583x(state)) {
                cx25840_audio_set_path(client);
                input_change(client);
        }
 
-       if (state->is_cx23885) {
+       if (is_cx2388x(state)) {
                /* Audio channel 1 src : Parallel 1 */
                cx25840_write(client, 0x124, 0x03);
 
@@ -741,7 +840,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
                 */
                cx25840_write(client, 0x918, 0xa0);
                cx25840_write(client, 0x919, 0x01);
-       } else if (state->is_cx231xx) {
+       } else if (is_cx231xx(state)) {
                /* Audio channel 1 src : Parallel 1 */
                cx25840_write(client, 0x124, 0x03);
 
@@ -805,7 +904,7 @@ static int set_v4lstd(struct i2c_client *client)
        cx25840_and_or(client, 0x400, ~0xf, fmt);
        cx25840_and_or(client, 0x403, ~0x3, pal_m);
        cx25840_std_setup(client);
-       if (!state->is_cx25836)
+       if (!is_cx2583x(state))
                input_change(client);
        return 0;
 }
@@ -868,7 +967,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        case V4L2_CID_AUDIO_TREBLE:
        case V4L2_CID_AUDIO_BALANCE:
        case V4L2_CID_AUDIO_MUTE:
-               if (state->is_cx25836)
+               if (is_cx2583x(state))
                        return -EINVAL;
                return cx25840_audio_s_ctrl(sd, ctrl);
 
@@ -905,7 +1004,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
        case V4L2_CID_AUDIO_TREBLE:
        case V4L2_CID_AUDIO_BALANCE:
        case V4L2_CID_AUDIO_MUTE:
-               if (state->is_cx25836)
+               if (is_cx2583x(state))
                        return -EINVAL;
                return cx25840_audio_g_ctrl(sd, ctrl);
        default:
@@ -1209,11 +1308,11 @@ static int cx25840_load_fw(struct v4l2_subdev *sd)
        if (!state->is_initialized) {
                /* initialize and load firmware */
                state->is_initialized = 1;
-               if (state->is_cx25836)
+               if (is_cx2583x(state))
                        cx25836_initialize(client);
-               else if (state->is_cx23885)
+               else if (is_cx2388x(state))
                        cx23885_initialize(client);
-               else if (state->is_cx231xx)
+               else if (is_cx231xx(state))
                        cx231xx_initialize(client);
                else
                        cx25840_initialize(client);
@@ -1256,17 +1355,17 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
        v4l_dbg(1, cx25840_debug, client, "%s output\n",
                        enable ? "enable" : "disable");
        if (enable) {
-               if (state->is_cx23885 || state->is_cx231xx) {
+               if (is_cx2388x(state) || is_cx231xx(state)) {
                        u8 v = (cx25840_read(client, 0x421) | 0x0b);
                        cx25840_write(client, 0x421, v);
                } else {
                        cx25840_write(client, 0x115,
-                                       state->is_cx25836 ? 0x0c : 0x8c);
+                                       is_cx2583x(state) ? 0x0c : 0x8c);
                        cx25840_write(client, 0x116,
-                                       state->is_cx25836 ? 0x04 : 0x07);
+                                       is_cx2583x(state) ? 0x04 : 0x07);
                }
        } else {
-               if (state->is_cx23885 || state->is_cx231xx) {
+               if (is_cx2388x(state) || is_cx231xx(state)) {
                        u8 v = cx25840_read(client, 0x421) & ~(0x0b);
                        cx25840_write(client, 0x421, v);
                } else {
@@ -1292,7 +1391,7 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
        default:
                break;
        }
-       if (state->is_cx25836)
+       if (is_cx2583x(state))
                return -EINVAL;
 
        switch (qc->id) {
@@ -1346,7 +1445,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd,
        struct cx25840_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (state->is_cx25836)
+       if (is_cx2583x(state))
                return -EINVAL;
        return set_input(client, state->vid_input, input);
 }
@@ -1356,7 +1455,7 @@ static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr
        struct cx25840_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (!state->is_cx25836)
+       if (!is_cx2583x(state))
                input_change(client);
        return 0;
 }
@@ -1373,7 +1472,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
                return 0;
 
        vt->signal = vpres ? 0xffff : 0x0;
-       if (state->is_cx25836)
+       if (is_cx2583x(state))
                return 0;
 
        vt->capability |=
@@ -1404,7 +1503,7 @@ static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
        struct cx25840_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (state->radio || state->is_cx25836)
+       if (state->radio || is_cx2583x(state))
                return 0;
 
        switch (vt->audmode) {
@@ -1445,11 +1544,11 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
        struct cx25840_state *state = to_state(sd);
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
-       if (state->is_cx25836)
+       if (is_cx2583x(state))
                cx25836_initialize(client);
-       else if (state->is_cx23885)
+       else if (is_cx2388x(state))
                cx23885_initialize(client);
-       else if (state->is_cx231xx)
+       else if (is_cx231xx(state))
                cx231xx_initialize(client);
        else
                cx25840_initialize(client);
@@ -1470,7 +1569,7 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
        struct i2c_client *client = v4l2_get_subdevdata(sd);
 
        log_video_status(client);
-       if (!state->is_cx25836)
+       if (!is_cx2583x(state))
                log_audio_status(client);
        return 0;
 }
@@ -1521,12 +1620,50 @@ static const struct v4l2_subdev_ops cx25840_ops = {
 
 /* ----------------------------------------------------------------------- */
 
+static u32 get_cx2388x_ident(struct i2c_client *client)
+{
+       u32 ret;
+
+       /* Come out of digital power down */
+       cx25840_write(client, 0x000, 0);
+
+       /* Detecting whether the part is cx23885/7/8 is more
+        * difficult than it needs to be. No ID register. Instead we
+        * probe certain registers indicated in the datasheets to look
+        * for specific defaults that differ between the silicon designs. */
+
+       /* It's either 885/7 if the IR Tx Clk Divider register exists */
+       if (cx25840_read4(client, 0x204) & 0xffff) {
+               /* CX23885 returns bogus repetitive byte values for the DIF,
+                * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */
+               ret = cx25840_read4(client, 0x300);
+               if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) {
+                       /* No DIF */
+                       ret = V4L2_IDENT_CX23885_AV;
+               } else {
+                       /* CX23887 has a broken DIF, but the registers
+                        * appear valid (but unsed), good enough to detect. */
+                       ret = V4L2_IDENT_CX23887_AV;
+               }
+       } else if (cx25840_read4(client, 0x300) & 0x0fffffff) {
+               /* DIF PLL Freq Word reg exists; chip must be a CX23888 */
+               ret = V4L2_IDENT_CX23888_AV;
+       } else {
+               v4l_err(client, "Unable to detect h/w, assuming cx23887\n");
+               ret = V4L2_IDENT_CX23887_AV;
+       }
+
+       /* Back into digital power down */
+       cx25840_write(client, 0x000, 2);
+       return ret;
+}
+
 static int cx25840_probe(struct i2c_client *client,
                         const struct i2c_device_id *did)
 {
        struct cx25840_state *state;
        struct v4l2_subdev *sd;
-       u32 id;
+       u32 id = V4L2_IDENT_NONE;
        u16 device_id;
 
        /* Check if the adapter supports the needed features */
@@ -1543,17 +1680,22 @@ static int cx25840_probe(struct i2c_client *client,
         * 0x83 for the cx2583x and 0x84 for the cx2584x */
        if ((device_id & 0xff00) == 0x8300) {
                id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
-       }
-       else if ((device_id & 0xff00) == 0x8400) {
+       } else if ((device_id & 0xff00) == 0x8400) {
                id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
        } else if (device_id == 0x0000) {
-               id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
-       } else if (device_id == 0x1313) {
-               id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+               id = get_cx2388x_ident(client);
        } else if ((device_id & 0xfff0) == 0x5A30) {
-               id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
-       }
-       else {
+               /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */
+               id = V4L2_IDENT_CX2310X_AV;
+       } else if ((device_id & 0xff) == (device_id >> 8)) {
+               v4l_err(client,
+                       "likely a confused/unresponsive cx2388[578] A/V decoder"
+                       " found @ 0x%x (%s)\n",
+                       client->addr << 1, client->adapter->name);
+               v4l_err(client, "A method to reset it from the cx25840 driver"
+                       " software is not known at this time\n");
+               return -ENODEV;
+       } else {
                v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
                return -ENODEV;
        }
@@ -1564,17 +1706,45 @@ static int cx25840_probe(struct i2c_client *client,
 
        sd = &state->sd;
        v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
-       /* Note: revision '(device_id & 0x0f) == 2' was never built. The
-          marking skips from 0x1 == 22 to 0x3 == 23. */
-       v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
-                   (device_id & 0xfff0) >> 4,
-                   (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
-                   client->addr << 1, client->adapter->name);
+       switch (id) {
+       case V4L2_IDENT_CX23885_AV:
+               v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n",
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX23887_AV:
+               v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n",
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX23888_AV:
+               v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n",
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX2310X_AV:
+               v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n",
+                        device_id, client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX25840:
+       case V4L2_IDENT_CX25841:
+       case V4L2_IDENT_CX25842:
+       case V4L2_IDENT_CX25843:
+               /* Note: revision '(device_id & 0x0f) == 2' was never built. The
+                  marking skips from 0x1 == 22 to 0x3 == 23. */
+               v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
+                        (device_id & 0xfff0) >> 4,
+                        (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1
+                                               : (device_id & 0x0f),
+                        client->addr << 1, client->adapter->name);
+               break;
+       case V4L2_IDENT_CX25836:
+       case V4L2_IDENT_CX25837:
+       default:
+               v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n",
+                        (device_id & 0xfff0) >> 4, device_id & 0x0f,
+                        client->addr << 1, client->adapter->name);
+               break;
+       }
 
        state->c = client;
-       state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
-       state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
-       state->is_cx231xx = (device_id == 0x5a3e);
        state->vid_input = CX25840_COMPOSITE7;
        state->aud_input = CX25840_AUDIO8;
        state->audclk_freq = 48000;
index 814b565..5534544 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/videodev2.h>
 #include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
 #include <linux/i2c.h>
 
 /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
@@ -48,9 +49,6 @@ struct cx25840_state {
        int vbi_line_offset;
        u32 id;
        u32 rev;
-       int is_cx25836;
-       int is_cx23885;
-       int is_cx231xx;
        int is_initialized;
        wait_queue_head_t fw_wait;    /* wake up when the fw load is finished */
        struct work_struct fw_work;   /* work entry for fw load */
@@ -61,6 +59,24 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
        return container_of(sd, struct cx25840_state, sd);
 }
 
+static inline bool is_cx2583x(struct cx25840_state *state)
+{
+       return state->id == V4L2_IDENT_CX25836 ||
+              state->id == V4L2_IDENT_CX25837;
+}
+
+static inline bool is_cx231xx(struct cx25840_state *state)
+{
+       return state->id == V4L2_IDENT_CX2310X_AV;
+}
+
+static inline bool is_cx2388x(struct cx25840_state *state)
+{
+       return state->id == V4L2_IDENT_CX23885_AV ||
+              state->id == V4L2_IDENT_CX23887_AV ||
+              state->id == V4L2_IDENT_CX23888_AV;
+}
+
 /* ----------------------------------------------------------------------- */
 /* cx25850-core.c                                                         */
 int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
index 1f483c1..8150200 100644 (file)
@@ -67,9 +67,9 @@ static const char *get_fw_name(struct i2c_client *client)
 
        if (firmware[0])
                return firmware;
-       if (state->is_cx23885)
+       if (is_cx2388x(state))
                return "v4l-cx23885-avcore-01.fw";
-       if (state->is_cx231xx)
+       if (is_cx231xx(state))
                return "v4l-cx231xx-avcore-01.fw";
        return "v4l-cx25840.fw";
 }
@@ -112,13 +112,13 @@ int cx25840_loadfw(struct i2c_client *client)
        int MAX_BUF_SIZE = FWSEND;
        u32 gpio_oe = 0, gpio_da = 0;
 
-       if (state->is_cx23885) {
+       if (is_cx2388x(state)) {
                /* Preserve the GPIO OE and output bits */
                gpio_oe = cx25840_read(client, 0x160);
                gpio_da = cx25840_read(client, 0x164);
        }
 
-       if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) {
+       if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
                v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
                MAX_BUF_SIZE = 16;  /* cx231xx cannot accept more than 16 bytes at a time */
        }
@@ -156,7 +156,7 @@ int cx25840_loadfw(struct i2c_client *client)
        size = fw->size;
        release_firmware(fw);
 
-       if (state->is_cx23885) {
+       if (is_cx2388x(state)) {
                /* Restore GPIO configuration after f/w load */
                cx25840_write(client, 0x160, gpio_oe);
                cx25840_write(client, 0x164, gpio_da);
index 4995298..c7e5851 100644 (file)
@@ -61,6 +61,8 @@ config VIDEO_CX88_DVB
        select DVB_STV0299 if !DVB_FE_CUSTOMISE
        select DVB_STV0288 if !DVB_FE_CUSTOMISE
        select DVB_STB6000 if !DVB_FE_CUSTOMISE
+       select DVB_STV0900 if !DVB_FE_CUSTOMISE
+       select DVB_STB6100 if !DVB_FE_CUSTOMISE
        select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
        ---help---
          This adds support for DVB/ATSC cards based on the
index 33be636..d844f2a 100644 (file)
@@ -2075,6 +2075,18 @@ static const struct cx88_board cx88_boards[] = {
                },
                .mpeg           = CX88_MPEG_DVB,
        },
+       [CX88_BOARD_PROF_7301] = {
+               .name           = "Prof 7301 DVB-S/S2",
+               .tuner_type     = UNSET,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+               .input          = { {
+                       .type   = CX88_VMUX_DVB,
+                       .vmux   = 0,
+               } },
+               .mpeg           = CX88_MPEG_DVB,
+       },
 };
 
 /* ------------------------------------------------------------------ */
@@ -2535,6 +2547,10 @@ static const struct cx88_subid cx88_subids[] = {
                .subvendor = 0x107d,
                .subdevice = 0x6618,
                .card      = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+       }, {
+               .subvendor = 0xb034,
+               .subdevice = 0x3034,
+               .card      = CX88_BOARD_PROF_7301,
        },
 };
 
@@ -3211,6 +3227,7 @@ static void cx88_card_setup(struct cx88_core *core)
        case  CX88_BOARD_TBS_8920:
        case  CX88_BOARD_PROF_6200:
        case  CX88_BOARD_PROF_7300:
+       case  CX88_BOARD_PROF_7301:
        case  CX88_BOARD_SATTRADE_ST4200:
                cx_write(MO_GP0_IO, 0x8000);
                msleep(100);
@@ -3267,7 +3284,7 @@ static void cx88_card_setup(struct cx88_core *core)
                            ctl.fname);
                call_all(core, tuner, s_config, &xc2028_cfg);
        }
-       call_all(core, tuner, s_standby);
+       call_all(core, core, s_power, 0);
 }
 
 /* ------------------------------------------------------------------ */
index cf63460..b354111 100644 (file)
@@ -624,7 +624,7 @@ int cx88_reset(struct cx88_core *core)
        /* setup image format */
        cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
 
-       /* setup FIFO Threshholds */
+       /* setup FIFO Thresholds */
        cx_write(MO_PDMA_STHRSH,   0x0807);
        cx_write(MO_PDMA_DTHRSH,   0x0807);
 
index 518bcfe..b142969 100644 (file)
@@ -53,6 +53,9 @@
 #include "stv0288.h"
 #include "stb6000.h"
 #include "cx24116.h"
+#include "stv0900.h"
+#include "stb6100.h"
+#include "stb6100_proc.h"
 
 MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
 MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
@@ -573,6 +576,15 @@ static int cx24116_set_ts_param(struct dvb_frontend *fe,
        return 0;
 }
 
+static int stv0900_set_ts_param(struct dvb_frontend *fe,
+       int is_punctured)
+{
+       struct cx8802_dev *dev = fe->dvb->priv;
+       dev->ts_gen_cntrl = 0;
+
+       return 0;
+}
+
 static int cx24116_reset_device(struct dvb_frontend *fe)
 {
        struct cx8802_dev *dev = fe->dvb->priv;
@@ -601,6 +613,23 @@ static struct cx24116_config tevii_s460_config = {
        .reset_device  = cx24116_reset_device,
 };
 
+static struct stv0900_config prof_7301_stv0900_config = {
+       .demod_address = 0x6a,
+/*     demod_mode = 0,*/
+       .xtal = 27000000,
+       .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+       .diseqc_mode = 2,/* 2/3 PWM */
+       .tun1_maddress = 0,/* 0x60 */
+       .tun1_adc = 0,/* 2 Vpp */
+       .path1_mode = 3,
+       .set_ts_params = stv0900_set_ts_param,
+};
+
+static struct stb6100_config prof_7301_stb6100_config = {
+       .tuner_address = 0x60,
+       .refclock = 27000000,
+};
+
 static struct stv0299_config tevii_tuner_sharp_config = {
        .demod_address = 0x68,
        .inittab = sharp_z0194a_inittab,
@@ -1149,6 +1178,31 @@ static int dvb_register(struct cx8802_dev *dev)
                                goto frontend_detach;
                }
                break;
+       case CX88_BOARD_PROF_7301:{
+               struct dvb_tuner_ops *tuner_ops = NULL;
+
+               fe0->dvb.frontend = dvb_attach(stv0900_attach,
+                                               &prof_7301_stv0900_config,
+                                               &core->i2c_adap, 0);
+               if (fe0->dvb.frontend != NULL) {
+                       if (!dvb_attach(stb6100_attach, fe0->dvb.frontend,
+                                       &prof_7301_stb6100_config,
+                                       &core->i2c_adap))
+                               goto frontend_detach;
+
+                       tuner_ops = &fe0->dvb.frontend->ops.tuner_ops;
+                       tuner_ops->set_frequency = stb6100_set_freq;
+                       tuner_ops->get_frequency = stb6100_get_freq;
+                       tuner_ops->set_bandwidth = stb6100_set_bandw;
+                       tuner_ops->get_bandwidth = stb6100_get_bandw;
+
+                       core->prev_set_voltage =
+                                       fe0->dvb.frontend->ops.set_voltage;
+                       fe0->dvb.frontend->ops.set_voltage =
+                                       tevii_dvbs_set_voltage;
+               }
+               break;
+               }
        default:
                printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
                       core->name);
@@ -1170,11 +1224,11 @@ static int dvb_register(struct cx8802_dev *dev)
                fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
 
        /* Put the analog decoder in standby to keep it quiet */
-       call_all(core, tuner, s_standby);
+       call_all(core, core, s_power, 0);
 
        /* register everything */
        return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
-               &dev->pci->dev, adapter_nr, mfe_shared);
+                                        &dev->pci->dev, adapter_nr, mfe_shared, NULL);
 
 frontend_detach:
        core->gate_ctrl = NULL;
index 78b3635..92b8cdf 100644 (file)
@@ -118,13 +118,13 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
 
                data = (data << 4) | ((gpio_key & 0xf0) >> 4);
 
-               ir_input_keydown(ir->input, &ir->ir, data, data);
+               ir_input_keydown(ir->input, &ir->ir, data);
                ir_input_nokey(ir->input, &ir->ir);
 
        } else if (ir->mask_keydown) {
                /* bit set on keydown */
                if (gpio & ir->mask_keydown) {
-                       ir_input_keydown(ir->input, &ir->ir, data, data);
+                       ir_input_keydown(ir->input, &ir->ir, data);
                } else {
                        ir_input_nokey(ir->input, &ir->ir);
                }
@@ -132,14 +132,14 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
        } else if (ir->mask_keyup) {
                /* bit cleared on keydown */
                if (0 == (gpio & ir->mask_keyup)) {
-                       ir_input_keydown(ir->input, &ir->ir, data, data);
+                       ir_input_keydown(ir->input, &ir->ir, data);
                } else {
                        ir_input_nokey(ir->input, &ir->ir);
                }
 
        } else {
                /* can't distinguish keydown/up :-/ */
-               ir_input_keydown(ir->input, &ir->ir, data, data);
+               ir_input_keydown(ir->input, &ir->ir, data);
                ir_input_nokey(ir->input, &ir->ir);
        }
 }
@@ -303,6 +303,23 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
                ir->mask_keydown = 0x02;
                ir->polling      = 50; /* ms */
                break;
+       case CX88_BOARD_OMICOM_SS4_PCI:
+       case CX88_BOARD_SATTRADE_ST4200:
+       case CX88_BOARD_TBS_8920:
+       case CX88_BOARD_TBS_8910:
+       case CX88_BOARD_PROF_7300:
+       case CX88_BOARD_PROF_7301:
+       case CX88_BOARD_PROF_6200:
+               ir_codes = &ir_codes_tbs_nec_table;
+               ir_type = IR_TYPE_PD;
+               ir->sampling = 0xff00; /* address */
+               break;
+       case CX88_BOARD_TEVII_S460:
+       case CX88_BOARD_TEVII_S420:
+               ir_codes = &ir_codes_tevii_nec_table;
+               ir_type = IR_TYPE_PD;
+               ir->sampling = 0xff00; /* address */
+               break;
        case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
                ir_codes         = &ir_codes_dntv_live_dvbt_pro_table;
                ir_type          = IR_TYPE_PD;
@@ -343,7 +360,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
        snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
        snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
 
-       ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       if (err < 0)
+               goto err_out_free;
+
        input_dev->name = ir->name;
        input_dev->phys = ir->phys;
        input_dev->id.bustype = BUS_PCI;
@@ -373,6 +393,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
        cx88_ir_stop(core, ir);
        core->ir = NULL;
  err_out_free:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
        kfree(ir);
        return err;
@@ -387,6 +408,7 @@ int cx88_ir_fini(struct cx88_core *core)
                return 0;
 
        cx88_ir_stop(core, ir);
+       ir_input_free(ir->input);
        input_unregister_device(ir->input);
        kfree(ir);
 
@@ -432,8 +454,17 @@ void cx88_ir_irq(struct cx88_core *core)
 
        /* decode it */
        switch (core->boardnr) {
+       case CX88_BOARD_TEVII_S460:
+       case CX88_BOARD_TEVII_S420:
        case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
        case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+       case CX88_BOARD_OMICOM_SS4_PCI:
+       case CX88_BOARD_SATTRADE_ST4200:
+       case CX88_BOARD_TBS_8920:
+       case CX88_BOARD_TBS_8910:
+       case CX88_BOARD_PROF_7300:
+       case CX88_BOARD_PROF_7301:
+       case CX88_BOARD_PROF_6200:
                ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4);
 
                if (ircode == 0xffffffff) { /* decoding error */
@@ -461,7 +492,7 @@ void cx88_ir_irq(struct cx88_core *core)
 
                ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f);
 
-               ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f, (ircode >> 16) & 0xff);
+               ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f);
                ir->release = jiffies + msecs_to_jiffies(120);
                break;
        case CX88_BOARD_HAUPPAUGE:
@@ -498,7 +529,7 @@ void cx88_ir_irq(struct cx88_core *core)
                if ( dev != 0x1e && dev != 0x1f )
                        /* not a hauppauge remote */
                        break;
-               ir_input_keydown(ir->input, &ir->ir, code, ircode);
+               ir_input_keydown(ir->input, &ir->ir, code);
                ir->release = jiffies + msecs_to_jiffies(120);
                break;
        case CX88_BOARD_PINNACLE_PCTV_HD_800i:
@@ -506,7 +537,7 @@ void cx88_ir_irq(struct cx88_core *core)
                ir_dprintk("biphase decoded: %x\n", ircode);
                if ((ircode & 0xfffff000) != 0x3000)
                        break;
-               ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode);
+               ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f);
                ir->release = jiffies + msecs_to_jiffies(120);
                break;
        }
index 57e6b12..d7e8fce 100644 (file)
@@ -935,7 +935,7 @@ static int video_release(struct file *file)
 
        mutex_lock(&dev->core->lock);
        if(atomic_dec_and_test(&dev->core->users))
-               call_all(dev->core, tuner, s_standby);
+               call_all(dev->core, core, s_power, 0);
        mutex_unlock(&dev->core->lock);
 
        return 0;
index d5cea41..e1c5217 100644 (file)
@@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[];
 #define CX88_BOARD_HAUPPAUGE_IRONLY        80
 #define CX88_BOARD_WINFAST_DTV1800H        81
 #define CX88_BOARD_WINFAST_DTV2000H_J      82
+#define CX88_BOARD_PROF_7301               83
 
 enum cx88_itype {
        CX88_VMUX_COMPOSITE1 = 1,
index 4629cab..3143900 100644 (file)
@@ -285,7 +285,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
 
        if ((ccdcparam->med_filt_thres < 0) ||
           (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
-               dev_dbg(dev, "Invalid value of median filter thresold\n");
+               dev_dbg(dev, "Invalid value of median filter threshold\n");
                return -EINVAL;
        }
 
@@ -959,7 +959,7 @@ static struct ccdc_hw_device ccdc_hw_dev = {
        },
 };
 
-static int dm355_ccdc_init(void)
+static int __init dm355_ccdc_init(void)
 {
        printk(KERN_NOTICE "dm355_ccdc_init\n");
        if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
@@ -969,7 +969,7 @@ static int dm355_ccdc_init(void)
        return 0;
 }
 
-static void dm355_ccdc_exit(void)
+static void __exit dm355_ccdc_exit(void)
 {
        vpfe_unregister_ccdc_device(&ccdc_hw_dev);
 }
index 2f19a91..d5fa193 100644 (file)
@@ -859,7 +859,7 @@ static struct ccdc_hw_device ccdc_hw_dev = {
        },
 };
 
-static int dm644x_ccdc_init(void)
+static int __init dm644x_ccdc_init(void)
 {
        printk(KERN_NOTICE "dm644x_ccdc_init\n");
        if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0)
@@ -869,7 +869,7 @@ static int dm644x_ccdc_init(void)
        return 0;
 }
 
-static void dm644x_ccdc_exit(void)
+static void __exit dm644x_ccdc_exit(void)
 {
        vpfe_unregister_ccdc_device(&ccdc_hw_dev);
 }
index 402ce43..12a1b3d 100644 (file)
@@ -660,7 +660,7 @@ static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
 
        frame_format = ccdc_dev->hw_ops.get_frame_format();
        if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
-               free_irq(IRQ_VDINT1, vpfe_dev);
+               free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
 }
 
 static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
@@ -1338,7 +1338,7 @@ static int vpfe_reqbufs(struct file *file, void *priv,
        vpfe_dev->memory = req_buf->memory;
        videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue,
                                &vpfe_videobuf_qops,
-                               NULL,
+                               vpfe_dev->pdev,
                                &vpfe_dev->irqlock,
                                req_buf->type,
                                vpfe_dev->fmt.fmt.pix.field,
@@ -1413,6 +1413,41 @@ static int vpfe_dqbuf(struct file *file, void *priv,
                                      buf, file->f_flags & O_NONBLOCK);
 }
 
+static int vpfe_queryctrl(struct file *file, void *priv,
+               struct v4l2_queryctrl *qctrl)
+{
+       struct vpfe_device *vpfe_dev = video_drvdata(file);
+       struct vpfe_subdev_info *sdinfo;
+
+       sdinfo = vpfe_dev->current_subdev;
+
+       return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+                                        core, queryctrl, qctrl);
+
+}
+
+static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+       struct vpfe_device *vpfe_dev = video_drvdata(file);
+       struct vpfe_subdev_info *sdinfo;
+
+       sdinfo = vpfe_dev->current_subdev;
+
+       return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+                                        core, g_ctrl, ctrl);
+}
+
+static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+       struct vpfe_device *vpfe_dev = video_drvdata(file);
+       struct vpfe_subdev_info *sdinfo;
+
+       sdinfo = vpfe_dev->current_subdev;
+
+       return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+                                        core, s_ctrl, ctrl);
+}
+
 /*
  * vpfe_calculate_offsets : This function calculates buffers offset
  * for top and bottom field
@@ -1577,7 +1612,7 @@ static int vpfe_cropcap(struct file *file, void *priv,
 
        v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n");
 
-       if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards))
+       if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards))
                return -EINVAL;
 
        memset(crop, 0, sizeof(struct v4l2_cropcap));
@@ -1710,6 +1745,9 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
        .vidioc_querystd         = vpfe_querystd,
        .vidioc_s_std            = vpfe_s_std,
        .vidioc_g_std            = vpfe_g_std,
+       .vidioc_queryctrl        = vpfe_queryctrl,
+       .vidioc_g_ctrl           = vpfe_g_ctrl,
+       .vidioc_s_ctrl           = vpfe_s_ctrl,
        .vidioc_reqbufs          = vpfe_reqbufs,
        .vidioc_querybuf         = vpfe_querybuf,
        .vidioc_qbuf             = vpfe_qbuf,
@@ -1978,8 +2016,7 @@ static __init int vpfe_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, vpfe_dev);
        /* set driver private data */
        video_set_drvdata(vpfe_dev->video_dev, vpfe_dev);
-       i2c_adap = i2c_get_adapter(1);
-       vpfe_cfg = pdev->dev.platform_data;
+       i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id);
        num_subdevs = vpfe_cfg->num_subdevs;
        vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs,
                                GFP_KERNEL);
index 6d709ca..453236b 100644 (file)
@@ -53,7 +53,7 @@ struct vpss_hw_ops {
        int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
        /* select input to ccdc */
        void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
-       /* clear wbl overlflow bit */
+       /* clear wbl overflow bit */
        int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
 };
 
index ac947ae..bd78338 100644 (file)
@@ -293,7 +293,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
        dprintk("opening device and trying to acquire exclusive lock\n");
 
        if (!dev) {
-               printk(KERN_ERR "BUG: em28xx can't find device struct."
+               em28xx_err("BUG: em28xx can't find device struct."
                                " Can't proceed with open\n");
                return -ENODEV;
        }
@@ -325,7 +325,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
 
        return 0;
 err:
-       printk(KERN_ERR "Error while configuring em28xx mixer\n");
+       em28xx_err("Error while configuring em28xx mixer\n");
        return ret;
 }
 
index c0fd5c6..82da205 100644 (file)
@@ -225,6 +225,14 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
        {       -1,             -1,     -1,             -1},
 };
 
+static struct em28xx_reg_seq vc211a_enable[] = {
+       {EM28XX_R08_GPIO,       0xff,   0x07,           10},
+       {EM28XX_R08_GPIO,       0xff,   0x0f,           10},
+       {EM28XX_R08_GPIO,       0xff,   0x0b,           10},
+       {       -1,             -1,     -1,             -1},
+};
+
+
 /*
  *  Board definitions
  */
@@ -829,7 +837,7 @@ struct em28xx_board em28xx_boards[] = {
                .mts_firmware   = 1,
                .has_dvb        = 1,
                .dvb_gpio       = hauppauge_wintv_hvr_900_digital,
-               .ir_codes       = &ir_codes_hauppauge_new_table,
+               .ir_codes       = &ir_codes_rc5_hauppauge_new_table,
                .decoder        = EM28XX_TVP5150,
                .input          = { {
                        .type     = EM28XX_VMUX_TELEVISION,
@@ -1009,6 +1017,23 @@ struct em28xx_board em28xx_boards[] = {
                        .amux     = EM28XX_AMUX_LINE_IN,
                } },
        },
+       [EM2800_BOARD_VC211A] = {
+               .name         = "Actionmaster/LinXcel/Digitus VC211A",
+               .is_em2800    = 1,
+               .tuner_type   = TUNER_ABSENT,   /* Capture-only board */
+               .decoder      = EM28XX_SAA711X,
+               .input        = { {
+                       .type     = EM28XX_VMUX_COMPOSITE1,
+                       .vmux     = SAA7115_COMPOSITE0,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = vc211a_enable,
+               }, {
+                       .type     = EM28XX_VMUX_SVIDEO,
+                       .vmux     = SAA7115_SVIDEO3,
+                       .amux     = EM28XX_AMUX_LINE_IN,
+                       .gpio     = vc211a_enable,
+               } },
+       },
        [EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
                .name         = "Leadtek Winfast USB II",
                .is_em2800    = 1,
@@ -1381,10 +1406,14 @@ struct em28xx_board em28xx_boards[] = {
        },
        [EM2882_BOARD_TERRATEC_HYBRID_XS] = {
                .name         = "Terratec Hybrid XS (em2882)",
-               .valid        = EM28XX_BOARD_NOT_VALIDATED,
                .tuner_type   = TUNER_XC2028,
                .tuner_gpio   = default_tuner_gpio,
+               .mts_firmware = 1,
                .decoder      = EM28XX_TVP5150,
+               .has_dvb      = 1,
+               .dvb_gpio     = hauppauge_wintv_hvr_900_digital,
+               .ir_codes     = &ir_codes_terratec_cinergy_xs_table,
+               .xclk         = EM28XX_XCLK_FREQUENCY_12MHZ,
                .input        = { {
                        .type     = EM28XX_VMUX_TELEVISION,
                        .vmux     = TVP5150_COMPOSITE0,
@@ -1608,6 +1637,8 @@ struct usb_device_id em28xx_id_table[] = {
                        .driver_info = EM2820_BOARD_UNKNOWN },
        { USB_DEVICE(0xeb1a, 0x2861),
                        .driver_info = EM2820_BOARD_UNKNOWN },
+       { USB_DEVICE(0xeb1a, 0x2862),
+                       .driver_info = EM2820_BOARD_UNKNOWN },
        { USB_DEVICE(0xeb1a, 0x2870),
                        .driver_info = EM2820_BOARD_UNKNOWN },
        { USB_DEVICE(0xeb1a, 0x2881),
@@ -2050,6 +2081,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
        switch (dev->model) {
        case EM2880_BOARD_EMPIRE_DUAL_TV:
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2882_BOARD_TERRATEC_HYBRID_XS:
                ctl->demod = XC3028_FE_ZARLINK456;
                break;
        case EM2880_BOARD_TERRATEC_HYBRID_XS:
@@ -2227,6 +2259,7 @@ static int em28xx_hint_board(struct em28xx *dev)
 /* ----------------------------------------------------------------------- */
 void em28xx_register_i2c_ir(struct em28xx *dev)
 {
+       struct i2c_board_info info;
        const unsigned short addr_list[] = {
                 0x30, 0x47, I2C_CLIENT_END
        };
@@ -2234,9 +2267,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
        if (disable_ir)
                return;
 
-       memset(&dev->info, 0, sizeof(&dev->info));
+       memset(&info, 0, sizeof(struct i2c_board_info));
        memset(&dev->init_data, 0, sizeof(dev->init_data));
-       strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE);
+       strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
 
        /* detect & configure */
        switch (dev->model) {
@@ -2259,8 +2292,8 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
        }
 
        if (dev->init_data.name)
-               dev->info.platform_data = &dev->init_data;
-       i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list);
+               info.platform_data = &dev->init_data;
+       i2c_new_probed_device(&dev->i2c_adap, &info, addr_list);
 }
 
 void em28xx_card_setup(struct em28xx *dev)
@@ -2524,6 +2557,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
                dev->chip_id = retval;
 
                switch (dev->chip_id) {
+               case CHIP_ID_EM2800:
+                       em28xx_info("chip ID is em2800\n");
+                       break;
                case CHIP_ID_EM2710:
                        em28xx_info("chip ID is em2710\n");
                        break;
@@ -2650,7 +2686,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
        em28xx_init_extension(dev);
 
        /* Save some power by putting tuner to sleep */
-       v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+       v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
 
        return 0;
 
index a88257a..3f86d36 100644 (file)
@@ -50,7 +50,7 @@ MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
                printk(KERN_INFO "%s %s :"fmt, \
                         dev->name, __func__ , ##arg); } while (0)
 
-static int alt = EM28XX_PINOUT;
+static int alt;
 module_param(alt, int, 0644);
 MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
 
@@ -533,8 +533,15 @@ int em28xx_audio_setup(struct em28xx *dev)
 
        vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
        if (vid1 < 0) {
-               /* Device likely doesn't support AC97 */
+               /*
+                * Device likely doesn't support AC97
+                * Note: (some) em2800 devices without eeprom reports 0x91 on
+                *       CHIPCFG register, even not having an AC97 chip
+                */
                em28xx_warn("AC97 chip type couldn't be determined\n");
+               dev->audio_mode.ac97 = EM28XX_NO_AC97;
+               dev->has_alsa_audio = 0;
+               dev->audio_mode.has_audio = 0;
                goto init_audio;
        }
 
@@ -778,6 +785,16 @@ int em28xx_set_alternate(struct em28xx *dev)
        int i;
        unsigned int min_pkt_size = dev->width * 2 + 4;
 
+       /*
+        * alt = 0 is used only for control messages, so, only values
+        * greater than 0 can be used for streaming.
+        */
+       if (alt && alt < dev->num_alt) {
+               em28xx_coredbg("alternate forced to %d\n", dev->alt);
+               dev->alt = alt;
+               goto set_alt;
+       }
+
        /* When image size is bigger than a certain value,
           the frame size should be increased, otherwise, only
           green screen will be received.
@@ -798,6 +815,7 @@ int em28xx_set_alternate(struct em28xx *dev)
                        dev->alt = i;
        }
 
+set_alt:
        if (dev->alt != prev_alt) {
                em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
                                min_pkt_size, dev->alt);
index db74946..cc0505e 100644 (file)
@@ -313,22 +313,20 @@ static int attach_xc3028(u8 addr, struct em28xx *dev)
        cfg.i2c_addr  = addr;
 
        if (!dev->dvb->frontend) {
-               printk(KERN_ERR "%s/2: dvb frontend not attached. "
-                               "Can't attach xc3028\n",
-                      dev->name);
+               em28xx_errdev("/2: dvb frontend not attached. "
+                               "Can't attach xc3028\n");
                return -EINVAL;
        }
 
        fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg);
        if (!fe) {
-               printk(KERN_ERR "%s/2: xc3028 attach failed\n",
-                      dev->name);
+               em28xx_errdev("/2: xc3028 attach failed\n");
                dvb_frontend_detach(dev->dvb->frontend);
                dev->dvb->frontend = NULL;
                return -EINVAL;
        }
 
-       printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name);
+       em28xx_info("%s/2: xc3028 attached\n", dev->name);
 
        return 0;
 }
@@ -463,7 +461,7 @@ static int dvb_init(struct em28xx *dev)
        dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
 
        if (dvb == NULL) {
-               printk(KERN_INFO "em28xx_dvb: memory allocation failed\n");
+               em28xx_info("em28xx_dvb: memory allocation failed\n");
                return -ENOMEM;
        }
        dev->dvb = dvb;
@@ -493,6 +491,7 @@ static int dvb_init(struct em28xx *dev)
                }
                break;
        case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+       case EM2882_BOARD_TERRATEC_HYBRID_XS:
        case EM2880_BOARD_EMPIRE_DUAL_TV:
                dvb->frontend = dvb_attach(zl10353_attach,
                                           &em28xx_zl10353_xc3028_no_i2c_gate,
@@ -569,15 +568,12 @@ static int dvb_init(struct em28xx *dev)
                }
                break;
        default:
-               printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
-                               " isn't supported yet\n",
-                      dev->name);
+               em28xx_errdev("/2: The frontend of your DVB/ATSC card"
+                               " isn't supported yet\n");
                break;
        }
        if (NULL == dvb->frontend) {
-               printk(KERN_ERR
-                      "%s/2: frontend initialization failed\n",
-                      dev->name);
+               em28xx_errdev("/2: frontend initialization failed\n");
                result = -EINVAL;
                goto out_free;
        }
@@ -591,7 +587,7 @@ static int dvb_init(struct em28xx *dev)
                goto out_free;
 
        em28xx_set_mode(dev, EM28XX_SUSPEND);
-       printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
+       em28xx_info("Successfully loaded em28xx-dvb\n");
        return 0;
 
 out_free:
index 7a0fe38..d96ec7c 100644 (file)
@@ -70,6 +70,7 @@ struct em28xx_IR {
        int polling;
        struct delayed_work work;
        unsigned int last_toggle:1;
+       unsigned int full_code:1;
        unsigned int last_readcount;
        unsigned int repeat_interval;
 
@@ -246,9 +247,10 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
                return;
        }
 
-       dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
+       dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n",
                poll_result.toggle_bit, poll_result.read_count,
-               ir->last_readcount, poll_result.rc_data[0]);
+               ir->last_readcount, poll_result.rc_address,
+               poll_result.rc_data[0]);
 
        if (ir->dev->chip_id == CHIP_ID_EM2874) {
                /* The em2874 clears the readcount field every time the
@@ -282,8 +284,15 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
 
        if (do_sendkey) {
                dprintk("sending keypress\n");
-               ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
-                                poll_result.rc_data[0]);
+
+               if (ir->full_code)
+                       ir_input_keydown(ir->input, &ir->ir,
+                                        poll_result.rc_address << 8 |
+                                        poll_result.rc_data[0]);
+               else
+                       ir_input_keydown(ir->input, &ir->ir,
+                                        poll_result.rc_data[0]);
+
                ir_input_nokey(ir->input, &ir->ir);
        }
 
@@ -333,6 +342,8 @@ int em28xx_ir_init(struct em28xx *dev)
        switch (dev->chip_id) {
        case CHIP_ID_EM2860:
        case CHIP_ID_EM2883:
+               if (dev->model == EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950)
+                       ir->full_code = 1;
                ir->get_key = default_polling_getkey;
                break;
        case CHIP_ID_EM2874:
@@ -356,7 +367,11 @@ int em28xx_ir_init(struct em28xx *dev)
        usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
        strlcat(ir->phys, "/input0", sizeof(ir->phys));
 
-       ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
+       err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER,
+                            dev->board.ir_codes);
+       if (err < 0)
+               goto err_out_free;
+
        input_dev->name = ir->name;
        input_dev->phys = ir->phys;
        input_dev->id.bustype = BUS_USB;
@@ -381,6 +396,7 @@ int em28xx_ir_init(struct em28xx *dev)
        em28xx_ir_stop(ir);
        dev->ir = NULL;
  err_out_free:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
        kfree(ir);
        return err;
@@ -395,6 +411,7 @@ int em28xx_ir_fini(struct em28xx *dev)
                return 0;
 
        em28xx_ir_stop(ir);
+       ir_input_free(ir->input);
        input_unregister_device(ir->input);
        kfree(ir);
 
index ed12e7f..058ac87 100644 (file)
 
 /* FIXME: Need to be populated with the other chip ID's */
 enum em28xx_chip_id {
+       CHIP_ID_EM2800 = 7,
        CHIP_ID_EM2710 = 17,
        CHIP_ID_EM2820 = 18,    /* Also used by some em2710 */
        CHIP_ID_EM2840 = 20,
index 3a1dfb7..7ad6537 100644 (file)
@@ -1060,12 +1060,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                /* the em2800 can only scale down to 50% */
                height = height > (3 * maxh / 4) ? maxh : maxh / 2;
                width = width > (3 * maxw / 4) ? maxw : maxw / 2;
-               /* According to empiatech support the MaxPacketSize is too small
-                * to support framesizes larger than 640x480 @ 30 fps or 640x576
-                * @ 25 fps.  As this would cut of a part of the image we prefer
-                * 360x576 or 360x480 for now */
-               if (width == maxw && height == maxh)
-                       width /= 2;
        } else {
                /* width must even because of the YUYV format
                   height must be even because of interlacing */
@@ -2225,7 +2219,7 @@ static int em28xx_v4l2_close(struct file *filp)
                }
 
                /* Save some power by putting tuner to sleep */
-               v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby);
+               v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
 
                /* do this before setting alternate! */
                em28xx_uninit_isoc(dev);
index 0a73e8b..441df64 100644 (file)
 #define EM2820_BOARD_SILVERCREST_WEBCAM           71
 #define EM2861_BOARD_GADMEI_UTV330PLUS           72
 #define EM2870_BOARD_REDDO_DVB_C_USB_BOX          73
+#define EM2800_BOARD_VC211A                      74
 
 /* Limits minimum and default number of buffers */
 #define EM28XX_MIN_BUF 4
  */
 #define EM28XX_NUM_PACKETS 40
 
-/* default alternate; 0 means choose the best */
-#define EM28XX_PINOUT 0
-
 #define EM28XX_INTERLACED_DEFAULT 1
 
 /*
@@ -615,7 +613,6 @@ struct em28xx {
        struct em28xx_dvb *dvb;
 
        /* I2C keyboard data */
-       struct i2c_board_info info;
        struct IR_i2c_init_data init_data;
 };
 
@@ -800,7 +797,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev)
        if (dev->board.is_webcam)
                return dev->sensor_xres;
 
-       if (dev->board.max_range_640_480)
+       if (dev->board.max_range_640_480 || dev->board.is_em2800)
                return 640;
 
        return 720;
index fe2e490..609d65b 100644 (file)
@@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A
          module will be called gspca_mr97310a.
 
 config USB_GSPCA_OV519
-       tristate "OV519 USB Camera Driver"
+       tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
        depends on VIDEO_V4L2 && USB_GSPCA
        help
-         Say Y here if you want support for cameras based on the OV519 chip.
+         Say Y here if you want support for cameras based on one of these:
+         OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
 
          To compile this driver as a module, choose M here: the
          module will be called gspca_ov519.
@@ -103,6 +104,15 @@ config USB_GSPCA_PAC207
          To compile this driver as a module, choose M here: the
          module will be called gspca_pac207.
 
+config USB_GSPCA_PAC7302
+       tristate "Pixart PAC7302 USB Camera Driver"
+       depends on VIDEO_V4L2 && USB_GSPCA
+       help
+         Say Y here if you want support for cameras based on the PAC7302 chip.
+
+         To compile this driver as a module, choose M here: the
+         module will be called gspca_pac7302.
+
 config USB_GSPCA_PAC7311
        tristate "Pixart PAC7311 USB Camera Driver"
        depends on VIDEO_V4L2 && USB_GSPCA
@@ -229,6 +239,15 @@ config USB_GSPCA_STK014
          To compile this driver as a module, choose M here: the
          module will be called gspca_stk014.
 
+config USB_GSPCA_STV0680
+       tristate "STV0680 USB Camera Driver"
+       depends on VIDEO_V4L2 && USB_GSPCA
+       help
+         Say Y here if you want support for cameras based on the STV0680 chip.
+
+         To compile this driver as a module, choose M here: the
+         module will be called gspca_stv0680.
+
 config USB_GSPCA_SUNPLUS
        tristate "SUNPLUS USB Camera Driver"
        depends on VIDEO_V4L2 && USB_GSPCA
index b742081..ff2c727 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
 obj-$(CONFIG_USB_GSPCA_OV519)    += gspca_ov519.o
 obj-$(CONFIG_USB_GSPCA_OV534)    += gspca_ov534.o
 obj-$(CONFIG_USB_GSPCA_PAC207)   += gspca_pac207.o
+obj-$(CONFIG_USB_GSPCA_PAC7302)  += gspca_pac7302.o
 obj-$(CONFIG_USB_GSPCA_PAC7311)  += gspca_pac7311.o
 obj-$(CONFIG_USB_GSPCA_SN9C20X)  += gspca_sn9c20x.o
 obj-$(CONFIG_USB_GSPCA_SONIXB)   += gspca_sonixb.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905)    += gspca_sq905.o
 obj-$(CONFIG_USB_GSPCA_SQ905C)   += gspca_sq905c.o
 obj-$(CONFIG_USB_GSPCA_SUNPLUS)  += gspca_sunplus.o
 obj-$(CONFIG_USB_GSPCA_STK014)   += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_STV0680)  += gspca_stv0680.o
 obj-$(CONFIG_USB_GSPCA_T613)     += gspca_t613.o
 obj-$(CONFIG_USB_GSPCA_TV8532)   += gspca_tv8532.o
 obj-$(CONFIG_USB_GSPCA_VC032X)   += gspca_vc032x.o
@@ -37,6 +39,7 @@ gspca_mr97310a-objs := mr97310a.o
 gspca_ov519-objs    := ov519.o
 gspca_ov534-objs    := ov534.o
 gspca_pac207-objs   := pac207.o
+gspca_pac7302-objs  := pac7302.o
 gspca_pac7311-objs  := pac7311.o
 gspca_sn9c20x-objs  := sn9c20x.o
 gspca_sonixb-objs   := sonixb.o
@@ -50,6 +53,7 @@ gspca_spca561-objs  := spca561.o
 gspca_sq905-objs    := sq905.o
 gspca_sq905c-objs   := sq905c.o
 gspca_stk014-objs   := stk014.o
+gspca_stv0680-objs  := stv0680.o
 gspca_sunplus-objs  := sunplus.o
 gspca_t613-objs     := t613.o
 gspca_tv8532-objs   := tv8532.o
index eca0035..2f0b8d6 100644 (file)
@@ -888,8 +888,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -897,16 +896,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        if (data[0] == 0xff && data[1] == 0xd8) {
 
                /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
 
                /* put the JPEG header in the new frame */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                       sd->jpeg_hdr, JPEG_HDR_SZ);
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
+                               sd->jpeg_hdr, JPEG_HDR_SZ);
                data += 2;
                len -= 2;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static void setbrightness(struct gspca_dev*gspca_dev)
index c1461e6..9de8641 100644 (file)
@@ -752,8 +752,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
 #undef LIMIT
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        int seqframe;
@@ -767,14 +766,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                       data[2], data[3], data[4], data[5]);
                data += 30;
                /* don't change datalength as the chips provided it */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                return;
        }
        if (len) {
                data += 8;
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+               gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
        } else {                        /* Drop Packet */
                gspca_dev->last_packet_type = DISCARD_PACKET;
        }
index 480ec5c..5d90e74 100644 (file)
@@ -82,7 +82,6 @@ static void dostream(struct work_struct *work)
        struct gspca_dev *gspca_dev = &dev->gspca_dev;
        struct urb *urb = gspca_dev->urb[0];
        u8 *data = urb->transfer_buffer;
-       struct gspca_frame *frame;
        int ret = 0;
        int len;
 
@@ -118,10 +117,6 @@ again:
                        }
                        if (!gspca_dev->present || !gspca_dev->streaming)
                                goto out;
-                       frame = gspca_get_i_frame(&dev->gspca_dev);
-                       if (frame == NULL)
-                               gspca_dev->last_packet_type = DISCARD_PACKET;
-
                        if (len < FPIX_MAX_TRANSFER ||
                                (data[len - 2] == 0xff &&
                                        data[len - 1] == 0xd9)) {
@@ -132,21 +127,17 @@ again:
                                 * but there's nothing we can do. We also end
                                 * here if the the jpeg ends right at the end
                                 * of the frame. */
-                               if (frame)
-                                       frame = gspca_frame_add(gspca_dev,
-                                                       LAST_PACKET,
-                                                       frame,
-                                                       data, len);
+                               gspca_frame_add(gspca_dev, LAST_PACKET,
+                                               data, len);
                                break;
                        }
 
                        /* got a partial image */
-                       if (frame)
-                               gspca_frame_add(gspca_dev,
-                                               gspca_dev->last_packet_type
-                                                       == LAST_PACKET
-                                               ? FIRST_PACKET : INTER_PACKET,
-                                               frame, data, len);
+                       gspca_frame_add(gspca_dev,
+                                       gspca_dev->last_packet_type
+                                               == LAST_PACKET
+                                       ? FIRST_PACKET : INTER_PACKET,
+                                       data, len);
                }
 
                /* We must wait before trying reading the next
index 39f6261..1355e52 100644 (file)
@@ -1,6 +1,5 @@
-/* @file gl860-mi1320.c
- * @author Olivier LORIN from my logs
- * @date 2009-08-27
+/* Subdriver for the GL860 chip with the MI1320 sensor
+ * Author Olivier LORIN from own logs
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -127,49 +126,49 @@ static u8 dat_wbalBL[] =
 
 static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
 
-static u8 s000[] =
+static u8 dat_common00[] =
        "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
        "\xd8\x04\x58\x00\x04\x02";
-static u8 s001[] =
+static u8 dat_common01[] =
        "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d"
        "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0";
-static u8 s002[] =
+static u8 dat_common02[] =
        "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e"
        "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00"
        "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff";
-static u8 s003[] =
+static u8 dat_common03[] =
        "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda"
        "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c"
        "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c";
-static u8 s004[] =
+static u8 dat_common04[] =
        "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
-static u8 s005[] =
+static u8 dat_common05[] =
        "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68"
        "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82"
        "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b";
-static u8 s006[] =
+static u8 dat_common06[] =
        "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06"
        "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4"
        "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f";
-static u8 s007[] =
+static u8 dat_common07[] =
        "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72"
        "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03"
        "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea"
        "\xe1\xff\xf1\x00";
-static u8 s008[] =
+static u8 dat_common08[] =
        "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7"
        "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06"
        "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a";
-static u8 s009[] =
+static u8 dat_common09[] =
        "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03"
        "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa"
        "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14";
-static u8 s010[] =
+static u8 dat_common10[] =
        "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00"
        "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f"
        "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01"
        "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10";
-static u8 s011[] =
+static u8 dat_common11[] =
        "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10"
        "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00"
        "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81";
@@ -222,26 +221,26 @@ void mi1320_init_settings(struct gspca_dev *gspca_dev)
 
 static void common(struct gspca_dev *gspca_dev)
 {
-       s32 n; /* reserved for FETCH macros */
+       s32 n; /* reserved for FETCH functions */
 
-       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000);
+       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00);
        ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01);
        n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06);
        keep_on_fetching_validx(gspca_dev, tbl_common,
                                        ARRAY_SIZE(tbl_common), n);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10);
        keep_on_fetching_validx(gspca_dev, tbl_common,
                                        ARRAY_SIZE(tbl_common), n);
-       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011);
+       ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11);
        keep_on_fetching_validx(gspca_dev, tbl_common,
                                        ARRAY_SIZE(tbl_common), n);
 }
index ffb09fe..80cb3f1 100644 (file)
@@ -1,7 +1,6 @@
-/* @file gl860-mi2020.c
- * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
+/* Subdriver for the GL860 chip with the MI2020 sensor
+ * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
  * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist.
- * @date 2009-08-27
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -41,7 +40,7 @@ static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
 static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
 static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
 
-static struct validx tbl_common_a[] = {
+static struct validx tbl_common1[] = {
        {0x0000, 0x0000},
        {1, 0xffff}, /* msleep(35); */
        {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0},
@@ -49,7 +48,7 @@ static struct validx tbl_common_a[] = {
        {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000},
 };
 
-static struct validx tbl_common_b[] = {
+static struct validx tbl_common2[] = {
        {0x006a, 0x0007},
        {35, 0xffff},
        {0x00ef, 0x0006},
@@ -60,7 +59,7 @@ static struct validx tbl_common_b[] = {
        {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
 };
 
-static struct idxdata tbl_common_c[] = {
+static struct idxdata tbl_common3[] = {
        {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
        {6, "\xff\xff\xff"}, /* 12 */
        {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
@@ -109,7 +108,7 @@ static struct idxdata tbl_common_c[] = {
        {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"},
 };
 
-static struct idxdata tbl_common_d[] = {
+static struct idxdata tbl_common4[] = {
        {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"},
        {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
        {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
@@ -118,7 +117,7 @@ static struct idxdata tbl_common_d[] = {
        {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"},
 };
 
-static struct idxdata tbl_common_e[] = {
+static struct idxdata tbl_common5[] = {
        {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"},
        {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"},
        {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"},
@@ -180,7 +179,7 @@ static struct validx tbl_init_at_startup[] = {
        {53, 0xffff},
 };
 
-static struct idxdata tbl_init_post_alt_low_a[] = {
+static struct idxdata tbl_init_post_alt_low1[] = {
        {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
        {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
        {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
@@ -189,7 +188,7 @@ static struct idxdata tbl_init_post_alt_low_a[] = {
        {0x33, "\x90\x00\x9b"},
 };
 
-static struct idxdata tbl_init_post_alt_low_b[] = {
+static struct idxdata tbl_init_post_alt_low2[] = {
        {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
        {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
        {2, "\xff\xff\xff"},
@@ -197,7 +196,7 @@ static struct idxdata tbl_init_post_alt_low_b[] = {
        {2, "\xff\xff\xff"},
 };
 
-static struct idxdata tbl_init_post_alt_low_c[] = {
+static struct idxdata tbl_init_post_alt_low3[] = {
        {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
        {2, "\xff\xff\xff"},
        {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
@@ -221,7 +220,7 @@ static struct idxdata tbl_init_post_alt_low_c[] = {
        {1, "\xff\xff\xff"},
 };
 
-static struct idxdata tbl_init_post_alt_low_d[] = {
+static struct idxdata tbl_init_post_alt_low4[] = {
        {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
        {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
        {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
@@ -267,7 +266,7 @@ static struct idxdata tbl_init_post_alt_low_d[] = {
        {0x32, "\x6c\x14\x08"},
 };
 
-static struct idxdata tbl_init_post_alt_big_a[] = {
+static struct idxdata tbl_init_post_alt_big1[] = {
        {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
        {2, "\xff\xff\xff"},
        {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
@@ -288,7 +287,7 @@ static struct idxdata tbl_init_post_alt_big_a[] = {
        {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
 };
 
-static struct idxdata tbl_init_post_alt_big_b[] = {
+static struct idxdata tbl_init_post_alt_big2[] = {
        {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
        {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
        {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
@@ -317,7 +316,7 @@ static struct idxdata tbl_init_post_alt_big_b[] = {
        {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
 };
 
-static struct idxdata tbl_init_post_alt_big_c[] = {
+static struct idxdata tbl_init_post_alt_big3[] = {
        {0x33, "\x8c\xa1\x02"},
        {0x33, "\x90\x00\x1f"},
        {0x33, "\x8c\xa1\x02"},
@@ -388,14 +387,14 @@ static void common(struct gspca_dev *gspca_dev)
        s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
 
        if (_MI2020b_) {
-               fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a));
+               fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1));
        } else {
                if (_MI2020_)
                        ctrl_out(gspca_dev, 0x40,  1, 0x0008, 0x0004,  0, NULL);
                else
                        ctrl_out(gspca_dev, 0x40,  1, 0x0002, 0x0004,  0, NULL);
                msleep(35);
-               fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b));
+               fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2));
        }
        ctrl_out(gspca_dev, 0x40,  3, 0x7a00, 0x0033,  3, "\x86\x25\x01");
        ctrl_out(gspca_dev, 0x40,  3, 0x7a00, 0x0033,  3, "\x86\x25\x00");
@@ -403,13 +402,13 @@ static void common(struct gspca_dev *gspca_dev)
        ctrl_out(gspca_dev, 0x40,  3, 0x7a00, 0x0030,  3, "\x1a\x0a\xcc");
        if (reso == IMAGE_1600)
                msleep(2); /* 1600 */
-       fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c));
+       fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3));
 
        if (_MI2020b_ || _MI2020_)
-               fetch_idxdata(gspca_dev, tbl_common_d,
-                               ARRAY_SIZE(tbl_common_d));
+               fetch_idxdata(gspca_dev, tbl_common4,
+                               ARRAY_SIZE(tbl_common4));
 
-       fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e));
+       fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5));
        if (_MI2020b_ || _MI2020_) {
                /* Different from fret */
                ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78");
@@ -525,15 +524,15 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
                                12, dat_800);
 
                if (_MI2020c_)
-                       fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a,
-                                       ARRAY_SIZE(tbl_init_post_alt_low_a));
+                       fetch_idxdata(gspca_dev, tbl_init_post_alt_low1,
+                                       ARRAY_SIZE(tbl_init_post_alt_low1));
 
                if (reso == IMAGE_800)
-                       fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b,
-                                       ARRAY_SIZE(tbl_init_post_alt_low_b));
+                       fetch_idxdata(gspca_dev, tbl_init_post_alt_low2,
+                                       ARRAY_SIZE(tbl_init_post_alt_low2));
 
-               fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c,
-                               ARRAY_SIZE(tbl_init_post_alt_low_c));
+               fetch_idxdata(gspca_dev, tbl_init_post_alt_low3,
+                               ARRAY_SIZE(tbl_init_post_alt_low3));
 
                if (_MI2020b_) {
                        ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
@@ -574,8 +573,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
                msleep(5);/* " */
 
                if (_MI2020c_) {
-                       fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d,
-                                       ARRAY_SIZE(tbl_init_post_alt_low_d));
+                       fetch_idxdata(gspca_dev, tbl_init_post_alt_low4,
+                                       ARRAY_SIZE(tbl_init_post_alt_low4));
                } else {
                        ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
                        msleep(14); /* 0xd8 */
@@ -644,8 +643,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
                                        3, "\x90\x04\xb0");
                }
 
-               fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a,
-                               ARRAY_SIZE(tbl_init_post_alt_big_a));
+               fetch_idxdata(gspca_dev, tbl_init_post_alt_big1,
+                               ARRAY_SIZE(tbl_init_post_alt_big1));
 
                if (reso == IMAGE_1600)
                        msleep(13); /* 1600 */
@@ -708,8 +707,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
                msleep(14);
 
                if (_MI2020c_)
-                       fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b,
-                                       ARRAY_SIZE(tbl_init_post_alt_big_b));
+                       fetch_idxdata(gspca_dev, tbl_init_post_alt_big2,
+                                       ARRAY_SIZE(tbl_init_post_alt_big2));
 
                /* flip/mirror */
                ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
@@ -738,8 +737,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
                sd->nbIm = 0;
 
                if (_MI2020c_)
-                       fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c,
-                                       ARRAY_SIZE(tbl_init_post_alt_big_c));
+                       fetch_idxdata(gspca_dev, tbl_init_post_alt_big3,
+                                       ARRAY_SIZE(tbl_init_post_alt_big3));
        }
 
        sd->vold.mirror    = mirror;
index 14b9c37..768cac5 100644 (file)
@@ -1,6 +1,5 @@
-/* @file gl860-ov2640.c
- * @author Olivier LORIN, from Malmostoso's logs
- * @date 2009-08-27
+/* Subdriver for the GL860 chip with the OV2640 sensor
+ * Author Olivier LORIN, from Malmostoso's logs
  *
  * This 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 "gl860.h"
 
 static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
-static u8 dat_init2[] = {0x61}; /* expected */
-static u8 dat_init3[] = {0x51}; /* expected */
+
+static u8 c61[] = {0x61}; /* expected */
+static u8 c51[] = {0x51}; /* expected */
+static u8 c50[] = {0x50}; /* expected */
+static u8 c28[] = {0x28}; /* expected */
+static u8 ca8[] = {0xa8}; /* expected */
 
 static u8 dat_post[] =
        "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01";
@@ -32,10 +35,6 @@ static u8 dat_800[]  = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21";
 static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01";
 static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41";
 
-static u8 c50[] = {0x50}; /* expected */
-static u8 c28[] = {0x28}; /* expected */
-static u8 ca8[] = {0xa8}; /* expected */
-
 static struct validx tbl_init_at_startup[] = {
        {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
        {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
@@ -92,7 +91,7 @@ static struct validx tbl_common[] = {
        {0x6000, 0x0010},
 };
 
-static struct validx tbl_sensor_settings_common_a[] = {
+static struct validx tbl_sensor_settings_common1[] = {
        {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
        {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
        {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000},
@@ -104,40 +103,10 @@ static struct validx tbl_sensor_settings_common_a[] = {
        {0x0040, 0x0000},
 };
 
-static struct validx tbl_sensor_settings_common_b[] = {
+static struct validx tbl_sensor_settings_common2[] = {
        {0x6001, 0x00ff}, {0x6038, 0x000c},
        {10, 0xffff},
        {0x6000, 0x0011},
-       /* backlight=31/64 */
-       {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
-       /* bright=0/256 */
-       {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d},
-       /* wbal=64/128 */
-       {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d},
-       /* cntr=0/256 */
-       {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d},
-       /* sat=128/256 */
-       {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d},
-       /* sharpness=0/32 */
-       {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093},
-       /* hue=0/256 */
-       {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d},
-       /* gam=32/64 */
-       {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d},
-       /* image right up */
-       {0xffff, 0xffff},
-       {15, 0xffff},
-       {0x6001, 0x00ff}, {0x6000, 0x8004},
-       {0xffff, 0xffff},
-       {0x60a8, 0x0004},
-       {15, 0xffff},
-       {0x6001, 0x00ff}, {0x6000, 0x8004},
-       {0xffff, 0xffff},
-       {0x60f8, 0x0004},
-       /* image right up */
-       {0xffff, 0xffff},
-       /* backlight=31/64 */
-       {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
 };
 
 static struct validx tbl_640[] = {
@@ -166,7 +135,7 @@ static struct validx tbl_800[] = {
        {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018},
 };
 
-static struct validx tbl_big_a[] = {
+static struct validx tbl_big1[] = {
        {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
        {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045},
        {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018},
@@ -176,14 +145,14 @@ static struct validx tbl_big_a[] = {
        {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c},
 };
 
-static struct validx tbl_big_b[] = {
+static struct validx tbl_big2[] = {
        {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052},
        {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057},
        {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3},
        {0x6000, 0x008e},
 };
 
-static struct validx tbl_big_c[] = {
+static struct validx tbl_big3[] = {
        {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd},
        {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
        {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7},
@@ -223,17 +192,19 @@ void ov2640_init_settings(struct gspca_dev *gspca_dev)
        sd->vcur.hue        =   0;
        sd->vcur.saturation = 128;
        sd->vcur.whitebal   =  64;
+       sd->vcur.mirror     =   0;
+       sd->vcur.flip       =   0;
 
        sd->vmax.backlight  =  64;
        sd->vmax.brightness = 255;
        sd->vmax.sharpness  =  31;
        sd->vmax.contrast   = 255;
        sd->vmax.gamma      =  64;
-       sd->vmax.hue        = 255 + 1;
+       sd->vmax.hue        = 254 + 2;
        sd->vmax.saturation = 255;
        sd->vmax.whitebal   = 128;
-       sd->vmax.mirror     = 0;
-       sd->vmax.flip       = 0;
+       sd->vmax.mirror     = 1;
+       sd->vmax.flip       = 1;
        sd->vmax.AC50Hz     = 0;
 
        sd->dev_camera_settings = ov2640_camera_settings;
@@ -259,11 +230,11 @@ static int ov2640_init_at_startup(struct gspca_dev *gspca_dev)
 
        common(gspca_dev);
 
-       ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2);
+       ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61);
 
        ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
 
-       ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3);
+       ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51);
 
        ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL);
 /*     ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
@@ -275,6 +246,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
+       sd->mirrorMask = 0;
+
        sd->vold.backlight  = -1;
        sd->vold.brightness = -1;
        sd->vold.sharpness  = -1;
@@ -283,6 +256,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
        sd->vold.gamma    = -1;
        sd->vold.hue      = -1;
        sd->vold.whitebal = -1;
+       sd->vold.mirror = -1;
+       sd->vold.flip   = -1;
 
        ov2640_init_post_alt(gspca_dev);
 
@@ -292,16 +267,16 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
 static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
 {
        s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
-       s32 n; /* reserved for FETCH macros */
+       s32 n; /* reserved for FETCH functions */
 
        ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
 
-       n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a,
-                       ARRAY_SIZE(tbl_sensor_settings_common_a));
+       n = fetch_validx(gspca_dev, tbl_sensor_settings_common1,
+                       ARRAY_SIZE(tbl_sensor_settings_common1));
        ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
        common(gspca_dev);
-       keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a,
-                               ARRAY_SIZE(tbl_sensor_settings_common_a), n);
+       keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1,
+                               ARRAY_SIZE(tbl_sensor_settings_common1), n);
 
        switch (reso) {
        case IMAGE_640:
@@ -316,18 +291,18 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
 
        case IMAGE_1600:
        case IMAGE_1280:
-               n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a));
+               n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1));
 
                if (reso == IMAGE_1280) {
-                       n = fetch_validx(gspca_dev, tbl_big_b,
-                                       ARRAY_SIZE(tbl_big_b));
+                       n = fetch_validx(gspca_dev, tbl_big2,
+                                       ARRAY_SIZE(tbl_big2));
                } else {
                        ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL);
                        ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL);
                        ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL);
                }
 
-               n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c));
+               n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3));
 
                if (reso == IMAGE_1280) {
                        ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
@@ -343,20 +318,8 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
                break;
        }
 
-       n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b,
-                       ARRAY_SIZE(tbl_sensor_settings_common_b));
-       ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
-       keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
-                               ARRAY_SIZE(tbl_sensor_settings_common_b), n);
-       ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
-       keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
-                               ARRAY_SIZE(tbl_sensor_settings_common_b), n);
-       ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
-       keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
-                               ARRAY_SIZE(tbl_sensor_settings_common_b), n);
-       ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
-       keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
-                               ARRAY_SIZE(tbl_sensor_settings_common_b), n);
+       n = fetch_validx(gspca_dev, tbl_sensor_settings_common2,
+                       ARRAY_SIZE(tbl_sensor_settings_common2));
 
        ov2640_camera_settings(gspca_dev);
 
@@ -393,18 +356,20 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
        s32 sat    = sd->vcur.saturation;
        s32 hue    = sd->vcur.hue;
        s32 wbal   = sd->vcur.whitebal;
+       s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0);
+       s32 flip   = (((sd->vcur.flip   > 0) ^ sd->mirrorMask) == 0);
 
        if (backlight != sd->vold.backlight) {
+               /* No sd->vold.backlight=backlight; (to be done again later) */
                if (backlight < 0 || backlight > sd->vmax.backlight)
                        backlight = 0;
 
                ctrl_out(gspca_dev, 0x40, 1, 0x6001                 , 0x00ff,
                                0, NULL);
-               ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight     , 0x0024,
+               ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight     , 0x0024,
                                0, NULL);
-               ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
+               ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
                                0, NULL);
-               /* No sd->vold.backlight=backlight; (to be done again later) */
        }
 
        if (bright != sd->vold.brightness) {
@@ -466,7 +431,7 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
                ctrl_out(gspca_dev, 0x40, 1, 0x6002     , 0x007c, 0, NULL);
                ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d,
                                0, NULL);
-               if (hue >= sd->vmax.hue)
+               if (hue >= 255)
                        sd->swapRB = 1;
                else
                        sd->swapRB = 0;
@@ -482,14 +447,33 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
                ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL);
        }
 
+       if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
+               sd->vold.mirror = mirror;
+               sd->vold.flip   = flip;
+
+               mirror = 0x80 * mirror;
+               ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+               ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+               ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
+               ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL);
+
+               flip = 0x50 * flip + mirror;
+               ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+               ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
+               ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
+               ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL);
+
+               ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
+       }
+
        if (backlight != sd->vold.backlight) {
                sd->vold.backlight = backlight;
 
                ctrl_out(gspca_dev, 0x40, 1, 0x6001                 , 0x00ff,
                                0, NULL);
-               ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight     , 0x0024,
+               ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight     , 0x0024,
                                0, NULL);
-               ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
+               ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
                                0, NULL);
        }
 
index eda3346..d412694 100644 (file)
@@ -1,7 +1,6 @@
-/* @file gl860-ov9655.c
- * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
+/* Subdriver for the GL860 chip with the OV9655 sensor
+ * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
  * on dsd's weblog
- * @date 2009-08-27
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -104,14 +103,14 @@ static u8 *tbl_800[] = {
 };
 
 static u8 c04[] = {0x04};
-static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
-static u8 dat_post_2[] = "\x10\x10\xc1\x02";
-static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
-static u8 dat_post_4[] = "\x10\x02\xc1\x06";
-static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
-static u8 dat_post_6[] = "\x10\x10\xc1\x05";
-static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
-static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
+static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
+static u8 dat_post2[] = "\x10\x10\xc1\x02";
+static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
+static u8 dat_post4[] = "\x10\x02\xc1\x06";
+static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
+static u8 dat_post6[] = "\x10\x10\xc1\x05";
+static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
+static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
 
 static struct validx tbl_init_post_alt[] = {
        {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff},
@@ -212,7 +211,7 @@ static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
 static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
 {
        s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
-       s32 n; /* reserved for FETCH macros */
+       s32 n; /* reserved for FETCH functions */
        s32 i;
        u8 **tbl;
 
@@ -243,7 +242,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
        ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
        keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
                                        ARRAY_SIZE(tbl_init_post_alt), n);
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
        keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
                                        ARRAY_SIZE(tbl_init_post_alt), n);
 
@@ -259,7 +258,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
        ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
        keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
                                        ARRAY_SIZE(tbl_init_post_alt), n);
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
        keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
                                        ARRAY_SIZE(tbl_init_post_alt), n);
 
@@ -270,18 +269,18 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
        keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
                                        ARRAY_SIZE(tbl_init_post_alt), n);
 
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
 
-       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2);
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3);
+       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3);
 
-       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4);
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5);
+       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5);
 
-       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6);
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7);
+       ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7);
 
-       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8);
+       ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8);
 
        ov9655_camera_settings(gspca_dev);
 
index 6ef59ac..a695e0a 100644 (file)
@@ -1,9 +1,7 @@
-/* @file gl860.c
- * @date 2009-08-27
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver core
  *
- * Genesys Logic webcam with gl860 subdrivers
- *
- * Driver by Olivier Lorin <o.lorin@laposte.net>
+ * 2009/09/24 Olivier Lorin <o.lorin@laposte.net>
  * GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
  * Thanks BUGabundo and Malmostoso for your amazing help!
  *
@@ -23,8 +21,8 @@
 #include "gspca.h"
 #include "gl860.h"
 
-MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>");
-MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver");
+MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>");
+MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver");
 MODULE_LICENSE("GPL");
 
 /*======================== static function declarations ====================*/
@@ -38,7 +36,7 @@ static int  sd_isoc_init(struct gspca_dev *gspca_dev);
 static int  sd_start(struct gspca_dev *gspca_dev);
 static void sd_stop0(struct gspca_dev *gspca_dev);
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame, u8 *data, s32 len);
+                       u8 *data, int len);
 static void sd_callback(struct gspca_dev *gspca_dev);
 
 static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
@@ -53,7 +51,7 @@ MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
 static char sensor[7];
 module_param_string(sensor, sensor, sizeof(sensor), 0644);
 MODULE_PARM_DESC(sensor,
-               " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')");
+               " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')");
 
 /*============================ webcam controls =============================*/
 
@@ -156,7 +154,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev)
        SET_MY_CTRL(V4L2_CID_VFLIP,
                V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip)
        SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY,
-               V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz)
+               V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz)
 
        return nCtrls;
 }
@@ -435,7 +433,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 
 /* This function is called when an image is being received */
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame, u8 *data, s32 len)
+                       u8 *data, int len)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        static s32 nSkipped;
@@ -447,11 +445,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        /* Test only against 0202h, so endianess does not matter */
        switch (*(s16 *) data) {
        case 0x0202:            /* End of frame, start a new one */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                nSkipped = 0;
                if (sd->nbIm >= 0 && sd->nbIm < 10)
                        sd->nbIm++;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
                break;
 
        default:
@@ -466,7 +464,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                                nSkipped = nToSkip + 1;
                        }
                        gspca_frame_add(gspca_dev,
-                               INTER_PACKET, frame, data, len);
+                               INTER_PACKET, data, len);
                }
                break;
        }
@@ -702,6 +700,7 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
                ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
                msleep(56);
 
+               PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX");
                nOV = 0;
                for (ntry = 0; ntry < 4; ntry++) {
                        ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
@@ -711,14 +710,14 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
                        ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
                        msleep(10);
                        ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
-                       PDEBUG(D_PROBE, "1st probe=%02x", probe);
+                       PDEBUG(D_PROBE, "probe=0x%02x", probe);
                        if (probe == 0xff)
                                nOV++;
                }
 
                if (nOV) {
-                       PDEBUG(D_PROBE, "0xff -> sensor OVXXXX");
-                       PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655");
+                       PDEBUG(D_PROBE, "0xff -> OVXXXX");
+                       PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655");
 
                        nb26 = nb96 = 0;
                        for (ntry = 0; ntry < 4; ntry++) {
@@ -728,40 +727,38 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
                                ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
                                                0, NULL);
                                msleep(10);
+
                                /* Wait for 26(OV2640) or 96(OV9655) */
                                ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
                                                1, &probe);
 
-                               PDEBUG(D_PROBE, "2nd probe=%02x", probe);
-                               if (probe == 0x00)
-                                       nb26++;
                                if (probe == 0x26 || probe == 0x40) {
+                                       PDEBUG(D_PROBE,
+                                               "probe=0x%02x -> OV2640",
+                                               probe);
                                        sd->sensor = ID_OV2640;
                                        nb26 += 4;
                                        break;
                                }
                                if (probe == 0x96 || probe == 0x55) {
+                                       PDEBUG(D_PROBE,
+                                               "probe=0x%02x -> OV9655",
+                                               probe);
                                        sd->sensor = ID_OV9655;
                                        nb96 += 4;
                                        break;
                                }
+                               PDEBUG(D_PROBE, "probe=0x%02x", probe);
+                               if (probe == 0x00)
+                                       nb26++;
                                if (probe == 0xff)
                                        nb96++;
                                msleep(3);
                        }
-                       if (nb26 < 4 && nb96 < 4) {
-                               PDEBUG(D_PROBE, "No relevant answer ");
-                               PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655");
-                               PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640");
-                               PDEBUG(D_PROBE,
-                                       "To force a sensor, add that line to "
-                                       "/etc/modprobe.d/options.conf:");
-                               PDEBUG(D_PROBE, "options gspca_gl860 "
-                                       "sensor=\"OV2640\" or \"OV9655\"");
+                       if (nb26 < 4 && nb96 < 4)
                                return -1;
-                       }
-               } else { /* probe = 0 */
-                       PDEBUG(D_PROBE, "No 0xff -> sensor MI2020");
+               } else {
+                       PDEBUG(D_PROBE, "Not any 0xff -> MI2020");
                        sd->sensor = ID_MI2020;
                }
        }
index cef4e24..305061f 100644 (file)
@@ -1,6 +1,7 @@
-/* @file gl860.h
- * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN
- * @date 2009-08-27
+/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
+ * Subdriver declarations
+ *
+ * 2009/10/14 Olivier LORIN <o.lorin@laposte.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
index 23d3fb7..4076f8e 100644 (file)
@@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
 MODULE_DESCRIPTION("GSPCA USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 7, 0)
+#define DRIVER_VERSION_NUMBER  KERNEL_VERSION(2, 8, 0)
 
 #ifdef GSPCA_DEBUG
 int gspca_debug = D_ERR | D_PROBE;
@@ -74,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
 #define PDEBUG_MODE(txt, pixfmt, w, h)
 #endif
 
-/* specific memory types - !! should different from V4L2_MEMORY_xxx */
+/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
 #define GSPCA_MEMORY_NO 0      /* V4L2_MEMORY_xxx starts from 1 */
 #define GSPCA_MEMORY_READ 7
 
@@ -126,7 +126,6 @@ EXPORT_SYMBOL(gspca_get_i_frame);
 static void fill_frame(struct gspca_dev *gspca_dev,
                        struct urb *urb)
 {
-       struct gspca_frame *frame;
        u8 *data;               /* address of data in the iso message */
        int i, len, st;
        cam_pkt_op pkt_scan;
@@ -135,21 +134,16 @@ static void fill_frame(struct gspca_dev *gspca_dev,
                if (urb->status == -ESHUTDOWN)
                        return;         /* disconnection */
 #ifdef CONFIG_PM
-               if (!gspca_dev->frozen)
+               if (gspca_dev->frozen)
+                       return;
 #endif
-                       PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
-               return;
+               PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+               urb->status = 0;
+               goto resubmit;
        }
        pkt_scan = gspca_dev->sd_desc->pkt_scan;
        for (i = 0; i < urb->number_of_packets; i++) {
 
-               /* check the availability of the frame buffer */
-               frame = gspca_get_i_frame(gspca_dev);
-               if (!frame) {
-                       gspca_dev->last_packet_type = DISCARD_PACKET;
-                       break;
-               }
-
                /* check the packet status and length */
                len = urb->iso_frame_desc[i].actual_length;
                if (len == 0) {
@@ -171,9 +165,10 @@ static void fill_frame(struct gspca_dev *gspca_dev,
                        i, urb->iso_frame_desc[i].offset, len);
                data = (u8 *) urb->transfer_buffer
                                        + urb->iso_frame_desc[i].offset;
-               pkt_scan(gspca_dev, frame, data, len);
+               pkt_scan(gspca_dev, data, len);
        }
 
+resubmit:
        /* resubmit the URB */
        st = usb_submit_urb(urb, GFP_ATOMIC);
        if (st < 0)
@@ -201,7 +196,6 @@ static void isoc_irq(struct urb *urb)
 static void bulk_irq(struct urb *urb)
 {
        struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
-       struct gspca_frame *frame;
        int st;
 
        PDEBUG(D_PACK, "bulk irq");
@@ -212,29 +206,22 @@ static void bulk_irq(struct urb *urb)
                break;
        case -ESHUTDOWN:
                return;         /* disconnection */
-       case -ECONNRESET:
-               urb->status = 0;
-               break;
        default:
 #ifdef CONFIG_PM
-               if (!gspca_dev->frozen)
+               if (gspca_dev->frozen)
+                       return;
 #endif
-                       PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
-               return;
+               PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+               urb->status = 0;
+               goto resubmit;
        }
 
-       /* check the availability of the frame buffer */
-       frame = gspca_get_i_frame(gspca_dev);
-       if (!frame) {
-               gspca_dev->last_packet_type = DISCARD_PACKET;
-       } else {
-               PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
-               gspca_dev->sd_desc->pkt_scan(gspca_dev,
-                                       frame,
-                                       urb->transfer_buffer,
-                                       urb->actual_length);
-       }
+       PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+       gspca_dev->sd_desc->pkt_scan(gspca_dev,
+                               urb->transfer_buffer,
+                               urb->actual_length);
 
+resubmit:
        /* resubmit the URB */
        if (gspca_dev->cam.bulk_nurbs != 0) {
                st = usb_submit_urb(urb, GFP_ATOMIC);
@@ -255,24 +242,27 @@ static void bulk_irq(struct urb *urb)
  * DISCARD_PACKET invalidates the whole frame.
  * On LAST_PACKET, a new frame is returned.
  */
-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
-                                   enum gspca_packet_type packet_type,
-                                   struct gspca_frame *frame,
-                                   const __u8 *data,
-                                   int len)
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+                       enum gspca_packet_type packet_type,
+                       const u8 *data,
+                       int len)
 {
+       struct gspca_frame *frame;
        int i, j;
 
        PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
 
+       /* check the availability of the frame buffer */
+       frame = gspca_dev->cur_frame;
+       if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+                                       != V4L2_BUF_FLAG_QUEUED) {
+               gspca_dev->last_packet_type = DISCARD_PACKET;
+               return;
+       }
+
        /* when start of a new frame, if the current frame buffer
         * is not queued, discard the whole frame */
        if (packet_type == FIRST_PACKET) {
-               if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
-                                               != V4L2_BUF_FLAG_QUEUED) {
-                       gspca_dev->last_packet_type = DISCARD_PACKET;
-                       return frame;
-               }
                frame->data_end = frame->data;
                jiffies_to_timeval(get_jiffies_64(),
                                   &frame->v4l2_buf.timestamp);
@@ -280,7 +270,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
        } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
                if (packet_type == LAST_PACKET)
                        gspca_dev->last_packet_type = packet_type;
-               return frame;
+               return;
        }
 
        /* append the packet to the frame buffer */
@@ -312,9 +302,9 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
                        i,
                        gspca_dev->fr_o);
                j = gspca_dev->fr_queue[i];
-               frame = &gspca_dev->frame[j];
+               gspca_dev->cur_frame = &gspca_dev->frame[j];
        }
-       return frame;
+       return;
 }
 EXPORT_SYMBOL(gspca_frame_add);
 
@@ -395,6 +385,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
                frame->v4l2_buf.m.offset = i * frsz;
        }
        gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
+       gspca_dev->cur_frame = &gspca_dev->frame[0];
        gspca_dev->last_packet_type = DISCARD_PACKET;
        gspca_dev->sequence = 0;
        return 0;
@@ -475,10 +466,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
        xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
                                   : USB_ENDPOINT_XFER_ISOC;
        i = gspca_dev->alt;                     /* previous alt setting */
-       while (--i >= 0) {
-               ep = alt_xfer(&intf->altsetting[i], xfer);
-               if (ep)
-                       break;
+       if (gspca_dev->cam.reverse_alts) {
+               while (++i < gspca_dev->nbalt) {
+                       ep = alt_xfer(&intf->altsetting[i], xfer);
+                       if (ep)
+                               break;
+               }
+       } else {
+               while (--i >= 0) {
+                       ep = alt_xfer(&intf->altsetting[i], xfer);
+                       if (ep)
+                               break;
+               }
        }
        if (ep == NULL) {
                err("no transfer endpoint found");
@@ -599,7 +598,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
 
        /* set the higher alternate setting and
         * loop until urb submit succeeds */
-       gspca_dev->alt = gspca_dev->nbalt;
+       if (gspca_dev->cam.reverse_alts)
+               gspca_dev->alt = 0;
+       else
+               gspca_dev->alt = gspca_dev->nbalt;
+
        if (gspca_dev->sd_desc->isoc_init) {
                ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
                if (ret < 0)
@@ -641,15 +644,19 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
                }
                if (ret >= 0)
                        break;
-               PDEBUG(D_ERR|D_STREAM,
-                       "usb_submit_urb alt %d err %d", gspca_dev->alt, ret);
                gspca_dev->streaming = 0;
                destroy_urbs(gspca_dev);
-               if (ret != -ENOSPC)
+               if (ret != -ENOSPC) {
+                       PDEBUG(D_ERR|D_STREAM,
+                               "usb_submit_urb alt %d err %d",
+                               gspca_dev->alt, ret);
                        goto out;
+               }
 
                /* the bandwidth is not wide enough
                 * negociate or try a lower alternate setting */
+               PDEBUG(D_ERR|D_STREAM,
+                       "bandwidth not wide enough - trying again");
                msleep(20);     /* wait for kill complete */
                if (gspca_dev->sd_desc->isoc_nego) {
                        ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
@@ -980,7 +987,7 @@ static void gspca_release(struct video_device *vfd)
 {
        struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
 
-       PDEBUG(D_STREAM, "device released");
+       PDEBUG(D_PROBE, "/dev/video%d released", gspca_dev->vdev.num);
 
        kfree(gspca_dev->usb_buf);
        kfree(gspca_dev);
@@ -991,7 +998,7 @@ static int dev_open(struct file *file)
        struct gspca_dev *gspca_dev;
        int ret;
 
-       PDEBUG(D_STREAM, "%s open", current->comm);
+       PDEBUG(D_STREAM, "[%s] open", current->comm);
        gspca_dev = (struct gspca_dev *) video_devdata(file);
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
@@ -1037,7 +1044,7 @@ static int dev_close(struct file *file)
 {
        struct gspca_dev *gspca_dev = file->private_data;
 
-       PDEBUG(D_STREAM, "%s close", current->comm);
+       PDEBUG(D_STREAM, "[%s] close", current->comm);
        if (mutex_lock_interruptible(&gspca_dev->queue_lock))
                return -ERESTARTSYS;
        gspca_dev->users--;
@@ -1138,10 +1145,13 @@ static int vidioc_queryctrl(struct file *file, void *priv,
                }
        } else {
                ctrls = get_ctrl(gspca_dev, id);
+               i = ctrls - gspca_dev->sd_desc->ctrls;
        }
        if (ctrls == NULL)
                return -EINVAL;
        memcpy(q_ctrl, ctrls, sizeof *q_ctrl);
+       if (gspca_dev->ctrl_inac & (1 << i))
+               q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
        return 0;
 }
 
@@ -1603,7 +1613,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma)
                size -= PAGE_SIZE;
        }
 
-       vma->vm_ops = &gspca_vm_ops;
+       vma->vm_ops = (struct vm_operations_struct *) &gspca_vm_ops;
        vma->vm_private_data = frame;
        gspca_vm_open(vma);
        ret = 0;
@@ -2001,11 +2011,15 @@ int gspca_dev_probe(struct usb_interface *intf,
        PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
 
        /* we don't handle multi-config cameras */
-       if (dev->descriptor.bNumConfigurations != 1)
+       if (dev->descriptor.bNumConfigurations != 1) {
+               PDEBUG(D_ERR, "Too many config");
                return -ENODEV;
+       }
        interface = &intf->cur_altsetting->desc;
-       if (interface->bInterfaceNumber > 0)
+       if (interface->bInterfaceNumber > 0) {
+               PDEBUG(D_ERR, "intf != 0");
                return -ENODEV;
+       }
 
        /* create the device */
        if (dev_size < sizeof *gspca_dev)
@@ -2059,7 +2073,7 @@ int gspca_dev_probe(struct usb_interface *intf,
        }
 
        usb_set_intfdata(intf, gspca_dev);
-       PDEBUG(D_PROBE, "probe ok");
+       PDEBUG(D_PROBE, "/dev/video%d created", gspca_dev->vdev.num);
        return 0;
 out:
        kfree(gspca_dev->usb_buf);
@@ -2078,6 +2092,7 @@ void gspca_disconnect(struct usb_interface *intf)
 {
        struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
 
+       PDEBUG(D_PROBE, "/dev/video%d disconnect", gspca_dev->vdev.num);
        mutex_lock(&gspca_dev->usb_lock);
        gspca_dev->present = 0;
 
@@ -2096,7 +2111,7 @@ void gspca_disconnect(struct usb_interface *intf)
        /* (this will call gspca_release() immediatly or on last close) */
        video_unregister_device(&gspca_dev->vdev);
 
-       PDEBUG(D_PROBE, "disconnect complete");
+/*     PDEBUG(D_PROBE, "disconnect complete"); */
 }
 EXPORT_SYMBOL(gspca_disconnect);
 
index 70b1fd8..1816173 100644 (file)
@@ -58,6 +58,7 @@ struct cam {
        u8 npkt;                /* number of packets in an ISOC message
                                 * 0 is the default value: 32 packets */
        u32 input_flags;        /* value for ENUM_INPUT status flags */
+       char reverse_alts;      /* Alt settings are in high to low order */
 };
 
 struct gspca_dev;
@@ -78,8 +79,7 @@ typedef int (*cam_streamparm_op) (struct gspca_dev *,
 typedef int (*cam_qmnu_op) (struct gspca_dev *,
                        struct v4l2_querymenu *);
 typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
-                               struct gspca_frame *frame,
-                               __u8 *data,
+                               u8 *data,
                                int len);
 
 struct ctrl {
@@ -142,6 +142,7 @@ struct gspca_dev {
        struct cam cam;                         /* device information */
        const struct sd_desc *sd_desc;          /* subdriver description */
        unsigned ctrl_dis;              /* disabled controls (bit map) */
+       unsigned ctrl_inac;             /* inactive controls (bit map) */
 
 #define USB_BUF_SZ 64
        __u8 *usb_buf;                          /* buffer for USB exchanges */
@@ -149,6 +150,7 @@ struct gspca_dev {
 
        __u8 *frbuf;                            /* buffer for nframes */
        struct gspca_frame frame[GSPCA_MAX_FRAMES];
+       struct gspca_frame *cur_frame;          /* frame beeing filled */
        __u32 frsz;                             /* frame size */
        char nframes;                           /* number of frames */
        char fr_i;                              /* frame being filled */
@@ -189,11 +191,10 @@ int gspca_dev_probe(struct usb_interface *intf,
                int dev_size,
                struct module *module);
 void gspca_disconnect(struct usb_interface *intf);
-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
-                                   enum gspca_packet_type packet_type,
-                                   struct gspca_frame *frame,
-                                   const __u8 *data,
-                                   int len);
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+                       enum gspca_packet_type packet_type,
+                       const u8 *data,
+                       int len);
 struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev);
 #ifdef CONFIG_PM
 int gspca_suspend(struct usb_interface *intf, pm_message_t message);
index a11c97e..2019b04 100644 (file)
@@ -181,11 +181,9 @@ static void jlj_dostream(struct work_struct *work)
 {
        struct sd *dev = container_of(work, struct sd, work_struct);
        struct gspca_dev *gspca_dev = &dev->gspca_dev;
-       struct gspca_frame *frame;
        int blocks_left; /* 0x200-sized blocks remaining in current frame. */
        int size_in_blocks;
        int act_len;
-       int discarding = 0; /* true if we failed to get space for frame. */
        int packet_type;
        int ret;
        u8 *buffer;
@@ -196,15 +194,6 @@ static void jlj_dostream(struct work_struct *work)
                goto quit_stream;
        }
        while (gspca_dev->present && gspca_dev->streaming) {
-               if (!gspca_dev->present)
-                       goto quit_stream;
-               /* Start a new frame, and add the JPEG header, first thing */
-               frame = gspca_get_i_frame(gspca_dev);
-               if (frame && !discarding)
-                       gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                                       dev->jpeg_hdr, JPEG_HDR_SZ);
-                else
-                       discarding = 1;
                /*
                 * Now request data block 0. Line 0 reports the size
                 * to download, in blocks of size 0x200, and also tells the
@@ -222,14 +211,15 @@ static void jlj_dostream(struct work_struct *work)
                size_in_blocks = buffer[0x0a];
                blocks_left = buffer[0x0a] - 1;
                PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
-               packet_type = INTER_PACKET;
-               if (frame && !discarding)
-                       /* Toss line 0 of data block 0, keep the rest. */
-                       gspca_frame_add(gspca_dev, packet_type,
-                               frame, buffer + FRAME_HEADER_LEN,
+
+               /* Start a new frame, and add the JPEG header, first thing */
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
+                               dev->jpeg_hdr, JPEG_HDR_SZ);
+               /* Toss line 0 of data block 0, keep the rest. */
+               gspca_frame_add(gspca_dev, INTER_PACKET,
+                               buffer + FRAME_HEADER_LEN,
                                JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
-                       else
-                               discarding = 1;
+
                while (blocks_left > 0) {
                        if (!gspca_dev->present)
                                goto quit_stream;
@@ -246,12 +236,8 @@ static void jlj_dostream(struct work_struct *work)
                                packet_type = LAST_PACKET;
                        else
                                packet_type = INTER_PACKET;
-                       if (frame && !discarding)
-                               gspca_frame_add(gspca_dev, packet_type,
-                                               frame, buffer,
-                                               JEILINJ_MAX_TRANSFER);
-                       else
-                               discarding = 1;
+                       gspca_frame_add(gspca_dev, packet_type,
+                                       buffer, JEILINJ_MAX_TRANSFER);
                }
        }
 quit_stream:
index 7f1e541..844fc1d 100644 (file)
@@ -274,8 +274,7 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev)
 }
 
 static void m5602_urb_complete(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,
-                       __u8 *data, int len)
+                               u8 *data, int len)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
@@ -295,19 +294,27 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
                len -= 6;
 
                /* Complete the last frame (if any) */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET,
-                                       frame, data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET,
+                               NULL, 0);
                sd->frame_count++;
 
                /* Create a new frame */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
 
                PDEBUG(D_FRAM, "Starting new frame %d",
                       sd->frame_count);
 
        } else {
-               int cur_frame_len = frame->data_end - frame->data;
+               struct gspca_frame *frame;
+               int cur_frame_len;
 
+               frame = gspca_get_i_frame(gspca_dev);
+               if (frame == NULL) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       return;
+               }
+
+               cur_frame_len = frame->data_end - frame->data;
                /* Remove urb header */
                data += 4;
                len -= 4;
@@ -316,12 +323,12 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
                        PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes",
                               sd->frame_count, len);
 
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       gspca_frame_add(gspca_dev, INTER_PACKET,
                                        data, len);
                } else if (frame->v4l2_buf.length - cur_frame_len > 0) {
                        /* Add the remaining data up to frame size */
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data,
-                                       frame->v4l2_buf.length - cur_frame_len);
+                       gspca_frame_add(gspca_dev, INTER_PACKET, data,
+                                   frame->v4l2_buf.length - cur_frame_len);
                }
        }
 }
index de769ca..9cf8d68 100644 (file)
@@ -325,8 +325,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -348,11 +347,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                            || data[5 + p] == 0x67) {
                                PDEBUG(D_PACK, "sof offset: %d len: %d",
                                        p, len);
-                               frame = gspca_frame_add(gspca_dev, LAST_PACKET,
-                                                       frame, data, p);
+                               gspca_frame_add(gspca_dev, LAST_PACKET,
+                                               data, p);
 
                                /* put the JPEG header */
-                               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               gspca_frame_add(gspca_dev, FIRST_PACKET,
                                        sd->jpeg_hdr, JPEG_HDR_SZ);
                                data += p + 16;
                                len -= p + 16;
@@ -360,7 +359,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        }
                }
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index f8328b9..126d968 100644 (file)
@@ -1,23 +1,30 @@
 /*
  * Mars MR97310A library
  *
+ * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
  * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
  *
  * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
  * and for the routines for detecting and classifying these various cameras,
+ * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
  *
+ * Support for the control settings for the CIF cameras is
+ * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li>
+ *
+ * Support for the control settings for the VGA cameras is
  * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
  *
- * Acknowledgements:
+ * Several previously unsupported cameras are owned and have been tested by
+ * Hans de Goede <hdgoede@redhat.com> and
+ * Thomas Kaiser <thomas@kaiser-linux.li> and
+ * Theodore Kilgore <kilgota@auburn.edu> and
+ * Edmond Rodriguez <erodrig_97@yahoo.com> and
+ * Aurelien Jacobs <aurel@gnuage.org>
  *
  * The MR97311A support in gspca/mars.c has been helpful in understanding some
  * of the registers in these cameras.
  *
- * Hans de Goede <hdgoede@redhat.com> and
- * Thomas Kaiser <thomas@kaiser-linux.li>
- * have assisted with their experience. Each of them has also helped by
- * testing a previously unsupported camera.
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
 #define CAM_TYPE_CIF                   0
 #define CAM_TYPE_VGA                   1
 
-#define MR97310A_BRIGHTNESS_MIN                -254
-#define MR97310A_BRIGHTNESS_MAX                255
 #define MR97310A_BRIGHTNESS_DEFAULT    0
 
-#define MR97310A_EXPOSURE_MIN          300
+#define MR97310A_EXPOSURE_MIN          0
 #define MR97310A_EXPOSURE_MAX          4095
 #define MR97310A_EXPOSURE_DEFAULT      1000
 
 #define MR97310A_GAIN_MAX              31
 #define MR97310A_GAIN_DEFAULT          25
 
+#define MR97310A_MIN_CLOCKDIV_MIN      3
+#define MR97310A_MIN_CLOCKDIV_MAX      8
+#define MR97310A_MIN_CLOCKDIV_DEFAULT  3
+
 MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,"
              "Theodore Kilgore <kilgota@auburn.edu>");
 MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
@@ -69,10 +78,12 @@ struct sd {
        u8 cam_type;    /* 0 is CIF and 1 is VGA */
        u8 sensor_type; /* We use 0 and 1 here, too. */
        u8 do_lcd_stop;
+       u8 adj_colors;
 
        int brightness;
        u16 exposure;
        u8 gain;
+       u8 min_clockdiv;
 };
 
 struct sensor_w_data {
@@ -82,26 +93,31 @@ struct sensor_w_data {
        int len;
 };
 
+static void sd_stopN(struct gspca_dev *gspca_dev);
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
 static void setbrightness(struct gspca_dev *gspca_dev);
 static void setexposure(struct gspca_dev *gspca_dev);
 static void setgain(struct gspca_dev *gspca_dev);
 
 /* V4L2 controls supported by the driver */
 static struct ctrl sd_ctrls[] = {
+/* Separate brightness control description for Argus QuickClix as it has
+   different limits from the other mr97310a cameras */
        {
-#define BRIGHTNESS_IDX 0
+#define NORM_BRIGHTNESS_IDX 0
                {
                        .id = V4L2_CID_BRIGHTNESS,
                        .type = V4L2_CTRL_TYPE_INTEGER,
                        .name = "Brightness",
-                       .minimum = MR97310A_BRIGHTNESS_MIN,
-                       .maximum = MR97310A_BRIGHTNESS_MAX,
+                       .minimum = -254,
+                       .maximum = 255,
                        .step = 1,
                        .default_value = MR97310A_BRIGHTNESS_DEFAULT,
                        .flags = 0,
@@ -110,7 +126,22 @@ static struct ctrl sd_ctrls[] = {
                .get = sd_getbrightness,
        },
        {
-#define EXPOSURE_IDX 1
+#define ARGUS_QC_BRIGHTNESS_IDX 1
+               {
+                       .id = V4L2_CID_BRIGHTNESS,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "Brightness",
+                       .minimum = 0,
+                       .maximum = 15,
+                       .step = 1,
+                       .default_value = MR97310A_BRIGHTNESS_DEFAULT,
+                       .flags = 0,
+               },
+               .set = sd_setbrightness,
+               .get = sd_getbrightness,
+       },
+       {
+#define EXPOSURE_IDX 2
                {
                        .id = V4L2_CID_EXPOSURE,
                        .type = V4L2_CTRL_TYPE_INTEGER,
@@ -125,7 +156,7 @@ static struct ctrl sd_ctrls[] = {
                .get = sd_getexposure,
        },
        {
-#define GAIN_IDX 2
+#define GAIN_IDX 3
                {
                        .id = V4L2_CID_GAIN,
                        .type = V4L2_CTRL_TYPE_INTEGER,
@@ -139,6 +170,21 @@ static struct ctrl sd_ctrls[] = {
                .set = sd_setgain,
                .get = sd_getgain,
        },
+       {
+#define MIN_CLOCKDIV_IDX 4
+               {
+                       .id = V4L2_CID_PRIVATE_BASE,
+                       .type = V4L2_CTRL_TYPE_INTEGER,
+                       .name = "Minimum Clock Divider",
+                       .minimum = MR97310A_MIN_CLOCKDIV_MIN,
+                       .maximum = MR97310A_MIN_CLOCKDIV_MAX,
+                       .step = 1,
+                       .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
+                       .flags = 0,
+               },
+               .set = sd_setmin_clockdiv,
+               .get = sd_getmin_clockdiv,
+       },
 };
 
 static const struct v4l2_pix_format vga_mode[] = {
@@ -230,12 +276,17 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
        int rc;
 
        buf = data;
-       rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
+       if (sd->cam_type == CAM_TYPE_CIF) {
+               rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
+               confirm_reg = sd->sensor_type ? 0x13 : 0x11;
+       } else {
+               rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
+               confirm_reg = 0x11;
+       }
        if (rc < 0)
                return rc;
 
        buf = 0x01;
-       confirm_reg = sd->sensor_type ? 0x13 : 0x11;
        rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
        if (rc < 0)
                return rc;
@@ -243,18 +294,26 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
        return 0;
 }
 
-static int cam_get_response16(struct gspca_dev *gspca_dev)
+static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
 {
-       __u8 *data = gspca_dev->usb_buf;
        int err_code;
 
-       data[0] = 0x21;
+       gspca_dev->usb_buf[0] = reg;
        err_code = mr_write(gspca_dev, 1);
        if (err_code < 0)
                return err_code;
 
        err_code = mr_read(gspca_dev, 16);
-       return err_code;
+       if (err_code < 0)
+               return err_code;
+
+       if (verbose)
+               PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg,
+                      gspca_dev->usb_buf[0],
+                      gspca_dev->usb_buf[1],
+                      gspca_dev->usb_buf[2]);
+
+       return 0;
 }
 
 static int zero_the_pointer(struct gspca_dev *gspca_dev)
@@ -264,7 +323,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
        u8 status = 0;
        int tries = 0;
 
-       err_code = cam_get_response16(gspca_dev);
+       err_code = cam_get_response16(gspca_dev, 0x21, 0);
        if (err_code < 0)
                return err_code;
 
@@ -275,7 +334,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
        if (err_code < 0)
                return err_code;
 
-       err_code = cam_get_response16(gspca_dev);
+       err_code = cam_get_response16(gspca_dev, 0x21, 0);
        if (err_code < 0)
                return err_code;
 
@@ -285,7 +344,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
        if (err_code < 0)
                return err_code;
 
-       err_code = cam_get_response16(gspca_dev);
+       err_code = cam_get_response16(gspca_dev, 0x21, 0);
        if (err_code < 0)
                return err_code;
 
@@ -295,7 +354,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
        if (err_code < 0)
                return err_code;
 
-       err_code = cam_get_response16(gspca_dev);
+       err_code = cam_get_response16(gspca_dev, 0x21, 0);
        if (err_code < 0)
                return err_code;
 
@@ -306,7 +365,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
                return err_code;
 
        while (status != 0x0a && tries < 256) {
-               err_code = cam_get_response16(gspca_dev);
+               err_code = cam_get_response16(gspca_dev, 0x21, 0);
                status = data[0];
                tries++;
                if (err_code < 0)
@@ -323,7 +382,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
                if (err_code < 0)
                        return err_code;
 
-               err_code = cam_get_response16(gspca_dev);
+               err_code = cam_get_response16(gspca_dev, 0x21, 0);
                status = data[0];
                tries++;
                if (err_code < 0)
@@ -342,89 +401,202 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static u8 get_sensor_id(struct gspca_dev *gspca_dev)
+static int stream_start(struct gspca_dev *gspca_dev)
 {
-       int err_code;
-
-       gspca_dev->usb_buf[0] = 0x1e;
-       err_code = mr_write(gspca_dev, 1);
-       if (err_code < 0)
-               return err_code;
+       gspca_dev->usb_buf[0] = 0x01;
+       gspca_dev->usb_buf[1] = 0x01;
+       return mr_write(gspca_dev, 2);
+}
 
-       err_code = mr_read(gspca_dev, 16);
-       if (err_code < 0)
-               return err_code;
+static void stream_stop(struct gspca_dev *gspca_dev)
+{
+       gspca_dev->usb_buf[0] = 0x01;
+       gspca_dev->usb_buf[1] = 0x00;
+       if (mr_write(gspca_dev, 2) < 0)
+               PDEBUG(D_ERR, "Stream Stop failed");
+}
 
-       PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]);
+static void lcd_stop(struct gspca_dev *gspca_dev)
+{
+       gspca_dev->usb_buf[0] = 0x19;
+       gspca_dev->usb_buf[1] = 0x54;
+       if (mr_write(gspca_dev, 2) < 0)
+               PDEBUG(D_ERR, "LCD Stop failed");
+}
 
-       return gspca_dev->usb_buf[0];
+static int isoc_enable(struct gspca_dev *gspca_dev)
+{
+       gspca_dev->usb_buf[0] = 0x00;
+       gspca_dev->usb_buf[1] = 0x4d;  /* ISOC transfering enable... */
+       return mr_write(gspca_dev, 2);
 }
 
-/* this function is called at probe time */
+/* This function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
                     const struct usb_device_id *id)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        struct cam *cam;
-       __u8 *data = gspca_dev->usb_buf;
        int err_code;
 
        cam = &gspca_dev->cam;
        cam->cam_mode = vga_mode;
        cam->nmodes = ARRAY_SIZE(vga_mode);
+       sd->do_lcd_stop = 0;
+
+       /* Several of the supported CIF cameras share the same USB ID but
+        * require different initializations and different control settings.
+        * The same is true of the VGA cameras. Therefore, we are forced
+        * to start the initialization process in order to determine which
+        * camera is present. Some of the supported cameras require the
+        * memory pointer to be set to 0 as the very first item of business
+        * or else they will not stream. So we do that immediately.
+        */
+       err_code = zero_the_pointer(gspca_dev);
+       if (err_code < 0)
+               return err_code;
+
+       err_code = stream_start(gspca_dev);
+       if (err_code < 0)
+               return err_code;
 
-       if (id->idProduct == 0x010e) {
+       if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
                sd->cam_type = CAM_TYPE_CIF;
                cam->nmodes--;
-
-               data[0] = 0x01;
-               data[1] = 0x01;
-               err_code = mr_write(gspca_dev, 2);
+               err_code = cam_get_response16(gspca_dev, 0x06, 1);
                if (err_code < 0)
                        return err_code;
-
-               msleep(200);
-               data[0] = get_sensor_id(gspca_dev);
                /*
-                * Known CIF cameras. If you have another to report, please do
+                * All but one of the known CIF cameras share the same USB ID,
+                * but two different init routines are in use, and the control
+                * settings are different, too. We need to detect which camera
+                * of the two known varieties is connected!
                 *
-                * Name                 byte just read          sd->sensor_type
-                *                                      reported by
-                * Sakar Spy-shot       0x28            T. Kilgore      0
-                * Innovage             0xf5 (unstable) T. Kilgore      0
-                * Vivitar Mini         0x53            H. De Goede     0
-                * Vivitar Mini         0x04 / 0x24     E. Rodriguez    0
-                * Vivitar Mini         0x08            T. Kilgore      1
-                * Elta-Media 8212dc    0x23            T. Kaiser       1
-                * Philips dig. keych.  0x37            T. Kilgore      1
+                * A list of known CIF cameras follows. They all report either
+                * 0002 for type 0 or 0003 for type 1.
+                * If you have another to report, please do
+                *
+                * Name         sd->sensor_type         reported by
+                *
+                * Sakar Spy-shot       0               T. Kilgore
+                * Innovage             0               T. Kilgore
+                * Vivitar Mini         0               H. De Goede
+                * Vivitar Mini         0               E. Rodriguez
+                * Vivitar Mini         1               T. Kilgore
+                * Elta-Media 8212dc    1               T. Kaiser
+                * Philips dig. keych.  1               T. Kilgore
+                * Trust Spyc@m 100     1               A. Jacobs
                 */
-               if ((data[0] & 0x78) == 8 ||
-                   ((data[0] & 0x2) == 0x2 && data[0] != 0x53))
-                       sd->sensor_type = 1;
-               else
+               switch (gspca_dev->usb_buf[1]) {
+               case 2:
                        sd->sensor_type = 0;
-
+                       break;
+               case 3:
+                       sd->sensor_type = 1;
+                       break;
+               default:
+                       PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x",
+                              gspca_dev->usb_buf[1]);
+                       return -ENODEV;
+               }
                PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d",
                       sd->sensor_type);
+       } else {
+               sd->cam_type = CAM_TYPE_VGA;
 
-               if (force_sensor_type != -1) {
-                       sd->sensor_type = !! force_sensor_type;
-                       PDEBUG(D_PROBE, "Forcing sensor type to: %d",
-                              sd->sensor_type);
+               err_code = cam_get_response16(gspca_dev, 0x07, 1);
+               if (err_code < 0)
+                       return err_code;
+
+               /*
+                * Here is a table of the responses to the previous command
+                * from the known MR97310A VGA cameras.
+                *
+                * Name                 gspca_dev->usb_buf[]    sd->sensor_type
+                *                              sd->do_lcd_stop
+                * Aiptek Pencam VGA+   0300            0               1
+                * ION digital          0350            0               1
+                * Argus DC-1620        0450            1               0
+                * Argus QuickClix      0420            1               1
+                *
+                * Based upon these results, we assume default settings
+                * and then correct as necessary, as follows.
+                *
+                */
+
+               sd->sensor_type = 1;
+               sd->do_lcd_stop = 0;
+               sd->adj_colors = 0;
+               if ((gspca_dev->usb_buf[0] != 0x03) &&
+                                       (gspca_dev->usb_buf[0] != 0x04)) {
+                       PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x",
+                                       gspca_dev->usb_buf[1]);
+                       PDEBUG(D_ERR, "Defaults assumed, may not work");
+                       PDEBUG(D_ERR, "Please report this");
+               }
+               /* Sakar Digital color needs to be adjusted. */
+               if ((gspca_dev->usb_buf[0] == 0x03) &&
+                                       (gspca_dev->usb_buf[1] == 0x50))
+                       sd->adj_colors = 1;
+               if (gspca_dev->usb_buf[0] == 0x04) {
+                       sd->do_lcd_stop = 1;
+                       switch (gspca_dev->usb_buf[1]) {
+                       case 0x50:
+                               sd->sensor_type = 0;
+                               PDEBUG(D_PROBE, "sensor_type corrected to 0");
+                               break;
+                       case 0x20:
+                               /* Nothing to do here. */
+                               break;
+                       default:
+                               PDEBUG(D_ERR,
+                                       "Unknown VGA Sensor id Byte 1: %02x",
+                                       gspca_dev->usb_buf[1]);
+                               PDEBUG(D_ERR,
+                                       "Defaults assumed, may not work");
+                               PDEBUG(D_ERR, "Please report this");
+                       }
                }
+               PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
+                      sd->sensor_type);
+       }
+       /* Stop streaming as we've started it to probe the sensor type. */
+       sd_stopN(gspca_dev);
+
+       if (force_sensor_type != -1) {
+               sd->sensor_type = !!force_sensor_type;
+               PDEBUG(D_PROBE, "Forcing sensor type to: %d",
+                      sd->sensor_type);
+       }
 
+       /* Setup controls depending on camera type */
+       if (sd->cam_type == CAM_TYPE_CIF) {
+               /* No brightness for sensor_type 0 */
                if (sd->sensor_type == 0)
-                       gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
+                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+                                             (1 << ARGUS_QC_BRIGHTNESS_IDX);
+               else
+                       gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+                                             (1 << MIN_CLOCKDIV_IDX);
        } else {
-               sd->cam_type = CAM_TYPE_VGA;
-               PDEBUG(D_PROBE, "MR97310A VGA camera detected");
-               gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) |
-                                     (1 << EXPOSURE_IDX) | (1 << GAIN_IDX);
+               /* All controls need to be disabled if VGA sensor_type is 0 */
+               if (sd->sensor_type == 0)
+                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
+                                             (1 << ARGUS_QC_BRIGHTNESS_IDX) |
+                                             (1 << EXPOSURE_IDX) |
+                                             (1 << GAIN_IDX) |
+                                             (1 << MIN_CLOCKDIV_IDX);
+               else if (sd->do_lcd_stop)
+                       /* Argus QuickClix has different brightness limits */
+                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX);
+               else
+                       gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX);
        }
 
        sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
        sd->exposure = MR97310A_EXPOSURE_DEFAULT;
        sd->gain = MR97310A_GAIN_DEFAULT;
+       sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
 
        return 0;
 }
@@ -455,11 +627,6 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
        };
 
        /* Note: Some of the above descriptions guessed from MR97113A driver */
-       data[0] = 0x01;
-       data[1] = 0x01;
-       err_code = mr_write(gspca_dev, 2);
-       if (err_code < 0)
-               return err_code;
 
        memcpy(data, startup_string, 11);
        if (sd->sensor_type)
@@ -533,22 +700,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
                err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
                                         ARRAY_SIZE(cif_sensor1_init_data));
        }
-       if (err_code < 0)
-               return err_code;
-
-       setbrightness(gspca_dev);
-       setexposure(gspca_dev);
-       setgain(gspca_dev);
-
-       msleep(200);
-
-       data[0] = 0x00;
-       data[1] = 0x4d;  /* ISOC transfering enable... */
-       err_code = mr_write(gspca_dev, 2);
-       if (err_code < 0)
-               return err_code;
-
-       return 0;
+       return err_code;
 }
 
 static int start_vga_cam(struct gspca_dev *gspca_dev)
@@ -558,84 +710,8 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
        int err_code;
        const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b,
                                       0x00, 0x00, 0x00, 0x50, 0xc0};
-
        /* What some of these mean is explained in start_cif_cam(), above */
-       sd->sof_read = 0;
-
-       /*
-        * We have to know which camera we have, because the register writes
-        * depend upon the camera. This test, run before we actually enter
-        * the initialization routine, distinguishes most of the cameras, If
-        * needed, another routine is done later, too.
-        */
-       memset(data, 0, 16);
-       data[0] = 0x20;
-       err_code = mr_write(gspca_dev, 1);
-       if (err_code < 0)
-               return err_code;
-
-       err_code = mr_read(gspca_dev, 16);
-       if (err_code < 0)
-               return err_code;
-
-       PDEBUG(D_PROBE, "Byte reported is %02x", data[0]);
-
-       msleep(200);
-       /*
-        * Known VGA cameras. If you have another to report, please do
-        *
-        * Name                 byte just read          sd->sensor_type
-        *                              sd->do_lcd_stop
-        * Aiptek Pencam VGA+   0x31            0       1
-        * ION digital          0x31            0       1
-        * Argus DC-1620        0x30            1       0
-        * Argus QuickClix      0x30            1       1 (not caught here)
-        */
-       sd->sensor_type = data[0] & 1;
-       sd->do_lcd_stop = (~data[0]) & 1;
-
-
-
-       /* Streaming setup begins here. */
-
-
-       data[0] = 0x01;
-       data[1] = 0x01;
-       err_code = mr_write(gspca_dev, 2);
-       if (err_code < 0)
-               return err_code;
 
-       /*
-        * A second test can now resolve any remaining ambiguity in the
-        * identification of the camera type,
-        */
-       if (!sd->sensor_type) {
-               data[0] = get_sensor_id(gspca_dev);
-               if (data[0] == 0x7f) {
-                       sd->sensor_type = 1;
-                       PDEBUG(D_PROBE, "sensor_type corrected to 1");
-               }
-               msleep(200);
-       }
-
-       if (force_sensor_type != -1) {
-               sd->sensor_type = !! force_sensor_type;
-               PDEBUG(D_PROBE, "Forcing sensor type to: %d",
-                      sd->sensor_type);
-       }
-
-       /*
-        * Known VGA cameras.
-        * This test is only run if the previous test returned 0x30, but
-        * here is the information for all others, too, just for reference.
-        *
-        * Name                 byte just read          sd->sensor_type
-        *
-        * Aiptek Pencam VGA+   0xfb    (this test not run)     1
-        * ION digital          0xbd    (this test not run)     1
-        * Argus DC-1620        0xe5    (no change)             0
-        * Argus QuickClix      0x7f    (reclassified)          1
-        */
        memcpy(data, startup_string, 11);
        if (!sd->sensor_type) {
                data[5]  = 0x00;
@@ -689,29 +765,44 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
                err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
                                         ARRAY_SIZE(vga_sensor0_init_data));
        } else {        /* sd->sensor_type = 1 */
-               const struct sensor_w_data vga_sensor1_init_data[] = {
+               const struct sensor_w_data color_adj[] = {
                        {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
-                               0x07, 0x00, 0x01}, 8},
+                               /* adjusted blue, green, red gain correct
+                                  too much blue from the Sakar Digital */
+                               0x05, 0x01, 0x04}, 8}
+               };
+
+               const struct sensor_w_data color_no_adj[] = {
+                       {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+                               /* default blue, green, red gain settings */
+                               0x07, 0x00, 0x01}, 8}
+               };
+
+               const struct sensor_w_data vga_sensor1_init_data[] = {
                        {0x11, 0x04, {0x01}, 1},
-                       /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */
-                       {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01,
+                       {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
+                       /* These settings may be better for some cameras */
+                       /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
                                0x00, 0x0a}, 7},
                        {0x11, 0x04, {0x01}, 1},
                        {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
                        {0x11, 0x04, {0x01}, 1},
                        {0, 0, {0}, 0}
                };
+
+               if (sd->adj_colors)
+                       err_code = sensor_write_regs(gspca_dev, color_adj,
+                                        ARRAY_SIZE(color_adj));
+               else
+                       err_code = sensor_write_regs(gspca_dev, color_no_adj,
+                                        ARRAY_SIZE(color_no_adj));
+
+               if (err_code < 0)
+                       return err_code;
+
                err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
                                         ARRAY_SIZE(vga_sensor1_init_data));
        }
-       if (err_code < 0)
-               return err_code;
-
-       msleep(200);
-       data[0] = 0x00;
-       data[1] = 0x4d;  /* ISOC transfering enable... */
-       err_code = mr_write(gspca_dev, 2);
-
        return err_code;
 }
 
@@ -719,97 +810,120 @@ static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int err_code;
-       struct cam *cam;
 
-       cam = &gspca_dev->cam;
        sd->sof_read = 0;
-       /*
-        * Some of the supported cameras require the memory pointer to be
-        * set to 0, or else they will not stream.
-        */
-       zero_the_pointer(gspca_dev);
-       msleep(200);
+
+       /* Some of the VGA cameras require the memory pointer
+        * to be set to 0 again. We have been forced to start the
+        * stream in sd_config() to detect the hardware, and closed it.
+        * Thus, we need here to do a completely fresh and clean start. */
+       err_code = zero_the_pointer(gspca_dev);
+       if (err_code < 0)
+               return err_code;
+
+       err_code = stream_start(gspca_dev);
+       if (err_code < 0)
+               return err_code;
+
        if (sd->cam_type == CAM_TYPE_CIF) {
                err_code = start_cif_cam(gspca_dev);
        } else {
                err_code = start_vga_cam(gspca_dev);
        }
-       return err_code;
+       if (err_code < 0)
+               return err_code;
+
+       setbrightness(gspca_dev);
+       setexposure(gspca_dev);
+       setgain(gspca_dev);
+
+       return isoc_enable(gspca_dev);
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int result;
-
-       gspca_dev->usb_buf[0] = 1;
-       gspca_dev->usb_buf[1] = 0;
-       result = mr_write(gspca_dev, 2);
-       if (result < 0)
-               PDEBUG(D_ERR, "Camera Stop failed");
 
+       stream_stop(gspca_dev);
        /* Not all the cams need this, but even if not, probably a good idea */
        zero_the_pointer(gspca_dev);
-       if (sd->do_lcd_stop) {
-               gspca_dev->usb_buf[0] = 0x19;
-               gspca_dev->usb_buf[1] = 0x54;
-               result = mr_write(gspca_dev, 2);
-               if (result < 0)
-                       PDEBUG(D_ERR, "Camera Stop failed");
-       }
+       if (sd->do_lcd_stop)
+               lcd_stop(gspca_dev);
 }
 
 static void setbrightness(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 val;
-
-       if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
+       u8 sign_reg = 7;  /* This reg and the next one used on CIF cams. */
+       u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
+       const u8 quick_clix_table[] =
+       /*        0  1  2   3  4  5  6  7  8  9  10  11  12  13  14  15 */
+               { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9,  7, 10, 13, 11, 14, 15};
+       /*
+        * This control is disabled for CIF type 1 and VGA type 0 cameras.
+        * It does not quite act linearly for the Argus QuickClix camera,
+        * but it does control brightness. The values are 0 - 15 only, and
+        * the table above makes them act consecutively.
+        */
+       if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
+           (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
                return;
 
-       /* Note register 7 is also seen as 0x8x or 0xCx in dumps */
+       if (sd->cam_type == CAM_TYPE_VGA) {
+               sign_reg += 4;
+               value_reg += 4;
+       }
+
+       /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
        if (sd->brightness > 0) {
-               sensor_write1(gspca_dev, 7, 0x00);
+               sensor_write1(gspca_dev, sign_reg, 0x00);
                val = sd->brightness;
        } else {
-               sensor_write1(gspca_dev, 7, 0x01);
-               val = 257 - sd->brightness;
+               sensor_write1(gspca_dev, sign_reg, 0x01);
+               val = (257 - sd->brightness);
        }
-       sensor_write1(gspca_dev, 8, val);
+       /* Use lookup table for funky Argus QuickClix brightness */
+       if (sd->do_lcd_stop)
+               val = quick_clix_table[val];
+
+       sensor_write1(gspca_dev, value_reg, val);
 }
 
 static void setexposure(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
+       int exposure;
+       u8 buf[2];
 
        if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
                return;
 
-       if (sd->sensor_type) {
-               val = sd->exposure >> 4;
-               sensor_write1(gspca_dev, 3, val);
-               val = sd->exposure & 0xf;
-               sensor_write1(gspca_dev, 4, val);
+       if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
+               /* This cam does not like exposure settings < 300,
+                  so scale 0 - 4095 to 300 - 4095 */
+               exposure = (sd->exposure * 9267) / 10000 + 300;
+               sensor_write1(gspca_dev, 3, exposure >> 4);
+               sensor_write1(gspca_dev, 4, exposure & 0x0f);
        } else {
-               u8 clockdiv;
-               int exposure;
-
                /* We have both a clock divider and an exposure register.
                   We first calculate the clock divider, as that determines
-                  the maximum exposure and then we calculayte the exposure
+                  the maximum exposure and then we calculate the exposure
                   register setting (which goes from 0 - 511).
 
                   Note our 0 - 4095 exposure is mapped to 0 - 511
                   milliseconds exposure time */
-               clockdiv = (60 * sd->exposure + 7999) / 8000;
+               u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
 
                /* Limit framerate to not exceed usb bandwidth */
-               if (clockdiv < 3 && gspca_dev->width >= 320)
-                       clockdiv = 3;
+               if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
+                       clockdiv = sd->min_clockdiv;
                else if (clockdiv < 2)
                        clockdiv = 2;
 
+               if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
+                       clockdiv = 4;
+
                /* Frame exposure time in ms = 1000 * clockdiv / 60 ->
                exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
                exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
@@ -819,9 +933,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
                /* exposure register value is reversed! */
                exposure = 511 - exposure;
 
+               buf[0] = exposure & 0xff;
+               buf[1] = exposure >> 8;
+               sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
                sensor_write1(gspca_dev, 0x02, clockdiv);
-               sensor_write1(gspca_dev, 0x0e, exposure & 0xff);
-               sensor_write1(gspca_dev, 0x0f, exposure >> 8);
        }
 }
 
@@ -832,7 +947,7 @@ static void setgain(struct gspca_dev *gspca_dev)
        if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
                return;
 
-       if (sd->sensor_type) {
+       if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
                sensor_write1(gspca_dev, 0x0e, sd->gain);
        } else {
                sensor_write1(gspca_dev, 0x10, sd->gain);
@@ -893,17 +1008,35 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
        return 0;
 }
 
+static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->min_clockdiv = val;
+       if (gspca_dev->streaming)
+               setexposure(gspca_dev);
+       return 0;
+}
+
+static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->min_clockdiv;
+       return 0;
+}
+
 /* Include pac common sof detection functions */
 #include "pac_common.h"
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,    /* target */
-                       __u8 *data,                   /* isoc packet */
-                       int len)                      /* iso packet length */
+                       u8 *data,               /* isoc packet */
+                       int len)                /* iso packet length */
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        unsigned char *sof;
 
-       sof = pac_find_sof(gspca_dev, data, len);
+       sof = pac_find_sof(&sd->sof_read, data, len);
        if (sof) {
                int n;
 
@@ -913,15 +1046,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        n -= sizeof pac_sof_marker;
                else
                        n = 0;
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+               gspca_frame_add(gspca_dev, LAST_PACKET,
                                        data, n);
                /* Start next frame. */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
                        pac_sof_marker, sizeof pac_sof_marker);
                len -= sof - data;
                data = sof;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 /* sub-driver description */
@@ -938,6 +1071,7 @@ static const struct sd_desc sd_desc = {
 
 /* -- module initialisation -- */
 static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x08ca, 0x0110)},   /* Trust Spyc@m 100 */
        {USB_DEVICE(0x08ca, 0x0111)},   /* Aiptek Pencam VGA+ */
        {USB_DEVICE(0x093a, 0x010f)},   /* All other known MR97310A VGA cams */
        {USB_DEVICE(0x093a, 0x010e)},   /* All known MR97310A CIF cams */
index a5c190e..ad9ec33 100644 (file)
@@ -2,14 +2,19 @@
  * OV519 driver
  *
  * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
  *
  * This module is adapted from the ov51x-jpeg package, which itself
  * was adapted from the ov511 driver.
  *
  * Original copyright for the ov511 driver is:
  *
- * Copyright (c) 1999-2004 Mark W. McClelland
+ * Copyright (c) 1999-2006 Mark W. McClelland
  * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
  *
  * ov51x-jpeg original copyright is:
  *
@@ -58,6 +63,8 @@ struct sd {
 #define BRIDGE_OV518           2
 #define BRIDGE_OV518PLUS       3
 #define BRIDGE_OV519           4
+#define BRIDGE_OVFX2           5
+#define BRIDGE_W9968CF         6
 #define BRIDGE_MASK            7
 
        char invert_led;
@@ -73,6 +80,10 @@ struct sd {
        __u8 vflip;
        __u8 autobrightness;
        __u8 freq;
+       __u8 quality;
+#define QUALITY_MIN 50
+#define QUALITY_MAX 70
+#define QUALITY_DEF 50
 
        __u8 stopped;           /* Streaming is temporarily paused */
 
@@ -81,17 +92,31 @@ struct sd {
 
        char sensor;            /* Type of image sensor chip (SEN_*) */
 #define SEN_UNKNOWN 0
-#define SEN_OV6620 1
-#define SEN_OV6630 2
-#define SEN_OV66308AF 3
-#define SEN_OV7610 4
-#define SEN_OV7620 5
-#define SEN_OV7640 6
-#define SEN_OV7670 7
-#define SEN_OV76BE 8
-#define SEN_OV8610 9
+#define SEN_OV2610 1
+#define SEN_OV3610 2
+#define SEN_OV6620 3
+#define SEN_OV6630 4
+#define SEN_OV66308AF 5
+#define SEN_OV7610 6
+#define SEN_OV7620 7
+#define SEN_OV7640 8
+#define SEN_OV7670 9
+#define SEN_OV76BE 10
+#define SEN_OV8610 11
+
+       u8 sensor_addr;
+       int sensor_width;
+       int sensor_height;
+       int sensor_reg_cache[256];
+
+       u8 *jpeg_hdr;
 };
 
+/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
+   the ov sensors which is already present here. When we have the time we
+   really should move the sensor drivers to v4l2 sub drivers. */
+#include "w996Xcf.c"
+
 /* V4L2 controls supported by the driver */
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
@@ -345,6 +370,75 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
                .priv = 0},
 };
 
+static const struct v4l2_pix_format ovfx2_vga_mode[] = {
+       {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_cif_mode[] = {
+       {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 160,
+               .sizeimage = 160 * 120,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 3},
+       {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 176,
+               .sizeimage = 176 * 144,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 320,
+               .sizeimage = 320 * 240,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 2},
+       {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 352,
+               .sizeimage = 352 * 288,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
+       {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 1600,
+               .sizeimage = 1600 * 1200,
+               .colorspace = V4L2_COLORSPACE_SRGB},
+};
+static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
+       {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 800,
+               .sizeimage = 800 * 600,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 1024,
+               .sizeimage = 1024 * 768,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 1},
+       {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 1600,
+               .sizeimage = 1600 * 1200,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+       {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+               .bytesperline = 2048,
+               .sizeimage = 2048 * 1536,
+               .colorspace = V4L2_COLORSPACE_SRGB,
+               .priv = 0},
+};
+
+
 /* Registers common to OV511 / OV518 */
 #define R51x_FIFO_PSIZE                        0x30    /* 2 bytes wide w/ OV518(+) */
 #define R51x_SYS_RESET                 0x50
@@ -406,6 +500,30 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
 
 #define OV511_ENDPOINT_ADDRESS  1      /* Isoc endpoint number */
 
+/*
+ * The FX2 chip does not give us a zero length read at end of frame.
+ * It does, however, give a short read at the end of a frame, if
+ * neccessary, rather than run two frames together.
+ *
+ * By choosing the right bulk transfer size, we are guaranteed to always
+ * get a short read for the last read of each frame.  Frame sizes are
+ * always a composite number (width * height, or a multiple) so if we
+ * choose a prime number, we are guaranteed that the last read of a
+ * frame will be short.
+ *
+ * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
+ * otherwise EOVERFLOW "babbling" errors occur.  I have not been able
+ * to figure out why.  [PMiller]
+ *
+ * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
+ *
+ * It isn't enough to know the number of bytes per frame, in case we
+ * have data dropouts or buffer overruns (even though the FX2 double
+ * buffers, there are some pretty strict real time constraints for
+ * isochronous transfer for larger frame sizes).
+ */
+#define OVFX2_BULK_SIZE (13 * 4096)
+
 /* I2C registers */
 #define R51x_I2C_W_SID         0x41
 #define R51x_I2C_SADDR_3       0x42
@@ -413,9 +531,11 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
 #define R51x_I2C_R_SID         0x44
 #define R51x_I2C_DATA          0x45
 #define R518_I2C_CTL           0x47    /* OV518(+) only */
+#define OVFX2_I2C_ADDR         0x00
 
 /* I2C ADDRESSES */
 #define OV7xx0_SID   0x42
+#define OV_HIRES_SID 0x60              /* OV9xxx / OV2xxx / OV3xxx */
 #define OV8xx0_SID   0xa0
 #define OV6xx0_SID   0xc0
 
@@ -508,6 +628,696 @@ struct ov_i2c_regvals {
        __u8 val;
 };
 
+/* Settings for OV2610 camera chip */
+static const struct ov_i2c_regvals norm_2610[] =
+{
+       { 0x12, 0x80 }, /* reset */
+};
+
+static const struct ov_i2c_regvals norm_3620b[] =
+{
+       /*
+        * From the datasheet: "Note that after writing to register COMH
+        * (0x12) to change the sensor mode, registers related to the
+        * sensor’s cropping window will be reset back to their default
+        * values."
+        *
+        * "wait 4096 external clock ... to make sure the sensor is
+        * stable and ready to access registers" i.e. 160us at 24MHz
+        */
+
+       { 0x12, 0x80 }, /* COMH reset */
+       { 0x12, 0x00 }, /* QXGA, master */
+
+       /*
+        * 11 CLKRC "Clock Rate Control"
+        * [7] internal frequency doublers: on
+        * [6] video port mode: master
+        * [5:0] clock divider: 1
+        */
+       { 0x11, 0x80 },
+
+       /*
+        * 13 COMI "Common Control I"
+        *                  = 192 (0xC0) 11000000
+        *    COMI[7] "AEC speed selection"
+        *                  =   1 (0x01) 1....... "Faster AEC correction"
+        *    COMI[6] "AEC speed step selection"
+        *                  =   1 (0x01) .1...... "Big steps, fast"
+        *    COMI[5] "Banding filter on off"
+        *                  =   0 (0x00) ..0..... "Off"
+        *    COMI[4] "Banding filter option"
+        *                  =   0 (0x00) ...0.... "Main clock is 48 MHz and
+        *                                         the PLL is ON"
+        *    COMI[3] "Reserved"
+        *                  =   0 (0x00) ....0...
+        *    COMI[2] "AGC auto manual control selection"
+        *                  =   0 (0x00) .....0.. "Manual"
+        *    COMI[1] "AWB auto manual control selection"
+        *                  =   0 (0x00) ......0. "Manual"
+        *    COMI[0] "Exposure control"
+        *                  =   0 (0x00) .......0 "Manual"
+        */
+       { 0x13, 0xC0 },
+
+       /*
+        * 09 COMC "Common Control C"
+        *                  =   8 (0x08) 00001000
+        *    COMC[7:5] "Reserved"
+        *                  =   0 (0x00) 000.....
+        *    COMC[4] "Sleep Mode Enable"
+        *                  =   0 (0x00) ...0.... "Normal mode"
+        *    COMC[3:2] "Sensor sampling reset timing selection"
+        *                  =   2 (0x02) ....10.. "Longer reset time"
+        *    COMC[1:0] "Output drive current select"
+        *                  =   0 (0x00) ......00 "Weakest"
+        */
+       { 0x09, 0x08 },
+
+       /*
+        * 0C COMD "Common Control D"
+        *                  =   8 (0x08) 00001000
+        *    COMD[7] "Reserved"
+        *                  =   0 (0x00) 0.......
+        *    COMD[6] "Swap MSB and LSB at the output port"
+        *                  =   0 (0x00) .0...... "False"
+        *    COMD[5:3] "Reserved"
+        *                  =   1 (0x01) ..001...
+        *    COMD[2] "Output Average On Off"
+        *                  =   0 (0x00) .....0.. "Output Normal"
+        *    COMD[1] "Sensor precharge voltage selection"
+        *                  =   0 (0x00) ......0. "Selects internal
+        *                                         reference precharge
+        *                                         voltage"
+        *    COMD[0] "Snapshot option"
+        *                  =   0 (0x00) .......0 "Enable live video output
+        *                                         after snapshot sequence"
+        */
+       { 0x0c, 0x08 },
+
+       /*
+        * 0D COME "Common Control E"
+        *                  = 161 (0xA1) 10100001
+        *    COME[7] "Output average option"
+        *                  =   1 (0x01) 1....... "Output average of 4 pixels"
+        *    COME[6] "Anti-blooming control"
+        *                  =   0 (0x00) .0...... "Off"
+        *    COME[5:3] "Reserved"
+        *                  =   4 (0x04) ..100...
+        *    COME[2] "Clock output power down pin status"
+        *                  =   0 (0x00) .....0.. "Tri-state data output pin
+        *                                         on power down"
+        *    COME[1] "Data output pin status selection at power down"
+        *                  =   0 (0x00) ......0. "Tri-state VSYNC, PCLK,
+        *                                         HREF, and CHSYNC pins on
+        *                                         power down"
+        *    COME[0] "Auto zero circuit select"
+        *                  =   1 (0x01) .......1 "On"
+        */
+       { 0x0d, 0xA1 },
+
+       /*
+        * 0E COMF "Common Control F"
+        *                  = 112 (0x70) 01110000
+        *    COMF[7] "System clock selection"
+        *                  =   0 (0x00) 0....... "Use 24 MHz system clock"
+        *    COMF[6:4] "Reserved"
+        *                  =   7 (0x07) .111....
+        *    COMF[3] "Manual auto negative offset canceling selection"
+        *                  =   0 (0x00) ....0... "Auto detect negative
+        *                                         offset and cancel it"
+        *    COMF[2:0] "Reserved"
+        *                  =   0 (0x00) .....000
+        */
+       { 0x0e, 0x70 },
+
+       /*
+        * 0F COMG "Common Control G"
+        *                  =  66 (0x42) 01000010
+        *    COMG[7] "Optical black output selection"
+        *                  =   0 (0x00) 0....... "Disable"
+        *    COMG[6] "Black level calibrate selection"
+        *                  =   1 (0x01) .1...... "Use optical black pixels
+        *                                         to calibrate"
+        *    COMG[5:4] "Reserved"
+        *                  =   0 (0x00) ..00....
+        *    COMG[3] "Channel offset adjustment"
+        *                  =   0 (0x00) ....0... "Disable offset adjustment"
+        *    COMG[2] "ADC black level calibration option"
+        *                  =   0 (0x00) .....0.. "Use B/G line and G/R
+        *                                         line to calibrate each
+        *                                         channel's black level"
+        *    COMG[1] "Reserved"
+        *                  =   1 (0x01) ......1.
+        *    COMG[0] "ADC black level calibration enable"
+        *                  =   0 (0x00) .......0 "Disable"
+        */
+       { 0x0f, 0x42 },
+
+       /*
+        * 14 COMJ "Common Control J"
+        *                  = 198 (0xC6) 11000110
+        *    COMJ[7:6] "AGC gain ceiling"
+        *                  =   3 (0x03) 11...... "8x"
+        *    COMJ[5:4] "Reserved"
+        *                  =   0 (0x00) ..00....
+        *    COMJ[3] "Auto banding filter"
+        *                  =   0 (0x00) ....0... "Banding filter is always
+        *                                         on off depending on
+        *                                         COMI[5] setting"
+        *    COMJ[2] "VSYNC drop option"
+        *                  =   1 (0x01) .....1.. "SYNC is dropped if frame
+        *                                         data is dropped"
+        *    COMJ[1] "Frame data drop"
+        *                  =   1 (0x01) ......1. "Drop frame data if
+        *                                         exposure is not within
+        *                                         tolerance.  In AEC mode,
+        *                                         data is normally dropped
+        *                                         when data is out of
+        *                                         range."
+        *    COMJ[0] "Reserved"
+        *                  =   0 (0x00) .......0
+        */
+       { 0x14, 0xC6 },
+
+       /*
+        * 15 COMK "Common Control K"
+        *                  =   2 (0x02) 00000010
+        *    COMK[7] "CHSYNC pin output swap"
+        *                  =   0 (0x00) 0....... "CHSYNC"
+        *    COMK[6] "HREF pin output swap"
+        *                  =   0 (0x00) .0...... "HREF"
+        *    COMK[5] "PCLK output selection"
+        *                  =   0 (0x00) ..0..... "PCLK always output"
+        *    COMK[4] "PCLK edge selection"
+        *                  =   0 (0x00) ...0.... "Data valid on falling edge"
+        *    COMK[3] "HREF output polarity"
+        *                  =   0 (0x00) ....0... "positive"
+        *    COMK[2] "Reserved"
+        *                  =   0 (0x00) .....0..
+        *    COMK[1] "VSYNC polarity"
+        *                  =   1 (0x01) ......1. "negative"
+        *    COMK[0] "HSYNC polarity"
+        *                  =   0 (0x00) .......0 "positive"
+        */
+       { 0x15, 0x02 },
+
+       /*
+        * 33 CHLF "Current Control"
+        *                  =   9 (0x09) 00001001
+        *    CHLF[7:6] "Sensor current control"
+        *                  =   0 (0x00) 00......
+        *    CHLF[5] "Sensor current range control"
+        *                  =   0 (0x00) ..0..... "normal range"
+        *    CHLF[4] "Sensor current"
+        *                  =   0 (0x00) ...0.... "normal current"
+        *    CHLF[3] "Sensor buffer current control"
+        *                  =   1 (0x01) ....1... "half current"
+        *    CHLF[2] "Column buffer current control"
+        *                  =   0 (0x00) .....0.. "normal current"
+        *    CHLF[1] "Analog DSP current control"
+        *                  =   0 (0x00) ......0. "normal current"
+        *    CHLF[1] "ADC current control"
+        *                  =   0 (0x00) ......0. "normal current"
+        */
+       { 0x33, 0x09 },
+
+       /*
+        * 34 VBLM "Blooming Control"
+        *                  =  80 (0x50) 01010000
+        *    VBLM[7] "Hard soft reset switch"
+        *                  =   0 (0x00) 0....... "Hard reset"
+        *    VBLM[6:4] "Blooming voltage selection"
+        *                  =   5 (0x05) .101....
+        *    VBLM[3:0] "Sensor current control"
+        *                  =   0 (0x00) ....0000
+        */
+       { 0x34, 0x50 },
+
+       /*
+        * 36 VCHG "Sensor Precharge Voltage Control"
+        *                  =   0 (0x00) 00000000
+        *    VCHG[7] "Reserved"
+        *                  =   0 (0x00) 0.......
+        *    VCHG[6:4] "Sensor precharge voltage control"
+        *                  =   0 (0x00) .000....
+        *    VCHG[3:0] "Sensor array common reference"
+        *                  =   0 (0x00) ....0000
+        */
+       { 0x36, 0x00 },
+
+       /*
+        * 37 ADC "ADC Reference Control"
+        *                  =   4 (0x04) 00000100
+        *    ADC[7:4] "Reserved"
+        *                  =   0 (0x00) 0000....
+        *    ADC[3] "ADC input signal range"
+        *                  =   0 (0x00) ....0... "Input signal 1.0x"
+        *    ADC[2:0] "ADC range control"
+        *                  =   4 (0x04) .....100
+        */
+       { 0x37, 0x04 },
+
+       /*
+        * 38 ACOM "Analog Common Ground"
+        *                  =  82 (0x52) 01010010
+        *    ACOM[7] "Analog gain control"
+        *                  =   0 (0x00) 0....... "Gain 1x"
+        *    ACOM[6] "Analog black level calibration"
+        *                  =   1 (0x01) .1...... "On"
+        *    ACOM[5:0] "Reserved"
+        *                  =  18 (0x12) ..010010
+        */
+       { 0x38, 0x52 },
+
+       /*
+        * 3A FREFA "Internal Reference Adjustment"
+        *                  =   0 (0x00) 00000000
+        *    FREFA[7:0] "Range"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x3a, 0x00 },
+
+       /*
+        * 3C FVOPT "Internal Reference Adjustment"
+        *                  =  31 (0x1F) 00011111
+        *    FVOPT[7:0] "Range"
+        *                  =  31 (0x1F) 00011111
+        */
+       { 0x3c, 0x1F },
+
+       /*
+        * 44 Undocumented  =   0 (0x00) 00000000
+        *    44[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x44, 0x00 },
+
+       /*
+        * 40 Undocumented  =   0 (0x00) 00000000
+        *    40[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x40, 0x00 },
+
+       /*
+        * 41 Undocumented  =   0 (0x00) 00000000
+        *    41[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x41, 0x00 },
+
+       /*
+        * 42 Undocumented  =   0 (0x00) 00000000
+        *    42[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x42, 0x00 },
+
+       /*
+        * 43 Undocumented  =   0 (0x00) 00000000
+        *    43[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x43, 0x00 },
+
+       /*
+        * 45 Undocumented  = 128 (0x80) 10000000
+        *    45[7:0] "It's a secret"
+        *                  = 128 (0x80) 10000000
+        */
+       { 0x45, 0x80 },
+
+       /*
+        * 48 Undocumented  = 192 (0xC0) 11000000
+        *    48[7:0] "It's a secret"
+        *                  = 192 (0xC0) 11000000
+        */
+       { 0x48, 0xC0 },
+
+       /*
+        * 49 Undocumented  =  25 (0x19) 00011001
+        *    49[7:0] "It's a secret"
+        *                  =  25 (0x19) 00011001
+        */
+       { 0x49, 0x19 },
+
+       /*
+        * 4B Undocumented  = 128 (0x80) 10000000
+        *    4B[7:0] "It's a secret"
+        *                  = 128 (0x80) 10000000
+        */
+       { 0x4B, 0x80 },
+
+       /*
+        * 4D Undocumented  = 196 (0xC4) 11000100
+        *    4D[7:0] "It's a secret"
+        *                  = 196 (0xC4) 11000100
+        */
+       { 0x4D, 0xC4 },
+
+       /*
+        * 35 VREF "Reference Voltage Control"
+        *                  =  76 (0x4C) 01001100
+        *    VREF[7:5] "Column high reference control"
+        *                  =   2 (0x02) 010..... "higher voltage"
+        *    VREF[4:2] "Column low reference control"
+        *                  =   3 (0x03) ...011.. "Highest voltage"
+        *    VREF[1:0] "Reserved"
+        *                  =   0 (0x00) ......00
+        */
+       { 0x35, 0x4C },
+
+       /*
+        * 3D Undocumented  =   0 (0x00) 00000000
+        *    3D[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x3D, 0x00 },
+
+       /*
+        * 3E Undocumented  =   0 (0x00) 00000000
+        *    3E[7:0] "It's a secret"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x3E, 0x00 },
+
+       /*
+        * 3B FREFB "Internal Reference Adjustment"
+        *                  =  24 (0x18) 00011000
+        *    FREFB[7:0] "Range"
+        *                  =  24 (0x18) 00011000
+        */
+       { 0x3b, 0x18 },
+
+       /*
+        * 33 CHLF "Current Control"
+        *                  =  25 (0x19) 00011001
+        *    CHLF[7:6] "Sensor current control"
+        *                  =   0 (0x00) 00......
+        *    CHLF[5] "Sensor current range control"
+        *                  =   0 (0x00) ..0..... "normal range"
+        *    CHLF[4] "Sensor current"
+        *                  =   1 (0x01) ...1.... "double current"
+        *    CHLF[3] "Sensor buffer current control"
+        *                  =   1 (0x01) ....1... "half current"
+        *    CHLF[2] "Column buffer current control"
+        *                  =   0 (0x00) .....0.. "normal current"
+        *    CHLF[1] "Analog DSP current control"
+        *                  =   0 (0x00) ......0. "normal current"
+        *    CHLF[1] "ADC current control"
+        *                  =   0 (0x00) ......0. "normal current"
+        */
+       { 0x33, 0x19 },
+
+       /*
+        * 34 VBLM "Blooming Control"
+        *                  =  90 (0x5A) 01011010
+        *    VBLM[7] "Hard soft reset switch"
+        *                  =   0 (0x00) 0....... "Hard reset"
+        *    VBLM[6:4] "Blooming voltage selection"
+        *                  =   5 (0x05) .101....
+        *    VBLM[3:0] "Sensor current control"
+        *                  =  10 (0x0A) ....1010
+        */
+       { 0x34, 0x5A },
+
+       /*
+        * 3B FREFB "Internal Reference Adjustment"
+        *                  =   0 (0x00) 00000000
+        *    FREFB[7:0] "Range"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x3b, 0x00 },
+
+       /*
+        * 33 CHLF "Current Control"
+        *                  =   9 (0x09) 00001001
+        *    CHLF[7:6] "Sensor current control"
+        *                  =   0 (0x00) 00......
+        *    CHLF[5] "Sensor current range control"
+        *                  =   0 (0x00) ..0..... "normal range"
+        *    CHLF[4] "Sensor current"
+        *                  =   0 (0x00) ...0.... "normal current"
+        *    CHLF[3] "Sensor buffer current control"
+        *                  =   1 (0x01) ....1... "half current"
+        *    CHLF[2] "Column buffer current control"
+        *                  =   0 (0x00) .....0.. "normal current"
+        *    CHLF[1] "Analog DSP current control"
+        *                  =   0 (0x00) ......0. "normal current"
+        *    CHLF[1] "ADC current control"
+        *                  =   0 (0x00) ......0. "normal current"
+        */
+       { 0x33, 0x09 },
+
+       /*
+        * 34 VBLM "Blooming Control"
+        *                  =  80 (0x50) 01010000
+        *    VBLM[7] "Hard soft reset switch"
+        *                  =   0 (0x00) 0....... "Hard reset"
+        *    VBLM[6:4] "Blooming voltage selection"
+        *                  =   5 (0x05) .101....
+        *    VBLM[3:0] "Sensor current control"
+        *                  =   0 (0x00) ....0000
+        */
+       { 0x34, 0x50 },
+
+       /*
+        * 12 COMH "Common Control H"
+        *                  =  64 (0x40) 01000000
+        *    COMH[7] "SRST"
+        *                  =   0 (0x00) 0....... "No-op"
+        *    COMH[6:4] "Resolution selection"
+        *                  =   4 (0x04) .100.... "XGA"
+        *    COMH[3] "Master slave selection"
+        *                  =   0 (0x00) ....0... "Master mode"
+        *    COMH[2] "Internal B/R channel option"
+        *                  =   0 (0x00) .....0.. "B/R use same channel"
+        *    COMH[1] "Color bar test pattern"
+        *                  =   0 (0x00) ......0. "Off"
+        *    COMH[0] "Reserved"
+        *                  =   0 (0x00) .......0
+        */
+       { 0x12, 0x40 },
+
+       /*
+        * 17 HREFST "Horizontal window start"
+        *                  =  31 (0x1F) 00011111
+        *    HREFST[7:0] "Horizontal window start, 8 MSBs"
+        *                  =  31 (0x1F) 00011111
+        */
+       { 0x17, 0x1F },
+
+       /*
+        * 18 HREFEND "Horizontal window end"
+        *                  =  95 (0x5F) 01011111
+        *    HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+        *                  =  95 (0x5F) 01011111
+        */
+       { 0x18, 0x5F },
+
+       /*
+        * 19 VSTRT "Vertical window start"
+        *                  =   0 (0x00) 00000000
+        *    VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x19, 0x00 },
+
+       /*
+        * 1A VEND "Vertical window end"
+        *                  =  96 (0x60) 01100000
+        *    VEND[7:0] "Vertical Window End, 8 MSBs"
+        *                  =  96 (0x60) 01100000
+        */
+       { 0x1a, 0x60 },
+
+       /*
+        * 32 COMM "Common Control M"
+        *                  =  18 (0x12) 00010010
+        *    COMM[7:6] "Pixel clock divide option"
+        *                  =   0 (0x00) 00...... "/1"
+        *    COMM[5:3] "Horizontal window end position, 3 LSBs"
+        *                  =   2 (0x02) ..010...
+        *    COMM[2:0] "Horizontal window start position, 3 LSBs"
+        *                  =   2 (0x02) .....010
+        */
+       { 0x32, 0x12 },
+
+       /*
+        * 03 COMA "Common Control A"
+        *                  =  74 (0x4A) 01001010
+        *    COMA[7:4] "AWB Update Threshold"
+        *                  =   4 (0x04) 0100....
+        *    COMA[3:2] "Vertical window end line control 2 LSBs"
+        *                  =   2 (0x02) ....10..
+        *    COMA[1:0] "Vertical window start line control 2 LSBs"
+        *                  =   2 (0x02) ......10
+        */
+       { 0x03, 0x4A },
+
+       /*
+        * 11 CLKRC "Clock Rate Control"
+        *                  = 128 (0x80) 10000000
+        *    CLKRC[7] "Internal frequency doublers on off seclection"
+        *                  =   1 (0x01) 1....... "On"
+        *    CLKRC[6] "Digital video master slave selection"
+        *                  =   0 (0x00) .0...... "Master mode, sensor
+        *                                         provides PCLK"
+        *    CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
+        *                  =   0 (0x00) ..000000
+        */
+       { 0x11, 0x80 },
+
+       /*
+        * 12 COMH "Common Control H"
+        *                  =   0 (0x00) 00000000
+        *    COMH[7] "SRST"
+        *                  =   0 (0x00) 0....... "No-op"
+        *    COMH[6:4] "Resolution selection"
+        *                  =   0 (0x00) .000.... "QXGA"
+        *    COMH[3] "Master slave selection"
+        *                  =   0 (0x00) ....0... "Master mode"
+        *    COMH[2] "Internal B/R channel option"
+        *                  =   0 (0x00) .....0.. "B/R use same channel"
+        *    COMH[1] "Color bar test pattern"
+        *                  =   0 (0x00) ......0. "Off"
+        *    COMH[0] "Reserved"
+        *                  =   0 (0x00) .......0
+        */
+       { 0x12, 0x00 },
+
+       /*
+        * 12 COMH "Common Control H"
+        *                  =  64 (0x40) 01000000
+        *    COMH[7] "SRST"
+        *                  =   0 (0x00) 0....... "No-op"
+        *    COMH[6:4] "Resolution selection"
+        *                  =   4 (0x04) .100.... "XGA"
+        *    COMH[3] "Master slave selection"
+        *                  =   0 (0x00) ....0... "Master mode"
+        *    COMH[2] "Internal B/R channel option"
+        *                  =   0 (0x00) .....0.. "B/R use same channel"
+        *    COMH[1] "Color bar test pattern"
+        *                  =   0 (0x00) ......0. "Off"
+        *    COMH[0] "Reserved"
+        *                  =   0 (0x00) .......0
+        */
+       { 0x12, 0x40 },
+
+       /*
+        * 17 HREFST "Horizontal window start"
+        *                  =  31 (0x1F) 00011111
+        *    HREFST[7:0] "Horizontal window start, 8 MSBs"
+        *                  =  31 (0x1F) 00011111
+        */
+       { 0x17, 0x1F },
+
+       /*
+        * 18 HREFEND "Horizontal window end"
+        *                  =  95 (0x5F) 01011111
+        *    HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+        *                  =  95 (0x5F) 01011111
+        */
+       { 0x18, 0x5F },
+
+       /*
+        * 19 VSTRT "Vertical window start"
+        *                  =   0 (0x00) 00000000
+        *    VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+        *                  =   0 (0x00) 00000000
+        */
+       { 0x19, 0x00 },
+
+       /*
+        * 1A VEND "Vertical window end"
+        *                  =  96 (0x60) 01100000
+        *    VEND[7:0] "Vertical Window End, 8 MSBs"
+        *                  =  96 (0x60) 01100000
+        */
+       { 0x1a, 0x60 },
+
+       /*
+        * 32 COMM "Common Control M"
+        *                  =  18 (0x12) 00010010
+        *    COMM[7:6] "Pixel clock divide option"
+        *                  =   0 (0x00) 00...... "/1"
+        *    COMM[5:3] "Horizontal window end position, 3 LSBs"
+        *                  =   2 (0x02) ..010...
+        *    COMM[2:0] "Horizontal window start position, 3 LSBs"
+        *                  =   2 (0x02) .....010
+        */
+       { 0x32, 0x12 },
+
+       /*
+        * 03 COMA "Common Control A"
+        *                  =  74 (0x4A) 01001010
+        *    COMA[7:4] "AWB Update Threshold"
+        *                  =   4 (0x04) 0100....
+        *    COMA[3:2] "Vertical window end line control 2 LSBs"
+        *                  =   2 (0x02) ....10..
+        *    COMA[1:0] "Vertical window start line control 2 LSBs"
+        *                  =   2 (0x02) ......10
+        */
+       { 0x03, 0x4A },
+
+       /*
+        * 02 RED "Red Gain Control"
+        *                  = 175 (0xAF) 10101111
+        *    RED[7] "Action"
+        *                  =   1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+        *    RED[6:0] "Value"
+        *                  =  47 (0x2F) .0101111
+        */
+       { 0x02, 0xAF },
+
+       /*
+        * 2D ADDVSL "VSYNC Pulse Width"
+        *                  = 210 (0xD2) 11010010
+        *    ADDVSL[7:0] "VSYNC pulse width, LSB"
+        *                  = 210 (0xD2) 11010010
+        */
+       { 0x2d, 0xD2 },
+
+       /*
+        * 00 GAIN          =  24 (0x18) 00011000
+        *    GAIN[7:6] "Reserved"
+        *                  =   0 (0x00) 00......
+        *    GAIN[5] "Double"
+        *                  =   0 (0x00) ..0..... "False"
+        *    GAIN[4] "Double"
+        *                  =   1 (0x01) ...1.... "True"
+        *    GAIN[3:0] "Range"
+        *                  =   8 (0x08) ....1000
+        */
+       { 0x00, 0x18 },
+
+       /*
+        * 01 BLUE "Blue Gain Control"
+        *                  = 240 (0xF0) 11110000
+        *    BLUE[7] "Action"
+        *                  =   1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+        *    BLUE[6:0] "Value"
+        *                  = 112 (0x70) .1110000
+        */
+       { 0x01, 0xF0 },
+
+       /*
+        * 10 AEC "Automatic Exposure Control"
+        *                  =  10 (0x0A) 00001010
+        *    AEC[7:0] "Automatic Exposure Control, 8 MSBs"
+        *                  =  10 (0x0A) 00001010
+        */
+       { 0x10, 0x0A },
+
+       { 0xE1, 0x67 },
+       { 0xE3, 0x03 },
+       { 0xE4, 0x26 },
+       { 0xE5, 0x3E },
+       { 0xF8, 0x01 },
+       { 0xFF, 0x01 },
+};
+
 static const struct ov_i2c_regvals norm_6x20[] = {
        { 0x12, 0x80 }, /* reset */
        { 0x11, 0x01 },
@@ -678,6 +1488,7 @@ static const struct ov_i2c_regvals norm_7610[] = {
 };
 
 static const struct ov_i2c_regvals norm_7620[] = {
+       { 0x12, 0x80 },         /* reset */
        { 0x00, 0x00 },         /* gain */
        { 0x01, 0x80 },         /* blue gain */
        { 0x02, 0x80 },         /* red gain */
@@ -1042,10 +1853,28 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
 }
 
 /* Write a OV519 register */
-static int reg_w(struct sd *sd, __u16 index, __u8 value)
+static int reg_w(struct sd *sd, __u16 index, __u16 value)
 {
-       int ret;
-       int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1;
+       int ret, req = 0;
+
+       switch (sd->bridge) {
+       case BRIDGE_OV511:
+       case BRIDGE_OV511PLUS:
+               req = 2;
+               break;
+       case BRIDGE_OVFX2:
+               req = 0x0a;
+               /* fall through */
+       case BRIDGE_W9968CF:
+               ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+                       req,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       value, index, NULL, 0, 500);
+               goto leave;
+       default:
+               req = 1;
+       }
 
        sd->gspca_dev.usb_buf[0] = value;
        ret = usb_control_msg(sd->gspca_dev.dev,
@@ -1054,17 +1883,35 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value)
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0, index,
                        sd->gspca_dev.usb_buf, 1, 500);
-       if (ret < 0)
-               PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value);
-       return ret;
+leave:
+       if (ret < 0) {
+               PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed",
+                      value, index);
+               return ret;
+       }
+
+       PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index);
+       return 0;
 }
 
-/* Read from a OV519 register */
+/* Read from a OV519 register, note not valid for the w9968cf!! */
 /* returns: negative is error, pos or zero is data */
 static int reg_r(struct sd *sd, __u16 index)
 {
        int ret;
-       int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1;
+       int req;
+
+       switch (sd->bridge) {
+       case BRIDGE_OV511:
+       case BRIDGE_OV511PLUS:
+               req = 3;
+               break;
+       case BRIDGE_OVFX2:
+               req = 0x0b;
+               break;
+       default:
+               req = 1;
+       }
 
        ret = usb_control_msg(sd->gspca_dev.dev,
                        usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
@@ -1072,10 +1919,12 @@ static int reg_r(struct sd *sd, __u16 index)
                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0, index, sd->gspca_dev.usb_buf, 1, 500);
 
-       if (ret >= 0)
+       if (ret >= 0) {
                ret = sd->gspca_dev.usb_buf[0];
-       else
+               PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret);
+       } else
                PDEBUG(D_ERR, "Read reg [0x%02x] failed", index);
+
        return ret;
 }
 
@@ -1095,6 +1944,7 @@ static int reg_r8(struct sd *sd,
                ret = sd->gspca_dev.usb_buf[0];
        else
                PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index);
+
        return ret;
 }
 
@@ -1140,9 +1990,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n)
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0, index,
                        sd->gspca_dev.usb_buf, n, 500);
-       if (ret < 0)
+       if (ret < 0) {
                PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value);
-       return ret;
+               return ret;
+       }
+
+       return 0;
 }
 
 static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value)
@@ -1324,32 +2177,110 @@ static int ov518_i2c_r(struct sd *sd, __u8 reg)
        return value;
 }
 
+static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value)
+{
+       int ret;
+
+       ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+                       0x02,
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       (__u16)value, (__u16)reg, NULL, 0, 500);
+
+       if (ret < 0) {
+               PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
+               return ret;
+       }
+
+       PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+       return 0;
+}
+
+static int ovfx2_i2c_r(struct sd *sd, __u8 reg)
+{
+       int ret;
+
+       ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+                       0x03,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, (__u16)reg, sd->gspca_dev.usb_buf, 1, 500);
+
+       if (ret >= 0) {
+               ret = sd->gspca_dev.usb_buf[0];
+               PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret);
+       } else
+               PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
+
+       return ret;
+}
+
 static int i2c_w(struct sd *sd, __u8 reg, __u8 value)
 {
+       int ret = -1;
+
+       if (sd->sensor_reg_cache[reg] == value)
+               return 0;
+
        switch (sd->bridge) {
        case BRIDGE_OV511:
        case BRIDGE_OV511PLUS:
-               return ov511_i2c_w(sd, reg, value);
+               ret = ov511_i2c_w(sd, reg, value);
+               break;
        case BRIDGE_OV518:
        case BRIDGE_OV518PLUS:
        case BRIDGE_OV519:
-               return ov518_i2c_w(sd, reg, value);
+               ret = ov518_i2c_w(sd, reg, value);
+               break;
+       case BRIDGE_OVFX2:
+               ret = ovfx2_i2c_w(sd, reg, value);
+               break;
+       case BRIDGE_W9968CF:
+               ret = w9968cf_i2c_w(sd, reg, value);
+               break;
        }
-       return -1; /* Should never happen */
+
+       if (ret >= 0) {
+               /* Up on sensor reset empty the register cache */
+               if (reg == 0x12 && (value & 0x80))
+                       memset(sd->sensor_reg_cache, -1,
+                              sizeof(sd->sensor_reg_cache));
+               else
+                       sd->sensor_reg_cache[reg] = value;
+       }
+
+       return ret;
 }
 
 static int i2c_r(struct sd *sd, __u8 reg)
 {
+       int ret = -1;
+
+       if (sd->sensor_reg_cache[reg] != -1)
+               return sd->sensor_reg_cache[reg];
+
        switch (sd->bridge) {
        case BRIDGE_OV511:
        case BRIDGE_OV511PLUS:
-               return ov511_i2c_r(sd, reg);
+               ret = ov511_i2c_r(sd, reg);
+               break;
        case BRIDGE_OV518:
        case BRIDGE_OV518PLUS:
        case BRIDGE_OV519:
-               return ov518_i2c_r(sd, reg);
+               ret = ov518_i2c_r(sd, reg);
+               break;
+       case BRIDGE_OVFX2:
+               ret = ovfx2_i2c_r(sd, reg);
+               break;
+       case BRIDGE_W9968CF:
+               ret = w9968cf_i2c_r(sd, reg);
+               break;
        }
-       return -1; /* Should never happen */
+
+       if (ret >= 0)
+               sd->sensor_reg_cache[reg] = ret;
+
+       return ret;
 }
 
 /* Writes bits at positions specified by mask to an I2C reg. Bits that are in
@@ -1389,6 +2320,10 @@ static inline int ov51x_stop(struct sd *sd)
                return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
        case BRIDGE_OV519:
                return reg_w(sd, OV519_SYS_RESET1, 0x0f);
+       case BRIDGE_OVFX2:
+               return reg_w_mask(sd, 0x0f, 0x00, 0x02);
+       case BRIDGE_W9968CF:
+               return reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
        }
 
        return 0;
@@ -1418,18 +2353,27 @@ static inline int ov51x_restart(struct sd *sd)
                return reg_w(sd, R51x_SYS_RESET, 0x00);
        case BRIDGE_OV519:
                return reg_w(sd, OV519_SYS_RESET1, 0x00);
+       case BRIDGE_OVFX2:
+               return reg_w_mask(sd, 0x0f, 0x02, 0x02);
+       case BRIDGE_W9968CF:
+               return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
        }
 
        return 0;
 }
 
+static int ov51x_set_slave_ids(struct sd *sd, __u8 slave);
+
 /* This does an initial reset of an OmniVision sensor and ensures that I2C
  * is synchronized. Returns <0 on failure.
  */
-static int init_ov_sensor(struct sd *sd)
+static int init_ov_sensor(struct sd *sd, __u8 slave)
 {
        int i;
 
+       if (ov51x_set_slave_ids(sd, slave) < 0)
+               return -EIO;
+
        /* Reset the sensor */
        if (i2c_w(sd, 0x12, 0x80) < 0)
                return -EIO;
@@ -1466,6 +2410,14 @@ static int ov51x_set_slave_ids(struct sd *sd,
 {
        int rc;
 
+       switch (sd->bridge) {
+       case BRIDGE_OVFX2:
+               return reg_w(sd, OVFX2_I2C_ADDR, slave);
+       case BRIDGE_W9968CF:
+               sd->sensor_addr = slave;
+               return 0;
+       }
+
        rc = reg_w(sd, R51x_I2C_W_SID, slave);
        if (rc < 0)
                return rc;
@@ -1508,6 +2460,39 @@ static int write_i2c_regvals(struct sd *sd,
  *
  ***************************************************************************/
 
+/* This initializes the OV2x10 / OV3610 / OV3620 */
+static int ov_hires_configure(struct sd *sd)
+{
+       int high, low;
+
+       if (sd->bridge != BRIDGE_OVFX2) {
+               PDEBUG(D_ERR, "error hires sensors only supported with ovfx2");
+               return -1;
+       }
+
+       PDEBUG(D_PROBE, "starting ov hires configuration");
+
+       /* Detect sensor (sub)type */
+       high = i2c_r(sd, 0x0a);
+       low = i2c_r(sd, 0x0b);
+       /* info("%x, %x", high, low); */
+       if (high == 0x96 && low == 0x40) {
+               PDEBUG(D_PROBE, "Sensor is an OV2610");
+               sd->sensor = SEN_OV2610;
+       } else if (high == 0x36 && (low & 0x0f) == 0x00) {
+               PDEBUG(D_PROBE, "Sensor is an OV3610");
+               sd->sensor = SEN_OV3610;
+       } else {
+               PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x",
+                      high, low);
+               return -1;
+       }
+
+       /* Set sensor-specific vars */
+       return 0;
+}
+
+
 /* This initializes the OV8110, OV8610 sensor. The OV8110 uses
  * the same register settings as the OV8610, since they are very similar.
  */
@@ -1966,12 +2951,29 @@ static int ov519_configure(struct sd *sd)
        return write_regvals(sd, init_519, ARRAY_SIZE(init_519));
 }
 
+static int ovfx2_configure(struct sd *sd)
+{
+       static const struct ov_regvals init_fx2[] = {
+               { 0x00, 0x60 },
+               { 0x02, 0x01 },
+               { 0x0f, 0x1d },
+               { 0xe9, 0x82 },
+               { 0xea, 0xc7 },
+               { 0xeb, 0x10 },
+               { 0xec, 0xf6 },
+       };
+
+       sd->stopped = 1;
+
+       return write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
+}
+
 /* this function is called at probe time */
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct cam *cam;
+       struct cam *cam = &gspca_dev->cam;
        int ret = 0;
 
        sd->bridge = id->driver_info & BRIDGE_MASK;
@@ -1989,6 +2991,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
        case BRIDGE_OV519:
                ret = ov519_configure(sd);
                break;
+       case BRIDGE_OVFX2:
+               ret = ovfx2_configure(sd);
+               cam->bulk_size = OVFX2_BULK_SIZE;
+               cam->bulk_nurbs = MAX_NURBS;
+               cam->bulk = 1;
+               break;
+       case BRIDGE_W9968CF:
+               ret = w9968cf_configure(sd);
+               cam->reverse_alts = 1;
+               break;
        }
 
        if (ret)
@@ -1996,49 +3008,39 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        ov51x_led_control(sd, 0);       /* turn LED off */
 
-       /* Test for 76xx */
-       if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0)
-               goto error;
-
        /* The OV519 must be more aggressive about sensor detection since
         * I2C write will never fail if the sensor is not present. We have
         * to try to initialize the sensor to detect its presence */
-       if (init_ov_sensor(sd) >= 0) {
+
+       /* Test for 76xx */
+       if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
                if (ov7xx0_configure(sd) < 0) {
                        PDEBUG(D_ERR, "Failed to configure OV7xx0");
                        goto error;
                }
-       } else {
-
-               /* Test for 6xx0 */
-               if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0)
+       /* Test for 6xx0 */
+       } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
+               if (ov6xx0_configure(sd) < 0) {
+                       PDEBUG(D_ERR, "Failed to configure OV6xx0");
+                       goto error;
+               }
+       /* Test for 8xx0 */
+       } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
+               if (ov8xx0_configure(sd) < 0) {
+                       PDEBUG(D_ERR, "Failed to configure OV8xx0");
                        goto error;
-
-               if (init_ov_sensor(sd) >= 0) {
-                       if (ov6xx0_configure(sd) < 0) {
-                               PDEBUG(D_ERR, "Failed to configure OV6xx0");
-                               goto error;
-                       }
-               } else {
-
-                       /* Test for 8xx0 */
-                       if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0)
-                               goto error;
-
-                       if (init_ov_sensor(sd) < 0) {
-                               PDEBUG(D_ERR,
-                                       "Can't determine sensor slave IDs");
-                               goto error;
-                       }
-                       if (ov8xx0_configure(sd) < 0) {
-                               PDEBUG(D_ERR,
-                                       "Failed to configure OV8xx0 sensor");
-                               goto error;
-                       }
                }
+       /* Test for 3xxx / 2xxx */
+       } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
+               if (ov_hires_configure(sd) < 0) {
+                       PDEBUG(D_ERR, "Failed to configure high res OV");
+                       goto error;
+               }
+       } else {
+               PDEBUG(D_ERR, "Can't determine sensor slave IDs");
+               goto error;
        }
 
-       cam = &gspca_dev->cam;
        switch (sd->bridge) {
        case BRIDGE_OV511:
        case BRIDGE_OV511PLUS:
@@ -2069,6 +3071,31 @@ static int sd_config(struct gspca_dev *gspca_dev,
                        cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
                }
                break;
+       case BRIDGE_OVFX2:
+               if (sd->sensor == SEN_OV2610) {
+                       cam->cam_mode = ovfx2_ov2610_mode;
+                       cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
+               } else if (sd->sensor == SEN_OV3610) {
+                       cam->cam_mode = ovfx2_ov3610_mode;
+                       cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
+               } else if (!sd->sif) {
+                       cam->cam_mode = ov519_vga_mode;
+                       cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+               } else {
+                       cam->cam_mode = ov519_sif_mode;
+                       cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+               }
+               break;
+       case BRIDGE_W9968CF:
+               cam->cam_mode = w9968cf_vga_mode;
+               cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
+               if (sd->sif)
+                       cam->nmodes--;
+
+               /* w9968cf needs initialisation once the sensor is known */
+               if (w9968cf_init(sd) < 0)
+                       goto error;
+               break;
        }
        sd->brightness = BRIGHTNESS_DEF;
        if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF)
@@ -2087,11 +3114,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
                gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) |
                                      (1 << OV7670_FREQ_IDX);
        }
+       sd->quality = QUALITY_DEF;
        if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
                gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX;
        /* OV8610 Frequency filter control should work but needs testing */
        if (sd->sensor == SEN_OV8610)
                gspca_dev->ctrl_dis |= 1 << FREQ_IDX;
+       /* No controls for the OV2610/OV3610 */
+       if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
+               gspca_dev->ctrl_dis |= 0xFF;
 
        return 0;
 error:
@@ -2106,6 +3137,20 @@ static int sd_init(struct gspca_dev *gspca_dev)
 
        /* initialize the sensor */
        switch (sd->sensor) {
+       case SEN_OV2610:
+               if (write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610)))
+                       return -EIO;
+               /* Enable autogain, autoexpo, awb, bandfilter */
+               if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0)
+                       return -EIO;
+               break;
+       case SEN_OV3610:
+               if (write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b)))
+                       return -EIO;
+               /* Enable autogain, autoexpo, awb, bandfilter */
+               if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0)
+                       return -EIO;
+               break;
        case SEN_OV6620:
                if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20)))
                        return -EIO;
@@ -2548,19 +3593,60 @@ static int ov519_mode_init_regs(struct sd *sd)
 static int mode_init_ov_sensor_regs(struct sd *sd)
 {
        struct gspca_dev *gspca_dev;
-       int qvga;
+       int qvga, xstart, xend, ystart, yend;
+       __u8 v;
 
        gspca_dev = &sd->gspca_dev;
        qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1;
 
        /******** Mode (VGA/QVGA) and sensor specific regs ********/
        switch (sd->sensor) {
+       case SEN_OV2610:
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+               i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+               i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+               i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+               i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+               i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+               return 0;
+       case SEN_OV3610:
+               if (qvga) {
+                       xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4);
+                       ystart = (776 - gspca_dev->height) / 2;
+               } else {
+                       xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
+                       ystart = (1544 - gspca_dev->height) / 2;
+               }
+               xend = xstart + gspca_dev->width;
+               yend = ystart + gspca_dev->height;
+               /* Writing to the COMH register resets the other windowing regs
+                  to their default values, so we must do this first. */
+               i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
+               i2c_w_mask(sd, 0x32,
+                          (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
+                          0x3f);
+               i2c_w_mask(sd, 0x03,
+                          (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
+                          0x0f);
+               i2c_w(sd, 0x17, xstart >> 4);
+               i2c_w(sd, 0x18, xend >> 4);
+               i2c_w(sd, 0x19, ystart >> 3);
+               i2c_w(sd, 0x1a, yend >> 3);
+               return 0;
        case SEN_OV8610:
                /* For OV8610 qvga means qsvga */
                i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
+               i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+               i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+               i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
+               i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
                break;
        case SEN_OV7610:
                i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w(sd, 0x35, qvga?0x1e:0x9e);
+               i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+               i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
                break;
        case SEN_OV7620:
        case SEN_OV76BE:
@@ -2571,6 +3657,10 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
                i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
                i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0);
                i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+               i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+               if (sd->sensor == SEN_OV76BE)
+                       i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
                break;
        case SEN_OV7640:
                i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
@@ -2580,6 +3670,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
 /*             i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */
 /*             i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */
 /*             i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */
+               i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
                break;
        case SEN_OV7670:
                /* set COM7_FMT_VGA or COM7_FMT_QVGA
@@ -2588,55 +3679,56 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
                i2c_w_mask(sd, OV7670_REG_COM7,
                         qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
                         OV7670_COM7_FMT_MASK);
+               i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+               i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
+                               OV7670_COM8_AWB);
+               if (qvga) {             /* QVGA from ov7670.c by
+                                        * Jonathan Corbet */
+                       xstart = 164;
+                       xend = 28;
+                       ystart = 14;
+                       yend = 494;
+               } else {                /* VGA */
+                       xstart = 158;
+                       xend = 14;
+                       ystart = 10;
+                       yend = 490;
+               }
+               /* OV7670 hardware window registers are split across
+                * multiple locations */
+               i2c_w(sd, OV7670_REG_HSTART, xstart >> 3);
+               i2c_w(sd, OV7670_REG_HSTOP, xend >> 3);
+               v = i2c_r(sd, OV7670_REG_HREF);
+               v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
+               msleep(10);     /* need to sleep between read and write to
+                                * same reg! */
+               i2c_w(sd, OV7670_REG_HREF, v);
+
+               i2c_w(sd, OV7670_REG_VSTART, ystart >> 2);
+               i2c_w(sd, OV7670_REG_VSTOP, yend >> 2);
+               v = i2c_r(sd, OV7670_REG_VREF);
+               v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
+               msleep(10);     /* need to sleep between read and write to
+                                * same reg! */
+               i2c_w(sd, OV7670_REG_VREF, v);
                break;
        case SEN_OV6620:
+               i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+               i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+               break;
        case SEN_OV6630:
        case SEN_OV66308AF:
                i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+               i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
                break;
        default:
                return -EINVAL;
        }
 
-       /******** Palette-specific regs ********/
-
-       /* The OV518 needs special treatment. Although both the OV518
-        * and the OV6630 support a 16-bit video bus, only the 8 bit Y
-        * bus is actually used. The UV bus is tied to ground.
-        * Therefore, the OV6630 needs to be in 8-bit multiplexed
-        * output mode */
-
-       /* OV7640 is 8-bit only */
-
-       if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF &&
-                                       sd->sensor != SEN_OV7640)
-               i2c_w_mask(sd, 0x13, 0x00, 0x20);
-
        /******** Clock programming ********/
        i2c_w(sd, 0x11, sd->clockdiv);
 
-       /******** Special Features ********/
-/* no evidence this is possible with OV7670, either */
-       /* Test Pattern */
-       if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670)
-               i2c_w_mask(sd, 0x12, 0x00, 0x02);
-
-       /* Enable auto white balance */
-       if (sd->sensor == SEN_OV7670)
-               i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
-                               OV7670_COM8_AWB);
-       else
-               i2c_w_mask(sd, 0x12, 0x04, 0x04);
-
-       /* This will go away as soon as ov51x_mode_init_sensor_regs() */
-       /* is fully tested. */
-       /* 7620/6620/6630? don't have register 0x35, so play it safe */
-       if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
-               if (!qvga)
-                       i2c_w(sd, 0x35, 0x9e);
-               else
-                       i2c_w(sd, 0x35, 0x1e);
-       }
        return 0;
 }
 
@@ -2659,8 +3751,12 @@ static int set_ov_sensor_window(struct sd *sd)
        struct gspca_dev *gspca_dev;
        int qvga, crop;
        int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
-       int ret, hstart, hstop, vstop, vstart;
-       __u8 v;
+       int ret;
+
+       /* mode setup is fully handled in mode_init_ov_sensor_regs for these */
+       if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 ||
+           sd->sensor == SEN_OV7670)
+               return mode_init_ov_sensor_regs(sd);
 
        gspca_dev = &sd->gspca_dev;
        qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1;
@@ -2708,11 +3804,6 @@ static int set_ov_sensor_window(struct sd *sd)
                hwebase = 0x1a;
                vwsbase = vwebase = 0x03;
                break;
-       case SEN_OV7670:
-               /*handling of OV7670 hardware sensor start and stop values
-                * is very odd, compared to the other OV sensors */
-               vwsbase = vwebase = hwebase = hwsbase = 0x00;
-               break;
        default:
                return -EINVAL;
        }
@@ -2753,58 +3844,11 @@ static int set_ov_sensor_window(struct sd *sd)
        if (ret < 0)
                return ret;
 
-       if (sd->sensor == SEN_OV8610) {
-               i2c_w_mask(sd, 0x2d, 0x05, 0x40);
-                               /* old 0x95, new 0x05 from windrv 090403 */
-                                               /* bits 5-7: reserved */
-               i2c_w_mask(sd, 0x28, 0x20, 0x20);
-                                       /* bit 5: progressive mode on */
-       }
-
-       /* The below is wrong for OV7670s because their window registers
-        * only store the high bits in 0x17 to 0x1a */
-
-       /* SRH Use sd->max values instead of requested win values */
-       /* SCS Since we're sticking with only the max hardware widths
-        * for a given mode */
-       /* I can hard code this for OV7670s */
-       /* Yes, these numbers do look odd, but they're tested and work! */
-       if (sd->sensor == SEN_OV7670) {
-               if (qvga) {             /* QVGA from ov7670.c by
-                                        * Jonathan Corbet */
-                       hstart = 164;
-                       hstop = 28;
-                       vstart = 14;
-                       vstop = 494;
-               } else {                /* VGA */
-                       hstart = 158;
-                       hstop = 14;
-                       vstart = 10;
-                       vstop = 490;
-               }
-               /* OV7670 hardware window registers are split across
-                * multiple locations */
-               i2c_w(sd, OV7670_REG_HSTART, hstart >> 3);
-               i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3);
-               v = i2c_r(sd, OV7670_REG_HREF);
-               v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07);
-               msleep(10);     /* need to sleep between read and write to
-                                * same reg! */
-               i2c_w(sd, OV7670_REG_HREF, v);
+       i2c_w(sd, 0x17, hwsbase);
+       i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
+       i2c_w(sd, 0x19, vwsbase);
+       i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
 
-               i2c_w(sd, OV7670_REG_VSTART, vstart >> 2);
-               i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2);
-               v = i2c_r(sd, OV7670_REG_VREF);
-               v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03);
-               msleep(10);     /* need to sleep between read and write to
-                                * same reg! */
-               i2c_w(sd, OV7670_REG_VREF, v);
-       } else {
-               i2c_w(sd, 0x17, hwsbase);
-               i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale));
-               i2c_w(sd, 0x19, vwsbase);
-               i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale));
-       }
        return 0;
 }
 
@@ -2814,6 +3858,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
        struct sd *sd = (struct sd *) gspca_dev;
        int ret = 0;
 
+       /* Default for most bridges, allow bridge_mode_init_regs to override */
+       sd->sensor_width = sd->gspca_dev.width;
+       sd->sensor_height = sd->gspca_dev.height;
+
        switch (sd->bridge) {
        case BRIDGE_OV511:
        case BRIDGE_OV511PLUS:
@@ -2826,6 +3874,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case BRIDGE_OV519:
                ret = ov519_mode_init_regs(sd);
                break;
+       /* case BRIDGE_OVFX2: nothing to do */
+       case BRIDGE_W9968CF:
+               ret = w9968cf_mode_init_regs(sd);
+               break;
        }
        if (ret < 0)
                goto out;
@@ -2859,10 +3911,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
        ov51x_led_control(sd, 0);
 }
 
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->bridge == BRIDGE_W9968CF)
+               w9968cf_stop0(sd);
+}
+
 static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *in,                       /* isoc packet */
-                       int len)                        /* iso packet length */
+                       u8 *in,                 /* isoc packet */
+                       int len)                /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
@@ -2893,11 +3952,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
                                return;
                        }
                        /* Add 11 byte footer to frame, might be usefull */
-                       gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11);
+                       gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
                        return;
                } else {
                        /* Frame start */
-                       gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0);
+                       gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
                        sd->packet_nr = 0;
                }
        }
@@ -2906,12 +3965,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
        len--;
 
        /* intermediate packet */
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
 }
 
 static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -2919,8 +3977,8 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
        /* A false positive here is likely, until OVT gives me
         * the definitive SOF/EOF format */
        if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
                sd->packet_nr = 0;
        }
 
@@ -2944,12 +4002,11 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
        }
 
        /* intermediate packet */
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        /* Header of ov519 is 16 bytes:
@@ -2972,7 +4029,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
                        len -= HDRSZ;
 #undef HDRSZ
                        if (data[0] == 0xff || data[1] == 0xd8)
-                               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                               gspca_frame_add(gspca_dev, FIRST_PACKET,
                                                data, len);
                        else
                                gspca_dev->last_packet_type = DISCARD_PACKET;
@@ -2980,20 +4037,31 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
                case 0x51:              /* end of frame */
                        if (data[9] != 0)
                                gspca_dev->last_packet_type = DISCARD_PACKET;
-                       gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
+                                       NULL, 0);
                        return;
                }
        }
 
        /* intermediate packet */
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                       data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data,                       /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       /* A short read signals EOF */
+       if (len < OVFX2_BULK_SIZE) {
+               gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+               return;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -3001,14 +4069,20 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        switch (sd->bridge) {
        case BRIDGE_OV511:
        case BRIDGE_OV511PLUS:
-               ov511_pkt_scan(gspca_dev, frame, data, len);
+               ov511_pkt_scan(gspca_dev, data, len);
                break;
        case BRIDGE_OV518:
        case BRIDGE_OV518PLUS:
-               ov518_pkt_scan(gspca_dev, frame, data, len);
+               ov518_pkt_scan(gspca_dev, data, len);
                break;
        case BRIDGE_OV519:
-               ov519_pkt_scan(gspca_dev, frame, data, len);
+               ov519_pkt_scan(gspca_dev, data, len);
+               break;
+       case BRIDGE_OVFX2:
+               ovfx2_pkt_scan(gspca_dev, data, len);
+               break;
+       case BRIDGE_W9968CF:
+               w9968cf_pkt_scan(gspca_dev, data, len);
                break;
        }
 }
@@ -3124,7 +4198,8 @@ static void setcolors(struct gspca_dev *gspca_dev)
 
 static void setautobrightness(struct sd *sd)
 {
-       if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
+       if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670 ||
+           sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
                return;
 
        i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10);
@@ -3132,6 +4207,9 @@ static void setautobrightness(struct sd *sd)
 
 static void setfreq(struct sd *sd)
 {
+       if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
+               return;
+
        if (sd->sensor == SEN_OV7670) {
                switch (sd->freq) {
                case 0: /* Banding filter disabled */
@@ -3301,8 +4379,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        sd->freq = val;
-       if (gspca_dev->streaming)
+       if (gspca_dev->streaming) {
                setfreq(sd);
+               /* Ugly but necessary */
+               if (sd->bridge == BRIDGE_W9968CF)
+                       w9968cf_set_crop_window(sd);
+       }
        return 0;
 }
 
@@ -3343,6 +4425,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
        return -EINVAL;
 }
 
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+                       struct v4l2_jpegcompression *jcomp)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->bridge != BRIDGE_W9968CF)
+               return -EINVAL;
+
+       memset(jcomp, 0, sizeof *jcomp);
+       jcomp->quality = sd->quality;
+       jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
+                             V4L2_JPEG_MARKER_DRI;
+       return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+                       struct v4l2_jpegcompression *jcomp)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->bridge != BRIDGE_W9968CF)
+               return -EINVAL;
+
+       if (gspca_dev->streaming)
+               return -EBUSY;
+
+       if (jcomp->quality < QUALITY_MIN)
+               sd->quality = QUALITY_MIN;
+       else if (jcomp->quality > QUALITY_MAX)
+               sd->quality = QUALITY_MAX;
+       else
+               sd->quality = jcomp->quality;
+
+       /* Return resulting jcomp params to app */
+       sd_get_jcomp(gspca_dev, jcomp);
+
+       return 0;
+}
+
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name = MODULE_NAME,
@@ -3352,18 +4473,23 @@ static const struct sd_desc sd_desc = {
        .init = sd_init,
        .start = sd_start,
        .stopN = sd_stopN,
+       .stop0 = sd_stop0,
        .pkt_scan = sd_pkt_scan,
        .querymenu = sd_querymenu,
+       .get_jcomp = sd_get_jcomp,
+       .set_jcomp = sd_set_jcomp,
 };
 
 /* -- module initialisation -- */
 static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
        {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x041e, 0x4064),
         .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+       {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x041e, 0x4068),
         .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
        {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 },
@@ -3373,11 +4499,16 @@ static const __devinitdata struct usb_device_id device_table[] = {
        {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
        {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 },
+       {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
        {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
        {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
        {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
        {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
+       {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
+       {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
+       {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
+       {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 },
        {}
 };
 
index 4b528b3..4dbb882 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * ov534 gspca driver
+ *
  * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
  * Copyright (C) 2008 Jim Paris <jim@jtan.com>
  * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr
@@ -8,6 +9,10 @@
  * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
  * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
  *
+ * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr
+ * PS3 Eye camera, brightness, contrast, hue, AWB control added
+ *     by Max Thrun <bear24rw@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
@@ -51,16 +56,335 @@ struct sd {
        u16 last_fid;
        u8 frame_rate;
 
+       u8 brightness;
+       u8 contrast;
+       u8 gain;
+       u8 exposure;
+       u8 redblc;
+       u8 blueblc;
+       u8 hue;
+       u8 autogain;
+       u8 awb;
+       s8 sharpness;
+       u8 hflip;
+       u8 vflip;
+       u8 satur;
+       u8 lightfreq;
+
        u8 sensor;
 #define SENSOR_OV772X 0
 #define SENSOR_OV965X 1
 };
 
 /* V4L2 controls supported by the driver */
-static struct ctrl sd_ctrls[] = {
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls_ov772x[] = {
+    {                                                  /* 0 */
+       {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define BRIGHTNESS_77_DEF 20
+               .default_value = BRIGHTNESS_77_DEF,
+       },
+       .set = sd_setbrightness,
+       .get = sd_getbrightness,
+    },
+    {                                                  /* 1 */
+       {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define CONTRAST_77_DEF 37
+               .default_value = CONTRAST_77_DEF,
+       },
+       .set = sd_setcontrast,
+       .get = sd_getcontrast,
+    },
+    {                                                  /* 2 */
+       {
+           .id      = V4L2_CID_GAIN,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Main Gain",
+           .minimum = 0,
+           .maximum = 63,
+           .step    = 1,
+#define GAIN_DEF 20
+           .default_value = GAIN_DEF,
+       },
+       .set = sd_setgain,
+       .get = sd_getgain,
+    },
+    {                                                  /* 3 */
+       {
+           .id      = V4L2_CID_EXPOSURE,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Exposure",
+           .minimum = 0,
+           .maximum = 255,
+           .step    = 1,
+#define EXPO_77_DEF 120
+           .default_value = EXPO_77_DEF,
+       },
+       .set = sd_setexposure,
+       .get = sd_getexposure,
+    },
+    {                                                  /* 4 */
+       {
+           .id      = V4L2_CID_RED_BALANCE,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Red Balance",
+           .minimum = 0,
+           .maximum = 255,
+           .step    = 1,
+#define RED_BALANCE_DEF 128
+           .default_value = RED_BALANCE_DEF,
+       },
+       .set = sd_setredblc,
+       .get = sd_getredblc,
+    },
+    {                                                  /* 5 */
+       {
+           .id      = V4L2_CID_BLUE_BALANCE,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Blue Balance",
+           .minimum = 0,
+           .maximum = 255,
+           .step    = 1,
+#define BLUE_BALANCE_DEF 128
+           .default_value = BLUE_BALANCE_DEF,
+       },
+       .set = sd_setblueblc,
+       .get = sd_getblueblc,
+    },
+    {                                                  /* 6 */
+       {
+               .id      = V4L2_CID_HUE,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Hue",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define HUE_DEF 143
+               .default_value = HUE_DEF,
+       },
+       .set = sd_sethue,
+       .get = sd_gethue,
+    },
+    {                                                  /* 7 */
+       {
+           .id      = V4L2_CID_AUTOGAIN,
+           .type    = V4L2_CTRL_TYPE_BOOLEAN,
+           .name    = "Autogain",
+           .minimum = 0,
+           .maximum = 1,
+           .step    = 1,
+#define AUTOGAIN_77_DEF 0
+           .default_value = AUTOGAIN_77_DEF,
+       },
+       .set = sd_setautogain,
+       .get = sd_getautogain,
+    },
+#define AWB_77_IDX 8
+    {                                                  /* 8 */
+       {
+               .id      = V4L2_CID_AUTO_WHITE_BALANCE,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto White Balance",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define AWB_DEF 0
+               .default_value = AWB_DEF,
+       },
+       .set = sd_setawb,
+       .get = sd_getawb,
+    },
+    {                                                  /* 9 */
+       {
+           .id      = V4L2_CID_SHARPNESS,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Sharpness",
+           .minimum = 0,
+           .maximum = 63,
+           .step    = 1,
+#define SHARPNESS_77_DEF 0
+           .default_value = SHARPNESS_77_DEF,
+       },
+       .set = sd_setsharpness,
+       .get = sd_getsharpness,
+    },
+    {                                                  /* 10 */
+       {
+           .id      = V4L2_CID_HFLIP,
+           .type    = V4L2_CTRL_TYPE_BOOLEAN,
+           .name    = "HFlip",
+           .minimum = 0,
+           .maximum = 1,
+           .step    = 1,
+#define HFLIP_DEF 0
+           .default_value = HFLIP_DEF,
+       },
+       .set = sd_sethflip,
+       .get = sd_gethflip,
+    },
+    {                                                  /* 11 */
+       {
+           .id      = V4L2_CID_VFLIP,
+           .type    = V4L2_CTRL_TYPE_BOOLEAN,
+           .name    = "VFlip",
+           .minimum = 0,
+           .maximum = 1,
+           .step    = 1,
+#define VFLIP_DEF 0
+           .default_value = VFLIP_DEF,
+       },
+       .set = sd_setvflip,
+       .get = sd_getvflip,
+    },
+};
+static struct ctrl sd_ctrls_ov965x[] = {
+    {                                                  /* 0 */
+       {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+               .maximum = 15,
+               .step    = 1,
+#define BRIGHTNESS_96_DEF 7
+               .default_value = BRIGHTNESS_96_DEF,
+       },
+       .set = sd_setbrightness,
+       .get = sd_getbrightness,
+    },
+    {                                                  /* 1 */
+       {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+               .maximum = 15,
+               .step    = 1,
+#define CONTRAST_96_DEF 3
+               .default_value = CONTRAST_96_DEF,
+       },
+       .set = sd_setcontrast,
+       .get = sd_getcontrast,
+    },
+    {                                                  /* 2 */
+       {
+           .id      = V4L2_CID_AUTOGAIN,
+           .type    = V4L2_CTRL_TYPE_BOOLEAN,
+           .name    = "Autogain",
+           .minimum = 0,
+           .maximum = 1,
+           .step    = 1,
+#define AUTOGAIN_96_DEF 1
+           .default_value = AUTOGAIN_96_DEF,
+       },
+       .set = sd_setautogain,
+       .get = sd_getautogain,
+    },
+#define EXPO_96_IDX 3
+    {                                                  /* 3 */
+       {
+           .id      = V4L2_CID_EXPOSURE,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Exposure",
+           .minimum = 0,
+           .maximum = 3,
+           .step    = 1,
+#define EXPO_96_DEF 0
+           .default_value = EXPO_96_DEF,
+       },
+       .set = sd_setexposure,
+       .get = sd_getexposure,
+    },
+    {                                                  /* 4 */
+       {
+           .id      = V4L2_CID_SHARPNESS,
+           .type    = V4L2_CTRL_TYPE_INTEGER,
+           .name    = "Sharpness",
+           .minimum = -1,              /* -1 = auto */
+           .maximum = 4,
+           .step    = 1,
+#define SHARPNESS_96_DEF -1
+           .default_value = SHARPNESS_96_DEF,
+       },
+       .set = sd_setsharpness,
+       .get = sd_getsharpness,
+    },
+    {                                                  /* 5 */
+       {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Saturation",
+               .minimum = 0,
+               .maximum = 4,
+               .step    = 1,
+#define SATUR_DEF 2
+               .default_value = SATUR_DEF,
+       },
+       .set = sd_setsatur,
+       .get = sd_getsatur,
+    },
+    {
+       {
+               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
+               .type    = V4L2_CTRL_TYPE_MENU,
+               .name    = "Light frequency filter",
+               .minimum = 0,
+               .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
+               .step    = 1,
+#define FREQ_DEF 0
+               .default_value = FREQ_DEF,
+       },
+       .set = sd_setfreq,
+       .get = sd_getfreq,
+    },
 };
 
-static const struct v4l2_pix_format vga_yuyv_mode[] = {
+static const struct v4l2_pix_format ov772x_mode[] = {
+       {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+        .bytesperline = 320 * 2,
+        .sizeimage = 320 * 240 * 2,
+        .colorspace = V4L2_COLORSPACE_SRGB,
+        .priv = 1},
        {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
         .bytesperline = 640 * 2,
         .sizeimage = 640 * 480 * 2,
@@ -68,20 +392,35 @@ static const struct v4l2_pix_format vga_yuyv_mode[] = {
         .priv = 0},
 };
 
-static const struct v4l2_pix_format vga_jpeg_mode[] = {
+static const struct v4l2_pix_format ov965x_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
         .bytesperline = 320,
         .sizeimage = 320 * 240 * 3 / 8 + 590,
         .colorspace = V4L2_COLORSPACE_JPEG,
-        .priv = 1},
+        .priv = 4},
        {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
         .bytesperline = 640,
         .sizeimage = 640 * 480 * 3 / 8 + 590,
         .colorspace = V4L2_COLORSPACE_JPEG,
+        .priv = 3},
+       {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+        .bytesperline = 800,
+        .sizeimage = 800 * 600 * 3 / 8 + 590,
+        .colorspace = V4L2_COLORSPACE_JPEG,
+        .priv = 2},
+       {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+        .bytesperline = 1024,
+        .sizeimage = 1024 * 768 * 3 / 8 + 590,
+        .colorspace = V4L2_COLORSPACE_JPEG,
+        .priv = 1},
+       {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+        .bytesperline = 1280,
+        .sizeimage = 1280 * 1024 * 3 / 8 + 590,
+        .colorspace = V4L2_COLORSPACE_JPEG,
         .priv = 0},
 };
 
-static const u8 bridge_init_ov722x[][2] = {
+static const u8 bridge_init_ov772x[][2] = {
        { 0xc2, 0x0c },
        { 0x88, 0xf8 },
        { 0xc3, 0x69 },
@@ -122,6 +461,7 @@ static const u8 bridge_init_ov722x[][2] = {
        { 0x1d, 0x40 },
        { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
        { 0x1d, 0x00 }, /* payload size */
+
        { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
        { 0x1d, 0x58 }, /* frame size */
        { 0x1d, 0x00 }, /* frame size */
@@ -138,10 +478,20 @@ static const u8 bridge_init_ov722x[][2] = {
        { 0xc1, 0x3c },
        { 0xc2, 0x0c },
 };
-
-static const u8 sensor_init_ov722x[][2] = {
+static const u8 sensor_init_ov772x[][2] = {
        { 0x12, 0x80 },
        { 0x11, 0x01 },
+/*fixme: better have a delay?*/
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
+       { 0x11, 0x01 },
 
        { 0x3d, 0x03 },
        { 0x17, 0x26 },
@@ -154,10 +504,10 @@ static const u8 sensor_init_ov722x[][2] = {
        { 0x65, 0x20 },
        { 0x11, 0x01 },
        { 0x42, 0x7f },
-       { 0x63, 0xe0 },
+       { 0x63, 0xaa },         /* AWB - was e0 */
        { 0x64, 0xff },
        { 0x66, 0x00 },
-       { 0x13, 0xf0 },
+       { 0x13, 0xf0 },         /* com8 */
        { 0x0d, 0x41 },
        { 0x0f, 0xc5 },
        { 0x14, 0x11 },
@@ -170,7 +520,7 @@ static const u8 sensor_init_ov722x[][2] = {
        { 0x2a, 0x00 },
        { 0x2b, 0x00 },
        { 0x6b, 0xaa },
-       { 0x13, 0xff },
+       { 0x13, 0xff },         /* AWB */
 
        { 0x90, 0x05 },
        { 0x91, 0x01 },
@@ -218,9 +568,51 @@ static const u8 sensor_init_ov722x[][2] = {
        { 0x14, 0x41 },
        { 0x0e, 0xcd },
        { 0xac, 0xbf },
-       { 0x8e, 0x00 },
+       { 0x8e, 0x00 },         /* De-noise threshold */
        { 0x0c, 0xd0 }
 };
+static const u8 bridge_start_ov772x_vga[][2] = {
+       {0x1c, 0x00},
+       {0x1d, 0x40},
+       {0x1d, 0x02},
+       {0x1d, 0x00},
+       {0x1d, 0x02},
+       {0x1d, 0x58},
+       {0x1d, 0x00},
+       {0xc0, 0x50},
+       {0xc1, 0x3c},
+};
+static const u8 sensor_start_ov772x_vga[][2] = {
+       {0x12, 0x00},
+       {0x17, 0x26},
+       {0x18, 0xa0},
+       {0x19, 0x07},
+       {0x1a, 0xf0},
+       {0x29, 0xa0},
+       {0x2c, 0xf0},
+       {0x65, 0x20},
+};
+static const u8 bridge_start_ov772x_qvga[][2] = {
+       {0x1c, 0x00},
+       {0x1d, 0x40},
+       {0x1d, 0x02},
+       {0x1d, 0x00},
+       {0x1d, 0x01},
+       {0x1d, 0x4b},
+       {0x1d, 0x00},
+       {0xc0, 0x28},
+       {0xc1, 0x1e},
+};
+static const u8 sensor_start_ov772x_qvga[][2] = {
+       {0x12, 0x40},
+       {0x17, 0x3f},
+       {0x18, 0x50},
+       {0x19, 0x03},
+       {0x1a, 0x78},
+       {0x29, 0x50},
+       {0x2c, 0x78},
+       {0x65, 0x2f},
+};
 
 static const u8 bridge_init_ov965x[][2] = {
        {0x88, 0xf8},
@@ -403,7 +795,7 @@ static const u8 sensor_init_ov965x[][2] = {
        {0xcb, 0xf0},
        {0xcc, 0xd8},
        {0xcd, 0xf1},
-       {0x4f, 0x98},
+       {0x4f, 0x98},   /* matrix */
        {0x50, 0x98},
        {0x51, 0x00},
        {0x52, 0x28},
@@ -412,6 +804,7 @@ static const u8 sensor_init_ov965x[][2] = {
        {0x58, 0x1a},
        {0xff, 0x41},   /* read 41, write ff 00 */
        {0x41, 0x40},   /* com16 */
+
        {0xc5, 0x03},   /* 60 Hz banding filter */
        {0x6a, 0x02},   /* 50 Hz banding filter */
 
@@ -455,8 +848,8 @@ static const u8 bridge_init_ov965x_2[][2] = {
        {0x52, 0x3c},
        {0x53, 0x00},
        {0x54, 0x00},
-       {0x55, 0x00},   /* brightness */
-       {0x57, 0x00},   /* contrast 2 */
+       {0x55, 0x00},
+       {0x57, 0x00},
        {0x5c, 0x00},
        {0x5a, 0xa0},
        {0x5b, 0x78},
@@ -479,14 +872,16 @@ static const u8 sensor_init_ov965x_2[][2] = {
        {0xa3, 0x3e},
        {0x2d, 0x00},
        {0xff, 0x42},   /* read 42, write ff 00 */
-       {0x42, 0xc0},
+       {0x42, 0xc0},   /* com17 */
        {0x2d, 0x00},
        {0xff, 0x42},   /* read 42, write ff 00 */
-       {0x42, 0xc1},
+       {0x42, 0xc1},   /* com17 */
+/* sharpness */
        {0x3f, 0x01},
        {0xff, 0x42},   /* read 42, write ff 00 */
-       {0x42, 0xc1},
-       {0x4f, 0x98},
+       {0x42, 0xc1},   /* com17 */
+/* saturation */
+       {0x4f, 0x98},   /* matrix */
        {0x50, 0x98},
        {0x51, 0x00},
        {0x52, 0x28},
@@ -495,14 +890,17 @@ static const u8 sensor_init_ov965x_2[][2] = {
        {0x58, 0x1a},
        {0xff, 0x41},   /* read 41, write ff 00 */
        {0x41, 0x40},   /* com16 */
+/* contrast */
        {0x56, 0x40},
+/* brightness */
        {0x55, 0x8f},
+/* expo */
        {0x10, 0x25},   /* aech - exposure high bits */
        {0xff, 0x13},   /* read 13, write ff 00 */
        {0x13, 0xe7},   /* com8 - everything (AGC, AWB and AEC) */
 };
 
-static const u8 sensor_start_ov965x[][2] = {
+static const u8 sensor_start_ov965x_1_vga[][2] = {     /* same for qvga */
        {0x12, 0x62},   /* com7 - 30fps VGA YUV */
        {0x36, 0xfa},   /* aref3 */
        {0x69, 0x0a},   /* hv */
@@ -523,10 +921,77 @@ static const u8 sensor_start_ov965x[][2] = {
        {0x1a, 0x3d},   /* vstop */
        {0x32, 0xff},   /* href */
        {0xc0, 0xaa},
-       {}
 };
 
-static const u8 bridge_start_ov965x[][2] = {
+static const u8 sensor_start_ov965x_1_svga[][2] = {
+       {0x12, 0x02},   /* com7 - YUYV - VGA 15 full resolution */
+       {0x36, 0xf8},   /* aref3 */
+       {0x69, 0x02},   /* hv */
+       {0x8c, 0x0d},   /* com22 */
+       {0x3e, 0x0c},   /* com14 */
+       {0x41, 0x40},   /* com16 */
+       {0x72, 0x00},
+       {0x73, 0x01},
+       {0x74, 0x3a},
+       {0x75, 0x35},
+       {0x76, 0x01},
+       {0xc7, 0x80},   /* com24 */
+       {0x03, 0x1b},   /* vref */
+       {0x17, 0x1d},   /* hstart */
+       {0x18, 0xbd},   /* hstop */
+       {0x19, 0x01},   /* vstrt */
+       {0x1a, 0x81},   /* vstop */
+       {0x32, 0xff},   /* href */
+       {0xc0, 0xe2},
+};
+
+static const u8 sensor_start_ov965x_1_xga[][2] = {
+       {0x12, 0x02},   /* com7 */
+       {0x36, 0xf8},   /* aref3 */
+       {0x69, 0x02},   /* hv */
+       {0x8c, 0x89},   /* com22 */
+       {0x14, 0x28},   /* com9 */
+       {0x3e, 0x0c},   /* com14 */
+       {0x41, 0x40},   /* com16 */
+       {0x72, 0x00},
+       {0x73, 0x01},
+       {0x74, 0x3a},
+       {0x75, 0x35},
+       {0x76, 0x01},
+       {0xc7, 0x80},   /* com24 */
+       {0x03, 0x1b},   /* vref */
+       {0x17, 0x1d},   /* hstart */
+       {0x18, 0xbd},   /* hstop */
+       {0x19, 0x01},   /* vstrt */
+       {0x1a, 0x81},   /* vstop */
+       {0x32, 0xff},   /* href */
+       {0xc0, 0xe2},
+};
+
+static const u8 sensor_start_ov965x_1_sxga[][2] = {
+       {0x12, 0x02},   /* com7 */
+       {0x36, 0xf8},   /* aref3 */
+       {0x69, 0x02},   /* hv */
+       {0x8c, 0x89},   /* com22 */
+       {0x14, 0x28},   /* com9 */
+       {0x3e, 0x0c},   /* com14 */
+       {0x41, 0x40},   /* com16 */
+       {0x72, 0x00},
+       {0x73, 0x01},
+       {0x74, 0x3a},
+       {0x75, 0x35},
+       {0x76, 0x01},
+       {0xc7, 0x80},   /* com24 */
+       {0x03, 0x1b},   /* vref */
+       {0x17, 0x1d},   /* hstart */
+       {0x18, 0x02},   /* hstop */
+       {0x19, 0x01},   /* vstrt */
+       {0x1a, 0x81},   /* vstop */
+       {0x32, 0xff},   /* href */
+       {0xc0, 0xe2},
+};
+
+static const u8 bridge_start_ov965x_qvga[][2] = {
        {0x94, 0xaa},
        {0xf1, 0x60},
        {0xe5, 0x04},
@@ -535,10 +1000,34 @@ static const u8 bridge_start_ov965x[][2] = {
        {0x8c, 0x00},
        {0x8d, 0x1c},
        {0x34, 0x05},
-       {}
+
+       {0xc2, 0x4c},
+       {0xc3, 0xf9},
+       {0xda, 0x00},
+       {0x50, 0x00},
+       {0x51, 0xa0},
+       {0x52, 0x78},
+       {0x53, 0x00},
+       {0x54, 0x00},
+       {0x55, 0x00},
+       {0x57, 0x00},
+       {0x5c, 0x00},
+       {0x5a, 0x50},
+       {0x5b, 0x3c},
+       {0x35, 0x02},
+       {0xd9, 0x10},
+       {0x94, 0x11},
 };
 
 static const u8 bridge_start_ov965x_vga[][2] = {
+       {0x94, 0xaa},
+       {0xf1, 0x60},
+       {0xe5, 0x04},
+       {0xc0, 0x50},
+       {0xc1, 0x3c},
+       {0x8c, 0x00},
+       {0x8d, 0x1c},
+       {0x34, 0x05},
        {0xc2, 0x0c},
        {0xc3, 0xf9},
        {0xda, 0x01},
@@ -555,30 +1044,98 @@ static const u8 bridge_start_ov965x_vga[][2] = {
        {0x35, 0x02},
        {0xd9, 0x10},
        {0x94, 0x11},
-       {}
 };
 
-static const u8 bridge_start_ov965x_cif[][2] = {
+static const u8 bridge_start_ov965x_svga[][2] = {
+       {0x94, 0xaa},
+       {0xf1, 0x60},
+       {0xe5, 0x04},
+       {0xc0, 0xa0},
+       {0xc1, 0x80},
+       {0x8c, 0x00},
+       {0x8d, 0x1c},
+       {0x34, 0x05},
        {0xc2, 0x4c},
        {0xc3, 0xf9},
-       {0xda, 0x00},
        {0x50, 0x00},
-       {0x51, 0xa0},
-       {0x52, 0x78},
+       {0x51, 0x40},
+       {0x52, 0x00},
        {0x53, 0x00},
        {0x54, 0x00},
-       {0x55, 0x00},
+       {0x55, 0x88},
        {0x57, 0x00},
        {0x5c, 0x00},
-       {0x5a, 0x50},
-       {0x5b, 0x3c},
+       {0x5a, 0xc8},
+       {0x5b, 0x96},
+       {0x35, 0x02},
+       {0xd9, 0x10},
+       {0xda, 0x00},
+       {0x94, 0x11},
+};
+
+static const u8 bridge_start_ov965x_xga[][2] = {
+       {0x94, 0xaa},
+       {0xf1, 0x60},
+       {0xe5, 0x04},
+       {0xc0, 0xa0},
+       {0xc1, 0x80},
+       {0x8c, 0x00},
+       {0x8d, 0x1c},
+       {0x34, 0x05},
+       {0xc2, 0x4c},
+       {0xc3, 0xf9},
+       {0x50, 0x00},
+       {0x51, 0x40},
+       {0x52, 0x00},
+       {0x53, 0x00},
+       {0x54, 0x00},
+       {0x55, 0x88},
+       {0x57, 0x00},
+       {0x5c, 0x01},
+       {0x5a, 0x00},
+       {0x5b, 0xc0},
        {0x35, 0x02},
        {0xd9, 0x10},
+       {0xda, 0x01},
        {0x94, 0x11},
-       {}
 };
 
-static const u8 sensor_start_ov965x_vga[][2] = {
+static const u8 bridge_start_ov965x_sxga[][2] = {
+       {0x94, 0xaa},
+       {0xf1, 0x60},
+       {0xe5, 0x04},
+       {0xc0, 0xa0},
+       {0xc1, 0x80},
+       {0x8c, 0x00},
+       {0x8d, 0x1c},
+       {0x34, 0x05},
+       {0xc2, 0x0c},
+       {0xc3, 0xf9},
+       {0xda, 0x00},
+       {0x35, 0x02},
+       {0xd9, 0x10},
+       {0x94, 0x11},
+};
+
+static const u8 sensor_start_ov965x_2_qvga[][2] = {
+       {0x3b, 0xe4},   /* com11 - night mode 1/4 frame rate */
+       {0x1e, 0x04},   /* mvfp */
+       {0x13, 0xe0},   /* com8 */
+       {0x00, 0x00},
+       {0x13, 0xe7},   /* com8 - everything (AGC, AWB and AEC) */
+       {0x11, 0x01},   /* clkrc */
+       {0x6b, 0x5a},   /* dblv */
+       {0x6a, 0x02},   /* 50 Hz banding filter */
+       {0xc5, 0x03},   /* 60 Hz banding filter */
+       {0xa2, 0x96},   /* bd50 */
+       {0xa3, 0x7d},   /* bd60 */
+
+       {0xff, 0x13},   /* read 13, write ff 00 */
+       {0x13, 0xe7},
+       {0x3a, 0x80},   /* tslb - yuyv */
+};
+
+static const u8 sensor_start_ov965x_2_vga[][2] = {
        {0x3b, 0xc4},   /* com11 - night mode 1/4 frame rate */
        {0x1e, 0x04},   /* mvfp */
        {0x13, 0xe0},   /* com8 */
@@ -592,35 +1149,36 @@ static const u8 sensor_start_ov965x_vga[][2] = {
        {0xa3, 0x3e},   /* bd60 */
 
        {0x2d, 0x00},   /* advfl */
-       {}
 };
 
-static const u8 sensor_start_ov965x_cif[][2] = {
-       {0x3b, 0xe4},   /* com11 - night mode 1/4 frame rate */
+static const u8 sensor_start_ov965x_2_svga[][2] = {    /* same for xga */
+       {0x3b, 0xc4},   /* com11 - night mode 1/4 frame rate */
        {0x1e, 0x04},   /* mvfp */
        {0x13, 0xe0},   /* com8 */
        {0x00, 0x00},
        {0x13, 0xe7},   /* com8 - everything (AGC, AWB and AEC) */
        {0x11, 0x01},   /* clkrc */
        {0x6b, 0x5a},   /* dblv */
-       {0x6a, 0x02},   /* 50 Hz banding filter */
-       {0xc5, 0x03},   /* 60 Hz banding filter */
-       {0xa2, 0x96},   /* bd50 */
-       {0xa3, 0x7d},   /* bd60 */
-
-       {0xff, 0x13},   /* read 13, write ff 00 */
-       {0x13, 0xe7},
-       {0x3a, 0x80},   /* tslb - yuyv */
-       {}
+       {0x6a, 0x0c},   /* 50 Hz banding filter */
+       {0xc5, 0x0f},   /* 60 Hz banding filter */
+       {0xa2, 0x4e},   /* bd50 */
+       {0xa3, 0x41},   /* bd60 */
 };
 
-static const u8 sensor_start_ov965x_2[][2] = {
-       {0xff, 0x42},   /* read 42, write ff 00 */
-       {0x42, 0xc1},   /* com17 - 50 Hz filter */
-       {}
+static const u8 sensor_start_ov965x_2_sxga[][2] = {
+       {0x13, 0xe0},   /* com8 */
+       {0x00, 0x00},
+       {0x13, 0xe7},   /* com8 - everything (AGC, AWB and AEC) */
+       {0x3b, 0xc4},   /* com11 - night mode 1/4 frame rate */
+       {0x1e, 0x04},   /* mvfp */
+       {0x11, 0x01},   /* clkrc */
+       {0x6b, 0x5a},   /* dblv */
+       {0x6a, 0x0c},   /* 50 Hz banding filter */
+       {0xc5, 0x0f},   /* 60 Hz banding filter */
+       {0xa2, 0x4e},   /* bd50 */
+       {0xa3, 0x41},   /* bd60 */
 };
 
-
 static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
 {
        struct usb_device *udev = gspca_dev->dev;
@@ -753,76 +1311,390 @@ static void sccb_w_array(struct gspca_dev *gspca_dev,
        }
 }
 
-/* set framerate */
-static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+/* ov772x specific controls */
+static void set_frame_rate(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i;
+       struct rate_s {
+               u8 fps;
+               u8 r11;
+               u8 r0d;
+               u8 re5;
+       };
+       const struct rate_s *r;
+       static const struct rate_s rate_0[] = { /* 640x480 */
+               {60, 0x01, 0xc1, 0x04},
+               {50, 0x01, 0x41, 0x02},
+               {40, 0x02, 0xc1, 0x04},
+               {30, 0x04, 0x81, 0x02},
+               {15, 0x03, 0x41, 0x04},
+       };
+       static const struct rate_s rate_1[] = { /* 320x240 */
+               {125, 0x02, 0x81, 0x02},
+               {100, 0x02, 0xc1, 0x04},
+               {75, 0x03, 0xc1, 0x04},
+               {60, 0x04, 0xc1, 0x04},
+               {50, 0x02, 0x41, 0x04},
+               {40, 0x03, 0x41, 0x04},
+               {30, 0x04, 0x41, 0x04},
+       };
+
+       if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) {
+               r = rate_0;
+               i = ARRAY_SIZE(rate_0);
+       } else {
+               r = rate_1;
+               i = ARRAY_SIZE(rate_1);
+       }
+       while (--i > 0) {
+               if (sd->frame_rate >= r->fps)
+                       break;
+               r++;
+       }
+
+       sccb_reg_write(gspca_dev, 0x11, r->r11);
+       sccb_reg_write(gspca_dev, 0x0d, r->r0d);
+       ov534_reg_write(gspca_dev, 0xe5, r->re5);
+
+       PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
+}
+
+static void setbrightness_77(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sccb_reg_write(gspca_dev, 0x9B, sd->brightness);
+}
+
+static void setcontrast_77(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sccb_reg_write(gspca_dev, 0x9C, sd->contrast);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int fr = sd->frame_rate;
+       u8 val;
 
-       switch (fr) {
-       case 50:
-               sccb_reg_write(gspca_dev, 0x11, 0x01);
-               sccb_reg_write(gspca_dev, 0x0d, 0x41);
-               ov534_reg_write(gspca_dev, 0xe5, 0x02);
+       val = sd->gain;
+       switch (val & 0x30) {
+       case 0x00:
+               val &= 0x0f;
                break;
-       case 40:
-               sccb_reg_write(gspca_dev, 0x11, 0x02);
-               sccb_reg_write(gspca_dev, 0x0d, 0xc1);
-               ov534_reg_write(gspca_dev, 0xe5, 0x04);
+       case 0x10:
+               val &= 0x0f;
+               val |= 0x30;
                break;
-/*     case 30: */
-       default:
-               fr = 30;
-               sccb_reg_write(gspca_dev, 0x11, 0x04);
-               sccb_reg_write(gspca_dev, 0x0d, 0x81);
-               ov534_reg_write(gspca_dev, 0xe5, 0x02);
+       case 0x20:
+               val &= 0x0f;
+               val |= 0x70;
                break;
-       case 15:
-               sccb_reg_write(gspca_dev, 0x11, 0x03);
-               sccb_reg_write(gspca_dev, 0x0d, 0x41);
-               ov534_reg_write(gspca_dev, 0xe5, 0x04);
+       default:
+/*     case 0x30: */
+               val &= 0x0f;
+               val |= 0xf0;
                break;
        }
+       sccb_reg_write(gspca_dev, 0x00, val);
+}
+
+static void setexposure_77(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
 
-       sd->frame_rate = fr;
-       PDEBUG(D_PROBE, "frame_rate: %d", fr);
+       val = sd->exposure;
+       sccb_reg_write(gspca_dev, 0x08, val >> 7);
+       sccb_reg_write(gspca_dev, 0x10, val << 1);
 }
 
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
-                    const struct usb_device_id *id)
+static void setredblc(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       struct cam *cam;
 
-       sd->sensor = id->driver_info;
+       sccb_reg_write(gspca_dev, 0x43, sd->redblc);
+}
 
-       cam = &gspca_dev->cam;
+static void setblueblc(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->sensor == SENSOR_OV772X) {
-               cam->cam_mode = vga_yuyv_mode;
-               cam->nmodes = ARRAY_SIZE(vga_yuyv_mode);
+       sccb_reg_write(gspca_dev, 0x42, sd->blueblc);
+}
 
-               cam->bulk = 1;
-               cam->bulk_size = 16384;
-               cam->bulk_nurbs = 2;
-       } else {                /* ov965x */
-               cam->cam_mode = vga_jpeg_mode;
-               cam->nmodes = ARRAY_SIZE(vga_jpeg_mode);
-       }
+static void sethue(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
 
-       return 0;
+       sccb_reg_write(gspca_dev, 0x01, sd->hue);
 }
 
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
+static void setautogain_77(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u16 sensor_id;
-       static const u8 sensor_addr[2] = {
-               0x42,                   /* 0 SENSOR_OV772X */
-               0x60,                   /* 1 SENSOR_OV965X */
-       };
+
+       if (sd->autogain) {
+               sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */
+               sccb_reg_write(gspca_dev, 0x64,
+                               sccb_reg_read(gspca_dev, 0x64) | 0x03);
+       } else {
+               sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */
+               sccb_reg_write(gspca_dev, 0x64,
+                               sccb_reg_read(gspca_dev, 0x64) & 0xfc);
+       }
+}
+
+static void setawb(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->awb)
+               sccb_reg_write(gspca_dev, 0x63, 0xe0);  /* AWB on */
+       else
+               sccb_reg_write(gspca_dev, 0x63, 0xaa);  /* AWB off */
+}
+
+static void setsharpness_77(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
+
+       val = sd->sharpness;
+       sccb_reg_write(gspca_dev, 0x91, val);   /* vga noise */
+       sccb_reg_write(gspca_dev, 0x8e, val);   /* qvga noise */
+}
+
+static void sethflip(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->hflip == 0)
+               sccb_reg_write(gspca_dev, 0x0c,
+                               sccb_reg_read(gspca_dev, 0x0c) | 0x40);
+       else
+               sccb_reg_write(gspca_dev, 0x0c,
+                               sccb_reg_read(gspca_dev, 0x0c) & 0xbf);
+}
+
+static void setvflip(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->vflip == 0)
+               sccb_reg_write(gspca_dev, 0x0c,
+                               sccb_reg_read(gspca_dev, 0x0c) | 0x80);
+       else
+               sccb_reg_write(gspca_dev, 0x0c,
+                               sccb_reg_read(gspca_dev, 0x0c) & 0x7f);
+}
+
+/* ov965x specific controls */
+static void setbrightness_96(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
+
+       val = sd->brightness;
+       if (val < 8)
+               val = 15 - val;         /* f .. 8 */
+       else
+               val = val - 8;          /* 0 .. 7 */
+       sccb_reg_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
+                       0x0f | (val << 4));
+}
+
+static void setcontrast_96(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sccb_reg_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
+                       sd->contrast << 4);
+}
+
+static void setexposure_96(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
+       static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
+
+       sccb_reg_write(gspca_dev, 0x10,                 /* aec[9:2] */
+                       expo[sd->exposure]);
+       val = sccb_reg_read(gspca_dev, 0x13);           /* com8 */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       sccb_reg_write(gspca_dev, 0x13, val);
+       val = sccb_reg_read(gspca_dev, 0xa1);           /* aech */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       sccb_reg_write(gspca_dev, 0xa1, val & 0xe0);    /* aec[15:10] = 0 */
+}
+
+static void setsharpness_96(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
+
+       val = sd->sharpness;
+       if (val < 0) {                          /* auto */
+               val = sccb_reg_read(gspca_dev, 0x42);   /* com17 */
+               sccb_reg_write(gspca_dev, 0xff, 0x00);
+               sccb_reg_write(gspca_dev, 0x42, val | 0x40);
+                               /* Edge enhancement strength auto adjust */
+               return;
+       }
+       if (val != 0)
+               val = 1 << (val - 1);
+       sccb_reg_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */
+                       val);
+       val = sccb_reg_read(gspca_dev, 0x42);           /* com17 */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       sccb_reg_write(gspca_dev, 0x42, val & 0xbf);
+}
+
+static void setautogain_96(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
+
+/*fixme: should adjust agc/awb/aec by different controls */
+       val = sd->autogain;
+       val = sccb_reg_read(gspca_dev, 0x13);           /* com8 */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       if (sd->autogain)
+               val |= 0x05;            /* agc & aec */
+       else
+               val &= 0xfa;
+       sccb_reg_write(gspca_dev, 0x13, val);
+}
+
+static void setsatur(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val1, val2, val3;
+       static const u8 matrix[5][2] = {
+               {0x14, 0x38},
+               {0x1e, 0x54},
+               {0x28, 0x70},
+               {0x32, 0x8c},
+               {0x48, 0x90}
+       };
+
+       val1 = matrix[sd->satur][0];
+       val2 = matrix[sd->satur][1];
+       val3 = val1 + val2;
+       sccb_reg_write(gspca_dev, 0x4f, val3);  /* matrix coeff */
+       sccb_reg_write(gspca_dev, 0x50, val3);
+       sccb_reg_write(gspca_dev, 0x51, 0x00);
+       sccb_reg_write(gspca_dev, 0x52, val1);
+       sccb_reg_write(gspca_dev, 0x53, val2);
+       sccb_reg_write(gspca_dev, 0x54, val3);
+       sccb_reg_write(gspca_dev, 0x58, 0x1a);  /* mtxs - coeff signs */
+       val1 = sccb_reg_read(gspca_dev, 0x41);  /* com16 */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       sccb_reg_write(gspca_dev, 0x41, val1);
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u8 val;
+
+       val = sccb_reg_read(gspca_dev, 0x13);           /* com8 */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       if (sd->lightfreq == 0) {
+               sccb_reg_write(gspca_dev, 0x13, val & 0xdf);
+               return;
+       }
+       sccb_reg_write(gspca_dev, 0x13, val | 0x20);
+
+       val = sccb_reg_read(gspca_dev, 0x42);           /* com17 */
+       sccb_reg_write(gspca_dev, 0xff, 0x00);
+       if (sd->lightfreq == 1)
+               val |= 0x01;
+       else
+               val &= 0xfe;
+       sccb_reg_write(gspca_dev, 0x42, val);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                    const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       sd->sensor = id->driver_info;
+
+       cam = &gspca_dev->cam;
+
+       if (sd->sensor == SENSOR_OV772X) {
+               cam->cam_mode = ov772x_mode;
+               cam->nmodes = ARRAY_SIZE(ov772x_mode);
+
+               cam->bulk = 1;
+               cam->bulk_size = 16384;
+               cam->bulk_nurbs = 2;
+       } else {                /* ov965x */
+               cam->cam_mode = ov965x_mode;
+               cam->nmodes = ARRAY_SIZE(ov965x_mode);
+       }
+
+       sd->frame_rate = 30;
+
+       if (sd->sensor == SENSOR_OV772X) {
+               sd->brightness = BRIGHTNESS_77_DEF;
+               sd->contrast = CONTRAST_77_DEF;
+               sd->gain = GAIN_DEF;
+               sd->exposure = EXPO_77_DEF;
+               sd->redblc = RED_BALANCE_DEF;
+               sd->blueblc = BLUE_BALANCE_DEF;
+               sd->hue = HUE_DEF;
+#if AUTOGAIN_77_DEF != 0
+               sd->autogain = AUTOGAIN_77_DEF;
+#else
+               gspca_dev->ctrl_inac |= (1 << AWB_77_IDX);
+#endif
+#if AWB_DEF != 0
+               sd->awb = AWB_DEF
+#endif
+#if SHARPNESS_77_DEF != 0
+               sd->sharpness = SHARPNESS_77_DEF;
+#endif
+#if HFLIP_DEF != 0
+               sd->hflip = HFLIP_DEF;
+#endif
+#if VFLIP_DEF != 0
+               sd->vflip = VFLIP_DEF;
+#endif
+       } else {
+               sd->brightness = BRIGHTNESS_96_DEF;
+               sd->contrast = CONTRAST_96_DEF;
+#if AUTOGAIN_96_DEF != 0
+               sd->autogain = AUTOGAIN_96_DEF;
+               gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX);
+#endif
+#if EXPO_96_DEF != 0
+               sd->exposure = EXPO_96_DEF;
+#endif
+#if SHARPNESS_96_DEF != 0
+               sd->sharpness = SHARPNESS_96_DEF;
+#endif
+               sd->satur = SATUR_DEF;
+               sd->lightfreq = FREQ_DEF;
+       }
+       return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       u16 sensor_id;
+       static const u8 sensor_addr[2] = {
+               0x42,                   /* 0 SENSOR_OV772X */
+               0x60,                   /* 1 SENSOR_OV965X */
+       };
 
        /* reset bridge */
        ov534_reg_write(gspca_dev, 0xe7, 0x3a);
@@ -847,14 +1719,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
        /* initialize */
        switch (sd->sensor) {
        case SENSOR_OV772X:
-               reg_w_array(gspca_dev, bridge_init_ov722x,
-                               ARRAY_SIZE(bridge_init_ov722x));
+               reg_w_array(gspca_dev, bridge_init_ov772x,
+                               ARRAY_SIZE(bridge_init_ov772x));
                ov534_set_led(gspca_dev, 1);
-               sccb_w_array(gspca_dev, sensor_init_ov722x,
-                               ARRAY_SIZE(sensor_init_ov722x));
+               sccb_w_array(gspca_dev, sensor_init_ov772x,
+                               ARRAY_SIZE(sensor_init_ov772x));
                ov534_reg_write(gspca_dev, 0xe0, 0x09);
                ov534_set_led(gspca_dev, 0);
-               ov534_set_frame_rate(gspca_dev);
+               set_frame_rate(gspca_dev);
                break;
        default:
 /*     case SENSOR_OV965X: */
@@ -875,60 +1747,115 @@ static int sd_init(struct gspca_dev *gspca_dev)
        return 0;
 }
 
-static int sd_start(struct gspca_dev *gspca_dev)
+static int sd_start_ov772x(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int mode;
 
-       switch (sd->sensor) {
-       case SENSOR_OV772X:
-               ov534_set_led(gspca_dev, 1);
-               ov534_reg_write(gspca_dev, 0xe0, 0x00);
-               break;
-       default:
-/*     case SENSOR_OV965X: */
-
-               sccb_w_array(gspca_dev, sensor_start_ov965x,
-                               ARRAY_SIZE(sensor_start_ov965x));
-               reg_w_array(gspca_dev, bridge_start_ov965x,
-                               ARRAY_SIZE(bridge_start_ov965x));
-               mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
-               if (mode != 0) {        /* 320x240 */
-                       reg_w_array(gspca_dev, bridge_start_ov965x_cif,
-                                       ARRAY_SIZE(bridge_start_ov965x_cif));
-                       sccb_w_array(gspca_dev, sensor_start_ov965x_cif,
-                                       ARRAY_SIZE(sensor_start_ov965x_cif));
-               } else {                /* 640x480 */
-                       reg_w_array(gspca_dev, bridge_start_ov965x_vga,
-                                       ARRAY_SIZE(bridge_start_ov965x_vga));
-                       sccb_w_array(gspca_dev, sensor_start_ov965x_vga,
-                                       ARRAY_SIZE(sensor_start_ov965x_vga));
-               }
-               sccb_w_array(gspca_dev, sensor_start_ov965x_2,
-                               ARRAY_SIZE(sensor_start_ov965x_2));
-               ov534_reg_write(gspca_dev, 0xe0, 0x00);
-               ov534_reg_write(gspca_dev, 0xe0, 0x00);
-               ov534_set_led(gspca_dev, 1);
+       mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+       if (mode != 0) {        /* 320x240 */
+               reg_w_array(gspca_dev, bridge_start_ov772x_qvga,
+                               ARRAY_SIZE(bridge_start_ov772x_qvga));
+               sccb_w_array(gspca_dev, sensor_start_ov772x_qvga,
+                               ARRAY_SIZE(sensor_start_ov772x_qvga));
+       } else {                /* 640x480 */
+               reg_w_array(gspca_dev, bridge_start_ov772x_vga,
+                               ARRAY_SIZE(bridge_start_ov772x_vga));
+               sccb_w_array(gspca_dev, sensor_start_ov772x_vga,
+                               ARRAY_SIZE(sensor_start_ov772x_vga));
        }
+       set_frame_rate(gspca_dev);
+
+       setautogain_77(gspca_dev);
+       setawb(gspca_dev);
+       setgain(gspca_dev);
+       setredblc(gspca_dev);
+       setblueblc(gspca_dev);
+       sethue(gspca_dev);
+       setexposure_77(gspca_dev);
+       setbrightness_77(gspca_dev);
+       setcontrast_77(gspca_dev);
+       setsharpness_77(gspca_dev);
+       setvflip(gspca_dev);
+       sethflip(gspca_dev);
+
+       ov534_set_led(gspca_dev, 1);
+       ov534_reg_write(gspca_dev, 0xe0, 0x00);
        return 0;
 }
 
-static void sd_stopN(struct gspca_dev *gspca_dev)
+static int sd_start_ov965x(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       int mode;
 
-       switch (sd->sensor) {
-       case SENSOR_OV772X:
-               ov534_reg_write(gspca_dev, 0xe0, 0x09);
-               ov534_set_led(gspca_dev, 0);
-               break;
+       mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+       switch (mode) {
        default:
-/*     case SENSOR_OV965X: */
-               ov534_reg_write(gspca_dev, 0xe0, 0x01);
-               ov534_set_led(gspca_dev, 0);
-               ov534_reg_write(gspca_dev, 0xe0, 0x00);
+/*     case 4:                  * 320x240 */
+               sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga,
+                               ARRAY_SIZE(sensor_start_ov965x_1_vga));
+               reg_w_array(gspca_dev, bridge_start_ov965x_qvga,
+                               ARRAY_SIZE(bridge_start_ov965x_qvga));
+               sccb_w_array(gspca_dev, sensor_start_ov965x_2_qvga,
+                               ARRAY_SIZE(sensor_start_ov965x_2_qvga));
+               break;
+       case 3:                 /* 640x480 */
+               sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga,
+                               ARRAY_SIZE(sensor_start_ov965x_1_vga));
+               reg_w_array(gspca_dev, bridge_start_ov965x_vga,
+                               ARRAY_SIZE(bridge_start_ov965x_vga));
+               sccb_w_array(gspca_dev, sensor_start_ov965x_2_vga,
+                               ARRAY_SIZE(sensor_start_ov965x_2_vga));
+               break;
+       case 2:                 /* 800x600 */
+               sccb_w_array(gspca_dev, sensor_start_ov965x_1_svga,
+                               ARRAY_SIZE(sensor_start_ov965x_1_svga));
+               reg_w_array(gspca_dev, bridge_start_ov965x_svga,
+                               ARRAY_SIZE(bridge_start_ov965x_svga));
+               sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga,
+                               ARRAY_SIZE(sensor_start_ov965x_2_svga));
+               break;
+       case 1:                 /* 1024x768 */
+               sccb_w_array(gspca_dev, sensor_start_ov965x_1_xga,
+                               ARRAY_SIZE(sensor_start_ov965x_1_xga));
+               reg_w_array(gspca_dev, bridge_start_ov965x_xga,
+                               ARRAY_SIZE(bridge_start_ov965x_xga));
+               sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga,
+                               ARRAY_SIZE(sensor_start_ov965x_2_svga));
+               break;
+       case 0:                 /* 1280x1024 */
+               sccb_w_array(gspca_dev, sensor_start_ov965x_1_sxga,
+                               ARRAY_SIZE(sensor_start_ov965x_1_sxga));
+               reg_w_array(gspca_dev, bridge_start_ov965x_sxga,
+                               ARRAY_SIZE(bridge_start_ov965x_sxga));
+               sccb_w_array(gspca_dev, sensor_start_ov965x_2_sxga,
+                               ARRAY_SIZE(sensor_start_ov965x_2_sxga));
                break;
        }
+       setfreq(gspca_dev);
+       setautogain_96(gspca_dev);
+       setbrightness_96(gspca_dev);
+       setcontrast_96(gspca_dev);
+       setexposure_96(gspca_dev);
+       setsharpness_96(gspca_dev);
+       setsatur(gspca_dev);
+
+       ov534_reg_write(gspca_dev, 0xe0, 0x00);
+       ov534_reg_write(gspca_dev, 0xe0, 0x00);
+       ov534_set_led(gspca_dev, 1);
+       return 0;
+}
+
+static void sd_stopN_ov772x(struct gspca_dev *gspca_dev)
+{
+       ov534_reg_write(gspca_dev, 0xe0, 0x09);
+       ov534_set_led(gspca_dev, 0);
+}
+
+static void sd_stopN_ov965x(struct gspca_dev *gspca_dev)
+{
+       ov534_reg_write(gspca_dev, 0xe0, 0x01);
+       ov534_set_led(gspca_dev, 0);
+       ov534_reg_write(gspca_dev, 0xe0, 0x00);
 }
 
 /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
@@ -941,8 +1868,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
 #define UVC_STREAM_EOF (1 << 1)
 #define UVC_STREAM_FID (1 << 0)
 
-static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
-                       __u8 *data, int len)
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data, int len)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        __u32 this_pts;
@@ -983,32 +1910,30 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
                /* If PTS or FID has changed, start a new frame. */
                if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
                        if (gspca_dev->last_packet_type == INTER_PACKET)
-                               frame = gspca_frame_add(gspca_dev,
-                                                       LAST_PACKET, frame,
-                                                       NULL, 0);
+                               gspca_frame_add(gspca_dev, LAST_PACKET,
+                                               NULL, 0);
                        sd->last_pts = this_pts;
                        sd->last_fid = this_fid;
-                       gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
                                        data + 12, len - 12);
                /* If this packet is marked as EOF, end the frame */
                } else if (data[1] & UVC_STREAM_EOF) {
                        sd->last_pts = 0;
-                       frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                               data + 12, len - 12);
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
+                                       data + 12, len - 12);
                } else {
 
                        /* Add the data from this payload */
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                                               data + 12, len - 12);
+                       gspca_frame_add(gspca_dev, INTER_PACKET,
+                                       data + 12, len - 12);
                }
 
-
                /* Done this payload */
                goto scan_next;
 
 discard:
                /* Discard data until a new frame starts. */
-               gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
+               gspca_dev->last_packet_type = DISCARD_PACKET;
 
 scan_next:
                remaining_len -= len;
@@ -1016,6 +1941,291 @@ scan_next:
        } while (remaining_len > 0);
 }
 
+/* controls */
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gain = val;
+       if (gspca_dev->streaming)
+               setgain(gspca_dev);
+       return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->gain;
+       return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->exposure = val;
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_OV772X)
+                       setexposure_77(gspca_dev);
+               else
+                       setexposure_96(gspca_dev);
+       }
+       return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->exposure;
+       return 0;
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_OV772X)
+                       setbrightness_77(gspca_dev);
+               else
+                       setbrightness_96(gspca_dev);
+       }
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_OV772X)
+                       setcontrast_77(gspca_dev);
+               else
+                       setcontrast_96(gspca_dev);
+       }
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->satur = val;
+       if (gspca_dev->streaming)
+               setsatur(gspca_dev);
+       return 0;
+}
+
+static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->satur;
+       return 0;
+}
+static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->lightfreq = val;
+       if (gspca_dev->streaming)
+               setfreq(gspca_dev);
+       return 0;
+}
+
+static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->lightfreq;
+       return 0;
+}
+
+static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->redblc = val;
+       if (gspca_dev->streaming)
+               setredblc(gspca_dev);
+       return 0;
+}
+
+static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->redblc;
+       return 0;
+}
+
+static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->blueblc = val;
+       if (gspca_dev->streaming)
+               setblueblc(gspca_dev);
+       return 0;
+}
+
+static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->blueblc;
+       return 0;
+}
+
+static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hue = val;
+       if (gspca_dev->streaming)
+               sethue(gspca_dev);
+       return 0;
+}
+
+static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->hue;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_OV772X) {
+
+                       /* the auto white balance control works only
+                        * when auto gain is set */
+                       if (val)
+                               gspca_dev->ctrl_inac &= ~(1 << AWB_77_IDX);
+                       else
+                               gspca_dev->ctrl_inac |= (1 << AWB_77_IDX);
+                       setautogain_77(gspca_dev);
+               } else {
+                       if (val)
+                               gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX);
+                       else
+                               gspca_dev->ctrl_inac &= ~(1 << EXPO_96_IDX);
+                       setautogain_96(gspca_dev);
+               }
+       }
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->awb = val;
+       if (gspca_dev->streaming)
+               setawb(gspca_dev);
+       return 0;
+}
+
+static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->awb;
+       return 0;
+}
+
+static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->sharpness = val;
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_OV772X)
+                       setsharpness_77(gspca_dev);
+               else
+                       setsharpness_96(gspca_dev);
+       }
+       return 0;
+}
+
+static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->sharpness;
+       return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hflip = val;
+       if (gspca_dev->streaming)
+               sethflip(gspca_dev);
+       return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->hflip;
+       return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->vflip = val;
+       if (gspca_dev->streaming)
+               setvflip(gspca_dev);
+       return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->vflip;
+       return 0;
+}
+
 /* get stream parameters (framerate) */
 static int sd_get_streamparm(struct gspca_dev *gspca_dev,
                             struct v4l2_streamparm *parm)
@@ -1047,7 +2257,8 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev,
 
        /* Set requested framerate */
        sd->frame_rate = tpf->denominator / tpf->numerator;
-       ov534_set_frame_rate(gspca_dev);
+       if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X)
+               set_frame_rate(gspca_dev);
 
        /* Return the actual framerate */
        tpf->numerator = 1;
@@ -1056,20 +2267,53 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev,
        return 0;
 }
 
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+                       struct v4l2_querymenu *menu)
+{
+       switch (menu->id) {
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               switch (menu->index) {
+               case 0:         /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+                       strcpy((char *) menu->name, "NoFliker");
+                       return 0;
+               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+                       strcpy((char *) menu->name, "50 Hz");
+                       return 0;
+               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+                       strcpy((char *) menu->name, "60 Hz");
+                       return 0;
+               }
+               break;
+       }
+       return -EINVAL;
+}
+
 /* sub-driver description */
-static const struct sd_desc sd_desc = {
+static const struct sd_desc sd_desc_ov772x = {
        .name     = MODULE_NAME,
-       .ctrls    = sd_ctrls,
-       .nctrls   = ARRAY_SIZE(sd_ctrls),
+       .ctrls    = sd_ctrls_ov772x,
+       .nctrls   = ARRAY_SIZE(sd_ctrls_ov772x),
        .config   = sd_config,
        .init     = sd_init,
-       .start    = sd_start,
-       .stopN    = sd_stopN,
+       .start    = sd_start_ov772x,
+       .stopN    = sd_stopN_ov772x,
        .pkt_scan = sd_pkt_scan,
        .get_streamparm = sd_get_streamparm,
        .set_streamparm = sd_set_streamparm,
 };
 
+static const struct sd_desc sd_desc_ov965x = {
+       .name     = MODULE_NAME,
+       .ctrls    = sd_ctrls_ov965x,
+       .nctrls   = ARRAY_SIZE(sd_ctrls_ov965x),
+       .config   = sd_config,
+       .init     = sd_init,
+       .start    = sd_start_ov965x,
+       .stopN    = sd_stopN_ov965x,
+       .pkt_scan = sd_pkt_scan,
+       .querymenu = sd_querymenu,
+};
+
 /* -- module initialisation -- */
 static const __devinitdata struct usb_device_id device_table[] = {
        {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X},
@@ -1082,8 +2326,12 @@ MODULE_DEVICE_TABLE(usb, device_table);
 /* -- device connect -- */
 static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
-       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
-                              THIS_MODULE);
+       return gspca_dev_probe(intf, id,
+                               id->driver_info == SENSOR_OV772X
+                                       ? &sd_desc_ov772x
+                                       : &sd_desc_ov965x,
+                               sizeof(struct sd),
+                               THIS_MODULE);
 }
 
 static struct usb_driver sd_driver = {
@@ -1101,6 +2349,7 @@ static struct usb_driver sd_driver = {
 static int __init sd_mod_init(void)
 {
        int ret;
+
        ret = usb_register(&sd_driver);
        if (ret < 0)
                return ret;
index 9665943..4706a82 100644 (file)
@@ -337,14 +337,13 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,
-                       __u8 *data,
+                       u8 *data,
                        int len)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        unsigned char *sof;
 
-       sof = pac_find_sof(gspca_dev, data, len);
+       sof = pac_find_sof(&sd->sof_read, data, len);
        if (sof) {
                int n;
 
@@ -354,10 +353,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        n -= sizeof pac_sof_marker;
                else
                        n = 0;
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, n);
+               gspca_frame_add(gspca_dev, LAST_PACKET,
+                               data, n);
                sd->header_read = 0;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
                len -= sof - data;
                data = sof;
        }
@@ -381,7 +380,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                sd->header_read = 11;
        }
 
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static void setbrightness(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
new file mode 100644 (file)
index 0000000..74accee
--- /dev/null
@@ -0,0 +1,1272 @@
+/*
+ *             Pixart PAC7302 library
+ *             Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ *
+ * Separated from Pixart PAC7311 library by Márton Németh <nm127@freemail.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * 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
+ */
+
+/* Some documentation about various registers as determined by trial and error.
+   When the register addresses differ between the 7202 and the 7311 the 2
+   different addresses are written as 7302addr/7311addr, when one of the 2
+   addresses is a - sign that register description is not valid for the
+   matching IC.
+
+   Register page 1:
+
+   Address     Description
+   -/0x08      Unknown compressor related, must always be 8 except when not
+               in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
+   -/0x1b      Auto white balance related, bit 0 is AWB enable (inverted)
+               bits 345 seem to toggle per color gains on/off (inverted)
+   0x78                Global control, bit 6 controls the LED (inverted)
+   -/0x80      JPEG compression ratio ? Best not touched
+
+   Register page 3/4:
+
+   Address     Description
+   0x02                Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+               the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+   -/0x0f      Master gain 1-245, low value = high gain
+   0x10/-      Master gain 0-31
+   -/0x10      Another gain 0-15, limited influence (1-2x gain I guess)
+   0x21                Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+   -/0x27      Seems to toggle various gains on / off, Setting bit 7 seems to
+               completely disable the analog amplification block. Set to 0x68
+               for max gain, 0x14 for minimal gain.
+
+   The registers are accessed in the following functions:
+
+   Page | Register   | Function
+   -----+------------+---------------------------------------------------
+    0   | 0x0f..0x20 | setcolors()
+    0   | 0xa2..0xab | setbrightcont()
+    0   | 0xc5       | setredbalance()
+    0   | 0xc6       | setwhitebalance()
+    0   | 0xc7       | setbluebalance()
+    0   | 0xdc       | setbrightcont(), setcolors()
+    3   | 0x02       | setexposure()
+    3   | 0x10       | setgain()
+    3   | 0x11       | setcolors(), setgain(), setexposure(), sethvflip()
+    3   | 0x21       | sethvflip()
+*/
+
+#define MODULE_NAME "pac7302"
+
+#include <media/v4l2-chip-ident.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7302");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor for pac7302 */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+
+       unsigned char brightness;
+       unsigned char contrast;
+       unsigned char colors;
+       unsigned char white_balance;
+       unsigned char red_balance;
+       unsigned char blue_balance;
+       unsigned char gain;
+       unsigned char exposure;
+       unsigned char autogain;
+       __u8 hflip;
+       __u8 vflip;
+
+       u8 sof_read;
+       u8 autogain_ignore_frames;
+
+       atomic_t avg_lum;
+};
+
+/* V4L2 controls supported by the driver */
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+
+static struct ctrl sd_ctrls[] = {
+/* This control is pac7302 only */
+       {
+           {
+               .id      = V4L2_CID_BRIGHTNESS,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Brightness",
+               .minimum = 0,
+#define BRIGHTNESS_MAX 0x20
+               .maximum = BRIGHTNESS_MAX,
+               .step    = 1,
+#define BRIGHTNESS_DEF 0x10
+               .default_value = BRIGHTNESS_DEF,
+           },
+           .set = sd_setbrightness,
+           .get = sd_getbrightness,
+       },
+/* This control is for both the 7302 and the 7311 */
+       {
+           {
+               .id      = V4L2_CID_CONTRAST,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Contrast",
+               .minimum = 0,
+#define CONTRAST_MAX 255
+               .maximum = CONTRAST_MAX,
+               .step    = 1,
+#define CONTRAST_DEF 127
+               .default_value = CONTRAST_DEF,
+           },
+           .set = sd_setcontrast,
+           .get = sd_getcontrast,
+       },
+/* This control is pac7302 only */
+       {
+           {
+               .id      = V4L2_CID_SATURATION,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Saturation",
+               .minimum = 0,
+#define COLOR_MAX 255
+               .maximum = COLOR_MAX,
+               .step    = 1,
+#define COLOR_DEF 127
+               .default_value = COLOR_DEF,
+           },
+           .set = sd_setcolors,
+           .get = sd_getcolors,
+       },
+       {
+           {
+               .id      = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "White Balance",
+               .minimum = 0,
+               .maximum = 255,
+               .step    = 1,
+#define WHITEBALANCE_DEF 4
+               .default_value = WHITEBALANCE_DEF,
+           },
+           .set = sd_setwhitebalance,
+           .get = sd_getwhitebalance,
+       },
+       {
+           {
+               .id      = V4L2_CID_RED_BALANCE,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Red",
+               .minimum = 0,
+               .maximum = 3,
+               .step    = 1,
+#define REDBALANCE_DEF 1
+               .default_value = REDBALANCE_DEF,
+           },
+           .set = sd_setredbalance,
+           .get = sd_getredbalance,
+       },
+       {
+           {
+               .id      = V4L2_CID_BLUE_BALANCE,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Blue",
+               .minimum = 0,
+               .maximum = 3,
+               .step    = 1,
+#define BLUEBALANCE_DEF 1
+               .default_value = BLUEBALANCE_DEF,
+           },
+           .set = sd_setbluebalance,
+           .get = sd_getbluebalance,
+       },
+/* All controls below are for both the 7302 and the 7311 */
+       {
+           {
+               .id      = V4L2_CID_GAIN,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Gain",
+               .minimum = 0,
+#define GAIN_MAX 255
+               .maximum = GAIN_MAX,
+               .step    = 1,
+#define GAIN_DEF 127
+#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
+               .default_value = GAIN_DEF,
+           },
+           .set = sd_setgain,
+           .get = sd_getgain,
+       },
+       {
+           {
+               .id      = V4L2_CID_EXPOSURE,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Exposure",
+               .minimum = 0,
+#define EXPOSURE_MAX 255
+               .maximum = EXPOSURE_MAX,
+               .step    = 1,
+#define EXPOSURE_DEF  16 /*  32 ms / 30 fps */
+#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
+               .default_value = EXPOSURE_DEF,
+           },
+           .set = sd_setexposure,
+           .get = sd_getexposure,
+       },
+       {
+           {
+               .id      = V4L2_CID_AUTOGAIN,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Auto Gain",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define AUTOGAIN_DEF 1
+               .default_value = AUTOGAIN_DEF,
+           },
+           .set = sd_setautogain,
+           .get = sd_getautogain,
+       },
+       {
+           {
+               .id      = V4L2_CID_HFLIP,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Mirror",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define HFLIP_DEF 0
+               .default_value = HFLIP_DEF,
+           },
+           .set = sd_sethflip,
+           .get = sd_gethflip,
+       },
+       {
+           {
+               .id      = V4L2_CID_VFLIP,
+               .type    = V4L2_CTRL_TYPE_BOOLEAN,
+               .name    = "Vflip",
+               .minimum = 0,
+               .maximum = 1,
+               .step    = 1,
+#define VFLIP_DEF 0
+               .default_value = VFLIP_DEF,
+           },
+           .set = sd_setvflip,
+           .get = sd_getvflip,
+       },
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+       {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+               .bytesperline = 640,
+               .sizeimage = 640 * 480 * 3 / 8 + 590,
+               .colorspace = V4L2_COLORSPACE_JPEG,
+               .priv = 0},
+};
+
+#define LOAD_PAGE3             255
+#define LOAD_PAGE4             254
+#define END_OF_SEQUENCE                0
+
+/* pac 7302 */
+static const __u8 init_7302[] = {
+/*     index,value */
+       0xff, 0x01,             /* page 1 */
+       0x78, 0x00,             /* deactivate */
+       0xff, 0x01,
+       0x78, 0x40,             /* led off */
+};
+static const __u8 start_7302[] = {
+/*     index, len, [value]* */
+       0xff, 1,        0x00,           /* page 0 */
+       0x00, 12,       0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
+                       0x00, 0x00, 0x00, 0x00,
+       0x0d, 24,       0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
+                       0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
+                       0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
+       0x26, 2,        0xaa, 0xaa,
+       0x2e, 1,        0x31,
+       0x38, 1,        0x01,
+       0x3a, 3,        0x14, 0xff, 0x5a,
+       0x43, 11,       0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
+                       0x00, 0x54, 0x11,
+       0x55, 1,        0x00,
+       0x62, 4,        0x10, 0x1e, 0x1e, 0x18,
+       0x6b, 1,        0x00,
+       0x6e, 3,        0x08, 0x06, 0x00,
+       0x72, 3,        0x00, 0xff, 0x00,
+       0x7d, 23,       0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
+                       0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
+                       0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
+       0xa2, 10,       0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
+                       0xd2, 0xeb,
+       0xaf, 1,        0x02,
+       0xb5, 2,        0x08, 0x08,
+       0xb8, 2,        0x08, 0x88,
+       0xc4, 4,        0xae, 0x01, 0x04, 0x01,
+       0xcc, 1,        0x00,
+       0xd1, 11,       0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
+                       0xc1, 0xd7, 0xec,
+       0xdc, 1,        0x01,
+       0xff, 1,        0x01,           /* page 1 */
+       0x12, 3,        0x02, 0x00, 0x01,
+       0x3e, 2,        0x00, 0x00,
+       0x76, 5,        0x01, 0x20, 0x40, 0x00, 0xf2,
+       0x7c, 1,        0x00,
+       0x7f, 10,       0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
+                       0x02, 0x00,
+       0x96, 5,        0x01, 0x10, 0x04, 0x01, 0x04,
+       0xc8, 14,       0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+                       0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
+       0xd8, 1,        0x01,
+       0xdb, 2,        0x00, 0x01,
+       0xde, 7,        0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
+       0xe6, 4,        0x00, 0x00, 0x00, 0x01,
+       0xeb, 1,        0x00,
+       0xff, 1,        0x02,           /* page 2 */
+       0x22, 1,        0x00,
+       0xff, 1,        0x03,           /* page 3 */
+       0, LOAD_PAGE3,                  /* load the page 3 */
+       0x11, 1,        0x01,
+       0xff, 1,        0x02,           /* page 2 */
+       0x13, 1,        0x00,
+       0x22, 4,        0x1f, 0xa4, 0xf0, 0x96,
+       0x27, 2,        0x14, 0x0c,
+       0x2a, 5,        0xc8, 0x00, 0x18, 0x12, 0x22,
+       0x64, 8,        0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
+       0x6e, 1,        0x08,
+       0xff, 1,        0x01,           /* page 1 */
+       0x78, 1,        0x00,
+       0, END_OF_SEQUENCE              /* end of sequence */
+};
+
+#define SKIP           0xaa
+/* page 3 - the value SKIP says skip the index - see reg_w_page() */
+static const __u8 page3_7302[] = {
+       0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
+       0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
+       0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
+       0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
+       0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
+       0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
+       0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
+       0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
+       0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
+       0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
+       0x00
+};
+
+static int reg_w_buf(struct gspca_dev *gspca_dev,
+                 __u8 index,
+                 const char *buffer, int len)
+{
+       int ret;
+
+       memcpy(gspca_dev->usb_buf, buffer, len);
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       1,              /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0,              /* value */
+                       index, gspca_dev->usb_buf, len,
+                       500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg_w_buf(): "
+               "Failed to write registers to index 0x%x, error %i",
+               index, ret);
+       return ret;
+}
+
+
+static int reg_w(struct gspca_dev *gspca_dev,
+                 __u8 index,
+                 __u8 value)
+{
+       int ret;
+
+       gspca_dev->usb_buf[0] = value;
+       ret = usb_control_msg(gspca_dev->dev,
+                       usb_sndctrlpipe(gspca_dev->dev, 0),
+                       0,                      /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, index, gspca_dev->usb_buf, 1,
+                       500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg_w(): "
+               "Failed to write register to index 0x%x, value 0x%x, error %i",
+               index, value, ret);
+       return ret;
+}
+
+static int reg_w_seq(struct gspca_dev *gspca_dev,
+               const __u8 *seq, int len)
+{
+       int ret = 0;
+       while (--len >= 0) {
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, seq[0], seq[1]);
+               seq += 2;
+       }
+       return ret;
+}
+
+/* load the beginning of a page */
+static int reg_w_page(struct gspca_dev *gspca_dev,
+                       const __u8 *page, int len)
+{
+       int index;
+       int ret = 0;
+
+       for (index = 0; index < len; index++) {
+               if (page[index] == SKIP)                /* skip this index */
+                       continue;
+               gspca_dev->usb_buf[0] = page[index];
+               ret = usb_control_msg(gspca_dev->dev,
+                               usb_sndctrlpipe(gspca_dev->dev, 0),
+                               0,                      /* request */
+                       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                               0, index, gspca_dev->usb_buf, 1,
+                               500);
+               if (ret < 0) {
+                       PDEBUG(D_ERR, "reg_w_page(): "
+                       "Failed to write register to index 0x%x, "
+                       "value 0x%x, error %i",
+                       index, page[index], ret);
+                       break;
+               }
+       }
+       return ret;
+}
+
+/* output a variable sequence */
+static int reg_w_var(struct gspca_dev *gspca_dev,
+                       const __u8 *seq,
+                       const __u8 *page3, unsigned int page3_len,
+                       const __u8 *page4, unsigned int page4_len)
+{
+       int index, len;
+       int ret = 0;
+
+       for (;;) {
+               index = *seq++;
+               len = *seq++;
+               switch (len) {
+               case END_OF_SEQUENCE:
+                       return ret;
+               case LOAD_PAGE4:
+                       ret = reg_w_page(gspca_dev, page4, page4_len);
+                       break;
+               case LOAD_PAGE3:
+                       ret = reg_w_page(gspca_dev, page3, page3_len);
+                       break;
+               default:
+                       if (len > USB_BUF_SZ) {
+                               PDEBUG(D_ERR|D_STREAM,
+                                       "Incorrect variable sequence");
+                               return -EINVAL;
+                       }
+                       while (len > 0) {
+                               if (len < 8) {
+                                       ret = reg_w_buf(gspca_dev,
+                                               index, seq, len);
+                                       if (ret < 0)
+                                               return ret;
+                                       seq += len;
+                                       break;
+                               }
+                               ret = reg_w_buf(gspca_dev, index, seq, 8);
+                               seq += 8;
+                               index += 8;
+                               len -= 8;
+                       }
+               }
+               if (ret < 0)
+                       return ret;
+       }
+       /* not reached */
+}
+
+/* this function is called at probe time for pac7302 */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+
+       cam = &gspca_dev->cam;
+
+       PDEBUG(D_CONF, "Find Sensor PAC7302");
+       cam->cam_mode = vga_mode;       /* only 640x480 */
+       cam->nmodes = ARRAY_SIZE(vga_mode);
+
+       sd->brightness = BRIGHTNESS_DEF;
+       sd->contrast = CONTRAST_DEF;
+       sd->colors = COLOR_DEF;
+       sd->white_balance = WHITEBALANCE_DEF;
+       sd->red_balance = REDBALANCE_DEF;
+       sd->blue_balance = BLUEBALANCE_DEF;
+       sd->gain = GAIN_DEF;
+       sd->exposure = EXPOSURE_DEF;
+       sd->autogain = AUTOGAIN_DEF;
+       sd->hflip = HFLIP_DEF;
+       sd->vflip = VFLIP_DEF;
+       return 0;
+}
+
+/* This function is used by pac7302 only */
+static int setbrightcont(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i, v;
+       int ret;
+       static const __u8 max[10] =
+               {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
+                0xd4, 0xec};
+       static const __u8 delta[10] =
+               {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
+                0x11, 0x0b};
+
+       ret = reg_w(gspca_dev, 0xff, 0x00);     /* page 0 */
+       for (i = 0; i < 10; i++) {
+               v = max[i];
+               v += (sd->brightness - BRIGHTNESS_MAX)
+                       * 150 / BRIGHTNESS_MAX;         /* 200 ? */
+               v -= delta[i] * sd->contrast / CONTRAST_MAX;
+               if (v < 0)
+                       v = 0;
+               else if (v > 0xff)
+                       v = 0xff;
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0xa2 + i, v);
+       }
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xdc, 0x01);
+       return ret;
+}
+
+/* This function is used by pac7302 only */
+static int setcolors(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int i, v;
+       int ret;
+       static const int a[9] =
+               {217, -212, 0, -101, 170, -67, -38, -315, 355};
+       static const int b[9] =
+               {19, 106, 0, 19, 106, 1, 19, 106, 1};
+
+       ret = reg_w(gspca_dev, 0xff, 0x03);     /* page 3 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xff, 0x00);     /* page 0 */
+       for (i = 0; i < 9; i++) {
+               v = a[i] * sd->colors / COLOR_MAX + b[i];
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
+       }
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xdc, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
+       return ret;
+}
+
+static int setwhitebalance(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_w(gspca_dev, 0xff, 0x00);     /* page 0 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xc6, sd->white_balance);
+
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xdc, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance);
+       return ret;
+}
+
+static int setredbalance(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_w(gspca_dev, 0xff, 0x00);     /* page 0 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xc5, sd->red_balance);
+
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xdc, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance);
+       return ret;
+}
+
+static int setbluebalance(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_w(gspca_dev, 0xff, 0x00);     /* page 0 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xc7, sd->blue_balance);
+
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xdc, 0x01);
+       PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance);
+       return ret;
+}
+
+static int setgain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+
+       ret = reg_w(gspca_dev, 0xff, 0x03);             /* page 3 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x10, sd->gain >> 3);
+
+       /* load registers to sensor (Bit 0, auto clear) */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
+}
+
+static int setexposure(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+       __u8 reg;
+
+       /* register 2 of frame 3/4 contains the clock divider configuring the
+          no fps according to the formula: 60 / reg. sd->exposure is the
+          desired exposure time in ms. */
+       reg = 120 * sd->exposure / 1000;
+       if (reg < 2)
+               reg = 2;
+       else if (reg > 63)
+               reg = 63;
+
+       /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
+          the nearest multiple of 3, except when between 6 and 12? */
+       if (reg < 6 || reg > 12)
+               reg = ((reg + 1) / 3) * 3;
+       ret = reg_w(gspca_dev, 0xff, 0x03);             /* page 3 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x02, reg);
+
+       /* load registers to sensor (Bit 0, auto clear) */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
+}
+
+static int sethvflip(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
+       __u8 data;
+
+       ret = reg_w(gspca_dev, 0xff, 0x03);             /* page 3 */
+       data = (sd->hflip ? 0x08 : 0x00) | (sd->vflip ? 0x04 : 0x00);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x21, data);
+       /* load registers to sensor (Bit 0, auto clear) */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
+}
+
+/* this function is called at probe and resume time for pac7302 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+       return reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret = 0;
+
+       sd->sof_read = 0;
+
+       ret = reg_w_var(gspca_dev, start_7302,
+               page3_7302, sizeof(page3_7302),
+               NULL, 0);
+       if (0 <= ret)
+               ret = setbrightcont(gspca_dev);
+       if (0 <= ret)
+               ret = setcolors(gspca_dev);
+       if (0 <= ret)
+               ret = setwhitebalance(gspca_dev);
+       if (0 <= ret)
+               ret = setredbalance(gspca_dev);
+       if (0 <= ret)
+               ret = setbluebalance(gspca_dev);
+       if (0 <= ret)
+               ret = setgain(gspca_dev);
+       if (0 <= ret)
+               ret = setexposure(gspca_dev);
+       if (0 <= ret)
+               ret = sethvflip(gspca_dev);
+
+       /* only resolution 640x480 is supported for pac7302 */
+
+       sd->sof_read = 0;
+       sd->autogain_ignore_frames = 0;
+       atomic_set(&sd->avg_lum, -1);
+
+       /* start stream */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xff, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x01);
+
+       return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       int ret;
+
+       /* stop stream */
+       ret = reg_w(gspca_dev, 0xff, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect for pac7302 */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+       int ret;
+
+       if (!gspca_dev->present)
+               return;
+       ret = reg_w(gspca_dev, 0xff, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x40);
+}
+
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int avg_lum = atomic_read(&sd->avg_lum);
+       int desired_lum, deadzone;
+
+       if (avg_lum == -1)
+               return;
+
+       desired_lum = 270 + sd->brightness * 4;
+       /* Hack hack, with the 7202 the first exposure step is
+          pretty large, so if we're about to make the first
+          exposure increase make the deadzone large to avoid
+          oscilating */
+       if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
+                       sd->exposure > EXPOSURE_DEF &&
+                       sd->exposure < 42)
+               deadzone = 90;
+       else
+               deadzone = 30;
+
+       if (sd->autogain_ignore_frames > 0)
+               sd->autogain_ignore_frames--;
+       else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
+                       deadzone, GAIN_KNEE, EXPOSURE_KNEE))
+               sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+}
+
+/* JPEG header, part 1 */
+static const unsigned char pac_jpeg_header1[] = {
+  0xff, 0xd8,          /* SOI: Start of Image */
+
+  0xff, 0xc0,          /* SOF0: Start of Frame (Baseline DCT) */
+  0x00, 0x11,          /* length = 17 bytes (including this length field) */
+  0x08                 /* Precision: 8 */
+  /* 2 bytes is placed here: number of image lines */
+  /* 2 bytes is placed here: samples per line */
+};
+
+/* JPEG header, continued */
+static const unsigned char pac_jpeg_header2[] = {
+  0x03,                        /* Number of image components: 3 */
+  0x01, 0x21, 0x00,    /* ID=1, Subsampling 1x1, Quantization table: 0 */
+  0x02, 0x11, 0x01,    /* ID=2, Subsampling 2x1, Quantization table: 1 */
+  0x03, 0x11, 0x01,    /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+  0xff, 0xda,          /* SOS: Start Of Scan */
+  0x00, 0x0c,          /* length = 12 bytes (including this length field) */
+  0x03,                        /* number of components: 3 */
+  0x01, 0x00,          /* selector 1, table 0x00 */
+  0x02, 0x11,          /* selector 2, table 0x11 */
+  0x03, 0x11,          /* selector 3, table 0x11 */
+  0x00, 0x3f,          /* Spectral selection: 0 .. 63 */
+  0x00                 /* Successive approximation: 0 */
+};
+
+static void pac_start_frame(struct gspca_dev *gspca_dev,
+               struct gspca_frame *frame,
+               __u16 lines, __u16 samples_per_line)
+{
+       unsigned char tmpbuf[4];
+
+       gspca_frame_add(gspca_dev, FIRST_PACKET,
+               pac_jpeg_header1, sizeof(pac_jpeg_header1));
+
+       tmpbuf[0] = lines >> 8;
+       tmpbuf[1] = lines & 0xff;
+       tmpbuf[2] = samples_per_line >> 8;
+       tmpbuf[3] = samples_per_line & 0xff;
+
+       gspca_frame_add(gspca_dev, INTER_PACKET,
+               tmpbuf, sizeof(tmpbuf));
+       gspca_frame_add(gspca_dev, INTER_PACKET,
+               pac_jpeg_header2, sizeof(pac_jpeg_header2));
+}
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data,                       /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_frame *frame;
+       unsigned char *sof;
+
+       sof = pac_find_sof(&sd->sof_read, data, len);
+       if (sof) {
+               int n, lum_offset, footer_length;
+
+               frame = gspca_get_i_frame(gspca_dev);
+               if (frame == NULL) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       return;
+               }
+
+               /* 6 bytes after the FF D9 EOF marker a number of lumination
+                  bytes are send corresponding to different parts of the
+                  image, the 14th and 15th byte after the EOF seem to
+                  correspond to the center of the image */
+               lum_offset = 61 + sizeof pac_sof_marker;
+               footer_length = 74;
+
+               /* Finish decoding current frame */
+               n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+               if (n < 0) {
+                       frame->data_end += n;
+                       n = 0;
+               }
+               gspca_frame_add(gspca_dev, INTER_PACKET,
+                                       data, n);
+               if (gspca_dev->last_packet_type != DISCARD_PACKET &&
+                               frame->data_end[-2] == 0xff &&
+                               frame->data_end[-1] == 0xd9)
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
+                                               NULL, 0);
+
+               n = sof - data;
+               len -= n;
+               data = sof;
+
+               /* Get average lumination */
+               if (gspca_dev->last_packet_type == LAST_PACKET &&
+                               n >= lum_offset)
+                       atomic_set(&sd->avg_lum, data[-lum_offset] +
+                                               data[-lum_offset + 1]);
+               else
+                       atomic_set(&sd->avg_lum, -1);
+
+               /* Start the new frame with the jpeg header */
+               /* The PAC7302 has the image rotated 90 degrees */
+               pac_start_frame(gspca_dev, frame,
+                       gspca_dev->width, gspca_dev->height);
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->brightness = val;
+       if (gspca_dev->streaming)
+               setbrightcont(gspca_dev);
+       return 0;
+}
+
+static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->brightness;
+       return 0;
+}
+
+static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->contrast = val;
+       if (gspca_dev->streaming) {
+               setbrightcont(gspca_dev);
+       }
+       return 0;
+}
+
+static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->contrast;
+       return 0;
+}
+
+static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->colors = val;
+       if (gspca_dev->streaming)
+               setcolors(gspca_dev);
+       return 0;
+}
+
+static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->colors;
+       return 0;
+}
+
+static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret = 0;
+
+       sd->white_balance = val;
+       if (gspca_dev->streaming)
+               ret = setwhitebalance(gspca_dev);
+       if (0 <= ret)
+               ret = 0;
+       return ret;
+}
+
+static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->white_balance;
+       return 0;
+}
+
+static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret = 0;
+
+       sd->red_balance = val;
+       if (gspca_dev->streaming)
+               ret = setredbalance(gspca_dev);
+       if (0 <= ret)
+               ret = 0;
+       return ret;
+}
+
+static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->red_balance;
+       return 0;
+}
+
+static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       int ret = 0;
+
+       sd->blue_balance = val;
+       if (gspca_dev->streaming)
+               ret = setbluebalance(gspca_dev);
+       if (0 <= ret)
+               ret = 0;
+       return ret;
+}
+
+static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->blue_balance;
+       return 0;
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->gain = val;
+       if (gspca_dev->streaming)
+               setgain(gspca_dev);
+       return 0;
+}
+
+static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->gain;
+       return 0;
+}
+
+static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->exposure = val;
+       if (gspca_dev->streaming)
+               setexposure(gspca_dev);
+       return 0;
+}
+
+static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->exposure;
+       return 0;
+}
+
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->autogain = val;
+       /* when switching to autogain set defaults to make sure
+          we are on a valid point of the autogain gain /
+          exposure knee graph, and give this change time to
+          take effect before doing autogain. */
+       if (sd->autogain) {
+               sd->exposure = EXPOSURE_DEF;
+               sd->gain = GAIN_DEF;
+               if (gspca_dev->streaming) {
+                       sd->autogain_ignore_frames =
+                               PAC_AUTOGAIN_IGNORE_FRAMES;
+                       setexposure(gspca_dev);
+                       setgain(gspca_dev);
+               }
+       }
+
+       return 0;
+}
+
+static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->autogain;
+       return 0;
+}
+
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->hflip = val;
+       if (gspca_dev->streaming)
+               sethvflip(gspca_dev);
+       return 0;
+}
+
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->hflip;
+       return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       sd->vflip = val;
+       if (gspca_dev->streaming)
+               sethvflip(gspca_dev);
+       return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       *val = sd->vflip;
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+                       struct v4l2_dbg_register *reg)
+{
+       int ret = -EINVAL;
+       __u8 index;
+       __u8 value;
+
+       /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
+                              long on the USB bus)
+       */
+       if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
+           reg->match.addr == 0 &&
+           (reg->reg < 0x000000ff) &&
+           (reg->val <= 0x000000ff)
+       ) {
+               /* Currently writing to page 0 is only supported. */
+               /* reg_w() only supports 8bit index */
+               index = reg->reg & 0x000000ff;
+               value = reg->val & 0x000000ff;
+
+               /* Note that there shall be no access to other page
+                  by any other function between the page swith and
+                  the actual register write */
+               ret = reg_w(gspca_dev, 0xff, 0x00);     /* page 0 */
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, index, value);
+
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0xdc, 0x01);
+       }
+       return ret;
+}
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+                       struct v4l2_dbg_chip_ident *chip)
+{
+       int ret = -EINVAL;
+
+       if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
+           chip->match.addr == 0) {
+               chip->revision = 0;
+               chip->ident = V4L2_IDENT_UNKNOWN;
+               ret = 0;
+       }
+       return ret;
+}
+#endif
+
+/* sub-driver description for pac7302 */
+static struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .init = sd_init,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .pkt_scan = sd_pkt_scan,
+       .dq_callback = do_autogain,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .set_register = sd_dbg_s_register,
+       .get_chip_ident = sd_chip_ident,
+#endif
+};
+
+/* -- module initialisation -- */
+static __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x06f8, 0x3009)},
+       {USB_DEVICE(0x093a, 0x2620)},
+       {USB_DEVICE(0x093a, 0x2621)},
+       {USB_DEVICE(0x093a, 0x2622)},
+       {USB_DEVICE(0x093a, 0x2624)},
+       {USB_DEVICE(0x093a, 0x2626)},
+       {USB_DEVICE(0x093a, 0x2628)},
+       {USB_DEVICE(0x093a, 0x2629)},
+       {USB_DEVICE(0x093a, 0x262a)},
+       {USB_DEVICE(0x093a, 0x262c)},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+       .suspend = gspca_suspend,
+       .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       int ret;
+       ret = usb_register(&sd_driver);
+       if (ret < 0)
+               return ret;
+       PDEBUG(D_PROBE, "registered");
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
index 0527144..e5697a6 100644 (file)
@@ -57,23 +57,17 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
 MODULE_DESCRIPTION("Pixart PAC7311");
 MODULE_LICENSE("GPL");
 
-/* specific webcam descriptor */
+/* specific webcam descriptor for pac7311 */
 struct sd {
        struct gspca_dev gspca_dev;             /* !! must be the first item */
 
-       unsigned char brightness;
        unsigned char contrast;
-       unsigned char colors;
        unsigned char gain;
        unsigned char exposure;
        unsigned char autogain;
        __u8 hflip;
        __u8 vflip;
 
-       __u8 sensor;
-#define SENSOR_PAC7302 0
-#define SENSOR_PAC7311 1
-
        u8 sof_read;
        u8 autogain_ignore_frames;
 
@@ -81,12 +75,8 @@ struct sd {
 };
 
 /* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
 static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
@@ -99,23 +89,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
 static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
 
 static struct ctrl sd_ctrls[] = {
-/* This control is pac7302 only */
-#define BRIGHTNESS_IDX 0
-       {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-#define BRIGHTNESS_MAX 0x20
-               .maximum = BRIGHTNESS_MAX,
-               .step    = 1,
-#define BRIGHTNESS_DEF 0x10
-               .default_value = BRIGHTNESS_DEF,
-           },
-           .set = sd_setbrightness,
-           .get = sd_getbrightness,
-       },
 /* This control is for both the 7302 and the 7311 */
        {
            {
@@ -132,23 +105,6 @@ static struct ctrl sd_ctrls[] = {
            .set = sd_setcontrast,
            .get = sd_getcontrast,
        },
-/* This control is pac7302 only */
-#define SATURATION_IDX 2
-       {
-           {
-               .id      = V4L2_CID_SATURATION,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Saturation",
-               .minimum = 0,
-#define COLOR_MAX 255
-               .maximum = COLOR_MAX,
-               .step    = 1,
-#define COLOR_DEF 127
-               .default_value = COLOR_DEF,
-           },
-           .set = sd_setcolors,
-           .get = sd_getcolors,
-       },
 /* All controls below are for both the 7302 and the 7311 */
        {
            {
@@ -244,101 +200,9 @@ static const struct v4l2_pix_format vga_mode[] = {
                .priv = 0},
 };
 
-/* pac 7302 */
-static const __u8 init_7302[] = {
-/*     index,value */
-       0xff, 0x01,             /* page 1 */
-       0x78, 0x00,             /* deactivate */
-       0xff, 0x01,
-       0x78, 0x40,             /* led off */
-};
-static const __u8 start_7302[] = {
-/*     index, len, [value]* */
-       0xff, 1,        0x00,           /* page 0 */
-       0x00, 12,       0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
-                       0x00, 0x00, 0x00, 0x00,
-       0x0d, 24,       0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
-                       0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
-                       0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
-       0x26, 2,        0xaa, 0xaa,
-       0x2e, 1,        0x31,
-       0x38, 1,        0x01,
-       0x3a, 3,        0x14, 0xff, 0x5a,
-       0x43, 11,       0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
-                       0x00, 0x54, 0x11,
-       0x55, 1,        0x00,
-       0x62, 4,        0x10, 0x1e, 0x1e, 0x18,
-       0x6b, 1,        0x00,
-       0x6e, 3,        0x08, 0x06, 0x00,
-       0x72, 3,        0x00, 0xff, 0x00,
-       0x7d, 23,       0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
-                       0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
-                       0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
-       0xa2, 10,       0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
-                       0xd2, 0xeb,
-       0xaf, 1,        0x02,
-       0xb5, 2,        0x08, 0x08,
-       0xb8, 2,        0x08, 0x88,
-       0xc4, 4,        0xae, 0x01, 0x04, 0x01,
-       0xcc, 1,        0x00,
-       0xd1, 11,       0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
-                       0xc1, 0xd7, 0xec,
-       0xdc, 1,        0x01,
-       0xff, 1,        0x01,           /* page 1 */
-       0x12, 3,        0x02, 0x00, 0x01,
-       0x3e, 2,        0x00, 0x00,
-       0x76, 5,        0x01, 0x20, 0x40, 0x00, 0xf2,
-       0x7c, 1,        0x00,
-       0x7f, 10,       0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
-                       0x02, 0x00,
-       0x96, 5,        0x01, 0x10, 0x04, 0x01, 0x04,
-       0xc8, 14,       0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
-                       0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
-       0xd8, 1,        0x01,
-       0xdb, 2,        0x00, 0x01,
-       0xde, 7,        0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
-       0xe6, 4,        0x00, 0x00, 0x00, 0x01,
-       0xeb, 1,        0x00,
-       0xff, 1,        0x02,           /* page 2 */
-       0x22, 1,        0x00,
-       0xff, 1,        0x03,           /* page 3 */
-       0x00, 255,                      /* load the page 3 */
-       0x11, 1,        0x01,
-       0xff, 1,        0x02,           /* page 2 */
-       0x13, 1,        0x00,
-       0x22, 4,        0x1f, 0xa4, 0xf0, 0x96,
-       0x27, 2,        0x14, 0x0c,
-       0x2a, 5,        0xc8, 0x00, 0x18, 0x12, 0x22,
-       0x64, 8,        0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
-       0x6e, 1,        0x08,
-       0xff, 1,        0x01,           /* page 1 */
-       0x78, 1,        0x00,
-       0, 0                            /* end of sequence */
-};
-
-/* page 3 - the value 0xaa says skip the index - see reg_w_page() */
-static const __u8 page3_7302[] = {
-       0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
-       0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
-       0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
-       0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
-       0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
-       0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
-       0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
-       0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
-       0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
-       0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
-       0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
-       0x00
-};
+#define LOAD_PAGE3             255
+#define LOAD_PAGE4             254
+#define END_OF_SEQUENCE                0
 
 /* pac 7311 */
 static const __u8 init_7311[] = {
@@ -378,119 +242,154 @@ static const __u8 start_7311[] = {
        0xf0, 13,       0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
                        0x3f, 0x00, 0x0a, 0x01, 0x00,
        0xff, 1,        0x04,           /* page 4 */
-       0x00, 254,                      /* load the page 4 */
+       0, LOAD_PAGE4,                  /* load the page 4 */
        0x11, 1,        0x01,
-       0, 0                            /* end of sequence */
+       0, END_OF_SEQUENCE              /* end of sequence */
 };
 
-/* page 4 - the value 0xaa says skip the index - see reg_w_page() */
+#define SKIP           0xaa
+/* page 4 - the value SKIP says skip the index - see reg_w_page() */
 static const __u8 page4_7311[] = {
-       0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
-       0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62,
-       0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa,
-       0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68,
+       SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
+       0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
+       0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
+       SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
        0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
        0x23, 0x28, 0x04, 0x11, 0x00, 0x00
 };
 
-static void reg_w_buf(struct gspca_dev *gspca_dev,
+static int reg_w_buf(struct gspca_dev *gspca_dev,
                  __u8 index,
                  const char *buffer, int len)
 {
+       int ret;
+
        memcpy(gspca_dev->usb_buf, buffer, len);
-       usb_control_msg(gspca_dev->dev,
+       ret = usb_control_msg(gspca_dev->dev,
                        usb_sndctrlpipe(gspca_dev->dev, 0),
                        1,              /* request */
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0,              /* value */
                        index, gspca_dev->usb_buf, len,
                        500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg_w_buf(): "
+               "Failed to write registers to index 0x%x, error %i",
+               index, ret);
+       return ret;
 }
 
 
-static void reg_w(struct gspca_dev *gspca_dev,
+static int reg_w(struct gspca_dev *gspca_dev,
                  __u8 index,
                  __u8 value)
 {
+       int ret;
+
        gspca_dev->usb_buf[0] = value;
-       usb_control_msg(gspca_dev->dev,
+       ret = usb_control_msg(gspca_dev->dev,
                        usb_sndctrlpipe(gspca_dev->dev, 0),
                        0,                      /* request */
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                        0, index, gspca_dev->usb_buf, 1,
                        500);
+       if (ret < 0)
+               PDEBUG(D_ERR, "reg_w(): "
+               "Failed to write register to index 0x%x, value 0x%x, error %i",
+               index, value, ret);
+       return ret;
 }
 
-static void reg_w_seq(struct gspca_dev *gspca_dev,
+static int reg_w_seq(struct gspca_dev *gspca_dev,
                const __u8 *seq, int len)
 {
+       int ret = 0;
        while (--len >= 0) {
-               reg_w(gspca_dev, seq[0], seq[1]);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, seq[0], seq[1]);
                seq += 2;
        }
+       return ret;
 }
 
 /* load the beginning of a page */
-static void reg_w_page(struct gspca_dev *gspca_dev,
+static int reg_w_page(struct gspca_dev *gspca_dev,
                        const __u8 *page, int len)
 {
        int index;
+       int ret = 0;
 
        for (index = 0; index < len; index++) {
-               if (page[index] == 0xaa)                /* skip this index */
+               if (page[index] == SKIP)                /* skip this index */
                        continue;
                gspca_dev->usb_buf[0] = page[index];
-               usb_control_msg(gspca_dev->dev,
+               ret = usb_control_msg(gspca_dev->dev,
                                usb_sndctrlpipe(gspca_dev->dev, 0),
                                0,                      /* request */
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                                0, index, gspca_dev->usb_buf, 1,
                                500);
+               if (ret < 0) {
+                       PDEBUG(D_ERR, "reg_w_page(): "
+                       "Failed to write register to index 0x%x, "
+                       "value 0x%x, error %i",
+                       index, page[index], ret);
+                       break;
+               }
        }
+       return ret;
 }
 
 /* output a variable sequence */
-static void reg_w_var(struct gspca_dev *gspca_dev,
-                       const __u8 *seq)
+static int reg_w_var(struct gspca_dev *gspca_dev,
+                       const __u8 *seq,
+                       const __u8 *page3, unsigned int page3_len,
+                       const __u8 *page4, unsigned int page4_len)
 {
        int index, len;
+       int ret = 0;
 
        for (;;) {
                index = *seq++;
                len = *seq++;
                switch (len) {
-               case 0:
-                       return;
-               case 254:
-                       reg_w_page(gspca_dev, page4_7311, sizeof page4_7311);
+               case END_OF_SEQUENCE:
+                       return ret;
+               case LOAD_PAGE4:
+                       ret = reg_w_page(gspca_dev, page4, page4_len);
                        break;
-               case 255:
-                       reg_w_page(gspca_dev, page3_7302, sizeof page3_7302);
+               case LOAD_PAGE3:
+                       ret = reg_w_page(gspca_dev, page3, page3_len);
                        break;
                default:
-                       if (len > 64) {
+                       if (len > USB_BUF_SZ) {
                                PDEBUG(D_ERR|D_STREAM,
                                        "Incorrect variable sequence");
-                               return;
+                               return -EINVAL;
                        }
                        while (len > 0) {
                                if (len < 8) {
-                                       reg_w_buf(gspca_dev, index, seq, len);
+                                       ret = reg_w_buf(gspca_dev,
+                                               index, seq, len);
+                                       if (ret < 0)
+                                               return ret;
                                        seq += len;
                                        break;
                                }
-                               reg_w_buf(gspca_dev, index, seq, 8);
+                               ret = reg_w_buf(gspca_dev, index, seq, 8);
                                seq += 8;
                                index += 8;
                                len -= 8;
                        }
                }
+               if (ret < 0)
+                       return ret;
        }
        /* not reached */
 }
 
-/* this function is called at probe time */
+/* this function is called at probe time for pac7311 */
 static int sd_config(struct gspca_dev *gspca_dev,
                        const struct usb_device_id *id)
 {
@@ -499,22 +398,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        cam = &gspca_dev->cam;
 
-       sd->sensor = id->driver_info;
-       if (sd->sensor == SENSOR_PAC7302) {
-               PDEBUG(D_CONF, "Find Sensor PAC7302");
-               cam->cam_mode = &vga_mode[2];   /* only 640x480 */
-               cam->nmodes = 1;
-       } else {
-               PDEBUG(D_CONF, "Find Sensor PAC7311");
-               cam->cam_mode = vga_mode;
-               cam->nmodes = ARRAY_SIZE(vga_mode);
-               gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX)
-                               | (1 << SATURATION_IDX);
-       }
+       PDEBUG(D_CONF, "Find Sensor PAC7311");
+       cam->cam_mode = vga_mode;
+       cam->nmodes = ARRAY_SIZE(vga_mode);
 
-       sd->brightness = BRIGHTNESS_DEF;
        sd->contrast = CONTRAST_DEF;
-       sd->colors = COLOR_DEF;
        sd->gain = GAIN_DEF;
        sd->exposure = EXPOSURE_DEF;
        sd->autogain = AUTOGAIN_DEF;
@@ -523,91 +411,47 @@ static int sd_config(struct gspca_dev *gspca_dev,
        return 0;
 }
 
-/* This function is used by pac7302 only */
-static void setbrightcont(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int i, v;
-       static const __u8 max[10] =
-               {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
-                0xd4, 0xec};
-       static const __u8 delta[10] =
-               {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
-                0x11, 0x0b};
-
-       reg_w(gspca_dev, 0xff, 0x00);   /* page 0 */
-       for (i = 0; i < 10; i++) {
-               v = max[i];
-               v += (sd->brightness - BRIGHTNESS_MAX)
-                       * 150 / BRIGHTNESS_MAX;         /* 200 ? */
-               v -= delta[i] * sd->contrast / CONTRAST_MAX;
-               if (v < 0)
-                       v = 0;
-               else if (v > 0xff)
-                       v = 0xff;
-               reg_w(gspca_dev, 0xa2 + i, v);
-       }
-       reg_w(gspca_dev, 0xdc, 0x01);
-}
-
 /* This function is used by pac7311 only */
-static void setcontrast(struct gspca_dev *gspca_dev)
+static int setcontrast(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
 
-       reg_w(gspca_dev, 0xff, 0x04);
-       reg_w(gspca_dev, 0x10, sd->contrast >> 4);
+       ret = reg_w(gspca_dev, 0xff, 0x04);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4);
        /* load registers to sensor (Bit 0, auto clear) */
-       reg_w(gspca_dev, 0x11, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
 }
 
-/* This function is used by pac7302 only */
-static void setcolors(struct gspca_dev *gspca_dev)
+static int setgain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int i, v;
-       static const int a[9] =
-               {217, -212, 0, -101, 170, -67, -38, -315, 355};
-       static const int b[9] =
-               {19, 106, 0, 19, 106, 1, 19, 106, 1};
-
-       reg_w(gspca_dev, 0xff, 0x03);   /* page 3 */
-       reg_w(gspca_dev, 0x11, 0x01);
-       reg_w(gspca_dev, 0xff, 0x00);   /* page 0 */
-       reg_w(gspca_dev, 0xff, 0x00);   /* page 0 */
-       for (i = 0; i < 9; i++) {
-               v = a[i] * sd->colors / COLOR_MAX + b[i];
-               reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
-               reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
-       }
-       reg_w(gspca_dev, 0xdc, 0x01);
-       PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
-}
+       int gain = GAIN_MAX - sd->gain;
+       int ret;
 
-static void setgain(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       if (gain < 1)
+               gain = 1;
+       else if (gain > 245)
+               gain = 245;
+       ret = reg_w(gspca_dev, 0xff, 0x04);             /* page 4 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x0e, 0x00);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x0f, gain);
 
-       if (sd->sensor == SENSOR_PAC7302) {
-               reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
-               reg_w(gspca_dev, 0x10, sd->gain >> 3);
-       } else {
-               int gain = GAIN_MAX - sd->gain;
-               if (gain < 1)
-                       gain = 1;
-               else if (gain > 245)
-                       gain = 245;
-               reg_w(gspca_dev, 0xff, 0x04);           /* page 4 */
-               reg_w(gspca_dev, 0x0e, 0x00);
-               reg_w(gspca_dev, 0x0f, gain);
-       }
        /* load registers to sensor (Bit 0, auto clear) */
-       reg_w(gspca_dev, 0x11, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static int setexposure(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
        __u8 reg;
 
        /* register 2 of frame 3/4 contains the clock divider configuring the
@@ -619,97 +463,94 @@ static void setexposure(struct gspca_dev *gspca_dev)
        else if (reg > 63)
                reg = 63;
 
-       if (sd->sensor == SENSOR_PAC7302) {
-               /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
-                  the nearest multiple of 3, except when between 6 and 12? */
-               if (reg < 6 || reg > 12)
-                       reg = ((reg + 1) / 3) * 3;
-               reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
-               reg_w(gspca_dev, 0x02, reg);
+       ret = reg_w(gspca_dev, 0xff, 0x04);             /* page 4 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x02, reg);
+       /* Page 1 register 8 must always be 0x08 except when not in
+          640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xff, 0x01);
+       if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
+                       reg <= 3) {
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x08, 0x09);
        } else {
-               reg_w(gspca_dev, 0xff, 0x04);           /* page 4 */
-               reg_w(gspca_dev, 0x02, reg);
-               /* Page 1 register 8 must always be 0x08 except when not in
-                  640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
-               reg_w(gspca_dev, 0xff, 0x01);
-               if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
-                               reg <= 3)
-                       reg_w(gspca_dev, 0x08, 0x09);
-               else
-                       reg_w(gspca_dev, 0x08, 0x08);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x08, 0x08);
        }
+
        /* load registers to sensor (Bit 0, auto clear) */
-       reg_w(gspca_dev, 0x11, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
 }
 
-static void sethvflip(struct gspca_dev *gspca_dev)
+static int sethvflip(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
        __u8 data;
 
-       if (sd->sensor == SENSOR_PAC7302) {
-               reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
-               data = (sd->hflip ? 0x08 : 0x00)
-                       | (sd->vflip ? 0x04 : 0x00);
-       } else {
-               reg_w(gspca_dev, 0xff, 0x04);           /* page 4 */
-               data = (sd->hflip ? 0x04 : 0x00)
-                       | (sd->vflip ? 0x08 : 0x00);
-       }
-       reg_w(gspca_dev, 0x21, data);
+       ret = reg_w(gspca_dev, 0xff, 0x04);             /* page 4 */
+       data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x21, data);
        /* load registers to sensor (Bit 0, auto clear) */
-       reg_w(gspca_dev, 0x11, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x11, 0x01);
+       return ret;
 }
 
-/* this function is called at probe and resume time */
+/* this function is called at probe and resume time for pac7311 */
 static int sd_init(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (sd->sensor == SENSOR_PAC7302)
-               reg_w_seq(gspca_dev, init_7302, sizeof init_7302);
-       else
-               reg_w_seq(gspca_dev, init_7311, sizeof init_7311);
-
-       return 0;
+       return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
 }
 
 static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
 
        sd->sof_read = 0;
 
-       if (sd->sensor == SENSOR_PAC7302) {
-               reg_w_var(gspca_dev, start_7302);
-               setbrightcont(gspca_dev);
-               setcolors(gspca_dev);
-       } else {
-               reg_w_var(gspca_dev, start_7311);
-               setcontrast(gspca_dev);
-       }
-       setgain(gspca_dev);
-       setexposure(gspca_dev);
-       sethvflip(gspca_dev);
+       ret = reg_w_var(gspca_dev, start_7311,
+               NULL, 0,
+               page4_7311, sizeof(page4_7311));
+       if (0 <= ret)
+               ret = setcontrast(gspca_dev);
+       if (0 <= ret)
+               ret = setgain(gspca_dev);
+       if (0 <= ret)
+               ret = setexposure(gspca_dev);
+       if (0 <= ret)
+               ret = sethvflip(gspca_dev);
 
        /* set correct resolution */
        switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
        case 2:                                 /* 160x120 pac7311 */
-               reg_w(gspca_dev, 0xff, 0x01);
-               reg_w(gspca_dev, 0x17, 0x20);
-               reg_w(gspca_dev, 0x87, 0x10);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0xff, 0x01);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x17, 0x20);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x87, 0x10);
                break;
        case 1:                                 /* 320x240 pac7311 */
-               reg_w(gspca_dev, 0xff, 0x01);
-               reg_w(gspca_dev, 0x17, 0x30);
-               reg_w(gspca_dev, 0x87, 0x11);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0xff, 0x01);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x17, 0x30);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x87, 0x11);
                break;
        case 0:                                 /* 640x480 */
-               if (sd->sensor == SENSOR_PAC7302)
-                       break;
-               reg_w(gspca_dev, 0xff, 0x01);
-               reg_w(gspca_dev, 0x17, 0x00);
-               reg_w(gspca_dev, 0x87, 0x12);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0xff, 0x01);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x17, 0x00);
+               if (0 <= ret)
+                       ret = reg_w(gspca_dev, 0x87, 0x12);
                break;
        }
 
@@ -718,47 +559,42 @@ static int sd_start(struct gspca_dev *gspca_dev)
        atomic_set(&sd->avg_lum, -1);
 
        /* start stream */
-       reg_w(gspca_dev, 0xff, 0x01);
-       if (sd->sensor == SENSOR_PAC7302)
-               reg_w(gspca_dev, 0x78, 0x01);
-       else
-               reg_w(gspca_dev, 0x78, 0x05);
-       return 0;
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xff, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x05);
+
+       return ret;
 }
 
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (sd->sensor == SENSOR_PAC7302) {
-               reg_w(gspca_dev, 0xff, 0x01);
-               reg_w(gspca_dev, 0x78, 0x00);
-               reg_w(gspca_dev, 0x78, 0x00);
-               return;
-       }
-       reg_w(gspca_dev, 0xff, 0x04);
-       reg_w(gspca_dev, 0x27, 0x80);
-       reg_w(gspca_dev, 0x28, 0xca);
-       reg_w(gspca_dev, 0x29, 0x53);
-       reg_w(gspca_dev, 0x2a, 0x0e);
-       reg_w(gspca_dev, 0xff, 0x01);
-       reg_w(gspca_dev, 0x3e, 0x20);
-       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
-       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
-       reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
-}
+       int ret;
 
-/* called on streamoff with alt 0 and on disconnect */
+       ret = reg_w(gspca_dev, 0xff, 0x04);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x27, 0x80);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x28, 0xca);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x29, 0x53);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x2a, 0x0e);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0xff, 0x01);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x3e, 0x20);
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+       if (0 <= ret)
+               ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+}
+
+/* called on streamoff with alt 0 and on disconnect for 7311 */
 static void sd_stop0(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (!gspca_dev->present)
-               return;
-       if (sd->sensor == SENSOR_PAC7302) {
-               reg_w(gspca_dev, 0xff, 0x01);
-               reg_w(gspca_dev, 0x78, 0x40);
-       }
 }
 
 /* Include pac common sof detection functions */
@@ -773,22 +609,8 @@ static void do_autogain(struct gspca_dev *gspca_dev)
        if (avg_lum == -1)
                return;
 
-       if (sd->sensor == SENSOR_PAC7302) {
-               desired_lum = 270 + sd->brightness * 4;
-               /* Hack hack, with the 7202 the first exposure step is
-                  pretty large, so if we're about to make the first
-                  exposure increase make the deadzone large to avoid
-                  oscilating */
-               if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
-                               sd->exposure > EXPOSURE_DEF &&
-                               sd->exposure < 42)
-                       deadzone = 90;
-               else
-                       deadzone = 30;
-       } else {
-               desired_lum = 200;
-               deadzone = 20;
-       }
+       desired_lum = 200;
+       deadzone = 20;
 
        if (sd->autogain_ignore_frames > 0)
                sd->autogain_ignore_frames--;
@@ -797,53 +619,92 @@ static void do_autogain(struct gspca_dev *gspca_dev)
                sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
 }
 
-static const unsigned char pac7311_jpeg_header1[] = {
-  0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08
+/* JPEG header, part 1 */
+static const unsigned char pac_jpeg_header1[] = {
+  0xff, 0xd8,          /* SOI: Start of Image */
+
+  0xff, 0xc0,          /* SOF0: Start of Frame (Baseline DCT) */
+  0x00, 0x11,          /* length = 17 bytes (including this length field) */
+  0x08                 /* Precision: 8 */
+  /* 2 bytes is placed here: number of image lines */
+  /* 2 bytes is placed here: samples per line */
 };
 
-static const unsigned char pac7311_jpeg_header2[] = {
-  0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda,
-  0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+/* JPEG header, continued */
+static const unsigned char pac_jpeg_header2[] = {
+  0x03,                        /* Number of image components: 3 */
+  0x01, 0x21, 0x00,    /* ID=1, Subsampling 1x1, Quantization table: 0 */
+  0x02, 0x11, 0x01,    /* ID=2, Subsampling 2x1, Quantization table: 1 */
+  0x03, 0x11, 0x01,    /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+  0xff, 0xda,          /* SOS: Start Of Scan */
+  0x00, 0x0c,          /* length = 12 bytes (including this length field) */
+  0x03,                        /* number of components: 3 */
+  0x01, 0x00,          /* selector 1, table 0x00 */
+  0x02, 0x11,          /* selector 2, table 0x11 */
+  0x03, 0x11,          /* selector 3, table 0x11 */
+  0x00, 0x3f,          /* Spectral selection: 0 .. 63 */
+  0x00                 /* Successive approximation: 0 */
 };
 
+static void pac_start_frame(struct gspca_dev *gspca_dev,
+               struct gspca_frame *frame,
+               __u16 lines, __u16 samples_per_line)
+{
+       unsigned char tmpbuf[4];
+
+       gspca_frame_add(gspca_dev, FIRST_PACKET,
+               pac_jpeg_header1, sizeof(pac_jpeg_header1));
+
+       tmpbuf[0] = lines >> 8;
+       tmpbuf[1] = lines & 0xff;
+       tmpbuf[2] = samples_per_line >> 8;
+       tmpbuf[3] = samples_per_line & 0xff;
+
+       gspca_frame_add(gspca_dev, INTER_PACKET,
+               tmpbuf, sizeof(tmpbuf));
+       gspca_frame_add(gspca_dev, INTER_PACKET,
+               pac_jpeg_header2, sizeof(pac_jpeg_header2));
+}
+
 /* this function is run at interrupt level */
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
        unsigned char *sof;
+       struct gspca_frame *frame;
 
-       sof = pac_find_sof(gspca_dev, data, len);
+       sof = pac_find_sof(&sd->sof_read, data, len);
        if (sof) {
-               unsigned char tmpbuf[4];
                int n, lum_offset, footer_length;
 
-               if (sd->sensor == SENSOR_PAC7302) {
-                 /* 6 bytes after the FF D9 EOF marker a number of lumination
-                    bytes are send corresponding to different parts of the
-                    image, the 14th and 15th byte after the EOF seem to
-                    correspond to the center of the image */
-                 lum_offset = 61 + sizeof pac_sof_marker;
-                 footer_length = 74;
-               } else {
-                 lum_offset = 24 + sizeof pac_sof_marker;
-                 footer_length = 26;
+               frame = gspca_get_i_frame(gspca_dev);
+               if (frame == NULL) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       return;
                }
 
+               /* 6 bytes after the FF D9 EOF marker a number of lumination
+                  bytes are send corresponding to different parts of the
+                  image, the 14th and 15th byte after the EOF seem to
+                  correspond to the center of the image */
+               lum_offset = 24 + sizeof pac_sof_marker;
+               footer_length = 26;
+
                /* Finish decoding current frame */
                n = (sof - data) - (footer_length + sizeof pac_sof_marker);
                if (n < 0) {
                        frame->data_end += n;
                        n = 0;
                }
-               frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+               gspca_frame_add(gspca_dev, INTER_PACKET,
                                        data, n);
                if (gspca_dev->last_packet_type != DISCARD_PACKET &&
                                frame->data_end[-2] == 0xff &&
                                frame->data_end[-1] == 0xd9)
-                       frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
                                                NULL, 0);
 
                n = sof - data;
@@ -859,43 +720,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                        atomic_set(&sd->avg_lum, -1);
 
                /* Start the new frame with the jpeg header */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                       pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1));
-               if (sd->sensor == SENSOR_PAC7302) {
-                       /* The PAC7302 has the image rotated 90 degrees */
-                       tmpbuf[0] = gspca_dev->width >> 8;
-                       tmpbuf[1] = gspca_dev->width & 0xff;
-                       tmpbuf[2] = gspca_dev->height >> 8;
-                       tmpbuf[3] = gspca_dev->height & 0xff;
-               } else {
-                       tmpbuf[0] = gspca_dev->height >> 8;
-                       tmpbuf[1] = gspca_dev->height & 0xff;
-                       tmpbuf[2] = gspca_dev->width >> 8;
-                       tmpbuf[3] = gspca_dev->width & 0xff;
-               }
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4);
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                       pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2));
+               pac_start_frame(gspca_dev, frame,
+                       gspca_dev->height, gspca_dev->width);
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightcont(gspca_dev);
-       return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->brightness;
-       return 0;
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
@@ -904,10 +732,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
 
        sd->contrast = val;
        if (gspca_dev->streaming) {
-               if (sd->sensor == SENSOR_PAC7302)
-                       setbrightcont(gspca_dev);
-               else
-                       setcontrast(gspca_dev);
+               setcontrast(gspca_dev);
        }
        return 0;
 }
@@ -920,24 +745,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
        return 0;
 }
 
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->colors = val;
-       if (gspca_dev->streaming)
-               setcolors(gspca_dev);
-       return 0;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->colors;
-       return 0;
-}
-
 static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -1041,7 +848,7 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
        return 0;
 }
 
-/* sub-driver description */
+/* sub-driver description for pac7311 */
 static struct sd_desc sd_desc = {
        .name = MODULE_NAME,
        .ctrls = sd_ctrls,
@@ -1057,21 +864,12 @@ static struct sd_desc sd_desc = {
 
 /* -- module initialisation -- */
 static __devinitdata struct usb_device_id device_table[] = {
-       {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311},
-       {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311},
-       {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311},
-       {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
-       {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
-       {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
-       {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
-       {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
+       {USB_DEVICE(0x093a, 0x2600)},
+       {USB_DEVICE(0x093a, 0x2601)},
+       {USB_DEVICE(0x093a, 0x2603)},
+       {USB_DEVICE(0x093a, 0x2608)},
+       {USB_DEVICE(0x093a, 0x260e)},
+       {USB_DEVICE(0x093a, 0x260f)},
        {}
 };
 MODULE_DEVICE_TABLE(usb, device_table);
index 34d4b14..20f67d9 100644 (file)
 static const unsigned char pac_sof_marker[5] =
                { 0xff, 0xff, 0x00, 0xff, 0x96 };
 
-static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev,
+/*
+   The following state machine finds the SOF marker sequence
+   0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream.
+
+          +----------+
+          | 0: START |<---------------\
+          +----------+<-\             |
+            |       \---/otherwise    |
+            v 0xff                    |
+          +----------+ otherwise      |
+          |     1    |--------------->*
+          |          |                ^
+          +----------+                |
+            |                         |
+            v 0xff                    |
+          +----------+<-\0xff         |
+       /->|          |--/             |
+       |  |     2    |--------------->*
+       |  |          | otherwise      ^
+       |  +----------+                |
+       |    |                         |
+       |    v 0x00                    |
+       |  +----------+                |
+       |  |     3    |                |
+       |  |          |--------------->*
+       |  +----------+ otherwise      ^
+       |    |                         |
+   0xff |    v 0xff                    |
+       |  +----------+                |
+       \--|     4    |                |
+          |          |----------------/
+          +----------+ otherwise
+            |
+            v 0x96
+          +----------+
+          |  FOUND   |
+          +----------+
+*/
+
+static unsigned char *pac_find_sof(u8 *sof_read,
                                        unsigned char *m, int len)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        int i;
 
        /* Search for the SOF marker (fixed part) in the header */
        for (i = 0; i < len; i++) {
-               if (m[i] == pac_sof_marker[sd->sof_read]) {
-                       sd->sof_read++;
-                       if (sd->sof_read == sizeof(pac_sof_marker)) {
+               switch (*sof_read) {
+               case 0:
+                       if (m[i] == 0xff)
+                               *sof_read = 1;
+                       break;
+               case 1:
+                       if (m[i] == 0xff)
+                               *sof_read = 2;
+                       else
+                               *sof_read = 0;
+                       break;
+               case 2:
+                       switch (m[i]) {
+                       case 0x00:
+                               *sof_read = 3;
+                               break;
+                       case 0xff:
+                               /* stay in this state */
+                               break;
+                       default:
+                               *sof_read = 0;
+                       }
+                       break;
+               case 3:
+                       if (m[i] == 0xff)
+                               *sof_read = 4;
+                       else
+                               *sof_read = 0;
+                       break;
+               case 4:
+                       switch (m[i]) {
+                       case 0x96:
+                               /* Pattern found */
                                PDEBUG(D_FRAM,
                                        "SOF found, bytes to analyze: %u."
                                        " Frame starts at byte #%u",
                                        len, i + 1);
-                               sd->sof_read = 0;
+                               *sof_read = 0;
                                return m + i + 1;
+                               break;
+                       case 0xff:
+                               *sof_read = 2;
+                               break;
+                       default:
+                               *sof_read = 0;
                        }
-               } else {
-                       sd->sof_read = 0;
+                       break;
+               default:
+                       *sof_read = 0;
                }
        }
 
index cdad3db..b1944a7 100644 (file)
@@ -2342,7 +2342,6 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
@@ -2378,22 +2377,22 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                avg_lum >>= 9;
                atomic_set(&sd->avg_lum, avg_lum);
                gspca_frame_add(gspca_dev, LAST_PACKET,
-                               frame, data, len);
+                               data, len);
                return;
        }
        if (gspca_dev->last_packet_type == LAST_PACKET) {
                if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
                                & MODE_JPEG) {
-                       gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
                                sd->jpeg_hdr, JPEG_HDR_SZ);
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       gspca_frame_add(gspca_dev, INTER_PACKET,
                                data, len);
                } else {
-                       gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
                                data, len);
                }
        } else {
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+               gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
        }
 }
 
index cf3af8d..5be95bc 100644 (file)
@@ -304,7 +304,7 @@ static const __u8 initOv6650[] = {
 };
 static const __u8 ov6650_sensor_init[][8] =
 {
-       /* Bright, contrast, etc are set througth SCBB interface.
+       /* Bright, contrast, etc are set through SCBB interface.
         * AVCAP on win2 do not send any data on this   controls. */
        /* Anyway, some registers appears to alter bright and constrat */
 
@@ -995,8 +995,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       unsigned char *data,            /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        int i;
@@ -1054,12 +1053,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                                        pkt_type = DISCARD_PACKET;
                                }
 
-                               frame = gspca_frame_add(gspca_dev, pkt_type,
-                                                       frame, data, 0);
+                               gspca_frame_add(gspca_dev, pkt_type,
+                                               NULL, 0);
                                data += i + fr_h_sz;
                                len -= i + fr_h_sz;
                                gspca_frame_add(gspca_dev, FIRST_PACKET,
-                                               frame, data, len);
+                                               data, len);
                                return;
                        }
                }
@@ -1068,15 +1067,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) {
                /* In raw mode we sometimes get some garbage after the frame
                   ignore this */
-               int used = frame->data_end - frame->data;
+               struct gspca_frame *frame;
+               int used;
                int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
 
+               frame = gspca_get_i_frame(gspca_dev);
+               if (frame == NULL) {
+                       gspca_dev->last_packet_type = DISCARD_PACKET;
+                       return;
+               }
+               used = frame->data_end - frame->data;
                if (used + len > size)
                        len = size - used;
        }
 
-       gspca_frame_add(gspca_dev, INTER_PACKET,
-                       frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index 33f4d0a..0bd36a0 100644 (file)
@@ -1,8 +1,8 @@
 /*
- *             Sonix sn9c102p sn9c105 sn9c120 (jpeg) library
- *             Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
  *
- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -72,8 +72,9 @@ struct sd {
 #define SENSOR_OV7630 5
 #define SENSOR_OV7648 6
 #define SENSOR_OV7660 7
-#define SENSOR_SP80708 8
-       u8 i2c_base;
+#define SENSOR_PO1030 8
+#define SENSOR_SP80708 9
+       u8 i2c_addr;
 
        u8 *jpeg_hdr;
 };
@@ -250,7 +251,7 @@ static struct ctrl sd_ctrls[] = {
                .minimum = 0,
                .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
                .step    = 1,
-#define FREQ_DEF 2
+#define FREQ_DEF 1
                .default_value = FREQ_DEF,
            },
            .set = sd_setfreq,
@@ -277,7 +278,9 @@ static __u32 ctrl_dis[] = {
        (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
                                                /* SENSOR_OV7660 7 */
        (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
-                             (1 << FREQ_IDX),  /* SENSOR_SP80708 8 */
+                             (1 << FREQ_IDX),  /* SENSOR_PO1030 8 */
+       (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
+                             (1 << FREQ_IDX),  /* SENSOR_SP80708 9 */
 };
 
 static const struct v4l2_pix_format vga_mode[] = {
@@ -304,7 +307,7 @@ static const u8 sn_hv7131[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
        0x00,   0x03,   0x64,   0x00,   0x1a,   0x20,   0x20,   0x20,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
-       0xa1,   0x11,   0x02,   0x09,   0x00,   0x00,   0x00,   0x10,
+       0x81,   0x11,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
        0x03,   0x00,   0x00,   0x01,   0x03,   0x28,   0x1e,   0x41,
 /*     reg18   reg19   reg1a   reg1b */
@@ -315,7 +318,7 @@ static const u8 sn_mi0360[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
        0x00,   0x61,   0x44,   0x00,   0x1a,   0x20,   0x20,   0x20,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
-       0xb1,   0x5d,   0x07,   0x00,   0x00,   0x00,   0x00,   0x10,
+       0x81,   0x5d,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
        0x03,   0x00,   0x00,   0x02,   0x0a,   0x28,   0x1e,   0x61,
 /*     reg18   reg19   reg1a   reg1b */
@@ -337,7 +340,7 @@ static const u8 sn_mt9v111[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
        0x00,   0x61,   0x40,   0x00,   0x1a,   0x20,   0x20,   0x20,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
-       0x81,   0x5c,   0x07,   0x00,   0x00,   0x00,   0x00,   0x00,
+       0x81,   0x5c,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
        0x03,   0x00,   0x00,   0x02,   0x1c,   0x28,   0x1e,   0x40,
 /*     reg18   reg19   reg1a   reg1b */
@@ -346,7 +349,7 @@ static const u8 sn_mt9v111[0x1c] = {
 
 static const u8 sn_om6802[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
-       0x00,   0x23,   0x72,   0x00,   0x1a,   0x34,   0x27,   0x20,
+       0x00,   0x23,   0x72,   0x00,   0x1a,   0x20,   0x20,   0x19,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
        0x80,   0x34,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
@@ -359,7 +362,7 @@ static const u8 sn_ov7630[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
        0x00,   0x21,   0x40,   0x00,   0x1a,   0x20,   0x1f,   0x20,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
-       0xa1,   0x21,   0x76,   0x21,   0x00,   0x00,   0x00,   0x10,
+       0x81,   0x21,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
        0x03,   0x00,   0x04,   0x01,   0x0a,   0x28,   0x1e,   0xc2,
 /*     reg18   reg19   reg1a   reg1b */
@@ -370,7 +373,7 @@ static const u8 sn_ov7648[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
        0x00,   0x63,   0x40,   0x00,   0x1a,   0x20,   0x20,   0x20,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
-       0x81,   0x21,   0x00,   0x00,   0x00,   0x00,   0x00,   0x10,
+       0x81,   0x21,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
        0x03,   0x00,   0x00,   0x01,   0x00,   0x28,   0x1e,   0x00,
 /*     reg18   reg19   reg1a   reg1b */
@@ -388,11 +391,22 @@ static const u8 sn_ov7660[0x1c] = {
        0x07,   0x00,   0x00,   0x00
 };
 
+static const u8 sn_po1030[0x1c] = {
+/*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
+       0x00,   0x21,   0x62,   0x00,   0x1a,   0x20,   0x20,   0x20,
+/*     reg8    reg9    rega    regb    regc    regd    rege    regf */
+       0x81,   0x6e,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
+/*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
+       0x03,   0x00,   0x00,   0x06,   0x06,   0x28,   0x1e,   0x00,
+/*     reg18   reg19   reg1a   reg1b */
+       0x07,   0x00,   0x00,   0x00
+};
+
 static const u8 sn_sp80708[0x1c] = {
 /*     reg0    reg1    reg2    reg3    reg4    reg5    reg6    reg7 */
        0x00,   0x63,   0x60,   0x00,   0x1a,   0x20,   0x20,   0x20,
 /*     reg8    reg9    rega    regb    regc    regd    rege    regf */
-       0x81,   0x18,   0x07,   0x00,   0x00,   0x00,   0x00,   0x00,
+       0x81,   0x18,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,
 /*     reg10   reg11   reg12   reg13   reg14   reg15   reg16   reg17 */
        0x03,   0x00,   0x00,   0x03,   0x04,   0x28,   0x1e,   0x00,
 /*     reg18   reg19   reg1a   reg1b */
@@ -409,6 +423,7 @@ static const u8 *sn_tb[] = {
        sn_ov7630,
        sn_ov7648,
        sn_ov7660,
+       sn_po1030,
        sn_sp80708
 };
 
@@ -455,7 +470,7 @@ static const u8 hv7131r_sensor_init[][8] = {
 
        {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
-       {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
 
@@ -464,6 +479,8 @@ static const u8 hv7131r_sensor_init[][8] = {
        {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
+                                                       /* set sensor clock */
        {}
 };
 static const u8 mi0360_sensor_init[][8] = {
@@ -545,7 +562,7 @@ static const u8 mo4000_sensor_init[][8] = {
 };
 static const u8 mt9v111_sensor_init[][8] = {
        {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
-       /* delay 20 ms */
+       {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
        {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
        {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
@@ -572,7 +589,9 @@ static const u8 mt9v111_sensor_init[][8] = {
        {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
        {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
        {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
-       /*******/
+       {}
+};
+static const u8 mt9v111_sensor_param1[][8] = {
        {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10},
@@ -585,14 +604,20 @@ static const u8 mt9v111_sensor_init[][8] = {
        {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
        {}
 };
+static const u8 om6802_init0[2][8] = {
+/*fixme: variable*/
+       {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
+       {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
+};
 static const u8 om6802_sensor_init[][8] = {
-       {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10},
-       {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10},
-       {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+       {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
+                                               /* factory mode */
        {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
+                                               /* output raw RGB */
+       {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
 /*     {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
        {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
-                                       /* white balance & auto-exposure */
+               /* auto-exposure speed (0) / white balance mode (auto RGB) */
 /*     {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
                                                         * set color mode */
 /*     {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
@@ -606,26 +631,29 @@ static const u8 om6802_sensor_init[][8] = {
 /*     {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
                                                         * preset gamma */
        {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
-                                       /* luminance mode (0x4f = AE) */
+                               /* luminance mode (0x4f -> AutoExpo on) */
        {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
                                                        /* preset shutter */
 /*     {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
                                                         * auto frame rate */
 /*     {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
-
-/*     {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */
-/*     {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */
-/*     {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */
-/*     {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */
+       {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+       {}
+};
+static const u8 om6802_sensor_param1[][8] = {
+       {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
+       {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
+       {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
+       {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
        {}
 };
 static const u8 ov7630_sensor_init[][8] = {
        {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
-/* win: delay 20ms */
+       {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
        {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
-/* win: delay 20ms */
+       {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
        {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
 /* win: i2c_r from 00 to 80 */
        {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
@@ -677,6 +705,7 @@ static const u8 ov7630_sensor_init[][8] = {
 static const u8 ov7648_sensor_init[][8] = {
        {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},       /* reset */
+       {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
        {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
        {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
        {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
@@ -701,7 +730,9 @@ static const u8 ov7648_sensor_init[][8] = {
 /*     {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
 /*     {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
 /*     {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
-/*...*/
+       {}
+};
+static const u8 ov7648_sensor_param1[][8] = {
 /*     {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
 /*     {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10},   * COMN
                                                         * set by setvflip */
@@ -723,7 +754,7 @@ static const u8 ov7648_sensor_init[][8] = {
 
 static const u8 ov7660_sensor_init[][8] = {
        {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
-/*             (delay 20ms) */
+       {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
        {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
                                                /* Outformat = rawRGB */
        {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
@@ -783,8 +814,11 @@ static const u8 ov7660_sensor_init[][8] = {
        {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
        {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
        {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
+/* not in all ms-win traces*/
        {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
-/****** (some exchanges in the win trace) ******/
+       {}
+};
+static const u8 ov7660_sensor_param1[][8] = {
        {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
                                                /* bits[3..0]reserved */
        {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
@@ -797,6 +831,7 @@ static const u8 ov7660_sensor_init[][8] = {
        {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
 /*     {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
 /****** (some exchanges in the win trace) ******/
+/*fixme:param2*/
        {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
        {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */
        {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */
@@ -804,6 +839,7 @@ static const u8 ov7660_sensor_init[][8] = {
 /*     {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10},  * RED */
 /****** (some exchanges in the win trace) ******/
 /******!! startsensor KO if changed !!****/
+/*fixme: param3*/
        {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
@@ -811,6 +847,60 @@ static const u8 ov7660_sensor_init[][8] = {
        {}
 };
 
+static const u8 po1030_sensor_init[][8] = {
+/* the sensor registers are described in m5602/m5602_po1030.h */
+       {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
+       {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+       {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
+       {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10},
+       {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
+       {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */
+       {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10},
+       {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
+       {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
+       {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
+       {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
+       {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10},
+       {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10},
+       {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
+       {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
+       {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10},
+       {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10},
+       {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10},
+       {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */
+       {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10},
+
+       {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
+       {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
+       {}
+};
+static const u8 po1030_sensor_param1[][8] = {
+/* from ms-win traces - these values change with auto gain/expo/wb.. */
+       {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+       {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+/* mean values */
+       {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
+       {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
+       {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
+
+       {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
+       {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
+       {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
+/*     {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
+       {}
+};
+
 static const u8 sp80708_sensor_init[][8] = {
        {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
@@ -883,7 +973,9 @@ static const u8 sp80708_sensor_init[][8] = {
        {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
-       /********/
+       {}
+};
+static const u8 sp80708_sensor_param1[][8] = {
        {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
        {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
@@ -894,6 +986,19 @@ static const u8 sp80708_sensor_init[][8] = {
        {}
 };
 
+static const u8 (*sensor_init[10])[8] = {
+       hv7131r_sensor_init,    /* HV7131R 0 */
+       mi0360_sensor_init,     /* MI0360 1 */
+       mo4000_sensor_init,     /* MO4000 2 */
+       mt9v111_sensor_init,    /* MT9V111 3 */
+       om6802_sensor_init,     /* OM6802 4 */
+       ov7630_sensor_init,     /* OV7630 5 */
+       ov7648_sensor_init,     /* OV7648 6 */
+       ov7660_sensor_init,     /* OV7660 7 */
+       po1030_sensor_init,     /* PO1030 8 */
+       sp80708_sensor_init,    /* SP80708 9 */
+};
+
 /* read <len> bytes to gspca_dev->usb_buf */
 static void reg_r(struct gspca_dev *gspca_dev,
                  u16 value, int len)
@@ -958,8 +1063,15 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
        struct sd *sd = (struct sd *) gspca_dev;
 
        PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val);
-       gspca_dev->usb_buf[0] = 0x81 | (2 << 4);        /* = a1 */
-       gspca_dev->usb_buf[1] = sd->i2c_base;
+       switch (sd->sensor) {
+       case SENSOR_OM6802:             /* i2c command = a0 (100 kHz) */
+               gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
+               break;
+       default:                        /* i2c command = a1 (400 kHz) */
+               gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
+               break;
+       }
+       gspca_dev->usb_buf[1] = sd->i2c_addr;
        gspca_dev->usb_buf[2] = reg;
        gspca_dev->usb_buf[3] = val;
        gspca_dev->usb_buf[4] = 0;
@@ -991,14 +1103,21 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
        msleep(2);
 }
 
-/* read 5 bytes in gspca_dev->usb_buf */
-static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
+/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
+static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 mode[8];
 
-       mode[0] = 0x81 | 0x10;
-       mode[1] = sd->i2c_base;
+       switch (sd->sensor) {
+       case SENSOR_OM6802:             /* i2c command = 90 (100 kHz) */
+               mode[0] = 0x80 | 0x10;
+               break;
+       default:                        /* i2c command = 91 (400 kHz) */
+               mode[0] = 0x81 | 0x10;
+               break;
+       }
+       mode[1] = sd->i2c_addr;
        mode[2] = reg;
        mode[3] = 0;
        mode[4] = 0;
@@ -1007,33 +1126,43 @@ static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
        mode[7] = 0x10;
        i2c_w8(gspca_dev, mode);
        msleep(2);
-       mode[0] = 0x81 | (5 << 4) | 0x02;
+       mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
        mode[2] = 0;
        i2c_w8(gspca_dev, mode);
        msleep(2);
        reg_r(gspca_dev, 0x0a, 5);
 }
 
-static int hv7131r_probe(struct gspca_dev *gspca_dev)
+static void i2c_w_seq(struct gspca_dev *gspca_dev,
+                       const u8 (*data)[8])
+{
+       while ((*data)[0] != 0) {
+               if ((*data)[0] != 0xdd)
+                       i2c_w8(gspca_dev, *data);
+               else
+                       msleep((*data)[1]);
+               data++;
+       }
+}
+
+static void hv7131r_probe(struct gspca_dev *gspca_dev)
 {
        i2c_w1(gspca_dev, 0x02, 0);                     /* sensor wakeup */
        msleep(10);
        reg_w1(gspca_dev, 0x02, 0x66);                  /* Gpio on */
        msleep(10);
-       i2c_r5(gspca_dev, 0);                           /* read sensor id */
+       i2c_r(gspca_dev, 0, 5);                         /* read sensor id */
        if (gspca_dev->usb_buf[0] == 0x02
            && gspca_dev->usb_buf[1] == 0x09
            && gspca_dev->usb_buf[2] == 0x01
            && gspca_dev->usb_buf[3] == 0x00
            && gspca_dev->usb_buf[4] == 0x00) {
-               PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
-               return 0;
+               PDEBUG(D_PROBE, "Sensor sn9c102P HV7131R found");
+               return;
        }
-       PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
+       PDEBUG(D_PROBE, "Sensor 0x%02x 0x%02x 0x%02x - sn9c102P not found",
                gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
                gspca_dev->usb_buf[2]);
-       PDEBUG(D_PROBE, "Sensor sn9c102P Not found");
-       return -ENODEV;
 }
 
 static void mi0360_probe(struct gspca_dev *gspca_dev)
@@ -1075,7 +1204,6 @@ static void mi0360_probe(struct gspca_dev *gspca_dev)
        case 0x823a:
                PDEBUG(D_PROBE, "Sensor mt9v111");
                sd->sensor = SENSOR_MT9V111;
-               sd->i2c_base = 0x5c;
                break;
        case 0x8243:
                PDEBUG(D_PROBE, "Sensor mi0360");
@@ -1086,7 +1214,42 @@ static void mi0360_probe(struct gspca_dev *gspca_dev)
        }
 }
 
-static int configure_gpio(struct gspca_dev *gspca_dev,
+static void ov7648_probe(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* check ov76xx */
+       reg_w1(gspca_dev, 0x17, 0x62);
+       reg_w1(gspca_dev, 0x01, 0x08);
+       sd->i2c_addr = 0x21;
+       i2c_r(gspca_dev, 0x0a, 2);
+       if (gspca_dev->usb_buf[3] == 0x76) {    /* ov76xx */
+               PDEBUG(D_PROBE, "Sensor ov%02x%02x",
+                       gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]);
+               return;
+       }
+
+       /* reset */
+       reg_w1(gspca_dev, 0x01, 0x29);
+       reg_w1(gspca_dev, 0x17, 0x42);
+
+       /* check po1030 */
+       reg_w1(gspca_dev, 0x17, 0x62);
+       reg_w1(gspca_dev, 0x01, 0x08);
+       sd->i2c_addr = 0x6e;
+       i2c_r(gspca_dev, 0x00, 2);
+       if (gspca_dev->usb_buf[3] == 0x10       /* po1030 */
+           && gspca_dev->usb_buf[4] == 0x30) {
+               PDEBUG(D_PROBE, "Sensor po1030");
+               sd->sensor = SENSOR_PO1030;
+               return;
+       }
+
+       PDEBUG(D_PROBE, "Unknown sensor %02x%02x",
+               gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]);
+}
+
+static void bridge_init(struct gspca_dev *gspca_dev,
                          const u8 *sn9c1xx)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -1103,9 +1266,10 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
        /* configure gpio */
        reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2);
        reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
-       reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);      /* jfm len was 3 */
+       reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
        switch (sd->sensor) {
        case SENSOR_OV7660:
+       case SENSOR_PO1030:
        case SENSOR_SP80708:
                reg9a = reg9a_spec;
                break;
@@ -1115,7 +1279,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
        }
        reg_w(gspca_dev, 0x9a, reg9a, 6);
 
-       reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/
+       reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
 
        reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
 
@@ -1127,10 +1291,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
                reg_w1(gspca_dev, 0x01, 0x40);
                break;
        case SENSOR_OM6802:
-               reg_w1(gspca_dev, 0x02, 0x71);
-               reg_w1(gspca_dev, 0x01, 0x42);
+               msleep(10);
+               reg_w1(gspca_dev, 0x02, 0x73);
+               reg_w1(gspca_dev, 0x17, 0x60);
+               reg_w1(gspca_dev, 0x01, 0x22);
+               msleep(100);
+               reg_w1(gspca_dev, 0x01, 0x62);
+               reg_w1(gspca_dev, 0x17, 0x64);
                reg_w1(gspca_dev, 0x17, 0x64);
                reg_w1(gspca_dev, 0x01, 0x42);
+               msleep(10);
+               reg_w1(gspca_dev, 0x01, 0x42);
+               i2c_w8(gspca_dev, om6802_init0[0]);
+               i2c_w8(gspca_dev, om6802_init0[1]);
+               msleep(15);
+               reg_w1(gspca_dev, 0x02, 0x71);
+               msleep(150);
                break;
        case SENSOR_OV7630:
                reg_w1(gspca_dev, 0x01, 0x61);
@@ -1144,7 +1320,14 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
                reg_w1(gspca_dev, 0x01, 0x62);
                reg_w1(gspca_dev, 0x01, 0x42);
                break;
+       case SENSOR_PO1030:
+               reg_w1(gspca_dev, 0x01, 0x61);
+               reg_w1(gspca_dev, 0x17, 0x20);
+               reg_w1(gspca_dev, 0x01, 0x60);
+               reg_w1(gspca_dev, 0x01, 0x40);
+               break;
        case SENSOR_OV7660:
+               /* fall thru */
        case SENSOR_SP80708:
                reg_w1(gspca_dev, 0x01, 0x63);
                reg_w1(gspca_dev, 0x17, 0x20);
@@ -1153,143 +1336,18 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
                msleep(100);
                reg_w1(gspca_dev, 0x02, 0x62);
                break;
+       default:
 /*     case SENSOR_HV7131R: */
 /*     case SENSOR_MI0360: */
 /*     case SENSOR_MO4000: */
-       default:
                reg_w1(gspca_dev, 0x01, 0x43);
                reg_w1(gspca_dev, 0x17, 0x61);
                reg_w1(gspca_dev, 0x01, 0x42);
-               if (sd->sensor == SENSOR_HV7131R) {
-                       if (hv7131r_probe(gspca_dev) < 0)
-                               return -ENODEV;
-               }
+               if (sd->sensor == SENSOR_HV7131R
+                   && sd->bridge == BRIDGE_SN9C102P)
+                       hv7131r_probe(gspca_dev);
                break;
        }
-       return 0;
-}
-
-static void hv7131R_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-       static const u8 SetSensorClk[] =        /* 0x08 Mclk */
-               { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };
-
-       while (hv7131r_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, hv7131r_sensor_init[i]);
-               i++;
-       }
-       i2c_w8(gspca_dev, SetSensorClk);
-}
-
-static void mi0360_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       while (mi0360_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, mi0360_sensor_init[i]);
-               i++;
-       }
-}
-
-static void mo4000_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       while (mo4000_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, mo4000_sensor_init[i]);
-               i++;
-       }
-}
-
-static void mt9v111_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
-       i++;
-       msleep(20);
-       while (mt9v111_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
-               i++;
-       }
-}
-
-static void om6802_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       while (om6802_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, om6802_sensor_init[i]);
-               i++;
-       }
-}
-
-static void ov7630_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       i2c_w8(gspca_dev, ov7630_sensor_init[i]);       /* 76 01 */
-       i++;
-       i2c_w8(gspca_dev, ov7630_sensor_init[i]);       /* 12 c8 (RGB+SRST) */
-       i++;
-       msleep(20);
-       i2c_w8(gspca_dev, ov7630_sensor_init[i]);       /* 12 48 */
-       i++;
-       i2c_w8(gspca_dev, ov7630_sensor_init[i]);       /* 12 c8 */
-       i++;
-       msleep(20);
-       i2c_w8(gspca_dev, ov7630_sensor_init[i]);       /* 12 48 */
-       i++;
-/*jfm:win i2c_r from 00 to 80*/
-
-       while (ov7630_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, ov7630_sensor_init[i]);
-               i++;
-       }
-}
-
-static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       i2c_w8(gspca_dev, ov7648_sensor_init[i]);
-       i++;
-/* win: dble reset */
-       i2c_w8(gspca_dev, ov7648_sensor_init[i]);       /* reset */
-       i++;
-       msleep(20);
-/* win: i2c reg read 00..7f */
-       while (ov7648_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, ov7648_sensor_init[i]);
-               i++;
-       }
-}
-
-static void ov7660_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       i2c_w8(gspca_dev, ov7660_sensor_init[i]);       /* reset SCCB */
-       i++;
-       msleep(20);
-       while (ov7660_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, ov7660_sensor_init[i]);
-               i++;
-       }
-}
-
-static void sp80708_InitSensor(struct gspca_dev *gspca_dev)
-{
-       int i = 0;
-
-       i2c_w8(gspca_dev, sp80708_sensor_init[i]);      /* reset SCCB */
-       i++;
-       msleep(20);
-       while (sp80708_sensor_init[i][0]) {
-               i2c_w8(gspca_dev, sp80708_sensor_init[i]);
-               i++;
-       }
 }
 
 /* this function is called at probe time */
@@ -1305,8 +1363,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        cam->npkt = 24;                 /* 24 packets per ISOC message */
 
        sd->bridge = id->driver_info >> 16;
-       sd->sensor = id->driver_info >> 8;
-       sd->i2c_base = id->driver_info;
+       sd->sensor = id->driver_info;
 
        sd->brightness = BRIGHTNESS_DEF;
        sd->contrast = CONTRAST_DEF;
@@ -1322,7 +1379,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        sd->quality = QUALITY_DEF;
        sd->jpegqual = 80;
 
-       gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
        return 0;
 }
 
@@ -1330,6 +1386,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
 static int sd_init(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       const u8 *sn9c1xx;
        u8 regGpio[] = { 0x29, 0x74 };
        u8 regF1;
 
@@ -1356,8 +1413,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
        case BRIDGE_SN9C120:
                if (regF1 != 0x12)
                        return -ENODEV;
-               if (sd->sensor == SENSOR_MI0360)
+               switch (sd->sensor) {
+               case SENSOR_MI0360:
                        mi0360_probe(gspca_dev);
+                       break;
+               case SENSOR_OV7648:
+                       ov7648_probe(gspca_dev);
+                       break;
+               }
                regGpio[1] = 0x70;
                reg_w(gspca_dev, 0x01, regGpio, 2);
                break;
@@ -1372,6 +1435,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
 
        reg_w1(gspca_dev, 0xf1, 0x01);
 
+       /* set the i2c address */
+       sn9c1xx = sn_tb[sd->sensor];
+       sd->i2c_addr = sn9c1xx[9];
+
+       gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+
        return 0;
 }
 
@@ -1383,7 +1452,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
        switch (sd->sensor) {
        case SENSOR_HV7131R: {
                u8 Expodoit[] =
-                       { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 };
+                       { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
 
                Expodoit[3] = expo >> 16;
                Expodoit[4] = expo >> 8;
@@ -1393,7 +1462,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
            }
        case SENSOR_MI0360: {
                u8 expoMi[] =           /* exposure 0x0635 -> 4 fp/s 0x10 */
-                       { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 };
+                       { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
                static const u8 doit[] =                /* update sensor */
                        { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
                static const u8 sensorgo[] =            /* sensor on */
@@ -1412,9 +1481,9 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
            }
        case SENSOR_MO4000: {
                u8 expoMof[] =
-                       { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 };
+                       { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 };
                u8 expoMo10[] =
-                       { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 };
+                       { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
                static const u8 gainMo[] =
                        { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
 
@@ -1450,6 +1519,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
        case SENSOR_OM6802: {
                u8 gainOm[] =
                        { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
+                               /* preset AGC - works when AutoExpo = off */
 
                if (expo > 0x03ff)
                        expo = 0x03ff;
@@ -1457,7 +1527,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
                        expo = 0x0001;
                gainOm[3] = expo >> 2;
                i2c_w8(gspca_dev, gainOm);
-               reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f);
+               reg_w1(gspca_dev, 0x96, expo >> 5);
                PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
                break;
            }
@@ -1489,7 +1559,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        case SENSOR_MT9V111:
                expo = sd->brightness >> 8;
                sd->exposure = setexposure(gspca_dev, expo);
-               break;
+               return;                 /* don't set the Y offset */
        case SENSOR_OM6802:
                expo = sd->brightness >> 6;
                sd->exposure = setexposure(gspca_dev, expo);
@@ -1497,8 +1567,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
                break;
        }
 
-       if (sd->sensor != SENSOR_MT9V111)
-               reg_w1(gspca_dev, 0x96, k2);    /* color matrix Y offset */
+       reg_w1(gspca_dev, 0x96, k2);    /* color matrix Y offset */
 }
 
 static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1526,6 +1595,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
                -24, -38, 64,           /* UR UG UB */
                 62, -51, -9            /* VR VG VB */
        };
+
        for (i = 0; i < 6; i++) {
                v = uv[i] * sd->colors / COLOR_DEF;
                reg8a[i * 2] = v;
@@ -1605,6 +1675,8 @@ static void setvflip(struct sd *sd)
 {
        u8 comn;
 
+       if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX))
+               return;
        if (sd->sensor == SENSOR_OV7630) {
                comn = 0x02;
                if (!sd->vflip)
@@ -1726,8 +1798,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int i;
-       u8 reg1, reg17;
+       u8 reg1, reg2, reg17;
        const u8 *sn9c1xx;
+       const u8 (*init)[8];
        int mode;
        static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
        static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
@@ -1743,8 +1816,26 @@ static int sd_start(struct gspca_dev *gspca_dev)
                        0x21);          /* JPEG 422 */
        jpeg_set_qual(sd->jpeg_hdr, sd->quality);
 
-       sn9c1xx = sn_tb[(int) sd->sensor];
-       configure_gpio(gspca_dev, sn9c1xx);
+       /* initialize the bridge */
+       sn9c1xx = sn_tb[sd->sensor];
+       bridge_init(gspca_dev, sn9c1xx);
+
+       /* initialize the sensor */
+       i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
+
+       switch (sd->sensor) {
+       case SENSOR_OM6802:
+               reg2 = 0x71;
+               break;
+       case SENSOR_SP80708:
+               reg2 = 0x62;
+               break;
+       default:
+               reg2 = 0x40;
+               break;
+       }
+       reg_w1(gspca_dev, 0x02, reg2);
+       reg_w1(gspca_dev, 0x02, reg2);
 
        reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
        reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
@@ -1771,6 +1862,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_OV7660:
                reg17 = 0xa0;
                break;
+       case SENSOR_PO1030:
+               reg17 = 0xa0;
+               break;
        default:
                reg17 = 0x60;
                break;
@@ -1791,6 +1885,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
                reg_w1(gspca_dev, 0x9a, 0x07);
                reg_w1(gspca_dev, 0x99, 0x59);
                break;
+       case SENSOR_OM6802:
+               reg_w1(gspca_dev, 0x9a, 0x08);
+               reg_w1(gspca_dev, 0x99, 0x10);
+               break;
        case SENSOR_OV7648:
                reg_w1(gspca_dev, 0x9a, 0x0a);
                reg_w1(gspca_dev, 0x99, 0x60);
@@ -1806,21 +1904,20 @@ static int sd_start(struct gspca_dev *gspca_dev)
                break;
        }
 
-       mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+       reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+       reg_w1(gspca_dev, 0x05, sn9c1xx[5]);    /* red */
+       reg_w1(gspca_dev, 0x07, sn9c1xx[7]);    /* green */
+       reg_w1(gspca_dev, 0x06, sn9c1xx[6]);    /* blue */
+
+       init = NULL;
+       mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
        if (mode)
                reg1 = 0x46;    /* 320x240: clk 48Mhz, video trf enable */
        else
                reg1 = 0x06;    /* 640x480: clk 24Mhz, video trf enable */
        reg17 = 0x61;           /* 0x:20: enable sensor clock */
        switch (sd->sensor) {
-       case SENSOR_HV7131R:
-               hv7131R_InitSensor(gspca_dev);
-               break;
-       case SENSOR_MI0360:
-               mi0360_InitSensor(gspca_dev);
-               break;
        case SENSOR_MO4000:
-               mo4000_InitSensor(gspca_dev);
                if (mode) {
 /*                     reg1 = 0x46;     * 320 clk 48Mhz 60fp/s */
                        reg1 = 0x06;    /* clk 24Mz */
@@ -1830,7 +1927,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                }
                break;
        case SENSOR_MT9V111:
-               mt9v111_InitSensor(gspca_dev);
+               init = mt9v111_sensor_param1;
                if (mode) {
                        reg1 = 0x04;    /* 320 clk 48Mhz */
                } else {
@@ -1839,22 +1936,21 @@ static int sd_start(struct gspca_dev *gspca_dev)
                }
                break;
        case SENSOR_OM6802:
-               om6802_InitSensor(gspca_dev);
+               init = om6802_sensor_param1;
                reg17 = 0x64;           /* 640 MCKSIZE */
                break;
        case SENSOR_OV7630:
-               ov7630_InitSensor(gspca_dev);
                setvflip(sd);
                reg17 = 0xe2;
                reg1 = 0x44;
                break;
        case SENSOR_OV7648:
-               ov7648_InitSensor(gspca_dev);
+               init = ov7648_sensor_param1;
                reg17 = 0x21;
 /*             reg1 = 0x42;             * 42 - 46? */
                break;
        case SENSOR_OV7660:
-               ov7660_InitSensor(gspca_dev);
+               init = ov7660_sensor_param1;
                if (sd->bridge == BRIDGE_SN9C120) {
                        if (mode) {             /* 320x240 - 160x120 */
                                reg17 = 0xa2;
@@ -1866,9 +1962,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
                                         * inverse power down */
                }
                break;
+       case SENSOR_PO1030:
+               init = po1030_sensor_param1;
+               reg17 = 0xa2;
+               reg1 = 0x44;
+               break;
        default:
 /*     case SENSOR_SP80708: */
-               sp80708_InitSensor(gspca_dev);
+               init = sp80708_sensor_param1;
                if (mode) {
 /*??                   reg1 = 0x04;     * 320 clk 48Mhz */
                } else {
@@ -1877,6 +1978,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
                }
                break;
        }
+
+       /* more sensor initialization - param1 */
+       if (init != NULL) {
+               i2c_w_seq(gspca_dev, init);
+/*             init = NULL; */
+       }
+
        reg_w(gspca_dev, 0xc0, C0, 6);
        reg_w(gspca_dev, 0xca, CA, 4);
        switch (sd->sensor) {
@@ -1891,6 +1999,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                break;
        }
 
+
        /* here change size mode 0 -> VGA; 1 -> CIF */
        sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
        reg_w1(gspca_dev, 0x18, sd->reg18);
@@ -1898,6 +2007,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        reg_w1(gspca_dev, 0x17, reg17);
        reg_w1(gspca_dev, 0x01, reg1);
+
        switch (sd->sensor) {
        case SENSOR_OV7630:
                setvflip(sd);
@@ -1937,14 +2047,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
                /* fall thru */
        case SENSOR_MT9V111:
        case SENSOR_OV7630:
+       case SENSOR_PO1030:
                data = 0x29;
                break;
-       default:
-/*     case SENSOR_MO4000: */
-/*     case SENSOR_OV7660: */
-               break;
        }
-       sn9c1xx = sn_tb[(int) sd->sensor];
+       sn9c1xx = sn_tb[sd->sensor];
        reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
        reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]);
        reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
@@ -1987,11 +2094,19 @@ static void do_autogain(struct gspca_dev *gspca_dev)
                        sd->exposure = setexposure(gspca_dev,
                                        (unsigned int) (expotimes << 8));
                        break;
+               case SENSOR_OM6802:
+                       expotimes = sd->exposure;
+                       expotimes += (luma_mean - delta) >> 2;
+                       if (expotimes < 0)
+                               expotimes = 0;
+                       sd->exposure = setexposure(gspca_dev,
+                                                  (unsigned int) expotimes);
+                       setredblue(gspca_dev);
+                       break;
                default:
 /*             case SENSOR_MO4000: */
 /*             case SENSOR_MI0360: */
 /*             case SENSOR_MT9V111: */
-/*             case SENSOR_OM6802: */
                        expotimes = sd->exposure;
                        expotimes += (luma_mean - delta) >> 6;
                        if (expotimes < 0)
@@ -2007,7 +2122,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
 /* scan the URB packets */
 /* This function is run at interrupt level. */
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
@@ -2019,7 +2133,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 
                /* end of frame */
                gspca_frame_add(gspca_dev, LAST_PACKET,
-                               frame, data, sof + 2);
+                               data, sof + 2);
                if (sd->ag_cnt < 0)
                        return;
 /* w1 w2 w3 */
@@ -2042,10 +2156,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        if (gspca_dev->last_packet_type == LAST_PACKET) {
 
                /* put the JPEG 422 header */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
                        sd->jpeg_hdr, JPEG_HDR_SZ);
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
@@ -2295,69 +2409,69 @@ static const struct sd_desc sd_desc = {
 };
 
 /* -- module initialisation -- */
-#define BSI(bridge, sensor, i2c_addr) \
+#define BS(bridge, sensor) \
        .driver_info = (BRIDGE_ ## bridge << 16) \
-                       | (SENSOR_ ## sensor << 8) \
-                       | (i2c_addr)
+                       | SENSOR_ ## sensor
 static const __devinitdata struct usb_device_id device_table[] = {
 #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
-       {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
-       {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)},
+       {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)},
+       {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
 #endif
-       {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
-       {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
-       {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
-       {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
-       {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
-       {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
-       {USB_DEVICE(0x06f8, 0x3008), BSI(SN9C105, OV7660, 0x21)},
-       {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
-/* bw600.inf:
-       {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
-/*     {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */
-/*     {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */
-       {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)},
-/*     {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */
-       {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)},
-/*     {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */
-/*     {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */
-       {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)},
-/*     {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */
-/*     {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */
-       {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)},
+       {USB_DEVICE(0x045e, 0x00f5), BS(SN9C105, OV7660)},
+       {USB_DEVICE(0x045e, 0x00f7), BS(SN9C105, OV7660)},
+       {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
+       {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
+       {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
+       {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
+       {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
+/*     {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
+       {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
+/*     {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
+/*     {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
+       {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
+/*     {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
+       {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)},
+/*     {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
+/*     {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
+/*     {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
+       {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
+/*     {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
+/*     {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
+       {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
 #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
-       {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)},
-       {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)},
+       {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
+       {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
 #endif
-       {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/
-/*     {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */
-/*     {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */
-       {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/
-       {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/
-       {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/
-       {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/
-/*     {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
-/*     {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
-       {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/
+       {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)},      /*sn9c128*/
+/*     {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, P1030xC)}, */
+/*     {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
+       {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)},      /*sn9c128*/
+       {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)},      /*sn9c128*/
+       {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)},     /*sn9c128*/
+       {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)},      /*sn9c128*/
+/*     {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
+/*     {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
+/*     {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
+       {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)},      /*sn9c325?*/
 /*bw600.inf:*/
-       {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/
-       {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)},
-       {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)},
-/*     {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */
+       {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)},      /*sn9c325?*/
+       {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
+       {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
+/*     {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
 #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
-       {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)},
+       {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
 #endif
-/*     {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */
-       {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)},
-       {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
+/*     {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
+       {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
+       {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
 #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
-       {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
+       {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
 #endif
-       {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
-       {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)},
-/*     {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/
-       {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/
-       {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/
+       {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
+       {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
+/*     {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)},      *sn9c120b*/
+       {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)},     /*sn9c120b*/
+       {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)},      /*sn9c120b*/
        {}
 };
 MODULE_DEVICE_TABLE(usb, device_table);
index fab7ef8..fe46868 100644 (file)
@@ -589,7 +589,7 @@ static void spca500_reinit(struct gspca_dev *gspca_dev)
        int err;
        __u8 Data;
 
-       /* some unknow command from Aiptek pocket dv and family300 */
+       /* some unknown command from Aiptek pocket dv and family300 */
 
        reg_w(gspca_dev, 0x00, 0x0d01, 0x01);
        reg_w(gspca_dev, 0x00, 0x0d03, 0x00);
@@ -899,8 +899,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -913,11 +912,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 /*                     gspca_dev->last_packet_type = DISCARD_PACKET; */
                        return;
                }
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+               gspca_frame_add(gspca_dev, LAST_PACKET,
                                        ffd9, 2);
 
                /* put the JPEG header in the new frame */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
                        sd->jpeg_hdr, JPEG_HDR_SZ);
 
                data += SPCA500_OFFSET_DATA;
@@ -931,7 +930,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        i = 0;
        do {
                if (data[i] == 0xff) {
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       gspca_frame_add(gspca_dev, INTER_PACKET,
                                        data, i + 1);
                        len -= i;
                        data += i;
@@ -940,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                }
                i++;
        } while (i < len);
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static void setbrightness(struct gspca_dev *gspca_dev)
index b74a342..6761a30 100644 (file)
@@ -1636,7 +1636,7 @@ static const __u16 spca501c_arowana_init_data[][3] = {
        {}
 };
 
-/* Unknow camera from Ori Usbid 0x0000:0x0000 */
+/* Unknown camera from Ori Usbid 0x0000:0x0000 */
 /* Based on snoops from Ori Cohen */
 static const __u16 spca501c_mysterious_open_data[][3] = {
        {0x02, 0x000f, 0x0005},
@@ -1945,7 +1945,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
                        goto error;
                break;
        case MystFromOriUnknownCamera:
-               /* UnKnow Ori CMOS Camera data */
+               /* Unknown Ori CMOS Camera data */
                if (write_vector(gspca_dev, spca501c_mysterious_open_data))
                        goto error;
                break;
@@ -1978,7 +1978,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                write_vector(gspca_dev, spca501c_arowana_open_data);
                break;
        case MystFromOriUnknownCamera:
-               /* UnKnow  CMOS Camera data */
+               /* Unknown CMOS Camera data */
                write_vector(gspca_dev, spca501c_mysterious_init_data);
                break;
        default:
@@ -2032,20 +2032,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        switch (data[0]) {
        case 0:                         /* start of frame */
-               frame = gspca_frame_add(gspca_dev,
-                                       LAST_PACKET,
-                                       frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                data += SPCA501_OFFSET_DATA;
                len -= SPCA501_OFFSET_DATA;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                return;
        case 0xff:                      /* drop */
 /*             gspca_dev->last_packet_type = DISCARD_PACKET; */
@@ -2053,8 +2048,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
        data++;
        len--;
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                       data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index ea8c9fe..0f9232f 100644 (file)
@@ -739,26 +739,22 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        switch (data[0]) {
        case 0:                         /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                data += SPCA50X_OFFSET_DATA;
                len -= SPCA50X_OFFSET_DATA;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                break;
        case 0xff:                      /* drop */
                break;
        default:
                data += 1;
                len -= 1;
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
                break;
        }
 }
index a199298..ab28cc2 100644 (file)
@@ -543,18 +543,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        switch (data[0]) {
        case 0:                         /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                data += SPCA50X_OFFSET_DATA;
                len -= SPCA50X_OFFSET_DATA;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                break;
        case 0xff:                      /* drop */
 /*             gspca_dev->last_packet_type = DISCARD_PACKET; */
@@ -562,8 +559,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        default:
                data += 1;
                len -= 1;
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
                break;
        }
 }
index 9696c4c..4d8e6cf 100644 (file)
@@ -1447,26 +1447,22 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        switch (data[0]) {
        case 0:                         /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                data += SPCA508_OFFSET_DATA;
                len -= SPCA508_OFFSET_DATA;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                break;
        case 0xff:                      /* drop */
                break;
        default:
                data += 1;
                len -= 1;
-               gspca_frame_add(gspca_dev, INTER_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
                break;
        }
 }
index 27e82b3..58c2f00 100644 (file)
@@ -779,8 +779,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame, /* target */
-                       __u8 *data,             /* isoc packet */
+                       u8 *data,               /* isoc packet */
                        int len)                /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -788,12 +787,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        len--;
        switch (*data++) {                      /* sequence number */
        case 0:                                 /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                if (data[1] & 0x10) {
                        /* compressed bayer */
-                       gspca_frame_add(gspca_dev, FIRST_PACKET,
-                                       frame, data, len);
+                       gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                } else {
                        /* raw bayer (with a header, which we skip) */
                        if (sd->chip_revision == Rev012A) {
@@ -803,14 +800,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                                data += 16;
                                len -= 16;
                        }
-                       gspca_frame_add(gspca_dev, FIRST_PACKET,
-                                               frame, data, len);
+                       gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                }
                return;
        case 0xff:                      /* drop (empty mpackets) */
                return;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 /* rev 72a only */
index 715a68f..1fcaca6 100644 (file)
@@ -168,18 +168,22 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev)
  *  request and read a block of data - see warning on sq905_command.
  */
 static int
-sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size)
+sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
 {
        int ret;
        int act_len;
 
        gspca_dev->usb_buf[0] = '\0';
+       if (need_lock)
+               mutex_lock(&gspca_dev->usb_lock);
        ret = usb_control_msg(gspca_dev->dev,
                              usb_sndctrlpipe(gspca_dev->dev, 0),
                              USB_REQ_SYNCH_FRAME,                /* request */
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                              SQ905_BULK_READ, size, gspca_dev->usb_buf,
                              1, SQ905_CMD_TIMEOUT);
+       if (need_lock)
+               mutex_unlock(&gspca_dev->usb_lock);
        if (ret < 0) {
                PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
                return ret;
@@ -210,11 +214,9 @@ static void sq905_dostream(struct work_struct *work)
 {
        struct sd *dev = container_of(work, struct sd, work_struct);
        struct gspca_dev *gspca_dev = &dev->gspca_dev;
-       struct gspca_frame *frame;
        int bytes_left; /* bytes remaining in current frame. */
        int data_len;   /* size to use for the next read. */
        int header_read; /* true if we have already read the frame header. */
-       int discarding; /* true if we failed to get space for frame. */
        int packet_type;
        int frame_sz;
        int ret;
@@ -222,7 +224,6 @@ static void sq905_dostream(struct work_struct *work)
        u8 *buffer;
 
        buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
-       mutex_lock(&gspca_dev->usb_lock);
        if (!buffer) {
                PDEBUG(D_ERR, "Couldn't allocate USB buffer");
                goto quit_stream;
@@ -232,28 +233,22 @@ static void sq905_dostream(struct work_struct *work)
                        + FRAME_HEADER_LEN;
 
        while (gspca_dev->present && gspca_dev->streaming) {
-               /* Need a short delay to ensure streaming flag was set by
-                * gspca and to make sure gspca can grab the mutex. */
-               mutex_unlock(&gspca_dev->usb_lock);
-               msleep(1);
-
                /* request some data and then read it until we have
                 * a complete frame. */
                bytes_left = frame_sz;
                header_read = 0;
-               discarding = 0;
 
-               while (bytes_left > 0) {
+               /* Note we do not check for gspca_dev->streaming here, as
+                  we must finish reading an entire frame, otherwise the
+                  next time we stream we start reading in the middle of a
+                  frame. */
+               while (bytes_left > 0 && gspca_dev->present) {
                        data_len = bytes_left > SQ905_MAX_TRANSFER ?
                                SQ905_MAX_TRANSFER : bytes_left;
-                       mutex_lock(&gspca_dev->usb_lock);
-                       if (!gspca_dev->present)
-                               goto quit_stream;
-                       ret = sq905_read_data(gspca_dev, buffer, data_len);
+                       ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
                        if (ret < 0)
                                goto quit_stream;
-                       mutex_unlock(&gspca_dev->usb_lock);
-                       PDEBUG(D_STREAM,
+                       PDEBUG(D_PACK,
                                "Got %d bytes out of %d for frame",
                                data_len, bytes_left);
                        bytes_left -= data_len;
@@ -270,34 +265,30 @@ static void sq905_dostream(struct work_struct *work)
                        } else {
                                packet_type = INTER_PACKET;
                        }
-                       frame = gspca_get_i_frame(gspca_dev);
-                       if (frame && !discarding) {
-                               frame = gspca_frame_add(gspca_dev, packet_type,
-                                               frame, data, data_len);
-                               /* If entire frame fits in one packet we still
-                                  need to add a LAST_PACKET */
-                               if (packet_type == FIRST_PACKET &&
-                                   bytes_left == 0)
-                                       frame = gspca_frame_add(gspca_dev,
-                                                       LAST_PACKET,
-                                                       frame, data, 0);
-                       } else {
-                               discarding = 1;
-                       }
+                       gspca_frame_add(gspca_dev, packet_type,
+                                       data, data_len);
+                       /* If entire frame fits in one packet we still
+                          need to add a LAST_PACKET */
+                       if (packet_type == FIRST_PACKET &&
+                           bytes_left == 0)
+                               gspca_frame_add(gspca_dev, LAST_PACKET,
+                                               NULL, 0);
+               }
+               if (gspca_dev->present) {
+                       /* acknowledge the frame */
+                       mutex_lock(&gspca_dev->usb_lock);
+                       ret = sq905_ack_frame(gspca_dev);
+                       mutex_unlock(&gspca_dev->usb_lock);
+                       if (ret < 0)
+                               goto quit_stream;
                }
-               /* acknowledge the frame */
-               mutex_lock(&gspca_dev->usb_lock);
-               if (!gspca_dev->present)
-                       goto quit_stream;
-               ret = sq905_ack_frame(gspca_dev);
-               if (ret < 0)
-                       goto quit_stream;
        }
 quit_stream:
-       /* the usb_lock is already acquired */
-       if (gspca_dev->present)
+       if (gspca_dev->present) {
+               mutex_lock(&gspca_dev->usb_lock);
                sq905_command(gspca_dev, SQ905_CLEAR);
-       mutex_unlock(&gspca_dev->usb_lock);
+               mutex_unlock(&gspca_dev->usb_lock);
+       }
        kfree(buffer);
 }
 
@@ -346,7 +337,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
        ret = sq905_command(gspca_dev, SQ905_ID);
        if (ret < 0)
                return ret;
-       ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4);
+       ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
        if (ret < 0)
                return ret;
        /* usb_buf is allocated with kmalloc so is aligned.
index 9168925..d70b156 100644 (file)
@@ -115,11 +115,9 @@ static void sq905c_dostream(struct work_struct *work)
 {
        struct sd *dev = container_of(work, struct sd, work_struct);
        struct gspca_dev *gspca_dev = &dev->gspca_dev;
-       struct gspca_frame *frame;
        int bytes_left; /* bytes remaining in current frame. */
        int data_len;   /* size to use for the next read. */
        int act_len;
-       int discarding = 0; /* true if we failed to get space for frame. */
        int packet_type;
        int ret;
        u8 *buffer;
@@ -131,8 +129,6 @@ static void sq905c_dostream(struct work_struct *work)
        }
 
        while (gspca_dev->present && gspca_dev->streaming) {
-               if (!gspca_dev->present)
-                       goto quit_stream;
                /* Request the header, which tells the size to download */
                ret = usb_bulk_msg(gspca_dev->dev,
                                usb_rcvbulkpipe(gspca_dev->dev, 0x81),
@@ -149,17 +145,11 @@ static void sq905c_dostream(struct work_struct *work)
                PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
                /* We keep the header. It has other information, too. */
                packet_type = FIRST_PACKET;
-               frame = gspca_get_i_frame(gspca_dev);
-               if (frame && !discarding) {
-                       gspca_frame_add(gspca_dev, packet_type,
-                               frame, buffer, FRAME_HEADER_LEN);
-                       } else
-                               discarding = 1;
-               while (bytes_left > 0) {
+               gspca_frame_add(gspca_dev, packet_type,
+                               buffer, FRAME_HEADER_LEN);
+               while (bytes_left > 0 && gspca_dev->present) {
                        data_len = bytes_left > SQ905C_MAX_TRANSFER ?
                                SQ905C_MAX_TRANSFER : bytes_left;
-                       if (!gspca_dev->present)
-                               goto quit_stream;
                        ret = usb_bulk_msg(gspca_dev->dev,
                                usb_rcvbulkpipe(gspca_dev->dev, 0x81),
                                buffer, data_len, &act_len,
@@ -174,19 +164,16 @@ static void sq905c_dostream(struct work_struct *work)
                                packet_type = LAST_PACKET;
                        else
                                packet_type = INTER_PACKET;
-                       frame = gspca_get_i_frame(gspca_dev);
-                       if (frame && !discarding)
-                               gspca_frame_add(gspca_dev, packet_type,
-                                               frame, buffer, data_len);
-                       else
-                               discarding = 1;
+                       gspca_frame_add(gspca_dev, packet_type,
+                                       buffer, data_len);
                }
        }
 quit_stream:
-       mutex_lock(&gspca_dev->usb_lock);
-       if (gspca_dev->present)
+       if (gspca_dev->present) {
+               mutex_lock(&gspca_dev->usb_lock);
                sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
-       mutex_unlock(&gspca_dev->usb_lock);
+               mutex_unlock(&gspca_dev->usb_lock);
+       }
        kfree(buffer);
 }
 
index 4762896..8e23320 100644 (file)
@@ -418,8 +418,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -435,11 +434,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
         *              (without ending - ff d9)
         */
        if (data[0] == 0xff && data[1] == 0xfe) {
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       ffd9, 2);
+               gspca_frame_add(gspca_dev, LAST_PACKET,
+                               ffd9, 2);
 
                /* put the JPEG 411 header */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
                        sd->jpeg_hdr, JPEG_HDR_SZ);
 
                /* beginning of the frame */
@@ -447,7 +446,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                data += STKHDRSZ;
                len -= STKHDRSZ;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
new file mode 100644 (file)
index 0000000..2a69d7c
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * STV0680 USB Camera Driver
+ *
+ * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 stv680 driver:
+ *
+ *  STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *
+ * Thanks to STMicroelectronics for information on the usb commands, and
+ * to Steve Miller at STM for his help and encouragement while I was
+ * writing this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#define MODULE_NAME "stv0680"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_DESCRIPTION("STV0680 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+       struct gspca_dev gspca_dev;             /* !! must be the first item */
+       struct v4l2_pix_format mode;
+       u8 orig_mode;
+       u8 video_mode;
+       u8 current_mode;
+};
+
+/* V4L2 controls supported by the driver */
+static struct ctrl sd_ctrls[] = {
+};
+
+static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
+                      int size)
+{
+       int ret = -1;
+       u8 req_type = 0;
+
+       switch (set) {
+       case 0: /*  0xc1  */
+               req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+               break;
+       case 1: /*  0x41  */
+               req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
+               break;
+       case 2: /*  0x80  */
+               req_type = USB_DIR_IN | USB_RECIP_DEVICE;
+               break;
+       case 3: /*  0x40  */
+               req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+               break;
+       }
+
+       ret = usb_control_msg(gspca_dev->dev,
+                             usb_rcvctrlpipe(gspca_dev->dev, 0),
+                             req, req_type,
+                             val, 0, gspca_dev->usb_buf, size, 500);
+
+       if ((ret < 0) && (req != 0x0a))
+               PDEBUG(D_ERR,
+                      "usb_control_msg error %i, request = 0x%x, error = %i",
+                      set, req, ret);
+
+       return ret;
+}
+
+static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret)
+{
+       stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */
+       PDEBUG(D_ERR, "last error: %i,  command = 0x%x",
+              gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+       return ret;
+}
+
+static int stv0680_get_video_mode(struct gspca_dev *gspca_dev)
+{
+       /* Note not sure if this init of usb_buf is really necessary */
+       memset(gspca_dev->usb_buf, 0, 8);
+       gspca_dev->usb_buf[0] = 0x0f;
+
+       if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) {
+               PDEBUG(D_ERR, "Get_Camera_Mode failed");
+               return stv0680_handle_error(gspca_dev, -EIO);
+       }
+
+       return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
+}
+
+static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (sd->current_mode == mode)
+               return 0;
+
+       memset(gspca_dev->usb_buf, 0, 8);
+       gspca_dev->usb_buf[0] = mode;
+
+       if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) {
+               PDEBUG(D_ERR, "Set_Camera_Mode failed");
+               return stv0680_handle_error(gspca_dev, -EIO);
+       }
+
+       /* Verify we got what we've asked for */
+       if (stv0680_get_video_mode(gspca_dev) != mode) {
+               PDEBUG(D_ERR, "Error setting camera video mode!");
+               return -EIO;
+       }
+
+       sd->current_mode = mode;
+
+       return 0;
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+                       const struct usb_device_id *id)
+{
+       int ret;
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam = &gspca_dev->cam;
+
+       /* ping camera to be sure STV0680 is present */
+       if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 ||
+           gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) {
+               PDEBUG(D_ERR, "STV(e): camera ping failed!!");
+               return stv0680_handle_error(gspca_dev, -ENODEV);
+       }
+
+       /* get camera descriptor */
+       if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09)
+               return stv0680_handle_error(gspca_dev, -ENODEV);
+
+       if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 ||
+           gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) {
+               PDEBUG(D_ERR, "Could not get descriptor 0200.");
+               return stv0680_handle_error(gspca_dev, -ENODEV);
+       }
+       if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02)
+               return stv0680_handle_error(gspca_dev, -ENODEV);
+       if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24)
+               return stv0680_handle_error(gspca_dev, -ENODEV);
+       if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+               return stv0680_handle_error(gspca_dev, -ENODEV);
+
+       if (!(gspca_dev->usb_buf[7] & 0x09)) {
+               PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode");
+               return -ENODEV;
+       }
+       if (gspca_dev->usb_buf[7] & 0x01)
+               PDEBUG(D_PROBE, "Camera supports CIF mode");
+       if (gspca_dev->usb_buf[7] & 0x02)
+               PDEBUG(D_PROBE, "Camera supports VGA mode");
+       if (gspca_dev->usb_buf[7] & 0x08)
+               PDEBUG(D_PROBE, "Camera supports QVGA mode");
+
+       if (gspca_dev->usb_buf[7] & 0x01)
+               sd->video_mode = 0x00; /* CIF */
+       else
+               sd->video_mode = 0x03; /* QVGA */
+
+       /* FW rev, ASIC rev, sensor ID  */
+       PDEBUG(D_PROBE, "Firmware rev is %i.%i",
+              gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
+       PDEBUG(D_PROBE, "ASIC rev is %i.%i",
+              gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]);
+       PDEBUG(D_PROBE, "Sensor ID is %i",
+              (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4));
+
+
+       ret = stv0680_get_video_mode(gspca_dev);
+       if (ret < 0)
+               return ret;
+       sd->current_mode = sd->orig_mode = ret;
+
+       ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+       if (ret < 0)
+               return ret;
+
+       /* Get mode details */
+       if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10)
+               return stv0680_handle_error(gspca_dev, -EIO);
+
+       cam->bulk = 1;
+       cam->bulk_nurbs = 1; /* The cam cannot handle more */
+       cam->bulk_size = (gspca_dev->usb_buf[0] << 24) |
+                        (gspca_dev->usb_buf[1] << 16) |
+                        (gspca_dev->usb_buf[2] << 8) |
+                        (gspca_dev->usb_buf[3]);
+       sd->mode.width = (gspca_dev->usb_buf[4] << 8) |
+                        (gspca_dev->usb_buf[5]);  /* 322, 356, 644 */
+       sd->mode.height = (gspca_dev->usb_buf[6] << 8) |
+                         (gspca_dev->usb_buf[7]); /* 242, 292, 484 */
+       sd->mode.pixelformat = V4L2_PIX_FMT_STV0680;
+       sd->mode.field = V4L2_FIELD_NONE;
+       sd->mode.bytesperline = sd->mode.width;
+       sd->mode.sizeimage = cam->bulk_size;
+       sd->mode.colorspace = V4L2_COLORSPACE_SRGB;
+
+       /* origGain = gspca_dev->usb_buf[12]; */
+
+       cam->cam_mode = &sd->mode;
+       cam->nmodes = 1;
+
+
+       ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+       if (ret < 0)
+               return ret;
+
+       if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
+           gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
+               PDEBUG(D_ERR, "Could not get descriptor 0100.");
+               return stv0680_handle_error(gspca_dev, -EIO);
+       }
+
+       return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+       return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+       int ret;
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
+       if (ret < 0)
+               return ret;
+
+       if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
+               return stv0680_handle_error(gspca_dev, -EIO);
+
+       /* Start stream at:
+          0x0000 = CIF (352x288)
+          0x0100 = VGA (640x480)
+          0x0300 = QVGA (320x240) */
+       if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0)
+               return stv0680_handle_error(gspca_dev, -EIO);
+
+       return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+       /* This is a high priority command; it stops all lower order cmds */
+       if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0)
+               stv0680_handle_error(gspca_dev, -EIO);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (!sd->gspca_dev.present)
+               return;
+
+       stv0680_set_video_mode(gspca_dev, sd->orig_mode);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data,
+                       int len)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* Every now and then the camera sends a 16 byte packet, no idea
+          what it contains, but it is not image data, when this
+          happens the frame received before this packet is corrupt,
+          so discard it. */
+       if (len != sd->mode.sizeimage) {
+               gspca_dev->last_packet_type = DISCARD_PACKET;
+               return;
+       }
+
+       /* Finish the previous frame, we do this upon reception of the next
+          packet, even though it is already complete so that the strange 16
+          byte packets send after a corrupt frame can discard it. */
+       gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+       /* Store the just received frame */
+       gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .init = sd_init,
+       .start = sd_start,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const __devinitdata struct usb_device_id device_table[] = {
+       {USB_DEVICE(0x0553, 0x0202)},
+       {USB_DEVICE(0x041e, 0x4007)},
+       {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+                               THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+       .name = MODULE_NAME,
+       .id_table = device_table,
+       .probe = sd_probe,
+       .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+       .suspend = gspca_suspend,
+       .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+       int ret;
+       ret = usb_register(&sd_driver);
+       if (ret < 0)
+               return ret;
+       PDEBUG(D_PROBE, "registered");
+       return 0;
+}
+static void __exit sd_mod_exit(void)
+{
+       usb_deregister(&sd_driver);
+       PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
index bfae63f..5d0241b 100644 (file)
@@ -312,8 +312,7 @@ out:
  * The 0005 and 0100 chunks seem to appear only in compressed stream.
  */
 static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -366,7 +365,7 @@ frame_data:
                                sd->to_skip -= skip;
                        }
 
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       gspca_frame_add(gspca_dev, INTER_PACKET,
                                        data, chunk_len);
                        break;
 
@@ -378,7 +377,7 @@ frame_data:
 
                        /* Create a new frame, chunk length should be zero */
                        gspca_frame_add(gspca_dev, FIRST_PACKET,
-                                       frame, data, 0);
+                                       NULL, 0);
 
                        if (sd->bridge == BRIDGE_ST6422)
                                sd->to_skip = gspca_dev->width * 4;
@@ -394,8 +393,8 @@ frame_data:
                        PDEBUG(D_PACK, "End of frame detected");
 
                        /* Complete the last frame (if any) */
-                       frame = gspca_frame_add(gspca_dev, LAST_PACKET,
-                                               frame, data, 0);
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
+                                       NULL, 0);
 
                        if (chunk_len)
                                PDEBUG(D_ERR, "Chunk length is "
index aa8f995..72bf3b4 100644 (file)
@@ -631,7 +631,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
        count = 200;
        while (--count > 0) {
                msleep(10);
-               /* gsmart mini2 write a each wait setting 1 ms is enought */
+               /* gsmart mini2 write a each wait setting 1 ms is enough */
 /*             reg_w_riv(dev, req, idx, val); */
                status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
                if (status == endcode) {
@@ -1116,7 +1116,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
@@ -1186,11 +1185,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                break;
        }
        if (sof) {              /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       ffd9, 2);
+               gspca_frame_add(gspca_dev, LAST_PACKET,
+                               ffd9, 2);
 
                /* put the JPEG header in the new frame */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
                        sd->jpeg_hdr, JPEG_HDR_SZ);
        }
 
@@ -1198,7 +1197,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        i = 0;
        do {
                if (data[i] == 0xff) {
-                       gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+                       gspca_frame_add(gspca_dev, INTER_PACKET,
                                        data, i + 1);
                        len -= i;
                        data += i;
@@ -1207,7 +1206,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                }
                i++;
        } while (i < len);
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index 1d321c3..55ef6a7 100644 (file)
@@ -938,7 +938,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
@@ -956,9 +955,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                /* extra bytes....., could be processed too but would be
                 * a waste of time, right now leave the application and
                 * libjpeg do it for ourserlves.. */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+               gspca_frame_add(gspca_dev, LAST_PACKET,
                                        ffd9, 2);
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                return;
        }
 
@@ -967,7 +966,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                 * other's do not include it... */
                len -= 2;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index 4b44dde..b74a3b6 100644 (file)
@@ -398,8 +398,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
-                       __u8 *data,                     /* isoc packet */
+                       u8 *data,                       /* isoc packet */
                        int len)                        /* iso packet length */
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -424,9 +423,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
         * - 4 bytes
         */
        gspca_frame_add(gspca_dev, packet_type0,
-                       frame, data + 2, gspca_dev->width);
+                       data + 2, gspca_dev->width);
        gspca_frame_add(gspca_dev, packet_type1,
-                       frame, data + gspca_dev->width + 5, gspca_dev->width);
+                       data + gspca_dev->width + 5, gspca_dev->width);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index 589042f..c090efc 100644 (file)
@@ -2987,7 +2987,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,      /* target */
                        u8 *data,                       /* isoc packet */
                        int len)                        /* iso pkt length */
 {
@@ -2996,21 +2995,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        if (data[0] == 0xff && data[1] == 0xd8) {
                PDEBUG(D_PACK,
                        "vc032x header packet found len %d", len);
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                               data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
                data += sd->image_offset;
                len -= sd->image_offset;
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
-                               data, len);
+               gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
                return;
        }
 
        /* The vc0321 sends some additional data after sending the complete
         * frame, we ignore this. */
-       if (sd->bridge == BRIDGE_VC0321
-           && len > frame->v4l2_buf.length - (frame->data_end - frame->data))
-               len = frame->v4l2_buf.length - (frame->data_end - frame->data);
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       if (sd->bridge == BRIDGE_VC0321) {
+               struct gspca_frame *frame;
+               int l;
+
+               frame = gspca_get_i_frame(gspca_dev);
+               l = frame->data_end - frame->data;
+               if (len > frame->v4l2_buf.length - l)
+                       len = frame->v4l2_buf.length - l;
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
@@ -3092,6 +3095,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
 
        switch (menu->id) {
        case V4L2_CID_POWER_LINE_FREQUENCY:
+               if (menu->index >= ARRAY_SIZE(freq_nm))
+                       break;
                strcpy((char *) menu->name, freq_nm[menu->index]);
                return 0;
        }
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
new file mode 100644 (file)
index 0000000..2fffe20
--- /dev/null
@@ -0,0 +1,609 @@
+/**
+ *
+ * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip.
+ *
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 w9968cf driver:
+ *
+ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Note this is not a stand alone driver, it gets included in ov519.c, this
+   is a bit of a hack, but it needs the driver code for a lot of different
+   ov sensors which is already present in ov519.c (the old v4l1 driver used
+   the ovchipcam framework). When we have the time we really should move
+   the sensor drivers to v4l2 sub drivers, and properly split of this
+   driver from ov519.c */
+
+/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
+#define CONEX_CAM
+#include "jpeg.h"
+
+#define W9968CF_I2C_BUS_DELAY    4 /* delay in us for I2C bit r/w operations */
+
+#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET)
+#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET)
+
+static const struct v4l2_pix_format w9968cf_vga_mode[] = {
+       {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+               .bytesperline = 160 * 2,
+               .sizeimage = 160 * 120 * 2,
+               .colorspace = V4L2_COLORSPACE_JPEG},
+       {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
+               .bytesperline = 176 * 2,
+               .sizeimage = 176 * 144 * 2,
+               .colorspace = V4L2_COLORSPACE_JPEG},
+       {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 320 * 2,
+               .sizeimage = 320 * 240 * 2,
+               .colorspace = V4L2_COLORSPACE_JPEG},
+       {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 352 * 2,
+               .sizeimage = 352 * 288 * 2,
+               .colorspace = V4L2_COLORSPACE_JPEG},
+       {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+               .bytesperline = 640 * 2,
+               .sizeimage = 640 * 480 * 2,
+               .colorspace = V4L2_COLORSPACE_JPEG},
+};
+
+static int reg_w(struct sd *sd, __u16 index, __u16 value);
+
+/*--------------------------------------------------------------------------
+  Write 64-bit data to the fast serial bus registers.
+  Return 0 on success, -1 otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_write_fsb(struct sd *sd, u16* data)
+{
+       struct usb_device* udev = sd->gspca_dev.dev;
+       u16 value;
+       int ret;
+
+       value = *data++;
+       memcpy(sd->gspca_dev.usb_buf, data, 6);
+
+       ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+                             USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+                             value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
+       if (ret < 0) {
+               PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*--------------------------------------------------------------------------
+  Write data to the serial bus control register.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_write_sb(struct sd *sd, u16 value)
+{
+       int ret;
+
+       /* We don't use reg_w here, as that would cause all writes when
+          bitbanging i2c to be logged, making the logs impossible to read */
+       ret = usb_control_msg(sd->gspca_dev.dev,
+               usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+               0,
+               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+               value, 0x01, NULL, 0, 500);
+
+       udelay(W9968CF_I2C_BUS_DELAY);
+
+       if (ret < 0) {
+               PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*--------------------------------------------------------------------------
+  Read data from the serial bus control register.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct sd *sd)
+{
+       int ret;
+
+       /* We don't use reg_r here, as the w9968cf is special and has 16
+          bit registers instead of 8 bit */
+       ret = usb_control_msg(sd->gspca_dev.dev,
+                       usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+                       1,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, 0x01, sd->gspca_dev.usb_buf, 2, 500);
+       if (ret >= 0)
+               ret = sd->gspca_dev.usb_buf[0] |
+                     (sd->gspca_dev.usb_buf[1] << 8);
+       else
+               PDEBUG(D_ERR, "Read SB reg [01] failed");
+
+       udelay(W9968CF_I2C_BUS_DELAY);
+
+       return ret;
+}
+
+/*--------------------------------------------------------------------------
+  Upload quantization tables for the JPEG compression.
+  This function is called by w9968cf_start_transfer().
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_upload_quantizationtables(struct sd *sd)
+{
+       u16 a, b;
+       int ret = 0, i, j;
+
+       ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */
+
+       for (i = 0, j = 0; i < 32; i++, j += 2) {
+               a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
+               b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
+               ret += reg_w(sd, 0x40+i, a);
+               ret += reg_w(sd, 0x60+i, b);
+       }
+       ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */
+
+       return ret;
+}
+
+/****************************************************************************
+ * Low-level I2C I/O functions.                                             *
+ * The adapter supports the following I2C transfer functions:               *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only)           *
+ * i2c_adap_read_byte_data()                                                *
+ * i2c_adap_read_byte()                                                     *
+ ****************************************************************************/
+
+static int w9968cf_smbus_start(struct sd *sd)
+{
+       int ret = 0;
+
+       ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+       ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+       return ret;
+}
+
+static int w9968cf_smbus_stop(struct sd *sd)
+{
+       int ret = 0;
+
+       ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+       ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+       ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+
+       return ret;
+}
+
+static int w9968cf_smbus_write_byte(struct sd *sd, u8 v)
+{
+       u8 bit;
+       int ret = 0, sda;
+
+       for (bit = 0 ; bit < 8 ; bit++) {
+               sda = (v & 0x80) ? 2 : 0;
+               v <<= 1;
+               /* SDE=1, SDA=sda, SCL=0 */
+               ret += w9968cf_write_sb(sd, 0x10 | sda);
+               /* SDE=1, SDA=sda, SCL=1 */
+               ret += w9968cf_write_sb(sd, 0x11 | sda);
+               /* SDE=1, SDA=sda, SCL=0 */
+               ret += w9968cf_write_sb(sd, 0x10 | sda);
+       }
+
+       return ret;
+}
+
+static int w9968cf_smbus_read_byte(struct sd *sd, u8* v)
+{
+       u8 bit;
+       int ret = 0;
+
+       /* No need to ensure SDA is high as we are always called after
+          read_ack which ends with SDA high */
+       *v = 0;
+       for (bit = 0 ; bit < 8 ; bit++) {
+               *v <<= 1;
+               /* SDE=1, SDA=1, SCL=1 */
+               ret += w9968cf_write_sb(sd, 0x0013);
+               *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0;
+               /* SDE=1, SDA=1, SCL=0 */
+               ret += w9968cf_write_sb(sd, 0x0012);
+       }
+
+       return ret;
+}
+
+static int w9968cf_smbus_write_nack(struct sd *sd)
+{
+       int ret = 0;
+
+       /* No need to ensure SDA is high as we are always called after
+          read_byte which ends with SDA high */
+       ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+       ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+
+       return ret;
+}
+
+static int w9968cf_smbus_read_ack(struct sd *sd)
+{
+       int ret = 0, sda;
+
+       /* Ensure SDA is high before raising clock to avoid a spurious stop */
+       ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+       ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+       sda = w9968cf_read_sb(sd);
+       ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+       if (sda < 0)
+               ret += sda;
+       else if (sda & 0x08) {
+               PDEBUG(D_USBI, "Did not receive i2c ACK");
+               ret += -1;
+       }
+
+       return ret;
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+       u16* data = (u16 *)sd->gspca_dev.usb_buf;
+       int ret = 0;
+
+       data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0);
+       data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0;
+       data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0);
+       data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0;
+       data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0;
+       data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0);
+       data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0;
+       data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0;
+       data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0);
+       data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0;
+
+       ret += w9968cf_write_fsb(sd, data);
+
+       data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0);
+       data[0] |= (reg & 0x40) ? 0x0540 : 0x0;
+       data[0] |= (reg & 0x20) ? 0x5000 : 0x0;
+       data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0);
+       data[1] |= (reg & 0x10) ? 0x0054 : 0x0;
+       data[1] |= (reg & 0x08) ? 0x1500 : 0x0;
+       data[1] |= (reg & 0x04) ? 0x4000 : 0x0;
+       data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0);
+       data[2] |= (reg & 0x02) ? 0x0150 : 0x0;
+       data[2] |= (reg & 0x01) ? 0x5400 : 0x0;
+       data[3] = 0x001d;
+
+       ret += w9968cf_write_fsb(sd, data);
+
+       data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+       data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+       data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+       data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+       data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+       data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+       data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+       data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+       data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+       data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+       data[3] = 0xfe1d;
+
+       ret += w9968cf_write_fsb(sd, data);
+
+       if (!ret)
+               PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
+       else
+               PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
+
+       return ret;
+}
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int w9968cf_i2c_r(struct sd *sd, u8 reg)
+{
+       int ret = 0;
+       u8 value;
+
+       /* Fast serial bus data control disable */
+       ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */
+
+       ret += w9968cf_smbus_start(sd);
+       ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr);
+       ret += w9968cf_smbus_read_ack(sd);
+       ret += w9968cf_smbus_write_byte(sd, reg);
+       ret += w9968cf_smbus_read_ack(sd);
+       ret += w9968cf_smbus_stop(sd);
+       ret += w9968cf_smbus_start(sd);
+       ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1);
+       ret += w9968cf_smbus_read_ack(sd);
+       ret += w9968cf_smbus_read_byte(sd, &value);
+       /* signal we don't want to read anymore, the v4l1 driver used to
+          send an ack here which is very wrong! (and then fixed
+          the issues this gave by retrying reads) */
+       ret += w9968cf_smbus_write_nack(sd);
+       ret += w9968cf_smbus_stop(sd);
+
+       /* Fast serial bus data control re-enable */
+       ret += w9968cf_write_sb(sd, 0x0030);
+
+       if (!ret) {
+               ret = value;
+               PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
+       } else
+               PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
+
+       return ret;
+}
+
+
+/*--------------------------------------------------------------------------
+  Turn on the LED on some webcams. A beep should be heard too.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_configure(struct sd *sd)
+{
+       int ret = 0;
+
+       ret += reg_w(sd, 0x00, 0xff00); /* power-down */
+       ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
+       ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
+       ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */
+       ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */
+       ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */
+       ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */
+
+       if (ret)
+               PDEBUG(D_ERR, "Couldn't turn on the LED");
+
+       sd->stopped = 1;
+
+       return ret;
+}
+
+static int w9968cf_init(struct sd *sd)
+{
+       int ret = 0;
+       unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2),
+                     y0 = 0x0000,
+                     u0 = y0 + hw_bufsize/2,
+                     v0 = u0 + hw_bufsize/4,
+                     y1 = v0 + hw_bufsize/4,
+                     u1 = y1 + hw_bufsize/2,
+                     v1 = u1 + hw_bufsize/4;
+
+       ret += reg_w(sd, 0x00, 0xff00); /* power off */
+       ret += reg_w(sd, 0x00, 0xbf10); /* power on */
+
+       ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */
+       ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */
+
+       ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */
+       ret += reg_w(sd, 0x21, y0 >> 16);    /* Y buf.0, high */
+       ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */
+       ret += reg_w(sd, 0x25, u0 >> 16);    /* U buf.0, high */
+       ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */
+       ret += reg_w(sd, 0x29, v0 >> 16);    /* V buf.0, high */
+
+       ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */
+       ret += reg_w(sd, 0x23, y1 >> 16);    /* Y buf.1, high */
+       ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */
+       ret += reg_w(sd, 0x27, u1 >> 16);    /* U buf.1, high */
+       ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */
+       ret += reg_w(sd, 0x2b, v1 >> 16);    /* V buf.1, high */
+
+       ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */
+       ret += reg_w(sd, 0x33, y1 >> 16);    /* JPEG buf 0 high */
+
+       ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */
+       ret += reg_w(sd, 0x35, y1 >> 16);    /* JPEG bug 1 high */
+
+       ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */
+       ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/
+       ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */
+       ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */
+
+       return ret;
+}
+
+static int w9968cf_set_crop_window(struct sd *sd)
+{
+       int ret = 0, start_cropx, start_cropy,  x, y, fw, fh, cw, ch,
+           max_width, max_height;
+
+       if (sd->sif) {
+               max_width  = 352;
+               max_height = 288;
+       } else {
+               max_width  = 640;
+               max_height = 480;
+       }
+
+       if (sd->sensor == SEN_OV7620) {
+               /* Sigh, this is dependend on the clock / framerate changes
+                  made by the frequency control, sick. */
+               if (sd->freq == 1) {
+                       start_cropx = 277;
+                       start_cropy = 37;
+               } else {
+                       start_cropx = 105;
+                       start_cropy = 37;
+               }
+       } else {
+               start_cropx = 320;
+               start_cropy = 35;
+       }
+
+       /* Work around to avoid FP arithmetics */
+       #define SC(x) ((x) << 10)
+
+       /* Scaling factors */
+       fw = SC(sd->gspca_dev.width) / max_width;
+       fh = SC(sd->gspca_dev.height) / max_height;
+
+       cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh;
+       ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height;
+
+       sd->sensor_width = max_width;
+       sd->sensor_height = max_height;
+
+       x = (max_width - cw) / 2;
+       y = (max_height - ch) / 2;
+
+       ret += reg_w(sd, 0x10, start_cropx + x);
+       ret += reg_w(sd, 0x11, start_cropy + y);
+       ret += reg_w(sd, 0x12, start_cropx + x + cw);
+       ret += reg_w(sd, 0x13, start_cropy + y + ch);
+
+       return ret;
+}
+
+static int w9968cf_mode_init_regs(struct sd *sd)
+{
+       int ret = 0, val, vs_polarity, hs_polarity;
+
+       ret += w9968cf_set_crop_window(sd);
+
+       ret += reg_w(sd, 0x14, sd->gspca_dev.width);
+       ret += reg_w(sd, 0x15, sd->gspca_dev.height);
+
+       /* JPEG width & height */
+       ret += reg_w(sd, 0x30, sd->gspca_dev.width);
+       ret += reg_w(sd, 0x31, sd->gspca_dev.height);
+
+       /* Y & UV frame buffer strides (in WORD) */
+       if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+           V4L2_PIX_FMT_JPEG) {
+               ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2);
+               ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4);
+       } else
+               ret += reg_w(sd, 0x2c, sd->gspca_dev.width);
+
+       ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
+       ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
+
+       /* Transfer size in WORDS (for UYVY format only) */
+       val = sd->gspca_dev.width * sd->gspca_dev.height;
+       ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */
+       ret += reg_w(sd, 0x3e, val >> 16);    /* high bits */
+
+       if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+           V4L2_PIX_FMT_JPEG) {
+               /* We may get called multiple times (usb isoc bw negotiat.) */
+               if (!sd->jpeg_hdr)
+                       sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+               if (!sd->jpeg_hdr)
+                       return -ENOMEM;
+
+               jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
+                           sd->gspca_dev.width, 0x22); /* JPEG 420 */
+               jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+               ret += w9968cf_upload_quantizationtables(sd);
+       }
+
+       /* Video Capture Control Register */
+       if (sd->sensor == SEN_OV7620) {
+               /* Seems to work around a bug in the image sensor */
+               vs_polarity = 1;
+               hs_polarity = 1;
+       } else {
+               vs_polarity = 1;
+               hs_polarity = 0;
+       }
+
+       val = (vs_polarity << 12) | (hs_polarity << 11);
+
+       /* NOTE: We may not have enough memory to do double buffering while
+          doing compression (amount of memory differs per model cam).
+          So we use the second image buffer also as jpeg stream buffer
+          (see w9968cf_init), and disable double buffering. */
+       if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
+           V4L2_PIX_FMT_JPEG) {
+               /* val |= 0x0002; YUV422P */
+               val |= 0x0003; /* YUV420P */
+       } else
+               val |= 0x0080; /* Enable HW double buffering */
+
+       /* val |= 0x0020; enable clamping */
+       /* val |= 0x0008; enable (1-2-1) filter */
+       /* val |= 0x000c; enable (2-3-6-3-2) filter */
+
+       val |= 0x8000; /* capt. enable */
+
+       ret += reg_w(sd, 0x16, val);
+
+       sd->gspca_dev.empty_packet = 0;
+
+       return ret;
+}
+
+static void w9968cf_stop0(struct sd *sd)
+{
+       if (sd->gspca_dev.present) {
+               reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
+               reg_w(sd, 0x16, 0x0000); /* stop video capture */
+       }
+
+       kfree(sd->jpeg_hdr);
+       sd->jpeg_hdr = NULL;
+}
+
+/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF
+   for the next frame). This seems to simply not be true when operating
+   in JPEG mode, in this case there may be empty packets within the
+   frame. So in JPEG mode use the JPEG SOI marker to detect SOF.
+
+   Note to make things even more interesting the w9968cf sends *PLANAR* jpeg,
+   to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS,
+   V-data, EOI. */
+static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
+                       u8 *data,                       /* isoc packet */
+                       int len)                        /* iso packet length */
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat ==
+           V4L2_PIX_FMT_JPEG) {
+               if (len >= 2 &&
+                   data[0] == 0xff &&
+                   data[1] == 0xd8) {
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
+                                       NULL, 0);
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
+                                       sd->jpeg_hdr, JPEG_HDR_SZ);
+                       /* Strip the ff d8, our own header (which adds
+                          huffman and quantization tables) already has this */
+                       len -= 2;
+                       data += 2;
+               }
+       } else {
+               /* In UYVY mode an empty packet signals EOF */
+               if (gspca_dev->empty_packet) {
+                       gspca_frame_add(gspca_dev, LAST_PACKET,
+                                               NULL, 0);
+                       gspca_frame_add(gspca_dev, FIRST_PACKET,
+                                       NULL, 0);
+                       gspca_dev->empty_packet = 0;
+               }
+       }
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
index cdf3357..69e5dc4 100644 (file)
@@ -61,17 +61,18 @@ struct sd {
 #define SENSOR_HV7131C 6
 #define SENSOR_ICM105A 7
 #define SENSOR_MC501CB 8
-#define SENSOR_OV7620 9
-/*#define SENSOR_OV7648 9 - same values */
-#define SENSOR_OV7630C 10
-#define SENSOR_PAS106 11
-#define SENSOR_PAS202B 12
-#define SENSOR_PB0330 13
-#define SENSOR_PO2030 14
-#define SENSOR_TAS5130CK 15
-#define SENSOR_TAS5130CXX 16
-#define SENSOR_TAS5130C_VF0250 17
-#define SENSOR_MAX 18
+#define SENSOR_MI0360SOC 9
+#define SENSOR_OV7620 10
+/*#define SENSOR_OV7648 10 - same values */
+#define SENSOR_OV7630C 11
+#define SENSOR_PAS106 12
+#define SENSOR_PAS202B 13
+#define SENSOR_PB0330 14       /* (MI0360) */
+#define SENSOR_PO2030 15
+#define SENSOR_TAS5130CK 16
+#define SENSOR_TAS5130CXX 17
+#define SENSOR_TAS5130C_VF0250 18
+#define SENSOR_MAX 19
        unsigned short chip_revision;
 
        u8 *jpeg_hdr;
@@ -420,9 +421,7 @@ static const struct usb_action adcm2700_NoFliker[] = {
        {0xaa, 0xfe, 0x0010},                           /* 00,fe,10,aa */
        {}
 };
-static const struct usb_action cs2102_Initial[] = {
-       {0xa1, 0x01, 0x0008},
-       {0xa1, 0x01, 0x0008},
+static const struct usb_action cs2102_Initial[] = {    /* 320x240 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -471,88 +470,10 @@ static const struct usb_action cs2102_Initial[] = {
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x68, ZC3XX_R18D_YTARGET},
        {0xa0, 0x00, 0x01ad},
-       {0xa1, 0x01, 0x0002},
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-       {0xa0, 0x24, ZC3XX_R120_GAMMA00},       /* gamma 5 */
-       {0xa0, 0x44, ZC3XX_R121_GAMMA01},
-       {0xa0, 0x64, ZC3XX_R122_GAMMA02},
-       {0xa0, 0x84, ZC3XX_R123_GAMMA03},
-       {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
-       {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
-       {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
-       {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
-       {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
-       {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
-       {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
-       {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
-       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
-       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
-       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
-       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
-       {0xa0, 0x18, ZC3XX_R130_GAMMA10},
-       {0xa0, 0x20, ZC3XX_R131_GAMMA11},
-       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
-       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
-       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
-       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
-       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
-       {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
-       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
-       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
-       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
-       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
-       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
-       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
-       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
-       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
-       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
-       {0xa0, 0x58, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf4, ZC3XX_R110_RGB20},
-       {0xa0, 0xf4, ZC3XX_R111_RGB21},
-       {0xa0, 0x58, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x23, 0x0001},
-       {0xaa, 0x24, 0x0055},
-       {0xaa, 0x25, 0x00cc},
-       {0xaa, 0x21, 0x003f},
-       {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
-       {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x40, ZC3XX_R116_RGAIN},
-       {0xa0, 0x40, ZC3XX_R117_GGAIN},
-       {0xa0, 0x40, ZC3XX_R118_BGAIN},
        {}
 };
 
-static const struct usb_action cs2102_InitialScale[] = {
-       {0xa1, 0x01, 0x0008},
-       {0xa1, 0x01, 0x0008},
+static const struct usb_action cs2102_InitialScale[] = {       /* 640x480 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
@@ -601,57 +522,75 @@ static const struct usb_action cs2102_InitialScale[] = {
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x68, ZC3XX_R18D_YTARGET},
        {0xa0, 0x00, 0x01ad},
-       {0xa1, 0x01, 0x0002},
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-       {0xa0, 0x24, ZC3XX_R120_GAMMA00},       /* gamma 5 */
-       {0xa0, 0x44, ZC3XX_R121_GAMMA01},
-       {0xa0, 0x64, ZC3XX_R122_GAMMA02},
-       {0xa0, 0x84, ZC3XX_R123_GAMMA03},
-       {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
-       {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
-       {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
-       {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
-       {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
-       {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
-       {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
-       {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
-       {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
-       {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
-       {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
-       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
-       {0xa0, 0x18, ZC3XX_R130_GAMMA10},
-       {0xa0, 0x20, ZC3XX_R131_GAMMA11},
-       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
-       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
-       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
-       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
-       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
-       {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
-       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
-       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
-       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
-       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
-       {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
-       {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
-       {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
-       {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
-       {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf4, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf4, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf4, ZC3XX_R10D_RGB10},
-       {0xa0, 0x58, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf4, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf4, ZC3XX_R110_RGB20},
-       {0xa0, 0xf4, ZC3XX_R111_RGB21},
-       {0xa0, 0x58, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action cs2102_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0001},
+       {0xaa, 0x24, 0x005f},
+       {0xaa, 0x25, 0x0090},
+       {0xaa, 0x21, 0x00dd},
+       {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action cs2102_50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0000},
+       {0xaa, 0x24, 0x00af},
+       {0xaa, 0x25, 0x00c8},
+       {0xaa, 0x21, 0x0068},
+       {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action cs2102_60HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0001},
+       {0xaa, 0x24, 0x0055},
+       {0xaa, 0x25, 0x00cc},
+       {0xaa, 0x21, 0x003f},
+       {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action cs2102_60HZScale[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
        {0xaa, 0x23, 0x0000},
        {0xaa, 0x24, 0x00aa},
@@ -671,162 +610,50 @@ static const struct usb_action cs2102_InitialScale[] = {
        {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
        {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x40, ZC3XX_R116_RGAIN},
-       {0xa0, 0x40, ZC3XX_R117_GGAIN},
-       {0xa0, 0x40, ZC3XX_R118_BGAIN},
-       {}
-};
-static const struct usb_action cs2102_50HZ[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */
-       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
-       {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */
-       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
-       {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */
-       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
-       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
-       {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
-       {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */
-       {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
-       {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
-       {}
-};
-static const struct usb_action cs2102_50HZScale[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */
-       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
-       {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */
-       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
-       {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */
-       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
-       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
-       {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
-       {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
-       {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */
-       {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
-       {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
-       {}
-};
-static const struct usb_action cs2102_60HZ[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */
-       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
-       {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */
-       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
-       {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */
-       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
-       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
-       {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
-       {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
-       {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */
-       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
-       {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */
-       {}
-};
-static const struct usb_action cs2102_60HZScale[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */
-       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
-       {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */
-       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
-       {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */
-       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
-       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
-       {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
-       {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
-       {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */
-       {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
-       {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
        {}
 };
 static const struct usb_action cs2102_NoFliker[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
-       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
-       {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
-       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
-       {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
-       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
-       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
-       {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
-       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
-       {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
-       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
-       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0001},
+       {0xaa, 0x24, 0x005f},
+       {0xaa, 0x25, 0x0000},
+       {0xaa, 0x21, 0x0001},
+       {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
        {}
 };
 static const struct usb_action cs2102_NoFlikerScale[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
-       {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
-       {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
-       {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
-       {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
-       {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
-       {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
-       {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
-       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
-       {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
-       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
-       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xaa, 0x23, 0x0000},
+       {0xaa, 0x24, 0x00af},
+       {0xaa, 0x25, 0x0080},
+       {0xaa, 0x21, 0x0001},
+       {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
        {}
 };
 
@@ -4409,170 +4236,80 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
        {}
 };
 
-static const struct usb_action pb03303x_Initial[] = {
+/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */
+static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
        {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
-       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},      /*jfm: was 03*/
+/*     {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
        {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xdd, 0x00, 0x0200},
        {0xaa, 0x01, 0x0001},
        {0xaa, 0x06, 0x0000},
        {0xaa, 0x08, 0x0483},
        {0xaa, 0x01, 0x0004},
        {0xaa, 0x08, 0x0006},
        {0xaa, 0x02, 0x0011},
-       {0xaa, 0x03, 0x01e7},
-       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x03, 0x01e5},                   /*jfm: was 01e7*/
+       {0xaa, 0x04, 0x0285},                   /*jfm: was 0287*/
        {0xaa, 0x07, 0x3002},
-       {0xaa, 0x20, 0x1100},
-       {0xaa, 0x35, 0x0050},
+       {0xaa, 0x20, 0x5100},                   /*jfm: was 1100*/
+       {0xaa, 0x35, 0x507f},                   /*jfm: was 0050*/
        {0xaa, 0x30, 0x0005},
        {0xaa, 0x31, 0x0000},
        {0xaa, 0x58, 0x0078},
        {0xaa, 0x62, 0x0411},
        {0xaa, 0x2b, 0x0028},
-       {0xaa, 0x2c, 0x0030},
-       {0xaa, 0x2d, 0x0030},
-       {0xaa, 0x2e, 0x0028},
+       {0xaa, 0x2c, 0x007f},                   /*jfm: was 0030*/
+       {0xaa, 0x2d, 0x007f},                   /*jfm: was 0030*/
+       {0xaa, 0x2e, 0x007f},                   /*jfm: was 0030*/
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
-       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x09, 0x01ad},                   /*jfm: was 00*/
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},       /* jfm: was 78 */
        {0xa0, 0x61, ZC3XX_R116_RGAIN},
        {0xa0, 0x65, ZC3XX_R118_BGAIN},
-
-       {0xa1, 0x01, 0x0002},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x0d, 0x003a},
-       {0xa0, 0x02, 0x003b},
-       {0xa0, 0x00, 0x0038},
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
-       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
-       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
-       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
-       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
-       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
-       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
-       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
-       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
-       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
-       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
-       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
-       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
-       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
-       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
-       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
-       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
-       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
-       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
-       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
-       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
-       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
-       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
-       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
-       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
-       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
-       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
-       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
-       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
-       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
-       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
-       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x05, 0x0009},
-       {0xaa, 0x09, 0x0134},
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-
-static const struct usb_action pb03303x_InitialScale[] = {
+static const struct usb_action mi0360soc_InitialScale[] = {    /* 320x240 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
        {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
-       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
+       {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-       {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+       {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},      /*jfm: was 03*/
+/*     {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
        {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
        {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+       {0xdd, 0x00, 0x0200},
        {0xaa, 0x01, 0x0001},
        {0xaa, 0x06, 0x0000},
        {0xaa, 0x08, 0x0483},
@@ -4582,111 +4319,111 @@ static const struct usb_action pb03303x_InitialScale[] = {
        {0xaa, 0x03, 0x01e7},
        {0xaa, 0x04, 0x0287},
        {0xaa, 0x07, 0x3002},
-       {0xaa, 0x20, 0x1100},
-       {0xaa, 0x35, 0x0050},
+       {0xaa, 0x20, 0x5100},                   /*jfm: was 1100*/
+       {0xaa, 0x35, 0x007f},                   /*jfm: was 0050*/
        {0xaa, 0x30, 0x0005},
        {0xaa, 0x31, 0x0000},
        {0xaa, 0x58, 0x0078},
        {0xaa, 0x62, 0x0411},
-       {0xaa, 0x2b, 0x0028},
-       {0xaa, 0x2c, 0x0030},
-       {0xaa, 0x2d, 0x0030},
-       {0xaa, 0x2e, 0x0028},
+       {0xaa, 0x2b, 0x007f},                   /*jfm: was 28*/
+       {0xaa, 0x2c, 0x007f},                   /*jfm: was 30*/
+       {0xaa, 0x2d, 0x007f},                   /*jfm: was 30*/
+       {0xaa, 0x2e, 0x007f},                   /*jfm: was 28*/
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
-       {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+       {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},      /*jfm: was 37*/
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x09, 0x01ad},                   /*jfm: was 00*/
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},       /*jfm: was 78*/
        {0xa0, 0x61, ZC3XX_R116_RGAIN},
        {0xa0, 0x65, ZC3XX_R118_BGAIN},
-
-       {0xa1, 0x01, 0x0002},
-
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-
-       {0xa0, 0x0d, 0x003a},
-       {0xa0, 0x02, 0x003b},
-       {0xa0, 0x00, 0x0038},
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x13, ZC3XX_R120_GAMMA00},       /* gamma 4 */
-       {0xa0, 0x38, ZC3XX_R121_GAMMA01},
-       {0xa0, 0x59, ZC3XX_R122_GAMMA02},
-       {0xa0, 0x79, ZC3XX_R123_GAMMA03},
-       {0xa0, 0x92, ZC3XX_R124_GAMMA04},
-       {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
-       {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
-       {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
-       {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
-       {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
-       {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
-       {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
-       {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
-       {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
-       {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
-       {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
-       {0xa0, 0x26, ZC3XX_R130_GAMMA10},
-       {0xa0, 0x22, ZC3XX_R131_GAMMA11},
-       {0xa0, 0x20, ZC3XX_R132_GAMMA12},
-       {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
-       {0xa0, 0x16, ZC3XX_R134_GAMMA14},
-       {0xa0, 0x13, ZC3XX_R135_GAMMA15},
-       {0xa0, 0x10, ZC3XX_R136_GAMMA16},
-       {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
-       {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
-       {0xa0, 0x09, ZC3XX_R139_GAMMA19},
-       {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
-       {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
-       {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
-       {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
-       {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
-       {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-
-       {0xa1, 0x01, 0x0180},
+       {}
+};
+static const struct usb_action mi360soc_AE50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0562},
+       {0xbb, 0x01, 0x09aa},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action mi360soc_AE50HZScale[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0509},
+       {0xbb, 0x01, 0x0934},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action mi360soc_AE60HZ[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x053d},
+       {0xbb, 0x01, 0x096e},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+static const struct usb_action mi360soc_AE60HZScale[] = {
        {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x05, 0x0009},
-       {0xaa, 0x09, 0x0134},
+       {0xbb, 0x00, 0x0509},
+       {0xbb, 0x01, 0x0983},
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
        {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
        {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
@@ -4696,20 +4433,60 @@ static const struct usb_action pb03303x_InitialScale[] = {
        {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
+       {}
+};
+static const struct usb_action mi360soc_AENoFliker[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0509},
+       {0xbb, 0x01, 0x0960},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
        {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action pb0330xx_Initial[] = {
-       {0xa1, 0x01, 0x0008},
-       {0xa1, 0x01, 0x0008},
+static const struct usb_action mi360soc_AENoFlikerScale[] = {
+       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0534},
+       {0xbb, 0x02, 0x0960},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+       {}
+};
+
+static const struct usb_action pb0330_Initial[] = {    /* 640x480 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -4721,11 +4498,12 @@ static const struct usb_action pb0330xx_Initial[] = {
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xdd, 0x00, 0x0200},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xaa, 0x01, 0x0006},
        {0xaa, 0x02, 0x0011},
-       {0xaa, 0x03, 0x01e7},
-       {0xaa, 0x04, 0x0287},
+       {0xaa, 0x03, 0x01e5},                   /*jfm: was 1e7*/
+       {0xaa, 0x04, 0x0285},                   /*jfm: was 0287*/
        {0xaa, 0x06, 0x0003},
        {0xaa, 0x07, 0x3002},
        {0xaa, 0x20, 0x1100},
@@ -4743,88 +4521,21 @@ static const struct usb_action pb0330xx_Initial[] = {
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x00, 0x01ad},
+       {0xa0, 0x09, 0x01ad},                   /*jfm: was 00 */
+       {0xa0, 0x15, 0x01ae},
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
        {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
-       {0xa1, 0x01, 0x0002},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
-       {0xa1, 0x01, 0x0091},
-       {0xa1, 0x01, 0x0095},
-       {0xa1, 0x01, 0x0096},
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x05, 0x0066},
-       {0xaa, 0x09, 0x02b2},
-       {0xaa, 0x10, 0x0002},
-
-       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0008},
-       {0xa1, 0x01, 0x0007},
-/*     {0xa0, 0x30, 0x0007}, */
-/*     {0xa0, 0x00, 0x0007}, */
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},       /*jfm: was 6c*/
        {}
 };
-
-static const struct usb_action pb0330xx_InitialScale[] = {
-       {0xa1, 0x01, 0x0008},
-       {0xa1, 0x01, 0x0008},
+static const struct usb_action pb0330_InitialScale[] = {       /* 320x240 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* 00 */
        {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
-       {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},   /* 10 */
+       {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
        {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
        {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
@@ -4836,6 +4547,7 @@ static const struct usb_action pb0330xx_InitialScale[] = {
        {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
        {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
        {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+       {0xdd, 0x00, 0x0200},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
        {0xaa, 0x01, 0x0006},
        {0xaa, 0x02, 0x0011},
@@ -4856,55 +4568,45 @@ static const struct usb_action pb0330xx_InitialScale[] = {
        {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
        {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
-       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x00, 0x01ad},
-       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
-       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
-       {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
-       {0xa1, 0x01, 0x0002},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
-       {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
-       {0xa1, 0x01, 0x0091},
-       {0xa1, 0x01, 0x0095},
-       {0xa1, 0x01, 0x0096},
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xf8, ZC3XX_R10B_RGB01},
-       {0xa0, 0xf8, ZC3XX_R10C_RGB02},
-       {0xa0, 0xf8, ZC3XX_R10D_RGB10},
-       {0xa0, 0x50, ZC3XX_R10E_RGB11},
-       {0xa0, 0xf8, ZC3XX_R10F_RGB12},
-       {0xa0, 0xf8, ZC3XX_R110_RGB20},
-       {0xa0, 0xf8, ZC3XX_R111_RGB21},
-       {0xa0, 0x50, ZC3XX_R112_RGB22},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+       {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+       {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+       {0xa0, 0x09, 0x01ad},
+       {0xa0, 0x15, 0x01ae},
+       {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+       {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+       {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+       {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x78, ZC3XX_R18D_YTARGET},       /*jfm: was 6c*/
+       {}
+};
+static const struct usb_action pb0330_50HZ[] = {
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x055c},
+       {0xbb, 0x01, 0x09aa},
+       {0xbb, 0x00, 0x1001},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+       {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
        {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0x05, 0x0066},
-       {0xaa, 0x09, 0x02b2},
-       {0xaa, 0x10, 0x0002},
+       {0xbb, 0x00, 0x0566},
+       {0xbb, 0x02, 0x09b2},
+       {0xbb, 0x00, 0x1002},
        {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
        {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
@@ -4912,124 +4614,102 @@ static const struct usb_action pb0330xx_InitialScale[] = {
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
        {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
        {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
        {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
        {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x09, 0x01ad},
-       {0xa0, 0x15, 0x01ae},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0008},
-       {0xa1, 0x01, 0x0007},
-/*     {0xa0, 0x30, 0x0007}, */
-/*     {0xa0, 0x00, 0x0007}, */
-       {}
-};
-static const struct usb_action pb0330_50HZ[] = {
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
-       {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
-       {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */
-       {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
-       {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
-       {}
-};
-static const struct usb_action pb0330_50HZScale[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
-       {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
-       {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */
-       {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */
-       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
        {}
 };
 static const struct usb_action pb0330_60HZ[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
-       {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
-       {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */
-       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
-       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0535},
+       {0xbb, 0x01, 0x0974},
+       {0xbb, 0x00, 0x1001},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
        {}
 };
 static const struct usb_action pb0330_60HZScale[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
-       {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
-       {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
-       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
-       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0535},
+       {0xbb, 0x02, 0x096c},
+       {0xbb, 0x00, 0x1002},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
        {}
 };
 static const struct usb_action pb0330_NoFliker[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
-       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
-       {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */
-       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
-       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0509},
+       {0xbb, 0x02, 0x0940},
+       {0xbb, 0x00, 0x1002},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
        {}
 };
 static const struct usb_action pb0330_NoFlikerScale[] = {
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},      /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},    /* 01,8f,20,cc */
-       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
-       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
-       {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},       /* 00,1d,09,cc */
-       {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},       /* 00,1e,40,cc */
-       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},       /* 00,1f,90,cc */
+       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+       {0xbb, 0x00, 0x0535},
+       {0xbb, 0x01, 0x0980},
+       {0xbb, 0x00, 0x1001},
+       {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+       {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+       {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+       {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+       {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
        {}
 };
 
@@ -5655,7 +5335,7 @@ static const struct usb_action tas5130CK_InitialScale[] = {
        {}
 };
 
-static const struct usb_action tas5130cxx_Initial[] = {
+static const struct usb_action tas5130cxx_InitialScale[] = {   /* 320x240 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5684,74 +5364,19 @@ static const struct usb_action tas5130cxx_Initial[] = {
        {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x95, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
        {0xa0, 0x00, 0x01ad},
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa1, 0x01, 0x0002},
-       {0xa1, 0x01, 0x0008},
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},  /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xec, ZC3XX_R10B_RGB01},
-       {0xa0, 0xec, ZC3XX_R10C_RGB02},
-       {0xa0, 0xec, ZC3XX_R10D_RGB10},
-       {0xa0, 0x68, ZC3XX_R10E_RGB11},
-       {0xa0, 0xec, ZC3XX_R10F_RGB12},
-       {0xa0, 0xec, ZC3XX_R110_RGB20},
-       {0xa0, 0xec, ZC3XX_R111_RGB21},
-       {0xa0, 0x68, ZC3XX_R112_RGB22},
-
-       {0xa1, 0x01, 0x018d},
-       {0xa0, 0x90, ZC3XX_R18D_YTARGET},       /* 90 */
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-
-       {0xaa, 0xa3, 0x0001},
-       {0xaa, 0xa4, 0x0077},
-       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
-       {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW},
-
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},     /* 00 */
-       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},      /* 03 */
-       {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW},      /* e8 */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},       /* 0 */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},        /* 0 */
-       {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW},        /* 7d */
-
-       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},      /* 08 */
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},       /* 24 */
-       {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
-       {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
-       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},    /* 50 */
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
-static const struct usb_action tas5130cxx_InitialScale[] = {
-/*??   {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */
+static const struct usb_action tas5130cxx_Initial[] = {        /* 640x480 */
        {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
        {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
-
        {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa1, 0x01, 0x0008},
-
        {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
        {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
        {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
@@ -5775,63 +5400,13 @@ static const struct usb_action tas5130cxx_InitialScale[] = {
        {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
        {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
        {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-       {0xa0, 0x68, ZC3XX_R18D_YTARGET},
-       {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+       {0xa0, 0x95, ZC3XX_R18D_YTARGET},
+       {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
        {0xa0, 0x00, 0x01ad},
        {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
        {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
        {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
        {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-       {0xa1, 0x01, 0x0002},
-       {0xa1, 0x01, 0x0008},
-
-       {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-       {0xa1, 0x01, 0x0008},   /* clock ? */
-       {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},   /* sharpness+ */
-       {0xa1, 0x01, 0x01c8},
-       {0xa1, 0x01, 0x01c9},
-       {0xa1, 0x01, 0x01ca},
-       {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},   /* sharpness- */
-
-       {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
-       {0xa0, 0xec, ZC3XX_R10B_RGB01},
-       {0xa0, 0xec, ZC3XX_R10C_RGB02},
-       {0xa0, 0xec, ZC3XX_R10D_RGB10},
-       {0xa0, 0x68, ZC3XX_R10E_RGB11},
-       {0xa0, 0xec, ZC3XX_R10F_RGB12},
-       {0xa0, 0xec, ZC3XX_R110_RGB20},
-       {0xa0, 0xec, ZC3XX_R111_RGB21},
-       {0xa0, 0x68, ZC3XX_R112_RGB22},
-
-       {0xa1, 0x01, 0x018d},
-       {0xa0, 0x90, ZC3XX_R18D_YTARGET},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-       {0xaa, 0xa3, 0x0001},
-       {0xaa, 0xa4, 0x0063},
-       {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
-       {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW},
-       {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
-       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
-       {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW},
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
-       {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
-       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
-       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
-       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
-       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
-       {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0},
-       {0xa0, 0xda, ZC3XX_R01E_HSYNC_1},
-       {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
-       {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
-       {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
-       {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
-       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
-       {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
-       {0xa1, 0x01, 0x0180},
-       {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
        {}
 };
 static const struct usb_action tas5130cxx_50HZ[] = {
@@ -5841,20 +5416,22 @@ static const struct usb_action tas5130cxx_50HZ[] = {
        {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
        {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
-       {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */
+       {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
        {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
        {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
        {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
        {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
        {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
 static const struct usb_action tas5130cxx_50HZScale[] = {
@@ -5864,20 +5441,22 @@ static const struct usb_action tas5130cxx_50HZScale[] = {
        {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
        {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
-       {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
+       {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
        {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
-       {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
        {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
        {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
        {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
        {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
 static const struct usb_action tas5130cxx_60HZ[] = {
@@ -5887,20 +5466,22 @@ static const struct usb_action tas5130cxx_60HZ[] = {
        {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
        {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+       {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
        {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
        {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
        {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
        {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
        {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
 static const struct usb_action tas5130cxx_60HZScale[] = {
@@ -5910,20 +5491,22 @@ static const struct usb_action tas5130cxx_60HZScale[] = {
        {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
        {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
-       {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
+       {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
        {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
        {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
        {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
-       {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
-       {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
-       {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+       {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+       {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
        {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
        {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
        {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
        {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+       {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
 static const struct usb_action tas5130cxx_NoFliker[] = {
@@ -5933,13 +5516,13 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
        {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
        {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
        {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
        {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
@@ -5947,6 +5530,8 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
        {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
        {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+       {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
 
@@ -5957,13 +5542,13 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
        {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
        {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
        {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
-       {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
-       {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
-       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
-       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
-       {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
-       {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
-       {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+       {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
+       {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+       {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+       {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+       {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+       {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+       {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
        {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
        {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
        {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
@@ -5971,6 +5556,8 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
        {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
        {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
        {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+       {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+       {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
        {}
 };
 
@@ -6303,8 +5890,10 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev,
 
        reg_w_i(gspca_dev->dev, reg, 0x0092);
        reg_w_i(gspca_dev->dev, 0x02, 0x0090);          /* <- read command */
-       msleep(25);
+       msleep(20);
        retbyte = reg_r_i(gspca_dev, 0x0091);           /* read status */
+       if (retbyte != 0x00)
+               err("i2c_r status error %02x", retbyte);
        retval = reg_r_i(gspca_dev, 0x0095);            /* read Lowbyte */
        retval |= reg_r_i(gspca_dev, 0x0096) << 8;      /* read Hightbyte */
        PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
@@ -6323,8 +5912,10 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev,
        reg_w_i(gspca_dev->dev, valL, 0x93);
        reg_w_i(gspca_dev->dev, valH, 0x94);
        reg_w_i(gspca_dev->dev, 0x01, 0x90);            /* <- write command */
-       msleep(15);
+       msleep(1);
        retbyte = reg_r_i(gspca_dev, 0x0091);           /* read status */
+       if (retbyte != 0x00)
+               err("i2c_w status error %02x", retbyte);
        PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
                        reg, valH, valL, retbyte);
        return retbyte;
@@ -6359,7 +5950,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
                        break;
                }
                action++;
-/*             msleep(1); */
+               msleep(1);
        }
 }
 
@@ -6380,11 +5971,13 @@ static void setmatrix(struct gspca_dev *gspca_dev)
                {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
        static const __u8 po2030_matrix[9] =
                {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+       static const u8 tas5130c_matrix[9] =
+               {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
        static const __u8 vf0250_matrix[9] =
                {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
        static const __u8 *matrix_tb[SENSOR_MAX] = {
                adcm2700_matrix, /* SENSOR_ADCM2700 0 */
-               NULL,           /* SENSOR_CS2102 1 */
+               ov7620_matrix,  /* SENSOR_CS2102 1 */
                NULL,           /* SENSOR_CS2102K 2 */
                gc0305_matrix,  /* SENSOR_GC0305 3 */
                NULL,           /* SENSOR_HDCS2020b 4 */
@@ -6392,15 +5985,16 @@ static void setmatrix(struct gspca_dev *gspca_dev)
                NULL,           /* SENSOR_HV7131C 6 */
                NULL,           /* SENSOR_ICM105A 7 */
                NULL,           /* SENSOR_MC501CB 8 */
-               ov7620_matrix,  /* SENSOR_OV7620 9 */
-               NULL,           /* SENSOR_OV7630C 10 */
-               NULL,           /* SENSOR_PAS106 11 */
-               pas202b_matrix, /* SENSOR_PAS202B 12 */
-               NULL,           /* SENSOR_PB0330 13 */
-               po2030_matrix,  /* SENSOR_PO2030 14 */
-               NULL,           /* SENSOR_TAS5130CK 15 */
-               NULL,           /* SENSOR_TAS5130CXX 16 */
-               vf0250_matrix,  /* SENSOR_TAS5130C_VF0250 17 */
+               gc0305_matrix,  /* SENSOR_MI0360SOC 9 */
+               ov7620_matrix,  /* SENSOR_OV7620 10 */
+               NULL,           /* SENSOR_OV7630C 11 */
+               NULL,           /* SENSOR_PAS106 12 */
+               pas202b_matrix, /* SENSOR_PAS202B 13 */
+               gc0305_matrix,  /* SENSOR_PB0330 14 */
+               po2030_matrix,  /* SENSOR_PO2030 15 */
+               NULL,           /* SENSOR_TAS5130CK 16 */
+               tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */
+               vf0250_matrix,  /* SENSOR_TAS5130C_VF0250 18 */
        };
 
        matrix = matrix_tb[sd->sensor];
@@ -6640,39 +6234,43 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
                {MC501CB_NoFliker, MC501CB_NoFlikerScale,
                 MC501CB_50HZ, MC501CB_50HZScale,
                 MC501CB_60HZ, MC501CB_60HZScale},
-/* SENSOR_OV7620 9 */
+/* SENSOR_MI0360SOC 9 */
+               {mi360soc_AENoFlikerScale, mi360soc_AENoFliker,
+                mi360soc_AE50HZScale, mi360soc_AE50HZ,
+                mi360soc_AE60HZScale, mi360soc_AE60HZ},
+/* SENSOR_OV7620 10 */
                {OV7620_NoFliker, OV7620_NoFliker,
                 OV7620_50HZ, OV7620_50HZ,
                 OV7620_60HZ, OV7620_60HZ},
-/* SENSOR_OV7630C 10 */
+/* SENSOR_OV7630C 11 */
                {NULL, NULL,
                 NULL, NULL,
                 NULL, NULL},
-/* SENSOR_PAS106 11 */
+/* SENSOR_PAS106 12 */
                {pas106b_NoFliker, pas106b_NoFliker,
                 pas106b_50HZ, pas106b_50HZ,
                 pas106b_60HZ, pas106b_60HZ},
-/* SENSOR_PAS202B 12 */
+/* SENSOR_PAS202B 13 */
                {pas202b_NoFlikerScale, pas202b_NoFliker,
                 pas202b_50HZScale, pas202b_50HZ,
                 pas202b_60HZScale, pas202b_60HZ},
-/* SENSOR_PB0330 13 */
-               {pb0330_NoFliker, pb0330_NoFlikerScale,
-                pb0330_50HZ, pb0330_50HZScale,
-                pb0330_60HZ, pb0330_60HZScale},
-/* SENSOR_PO2030 14 */
+/* SENSOR_PB0330 14 */
+               {pb0330_NoFlikerScale, pb0330_NoFliker,
+                pb0330_50HZScale, pb0330_50HZ,
+                pb0330_60HZScale, pb0330_60HZ},
+/* SENSOR_PO2030 15 */
                {PO2030_NoFliker, PO2030_NoFliker,
                 PO2030_50HZ, PO2030_50HZ,
                 PO2030_60HZ, PO2030_60HZ},
-/* SENSOR_TAS5130CK 15 */
-               {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
-                tas5130cxx_50HZ, tas5130cxx_50HZScale,
-                tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130CXX 16 */
-               {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
-                tas5130cxx_50HZ, tas5130cxx_50HZScale,
-                tas5130cxx_60HZ, tas5130cxx_60HZScale},
-/* SENSOR_TAS5130C_VF0250 17 */
+/* SENSOR_TAS5130CK 16 */
+               {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker,
+                tas5130cxx_50HZScale, tas5130cxx_50HZ,
+                tas5130cxx_60HZScale, tas5130cxx_60HZ},
+/* SENSOR_TAS5130CXX 17 */
+               {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker,
+                tas5130cxx_50HZScale, tas5130cxx_50HZ,
+                tas5130cxx_60HZScale, tas5130cxx_60HZ},
+/* SENSOR_TAS5130C_VF0250 18 */
                {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
                 tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
                 tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
@@ -6729,6 +6327,7 @@ static void send_unknown(struct usb_device *dev, int sensor)
        case SENSOR_ADCM2700:
        case SENSOR_GC0305:
        case SENSOR_OV7620:
+       case SENSOR_MI0360SOC:
        case SENSOR_PB0330:
        case SENSOR_PO2030:
                reg_w(dev, 0x0d, 0x003a);
@@ -6820,7 +6419,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
        start_2wr_probe(dev, 0x0e);             /* PAS202BCB */
        reg_w(dev, 0x08, 0x008d);
        i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
-       msleep(500);
+       msleep(50);
        retword = i2c_read(gspca_dev, 0x03);
        if (retword != 0)
                return 0x0e;                    /* PAS202BCB */
@@ -6863,7 +6462,8 @@ struct sensor_by_chipset_revision {
        __u8 internal_sensor_id;
 };
 static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
-       {0xc001, 0x13},         /* MI0360 */
+       {0xc000, 0x12},         /* TAS5130C */
+       {0xc001, 0x13},         /* MI0360SOC */
        {0xe001, 0x13},
        {0x8001, 0x13},
        {0x8000, 0x14},         /* CS2102K */
@@ -6963,7 +6563,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
        reg_w(dev, 0x01, 0x0001);
        reg_w(dev, 0xee, 0x008b);
        reg_w(dev, 0x03, 0x0012);
-/*     msleep(150); */
        reg_w(dev, 0x01, 0x0012);
        reg_w(dev, 0x05, 0x0012);
        retword = i2c_read(gspca_dev, 0x00) << 8;       /* ID 0 */
@@ -7025,7 +6624,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
        int vga = 1;            /* 1: vga, 0: sif */
        static const __u8 gamma[SENSOR_MAX] = {
                4,      /* SENSOR_ADCM2700 0 */
-               5,      /* SENSOR_CS2102 1 */
+               4,      /* SENSOR_CS2102 1 */
                5,      /* SENSOR_CS2102K 2 */
                4,      /* SENSOR_GC0305 3 */
                4,      /* SENSOR_HDCS2020b 4 */
@@ -7033,15 +6632,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
                4,      /* SENSOR_HV7131C 6 */
                4,      /* SENSOR_ICM105A 7 */
                4,      /* SENSOR_MC501CB 8 */
-               3,      /* SENSOR_OV7620 9 */
-               4,      /* SENSOR_OV7630C 10 */
-               4,      /* SENSOR_PAS106 11 */
-               4,      /* SENSOR_PAS202B 12 */
-               4,      /* SENSOR_PB0330 13 */
-               4,      /* SENSOR_PO2030 14 */
-               4,      /* SENSOR_TAS5130CK 15 */
-               4,      /* SENSOR_TAS5130CXX 16 */
-               3,      /* SENSOR_TAS5130C_VF0250 17 */
+               4,      /* SENSOR_MI0360SOC 9 */
+               3,      /* SENSOR_OV7620 10 */
+               4,      /* SENSOR_OV7630C 11 */
+               4,      /* SENSOR_PAS106 12 */
+               4,      /* SENSOR_PAS202B 13 */
+               4,      /* SENSOR_PB0330 14 */
+               4,      /* SENSOR_PO2030 15 */
+               4,      /* SENSOR_TAS5130CK 16 */
+               3,      /* SENSOR_TAS5130CXX 17 */
+               3,      /* SENSOR_TAS5130C_VF0250 18 */
        };
 
        /* define some sensors from the vendor/product */
@@ -7065,7 +6665,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
                                break;
                        default:
                                PDEBUG(D_PROBE,
-                                       "Sensor UNKNOW_0 force Tas5130");
+                                       "Sensor UNKNOWN_0 force Tas5130");
                                sd->sensor = SENSOR_TAS5130CXX;
                        }
                        break;
@@ -7103,7 +6703,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
                        break;
                case 0x10:
                case 0x12:
-                       PDEBUG(D_PROBE, "Find Sensor TAS5130");
+                       PDEBUG(D_PROBE, "Find Sensor TAS5130C");
                        sd->sensor = SENSOR_TAS5130CXX;
                        break;
                case 0x11:
@@ -7112,9 +6712,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
                        break;
                case 0x13:
                        PDEBUG(D_PROBE,
-                               "Find Sensor MI0360. Chip revision %x",
+                               "Find Sensor MI0360SOC. Chip revision %x",
                                sd->chip_revision);
-                       sd->sensor = SENSOR_PB0330;
+                       sd->sensor = SENSOR_MI0360SOC;
                        break;
                case 0x14:
                        PDEBUG(D_PROBE,
@@ -7228,17 +6828,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
                {hv7131cxx_InitialScale, hv7131cxx_Initial},    /* 6 */
                {icm105axx_InitialScale, icm105axx_Initial},    /* 7 */
                {MC501CB_InitialScale, MC501CB_Initial},        /* 8 */
-               {OV7620_mode0, OV7620_mode1},                   /* 9 */
-               {ov7630c_InitialScale, ov7630c_Initial},        /* 10 */
-               {pas106b_InitialScale, pas106b_Initial},        /* 11 */
-               {pas202b_Initial, pas202b_InitialScale},        /* 12 */
-               {pb0330xx_InitialScale, pb0330xx_Initial},      /* 13 */
-/* or          {pb03303x_InitialScale, pb03303x_Initial}, */
-               {PO2030_mode0, PO2030_mode1},                   /* 14 */
-               {tas5130CK_InitialScale, tas5130CK_Initial},    /* 15 */
-               {tas5130cxx_InitialScale, tas5130cxx_Initial},  /* 16 */
+               {mi0360soc_Initial, mi0360soc_InitialScale},    /* 9 */
+               {OV7620_mode0, OV7620_mode1},                   /* 10 */
+               {ov7630c_InitialScale, ov7630c_Initial},        /* 11 */
+               {pas106b_InitialScale, pas106b_Initial},        /* 12 */
+               {pas202b_Initial, pas202b_InitialScale},        /* 13 */
+               {pb0330_Initial, pb0330_InitialScale},          /* 14 */
+               {PO2030_mode0, PO2030_mode1},                   /* 15 */
+               {tas5130CK_InitialScale, tas5130CK_Initial},    /* 16 */
+               {tas5130cxx_Initial, tas5130cxx_InitialScale},  /* 17 */
                {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial},
-                                                               /* 17 */
+                                                               /* 18 */
        };
 
        /* create the JPEG header */
@@ -7258,19 +6858,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_PAS106:
                usb_exchange(gspca_dev, pas106b_Initial_com);
                break;
-       case SENSOR_PB0330:
-               if (mode) {
-                       if (sd->chip_revision == 0xc001
-                           || sd->chip_revision == 0xe001
-                           || sd->chip_revision == 0x8001)
-                               zc3_init = pb03303x_Initial;
-               } else {
-                       if (sd->chip_revision == 0xc001
-                           || sd->chip_revision == 0xe001
-                           || sd->chip_revision == 0x8001)
-                               zc3_init = pb03303x_InitialScale;
-               }
-               break;
        }
        usb_exchange(gspca_dev, zc3_init);
 
@@ -7310,10 +6897,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        /* set the gamma tables when not set */
        switch (sd->sensor) {
-       case SENSOR_CS2102:             /* gamma set in xxx_Initial */
-       case SENSOR_CS2102K:
+       case SENSOR_CS2102K:            /* gamma set in xxx_Initial */
        case SENSOR_HDCS2020b:
-       case SENSOR_PB0330:             /* pb with chip_revision - see above */
        case SENSOR_OV7630C:
        case SENSOR_TAS5130CK:
                break;
@@ -7365,7 +6950,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
        setautogain(gspca_dev);
        switch (sd->sensor) {
        case SENSOR_PO2030:
-               msleep(500);
+               msleep(50);
                reg_r(gspca_dev, 0x0008);
                reg_r(gspca_dev, 0x0007);
                /*fall thru*/
@@ -7389,17 +6974,16 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
 }
 
 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
-                       struct gspca_frame *frame,
-                       __u8 *data,
+                       u8 *data,
                        int len)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (data[0] == 0xff && data[1] == 0xd8) {       /* start of frame */
-               frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
-                                       data, 0);
+               gspca_frame_add(gspca_dev, LAST_PACKET,
+                                       NULL, 0);
                /* put the JPEG header in the new frame */
-               gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+               gspca_frame_add(gspca_dev, FIRST_PACKET,
                        sd->jpeg_hdr, JPEG_HDR_SZ);
 
                /* remove the webcam's header:
@@ -7411,7 +6995,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
                data += 18;
                len -= 18;
        }
-       gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+       gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
 static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
index 2eb9dc2..b5439ca 100644 (file)
@@ -139,7 +139,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
                urb = usb_alloc_urb(0, GFP_KERNEL);
                if (!urb) {
                        v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
-                       goto exit;
+                       goto exit_urb;
                }
                buf->urb = urb;
 
@@ -148,7 +148,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
                if (!mem) {
                        v4l2_err(&dev->v4l2_dev,
                                 "cannot allocate usb transfer buffer\n");
-                       goto exit;
+                       goto exit_urb_buffer;
                }
 
                usb_fill_bulk_urb(buf->urb, dev->udev,
@@ -161,6 +161,10 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
                list_add_tail(&buf->buff_list, &dev->free_buff_list);
        }
        return 0;
+exit_urb_buffer:
+       usb_free_urb(urb);
+exit_urb:
+       kfree(buf);
 exit:
        hdpvr_free_buffers(dev);
        return retval;
index 71c2114..60d992e 100644 (file)
@@ -251,7 +251,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
 
        DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
 
-       if (input < 0 || input >= HEXIUM_INPUTS)
+       if (input >= HEXIUM_INPUTS)
                return -EINVAL;
 
        hexium->cur_input = input;
index 39d65ca..938a1f8 100644 (file)
@@ -350,7 +350,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
        struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
        struct hexium *hexium = (struct hexium *) dev->ext_priv;
 
-       if (input < 0 || input >= HEXIUM_INPUTS)
+       if (input >= HEXIUM_INPUTS)
                return -EINVAL;
 
        hexium->cur_input = input;
index 247d311..64360d2 100644 (file)
@@ -275,7 +275,7 @@ static void ir_key_poll(struct IR_i2c *ir)
        if (0 == rc) {
                ir_input_nokey(ir->input, &ir->ir);
        } else {
-               ir_input_keydown(ir->input, &ir->ir, ir_key, ir_raw);
+               ir_input_keydown(ir->input, &ir->ir, ir_key);
        }
 }
 
@@ -299,7 +299,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        struct ir_scancode_table *ir_codes = NULL;
        const char *name = NULL;
-       int ir_type;
+       int ir_type = 0;
        struct IR_i2c *ir;
        struct input_dev *input_dev;
        struct i2c_adapter *adap = client->adapter;
@@ -353,10 +353,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
                ir_type     = IR_TYPE_RC5;
                ir_codes    = &ir_codes_fusionhdtv_mce_table;
                break;
-       case 0x7a:
        case 0x47:
        case 0x71:
-       case 0x2d:
                if (adap->id == I2C_HW_B_CX2388x ||
                    adap->id == I2C_HW_B_CX2341X) {
                        /* Handled by cx88-input */
@@ -381,10 +379,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
                ir_type     = IR_TYPE_OTHER;
                ir_codes    = &ir_codes_avermedia_cardbus_table;
                break;
-       default:
-               dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr);
-               err = -ENODEV;
-               goto err_out_free;
        }
 
        /* Let the caller override settings */
@@ -427,7 +421,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
        }
 
        /* Make sure we are all setup before going on */
-       if (!name || !ir->get_key || !ir_codes) {
+       if (!name || !ir->get_key || !ir_type || !ir_codes) {
                dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n",
                        addr);
                err = -ENODEV;
@@ -443,7 +437,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
                 dev_name(&client->dev));
 
        /* init + register input device */
-       ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
+       err = ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes);
+       if (err < 0)
+               goto err_out_free;
+
        input_dev->id.bustype = BUS_I2C;
        input_dev->name       = ir->name;
        input_dev->phys       = ir->phys;
@@ -462,6 +459,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
        return 0;
 
  err_out_free:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
        kfree(ir);
        return err;
@@ -475,6 +473,7 @@ static int ir_remove(struct i2c_client *client)
        cancel_delayed_work_sync(&ir->work);
 
        /* unregister device */
+       ir_input_free(ir->input);
        input_unregister_device(ir->input);
 
        /* free memory */
index 4873b6c..79d0fe4 100644 (file)
@@ -136,7 +136,8 @@ static const struct ivtv_card ivtv_card_pvr350 = {
        .hw_audio = IVTV_HW_MSP34XX,
        .hw_audio_ctrl = IVTV_HW_MSP34XX,
        .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 |
-                 IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+                 IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+                 IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT,
        .video_inputs = {
                { IVTV_CARD_INPUT_VID_TUNER,  0, IVTV_SAA71XX_COMPOSITE4 },
                { IVTV_CARD_INPUT_SVIDEO1,    1, IVTV_SAA71XX_SVIDEO0    },
@@ -199,7 +200,9 @@ static const struct ivtv_card ivtv_card_pvr150 = {
        .hw_audio_ctrl = IVTV_HW_CX25840,
        .hw_muxer = IVTV_HW_WM8775,
        .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 |
-                 IVTV_HW_TVEEPROM | IVTV_HW_TUNER,
+                 IVTV_HW_TVEEPROM | IVTV_HW_TUNER |
+                 IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT |
+                 IVTV_HW_Z8F0811_IR_HAUP,
        .video_inputs = {
                { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE7 },
                { IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO1    },
@@ -955,7 +958,8 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
        .hw_video = IVTV_HW_CX25840,
        .hw_audio = IVTV_HW_CX25840,
        .hw_audio_ctrl = IVTV_HW_CX25840,
-       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739,
+       .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 |
+                 IVTV_HW_I2C_IR_RX_AVER,
        .video_inputs = {
                { IVTV_CARD_INPUT_VID_TUNER,  0, CX25840_COMPOSITE2 },
                { IVTV_CARD_INPUT_SVIDEO1,    1, CX25840_SVIDEO3    },
@@ -965,6 +969,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
                { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5       },
                { IVTV_CARD_INPUT_LINE_IN1,   CX25840_AUDIO_SERIAL, 1 },
        },
+       .radio_input = { IVTV_CARD_INPUT_AUD_TUNER,  CX25840_AUDIO5 },
        /* enable line-in */
        .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
        .xceive_pin = 10,
@@ -1025,13 +1030,15 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = {
 /* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */
 
 static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = {
-       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 },
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */
+       { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */
        { 0, 0, 0 }
 };
 
 static const struct ivtv_card ivtv_card_aver_ultra1500mce = {
        .type = IVTV_CARD_AVER_ULTRA1500MCE,
        .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner",
+       .comment = "For non-NTSC tuners, use the pal= or secam= module options",
        .v4l2_capabilities = IVTV_CAP_ENCODER,
        .hw_video = IVTV_HW_CX25840,
        .hw_audio = IVTV_HW_CX25840,
@@ -1058,6 +1065,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = {
        .tuners = {
                /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */
                { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 },
+               { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 },
        },
        .pci_list = ivtv_pci_aver_ultra1500mce,
        .i2c = &ivtv_i2c_std,
index e99a0a2..6148827 100644 (file)
 #define IVTV_PCI_ID_GOTVIEW1           0xffac
 #define IVTV_PCI_ID_GOTVIEW2           0xffad
 
-/* hardware flags, no gaps allowed, IVTV_HW_GPIO must always be last */
-#define IVTV_HW_CX25840   (1 << 0)
-#define IVTV_HW_SAA7115   (1 << 1)
-#define IVTV_HW_SAA7127   (1 << 2)
-#define IVTV_HW_MSP34XX   (1 << 3)
-#define IVTV_HW_TUNER     (1 << 4)
-#define IVTV_HW_WM8775    (1 << 5)
-#define IVTV_HW_CS53L32A  (1 << 6)
-#define IVTV_HW_TVEEPROM  (1 << 7)
-#define IVTV_HW_SAA7114   (1 << 8)
-#define IVTV_HW_UPD64031A (1 << 9)
-#define IVTV_HW_UPD6408X  (1 << 10)
-#define IVTV_HW_SAA717X   (1 << 11)
-#define IVTV_HW_WM8739    (1 << 12)
-#define IVTV_HW_VP27SMPX  (1 << 13)
-#define IVTV_HW_M52790    (1 << 14)
-#define IVTV_HW_GPIO      (1 << 15)
+/* hardware flags, no gaps allowed */
+#define IVTV_HW_CX25840                        (1 << 0)
+#define IVTV_HW_SAA7115                        (1 << 1)
+#define IVTV_HW_SAA7127                        (1 << 2)
+#define IVTV_HW_MSP34XX                        (1 << 3)
+#define IVTV_HW_TUNER                  (1 << 4)
+#define IVTV_HW_WM8775                 (1 << 5)
+#define IVTV_HW_CS53L32A               (1 << 6)
+#define IVTV_HW_TVEEPROM               (1 << 7)
+#define IVTV_HW_SAA7114                        (1 << 8)
+#define IVTV_HW_UPD64031A              (1 << 9)
+#define IVTV_HW_UPD6408X               (1 << 10)
+#define IVTV_HW_SAA717X                        (1 << 11)
+#define IVTV_HW_WM8739                 (1 << 12)
+#define IVTV_HW_VP27SMPX               (1 << 13)
+#define IVTV_HW_M52790                 (1 << 14)
+#define IVTV_HW_GPIO                   (1 << 15)
+#define IVTV_HW_I2C_IR_RX_AVER         (1 << 16)
+#define IVTV_HW_I2C_IR_RX_HAUP_EXT     (1 << 17) /* External before internal */
+#define IVTV_HW_I2C_IR_RX_HAUP_INT     (1 << 18)
+#define IVTV_HW_Z8F0811_IR_TX_HAUP     (1 << 19)
+#define IVTV_HW_Z8F0811_IR_RX_HAUP     (1 << 20)
+
+#define IVTV_HW_Z8F0811_IR_HAUP        (IVTV_HW_Z8F0811_IR_RX_HAUP | \
+                                IVTV_HW_Z8F0811_IR_TX_HAUP)
 
 #define IVTV_HW_SAA711X   (IVTV_HW_SAA7115 | IVTV_HW_SAA7114)
 
+#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \
+                          IVTV_HW_I2C_IR_RX_HAUP_EXT | \
+                          IVTV_HW_I2C_IR_RX_HAUP_INT | \
+                          IVTV_HW_Z8F0811_IR_RX_HAUP)
+
+#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP)
+
+#define IVTV_HW_IR_ANY   (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY)
+
 /* video inputs */
 #define        IVTV_CARD_INPUT_VID_TUNER       1
 #define        IVTV_CARD_INPUT_SVIDEO1         2
index 463ec34..347c334 100644 (file)
@@ -91,10 +91,15 @@ static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
                                     -1, -1, -1, -1, -1, -1, -1, -1,
                                     -1, -1, -1, -1, -1, -1, -1, -1,
                                     -1, -1, -1, -1, -1, -1, -1, -1 };
+static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+                                              -1, -1, -1, -1, -1, -1, -1, -1,
+                                              -1, -1, -1, -1, -1, -1, -1, -1,
+                                              -1, -1, -1, -1, -1, -1, -1, -1 };
 
 static unsigned int cardtype_c = 1;
 static unsigned int tuner_c = 1;
 static unsigned int radio_c = 1;
+static unsigned int i2c_clock_period_c = 1;
 static char pal[] = "---";
 static char secam[] = "--";
 static char ntsc[] = "-";
@@ -151,6 +156,7 @@ module_param(dec_vbi_buffers, int, 0644);
 
 module_param(tunertype, int, 0644);
 module_param(newi2c, int, 0644);
+module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644);
 
 MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
                        "\t\t\tsee tuner.h for values");
@@ -245,6 +251,10 @@ MODULE_PARM_DESC(newi2c,
                 "Use new I2C implementation\n"
                 "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
                 "\t\t\tDefault is autodetect");
+MODULE_PARM_DESC(i2c_clock_period,
+                "Period of SCL for the I2C bus controlled by the CX23415/6\n"
+                "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n"
+                "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD));
 
 MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card");
 
@@ -600,6 +610,15 @@ static void ivtv_process_options(struct ivtv *itv)
        itv->options.cardtype = cardtype[itv->instance];
        itv->options.tuner = tuner[itv->instance];
        itv->options.radio = radio[itv->instance];
+
+       itv->options.i2c_clock_period = i2c_clock_period[itv->instance];
+       if (itv->options.i2c_clock_period == -1)
+               itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD;
+       else if (itv->options.i2c_clock_period < 10)
+               itv->options.i2c_clock_period = 10;
+       else if (itv->options.i2c_clock_period > 4500)
+               itv->options.i2c_clock_period = 4500;
+
        itv->options.newi2c = newi2c;
        if (tunertype < -1 || tunertype > 1) {
                IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
@@ -865,6 +884,10 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
                        itv->hw_flags |= device;
        }
 
+       /* probe for legacy IR controllers that aren't in card definitions */
+       if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0)
+               ivtv_i2c_new_ir_legacy(itv);
+
        if (itv->card->hw_all & IVTV_HW_CX25840)
                itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
        else if (itv->card->hw_all & IVTV_HW_SAA717X)
@@ -1361,7 +1384,7 @@ static struct pci_driver ivtv_pci_driver = {
       .remove =   ivtv_remove,
 };
 
-static int module_start(void)
+static int __init module_start(void)
 {
        printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
 
@@ -1385,7 +1408,7 @@ static int module_start(void)
        return 0;
 }
 
-static void module_cleanup(void)
+static void __exit module_cleanup(void)
 {
        pci_unregister_driver(&ivtv_pci_driver);
 }
index 440f732..e4816da 100644 (file)
@@ -64,6 +64,7 @@
 #include <media/v4l2-device.h>
 #include <media/tuner.h>
 #include <media/cx2341x.h>
+#include <media/ir-kbd-i2c.h>
 
 #include <linux/ivtv.h>
 
@@ -176,12 +177,16 @@ extern int ivtv_debug;
 
 #define IVTV_MAX_PGM_INDEX (400)
 
+/* Default I2C SCL period in microseconds */
+#define IVTV_DEFAULT_I2C_CLOCK_PERIOD  20
+
 struct ivtv_options {
        int kilobytes[IVTV_MAX_STREAMS];        /* size in kilobytes of each stream */
        int cardtype;                           /* force card type on load */
        int tuner;                              /* set tuner on load */
        int radio;                              /* enable/disable radio */
        int newi2c;                             /* new I2C algorithm */
+       int i2c_clock_period;                   /* period of SCL for I2C bus */
 };
 
 /* ivtv-specific mailbox template */
@@ -677,6 +682,7 @@ struct ivtv {
        int i2c_state;                  /* i2c bit state */
        struct mutex i2c_bus_lock;      /* lock i2c bus */
 
+       struct IR_i2c_init_data ir_i2c_init_data;
 
        /* Program Index information */
        u32 pgm_info_offset;            /* start of pgm info in encoder memory */
index b9c71e6..2ee03c2 100644 (file)
 #define IVTV_UPD64083_I2C_ADDR                 0x5c
 #define IVTV_VP27SMPX_I2C_ADDR         0x5b
 #define IVTV_M52790_I2C_ADDR           0x48
+#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR  0x40
+#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR   0x1a
+#define IVTV_HAUP_INT_IR_RX_I2C_ADDR   0x18
+#define IVTV_Z8F0811_IR_TX_I2C_ADDR    0x70
+#define IVTV_Z8F0811_IR_RX_I2C_ADDR    0x71
 
 /* This array should match the IVTV_HW_ defines */
 static const u8 hw_addrs[] = {
@@ -106,7 +111,12 @@ static const u8 hw_addrs[] = {
        IVTV_WM8739_I2C_ADDR,
        IVTV_VP27SMPX_I2C_ADDR,
        IVTV_M52790_I2C_ADDR,
-       0               /* IVTV_HW_GPIO dummy driver ID */
+       0,                              /* IVTV_HW_GPIO dummy driver ID */
+       IVTV_AVERMEDIA_IR_RX_I2C_ADDR,  /* IVTV_HW_I2C_IR_RX_AVER */
+       IVTV_HAUP_EXT_IR_RX_I2C_ADDR,   /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+       IVTV_HAUP_INT_IR_RX_I2C_ADDR,   /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+       IVTV_Z8F0811_IR_TX_I2C_ADDR,    /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+       IVTV_Z8F0811_IR_RX_I2C_ADDR,    /* IVTV_HW_Z8F0811_IR_RX_HAUP */
 };
 
 /* This array should match the IVTV_HW_ defines */
@@ -126,7 +136,12 @@ static const char *hw_modules[] = {
        "wm8739",
        "vp27smpx",
        "m52790",
-       NULL
+       NULL,
+       NULL,           /* IVTV_HW_I2C_IR_RX_AVER */
+       NULL,           /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+       NULL,           /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+       NULL,           /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+       NULL,           /* IVTV_HW_Z8F0811_IR_RX_HAUP */
 };
 
 /* This array should match the IVTV_HW_ defines */
@@ -147,8 +162,95 @@ static const char * const hw_devicenames[] = {
        "vp27smpx",
        "m52790",
        "gpio",
+       "ir_video",             /* IVTV_HW_I2C_IR_RX_AVER */
+       "ir_video",             /* IVTV_HW_I2C_IR_RX_HAUP_EXT */
+       "ir_video",             /* IVTV_HW_I2C_IR_RX_HAUP_INT */
+       "ir_tx_z8f0811_haup",   /* IVTV_HW_Z8F0811_IR_TX_HAUP */
+       "ir_rx_z8f0811_haup",   /* IVTV_HW_Z8F0811_IR_RX_HAUP */
 };
 
+static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr)
+{
+       struct i2c_board_info info;
+       struct i2c_adapter *adap = &itv->i2c_adap;
+       struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data;
+       unsigned short addr_list[2] = { addr, I2C_CLIENT_END };
+
+       /* Only allow one IR transmitter to be registered per board */
+       if (hw & IVTV_HW_IR_TX_ANY) {
+               if (itv->hw_flags & IVTV_HW_IR_TX_ANY)
+                       return -1;
+               memset(&info, 0, sizeof(struct i2c_board_info));
+               strlcpy(info.type, type, I2C_NAME_SIZE);
+               return i2c_new_probed_device(adap, &info, addr_list) == NULL
+                                                                    ? -1 : 0;
+       }
+
+       /* Only allow one IR receiver to be registered per board */
+       if (itv->hw_flags & IVTV_HW_IR_RX_ANY)
+               return -1;
+
+       /* Our default information for ir-kbd-i2c.c to use */
+       switch (hw) {
+       case IVTV_HW_I2C_IR_RX_AVER:
+               init_data->ir_codes = &ir_codes_avermedia_cardbus_table;
+               init_data->internal_get_key_func =
+                                       IR_KBD_GET_KEY_AVERMEDIA_CARDBUS;
+               init_data->type = IR_TYPE_OTHER;
+               init_data->name = "AVerMedia AVerTV card";
+               break;
+       case IVTV_HW_I2C_IR_RX_HAUP_EXT:
+       case IVTV_HW_I2C_IR_RX_HAUP_INT:
+               /* Default to old black remote */
+               init_data->ir_codes = &ir_codes_rc5_tv_table;
+               init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP;
+               init_data->type = IR_TYPE_RC5;
+               init_data->name = itv->card_name;
+               break;
+       case IVTV_HW_Z8F0811_IR_RX_HAUP:
+               /* Default to grey remote */
+               init_data->ir_codes = &ir_codes_hauppauge_new_table;
+               init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+               init_data->type = IR_TYPE_RC5;
+               init_data->name = itv->card_name;
+               break;
+       }
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       info.platform_data = init_data;
+       strlcpy(info.type, type, I2C_NAME_SIZE);
+
+       return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0;
+}
+
+/* Instantiate the IR receiver device using probing -- undesirable */
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv)
+{
+       struct i2c_board_info info;
+       /*
+        * The external IR receiver is at i2c address 0x34.
+        * The internal IR receiver is at i2c address 0x30.
+        *
+        * In theory, both can be fitted, and Hauppauge suggests an external
+        * overrides an internal.  That's why we probe 0x1a (~0x34) first. CB
+        *
+        * Some of these addresses we probe may collide with other i2c address
+        * allocations, so this function must be called after all other i2c
+        * devices we care about are registered.
+        */
+       const unsigned short addr_list[] = {
+               0x1a,   /* Hauppauge IR external - collides with WM8739 */
+               0x18,   /* Hauppauge IR internal */
+               0x71,   /* Hauppauge IR (PVR150) */
+               0x6b,   /* Adaptec IR */
+               I2C_CLIENT_END
+       };
+
+       memset(&info, 0, sizeof(struct i2c_board_info));
+       strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+       return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list);
+}
+
 int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
 {
        struct v4l2_subdev *sd;
@@ -178,8 +280,15 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
                        sd->grp_id = 1 << idx;
                return sd ? 0 : -1;
        }
+
+       if (hw & IVTV_HW_IR_ANY)
+               return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]);
+
+       /* Is it not an I2C device or one we do not wish to register? */
        if (!hw_addrs[idx])
                return -1;
+
+       /* It's an I2C device other than an analog tuner or IR chip */
        if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
                sd = v4l2_i2c_new_subdev(&itv->v4l2_dev,
                                adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx]));
@@ -564,20 +673,22 @@ static struct i2c_adapter ivtv_i2c_adap_template = {
        .owner = THIS_MODULE,
 };
 
+#define IVTV_ALGO_BIT_TIMEOUT  (2)     /* seconds */
+
 static const struct i2c_algo_bit_data ivtv_i2c_algo_template = {
        .setsda         = ivtv_setsda_old,
        .setscl         = ivtv_setscl_old,
        .getsda         = ivtv_getsda_old,
        .getscl         = ivtv_getscl_old,
-       .udelay         = 10,
-       .timeout        = 200,
+       .udelay         = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2,  /* microseconds */
+       .timeout        = IVTV_ALGO_BIT_TIMEOUT * HZ,         /* jiffies */
 };
 
 static struct i2c_client ivtv_i2c_client_template = {
        .name = "ivtv internal",
 };
 
-/* init + register i2c adapter + instantiate IR receiver */
+/* init + register i2c adapter */
 int init_ivtv_i2c(struct ivtv *itv)
 {
        int retval;
@@ -585,11 +696,10 @@ int init_ivtv_i2c(struct ivtv *itv)
        IVTV_DEBUG_I2C("i2c init\n");
 
        /* Sanity checks for the I2C hardware arrays. They must be the
-        * same size and GPIO must be the last entry.
+        * same size.
         */
        if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
-           ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) ||
-           IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) {
+           ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules)) {
                IVTV_ERR("Mismatched I2C hardware arrays\n");
                return -ENODEV;
        }
@@ -602,6 +712,7 @@ int init_ivtv_i2c(struct ivtv *itv)
                memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template,
                       sizeof(struct i2c_algo_bit_data));
        }
+       itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2;
        itv->i2c_algo.data = itv;
        itv->i2c_adap.algo_data = &itv->i2c_algo;
 
@@ -623,32 +734,6 @@ int init_ivtv_i2c(struct ivtv *itv)
        else
                retval = i2c_bit_add_bus(&itv->i2c_adap);
 
-       /* Instantiate the IR receiver device, if present */
-       if (retval == 0) {
-               struct i2c_board_info info;
-               /* The external IR receiver is at i2c address 0x34 (0x35 for
-                  reads).  Future Hauppauge cards will have an internal
-                  receiver at 0x30 (0x31 for reads).  In theory, both can be
-                  fitted, and Hauppauge suggest an external overrides an
-                  internal.
-
-                  That's why we probe 0x1a (~0x34) first. CB
-               */
-               const unsigned short addr_list[] = {
-                       0x1a,   /* Hauppauge IR external */
-                       0x18,   /* Hauppauge IR internal */
-                       0x71,   /* Hauppauge IR (PVR150) */
-                       0x64,   /* Pixelview IR */
-                       0x30,   /* KNC ONE IR */
-                       0x6b,   /* Adaptec IR */
-                       I2C_CLIENT_END
-               };
-
-               memset(&info, 0, sizeof(struct i2c_board_info));
-               strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
-               i2c_new_probed_device(&itv->i2c_adap, &info, addr_list);
-       }
-
        return retval;
 }
 
index 396928a..9332920 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef IVTV_I2C_H
 #define IVTV_I2C_H
 
+struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv);
 int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
 struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
 
index 3454070..c1fc6dc 100644 (file)
@@ -478,7 +478,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
 
        DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
 
-       if (input < 0 || input >= MXB_INPUTS)
+       if (input >= MXB_INPUTS)
                return -EINVAL;
 
        mxb->cur_input = input;
index eccb40a..2052293 100644 (file)
 
 /* COM5 */
 #define AFR_ON_OFF      0x80   /* Auto frame rate control ON/OFF selection */
-#define AFR_SPPED       0x40   /* Auto frame rate control speed slection */
+#define AFR_SPPED       0x40   /* Auto frame rate control speed selection */
                                /* Auto frame rate max rate control */
 #define AFR_NO_RATE     0x00   /*     No  reduction of frame rate */
 #define AFR_1p2         0x10   /*     Max reduction to 1/2 frame rate */
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
new file mode 100644 (file)
index 0000000..c81ae21
--- /dev/null
@@ -0,0 +1,801 @@
+/*
+ * OmniVision OV96xx Camera Driver
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/soc_camera.h>
+
+#include "ov9640.h"
+
+/* default register setup */
+static const struct ov9640_reg ov9640_regs_dflt[] = {
+       { OV9640_COM5,  OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP },
+       { OV9640_COM6,  OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS |
+                       OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN },
+       { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) },
+       { OV9640_ACOM,  OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD },
+       { OV9640_TSLB,  OV9640_TSLB_YUYV_UYVY },
+       { OV9640_COM16, OV9640_COM16_RB_AVG },
+
+       /* Gamma curve P */
+       { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 },
+       { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 },
+       { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },
+       { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 },
+
+       /* Gamma curve T */
+       { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 },
+       { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },
+       { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e },
+       { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 },
+};
+
+/* Configurations
+ * NOTE: for YUV, alter the following registers:
+ *             COM12 |= OV9640_COM12_YUV_AVG
+ *
+ *      for RGB, alter the following registers:
+ *             COM7  |= OV9640_COM7_RGB
+ *             COM13 |= OV9640_COM13_RGB_AVG
+ *             COM15 |= proper RGB color encoding mode
+ */
+static const struct ov9640_reg ov9640_regs_qqcif[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) },
+       { OV9640_COM1,  OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+       { OV9640_COM4,  OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+       { OV9640_COM7,  OV9640_COM7_QCIF },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qqvga[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+       { OV9640_COM1,  OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+       { OV9640_COM4,  OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+       { OV9640_COM7,  OV9640_COM7_QVGA },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qcif[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+       { OV9640_COM4,  OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+       { OV9640_COM7,  OV9640_COM7_QCIF },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qvga[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+       { OV9640_COM4,  OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+       { OV9640_COM7,  OV9640_COM7_QVGA },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_cif[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+       { OV9640_COM3,  OV9640_COM3_VP },
+       { OV9640_COM7,  OV9640_COM7_CIF },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_vga[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+       { OV9640_COM3,  OV9640_COM3_VP },
+       { OV9640_COM7,  OV9640_COM7_VGA },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_sxga[] = {
+       { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+       { OV9640_COM3,  OV9640_COM3_VP },
+       { OV9640_COM7,  0 },
+       { OV9640_COM12, OV9640_COM12_RSVD },
+       { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+       { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_yuv[] = {
+       { OV9640_MTX1,  0x58 },
+       { OV9640_MTX2,  0x48 },
+       { OV9640_MTX3,  0x10 },
+       { OV9640_MTX4,  0x28 },
+       { OV9640_MTX5,  0x48 },
+       { OV9640_MTX6,  0x70 },
+       { OV9640_MTX7,  0x40 },
+       { OV9640_MTX8,  0x40 },
+       { OV9640_MTX9,  0x40 },
+       { OV9640_MTXS,  0x0f },
+};
+
+static const struct ov9640_reg ov9640_regs_rgb[] = {
+       { OV9640_MTX1,  0x71 },
+       { OV9640_MTX2,  0x3e },
+       { OV9640_MTX3,  0x0c },
+       { OV9640_MTX4,  0x33 },
+       { OV9640_MTX5,  0x72 },
+       { OV9640_MTX6,  0x00 },
+       { OV9640_MTX7,  0x2b },
+       { OV9640_MTX8,  0x66 },
+       { OV9640_MTX9,  0xd2 },
+       { OV9640_MTXS,  0x65 },
+};
+
+/*
+ * TODO: this sensor also supports RGB555 and RGB565 formats, but support for
+ * them has not yet been sufficiently tested and so it is not included with
+ * this version of the driver. To test and debug these formats add two entries
+ * to the below array, see ov722x.c for an example.
+ */
+static const struct soc_camera_data_format ov9640_fmt_lists[] = {
+       {
+               .name           = "UYVY",
+               .fourcc         = V4L2_PIX_FMT_UYVY,
+               .depth          = 16,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+       },
+};
+
+static const struct v4l2_queryctrl ov9640_controls[] = {
+       {
+               .id             = V4L2_CID_VFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Flip Vertically",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+       },
+       {
+               .id             = V4L2_CID_HFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Flip Horizontally",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+       },
+};
+
+/* read a register */
+static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+       int ret;
+       u8 data = reg;
+       struct i2c_msg msg = {
+               .addr   = client->addr,
+               .flags  = 0,
+               .len    = 1,
+               .buf    = &data,
+       };
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret < 0)
+               goto err;
+
+       msg.flags = I2C_M_RD;
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret < 0)
+               goto err;
+
+       *val = data;
+       return 0;
+
+err:
+       dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+       return ret;
+}
+
+/* write a register */
+static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+       int ret;
+       u8 _val;
+       unsigned char data[2] = { reg, val };
+       struct i2c_msg msg = {
+               .addr   = client->addr,
+               .flags  = 0,
+               .len    = 2,
+               .buf    = data,
+       };
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+               return ret;
+       }
+
+       /* we have to read the register back ... no idea why, maybe HW bug */
+       ret = ov9640_reg_read(client, reg, &_val);
+       if (ret)
+               dev_err(&client->dev,
+                       "Failed reading back register 0x%02x!\n", reg);
+
+       return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset)
+{
+       u8 val;
+       int ret;
+
+       ret = ov9640_reg_read(client, reg, &val);
+       if (ret) {
+               dev_err(&client->dev,
+                       "[Read]-Modify-Write of register %02x failed!\n", reg);
+               return val;
+       }
+
+       val |= set;
+       val &= ~unset;
+
+       ret = ov9640_reg_write(client, reg, val);
+       if (ret)
+               dev_err(&client->dev,
+                       "Read-Modify-[Write] of register %02x failed!\n", reg);
+
+       return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov9640_reset(struct i2c_client *client)
+{
+       int ret;
+
+       ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET);
+       if (ret)
+               dev_err(&client->dev,
+                       "An error occured while entering soft reset!\n");
+
+       return ret;
+}
+
+/* Start/Stop streaming from the device */
+static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov9640_set_bus_param(struct soc_camera_device *icd,
+                               unsigned long flags)
+{
+       return 0;
+}
+
+/* Request bus settings on camera side */
+static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd)
+{
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+       /*
+        * REVISIT: the camera probably can do 10 bit transfers, but I don't
+        *          have those pins connected on my hardware.
+        */
+       unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+               SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+               SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+       return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+/* Get status of additional camera capabilities */
+static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
+                                       struct ov9640_priv, subdev);
+
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               ctrl->value = priv->flag_vflip;
+               break;
+       case V4L2_CID_HFLIP:
+               ctrl->value = priv->flag_hflip;
+               break;
+       }
+       return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
+                                       struct ov9640_priv, subdev);
+
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               priv->flag_vflip = ctrl->value;
+               if (ctrl->value)
+                       ret = ov9640_reg_rmw(client, OV9640_MVFP,
+                                                       OV9640_MVFP_V, 0);
+               else
+                       ret = ov9640_reg_rmw(client, OV9640_MVFP,
+                                                       0, OV9640_MVFP_V);
+               break;
+       case V4L2_CID_HFLIP:
+               priv->flag_hflip = ctrl->value;
+               if (ctrl->value)
+                       ret = ov9640_reg_rmw(client, OV9640_MVFP,
+                                                       OV9640_MVFP_H, 0);
+               else
+                       ret = ov9640_reg_rmw(client, OV9640_MVFP,
+                                                       0, OV9640_MVFP_H);
+               break;
+       }
+
+       return ret;
+}
+
+/* Get chip identification */
+static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
+                               struct v4l2_dbg_chip_ident *id)
+{
+       struct i2c_client *client = sd->priv;
+       struct ov9640_priv *priv = container_of(i2c_get_clientdata(client),
+                                       struct ov9640_priv, subdev);
+
+       id->ident       = priv->model;
+       id->revision    = priv->revision;
+
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9640_get_register(struct v4l2_subdev *sd,
+                               struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = sd->priv;
+       int ret;
+       u8 val;
+
+       if (reg->reg & ~0xff)
+               return -EINVAL;
+
+       reg->size = 1;
+
+       ret = ov9640_reg_read(client, reg->reg, &val);
+       if (ret)
+               return ret;
+
+       reg->val = (__u64)val;
+
+       return 0;
+}
+
+static int ov9640_set_register(struct v4l2_subdev *sd,
+                               struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = sd->priv;
+
+       if (reg->reg & ~0xff || reg->val & ~0xff)
+               return -EINVAL;
+
+       return ov9640_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* select nearest higher resolution for capture */
+static void ov9640_res_roundup(u32 *width, u32 *height)
+{
+       int i;
+       enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+       int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
+       int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
+
+       for (i = 0; i < ARRAY_SIZE(res_x); i++) {
+               if (res_x[i] >= *width && res_y[i] >= *height) {
+                       *width = res_x[i];
+                       *height = res_y[i];
+                       return;
+               }
+       }
+
+       *width = res_x[SXGA];
+       *height = res_y[SXGA];
+}
+
+/* Prepare necessary register changes depending on color encoding */
+static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt)
+{
+       switch (pixfmt) {
+       case V4L2_PIX_FMT_UYVY:
+               alt->com12      = OV9640_COM12_YUV_AVG;
+               alt->com13      = OV9640_COM13_Y_DELAY_EN |
+                                       OV9640_COM13_YUV_DLY(0x01);
+               break;
+       case V4L2_PIX_FMT_RGB555:
+               alt->com7       = OV9640_COM7_RGB;
+               alt->com13      = OV9640_COM13_RGB_AVG;
+               alt->com15      = OV9640_COM15_RGB_555;
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               alt->com7       = OV9640_COM7_RGB;
+               alt->com13      = OV9640_COM13_RGB_AVG;
+               alt->com15      = OV9640_COM15_RGB_565;
+               break;
+       };
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9640_write_regs(struct i2c_client *client,
+               u32 width, u32 pixfmt, struct ov9640_reg_alt *alts)
+{
+       const struct ov9640_reg *ov9640_regs, *matrix_regs;
+       int                     ov9640_regs_len, matrix_regs_len;
+       int                     i, ret;
+       u8                      val;
+
+       /* select register configuration for given resolution */
+       switch (width) {
+       case W_QQCIF:
+               ov9640_regs     = ov9640_regs_qqcif;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif);
+               break;
+       case W_QQVGA:
+               ov9640_regs     = ov9640_regs_qqvga;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga);
+               break;
+       case W_QCIF:
+               ov9640_regs     = ov9640_regs_qcif;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif);
+               break;
+       case W_QVGA:
+               ov9640_regs     = ov9640_regs_qvga;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga);
+               break;
+       case W_CIF:
+               ov9640_regs     = ov9640_regs_cif;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif);
+               break;
+       case W_VGA:
+               ov9640_regs     = ov9640_regs_vga;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga);
+               break;
+       case W_SXGA:
+               ov9640_regs     = ov9640_regs_sxga;
+               ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga);
+               break;
+       default:
+               dev_err(&client->dev, "Failed to select resolution!\n");
+               return -EINVAL;
+       }
+
+       /* select color matrix configuration for given color encoding */
+       if (pixfmt == V4L2_PIX_FMT_UYVY) {
+               matrix_regs     = ov9640_regs_yuv;
+               matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
+       } else {
+               matrix_regs     = ov9640_regs_rgb;
+               matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb);
+       }
+
+       /* write register settings into the module */
+       for (i = 0; i < ov9640_regs_len; i++) {
+               val = ov9640_regs[i].val;
+
+               switch (ov9640_regs[i].reg) {
+               case OV9640_COM7:
+                       val |= alts->com7;
+                       break;
+               case OV9640_COM12:
+                       val |= alts->com12;
+                       break;
+               case OV9640_COM13:
+                       val |= alts->com13;
+                       break;
+               case OV9640_COM15:
+                       val |= alts->com15;
+                       break;
+               }
+
+               ret = ov9640_reg_write(client, ov9640_regs[i].reg, val);
+               if (ret)
+                       return ret;
+       }
+
+       /* write color matrix configuration into the module */
+       for (i = 0; i < matrix_regs_len; i++) {
+               ret = ov9640_reg_write(client, matrix_regs[i].reg,
+                                               matrix_regs[i].val);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/* program default register values */
+static int ov9640_prog_dflt(struct i2c_client *client)
+{
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
+               ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
+                                               ov9640_regs_dflt[i].val);
+               if (ret)
+                       return ret;
+       }
+
+       /* wait for the changes to actually happen, 140ms are not enough yet */
+       mdelay(150);
+
+       return 0;
+}
+
+/* set the format we will capture in */
+static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+       struct i2c_client *client = sd->priv;
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       struct ov9640_reg_alt alts = {0};
+       int ret;
+
+       ov9640_res_roundup(&pix->width, &pix->height);
+       ov9640_alter_regs(pix->pixelformat, &alts);
+
+       ov9640_reset(client);
+
+       ret = ov9640_prog_dflt(client);
+       if (ret)
+               return ret;
+
+       return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts);
+}
+
+static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+
+       ov9640_res_roundup(&pix->width, &pix->height);
+       pix->field  = V4L2_FIELD_NONE;
+
+       return 0;
+}
+
+static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+       a->c.left       = 0;
+       a->c.top        = 0;
+       a->c.width      = W_SXGA;
+       a->c.height     = H_SXGA;
+       a->type         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       return 0;
+}
+
+static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+       a->bounds.left                  = 0;
+       a->bounds.top                   = 0;
+       a->bounds.width                 = W_SXGA;
+       a->bounds.height                = H_SXGA;
+       a->defrect                      = a->bounds;
+       a->type                         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       a->pixelaspect.numerator        = 1;
+       a->pixelaspect.denominator      = 1;
+
+       return 0;
+}
+
+
+
+static int ov9640_video_probe(struct soc_camera_device *icd,
+                               struct i2c_client *client)
+{
+       struct ov9640_priv *priv = i2c_get_clientdata(client);
+       u8              pid, ver, midh, midl;
+       const char      *devname;
+       int             ret = 0;
+
+       /*
+        * We must have a parent by now. And it cannot be a wrong one.
+        * So this entire test is completely redundant.
+        */
+       if (!icd->dev.parent ||
+           to_soc_camera_host(icd->dev.parent)->nr != icd->iface) {
+               dev_err(&client->dev, "Parent missing or invalid!\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       icd->formats            = ov9640_fmt_lists;
+       icd->num_formats        = ARRAY_SIZE(ov9640_fmt_lists);
+
+       /*
+        * check and show product ID and manufacturer ID
+        */
+
+       ret = ov9640_reg_read(client, OV9640_PID, &pid);
+       if (ret)
+               goto err;
+
+       ret = ov9640_reg_read(client, OV9640_VER, &ver);
+       if (ret)
+               goto err;
+
+       ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+       if (ret)
+               goto err;
+
+       ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
+       if (ret)
+               goto err;
+
+       switch (VERSION(pid, ver)) {
+       case OV9640_V2:
+               devname         = "ov9640";
+               priv->model     = V4L2_IDENT_OV9640;
+               priv->revision  = 2;
+       case OV9640_V3:
+               devname         = "ov9640";
+               priv->model     = V4L2_IDENT_OV9640;
+               priv->revision  = 3;
+               break;
+       default:
+               dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+                devname, pid, ver, midh, midl);
+
+err:
+       return ret;
+}
+
+static struct soc_camera_ops ov9640_ops = {
+       .set_bus_param          = ov9640_set_bus_param,
+       .query_bus_param        = ov9640_query_bus_param,
+       .controls               = ov9640_controls,
+       .num_controls           = ARRAY_SIZE(ov9640_controls),
+};
+
+static struct v4l2_subdev_core_ops ov9640_core_ops = {
+       .g_ctrl                 = ov9640_g_ctrl,
+       .s_ctrl                 = ov9640_s_ctrl,
+       .g_chip_ident           = ov9640_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .g_register             = ov9640_get_register,
+       .s_register             = ov9640_set_register,
+#endif
+
+};
+
+static struct v4l2_subdev_video_ops ov9640_video_ops = {
+       .s_stream               = ov9640_s_stream,
+       .s_fmt                  = ov9640_s_fmt,
+       .try_fmt                = ov9640_try_fmt,
+       .cropcap                = ov9640_cropcap,
+       .g_crop                 = ov9640_g_crop,
+
+};
+
+static struct v4l2_subdev_ops ov9640_subdev_ops = {
+       .core   = &ov9640_core_ops,
+       .video  = &ov9640_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9640_probe(struct i2c_client *client,
+                       const struct i2c_device_id *did)
+{
+       struct ov9640_priv *priv;
+       struct soc_camera_device *icd   = client->dev.platform_data;
+       struct soc_camera_link *icl;
+       int ret;
+
+       if (!icd) {
+               dev_err(&client->dev, "Missing soc-camera data!\n");
+               return -EINVAL;
+       }
+
+       icl = to_soc_camera_link(icd);
+       if (!icl) {
+               dev_err(&client->dev, "Missing platform_data for driver\n");
+               return -EINVAL;
+       }
+
+       priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(&client->dev,
+                       "Failed to allocate memory for private data!\n");
+               return -ENOMEM;
+       }
+
+       v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
+
+       icd->ops        = &ov9640_ops;
+
+       ret = ov9640_video_probe(icd, client);
+
+       if (ret) {
+               icd->ops = NULL;
+               i2c_set_clientdata(client, NULL);
+               kfree(priv);
+       }
+
+       return ret;
+}
+
+static int ov9640_remove(struct i2c_client *client)
+{
+       struct ov9640_priv *priv = i2c_get_clientdata(client);
+
+       i2c_set_clientdata(client, NULL);
+       kfree(priv);
+       return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+       { "ov9640", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640_i2c_driver = {
+       .driver = {
+               .name = "ov9640",
+       },
+       .probe    = ov9640_probe,
+       .remove   = ov9640_remove,
+       .id_table = ov9640_id,
+};
+
+static int __init ov9640_module_init(void)
+{
+       return i2c_add_driver(&ov9640_i2c_driver);
+}
+
+static void __exit ov9640_module_exit(void)
+{
+       i2c_del_driver(&ov9640_i2c_driver);
+}
+
+module_init(ov9640_module_init);
+module_exit(ov9640_module_exit);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h
new file mode 100644 (file)
index 0000000..f8a51b7
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * OmniVision OV96xx Camera Header File
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@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.
+ */
+
+#ifndef        __DRIVERS_MEDIA_VIDEO_OV9640_H__
+#define        __DRIVERS_MEDIA_VIDEO_OV9640_H__
+
+/* Register definitions */
+#define        OV9640_GAIN     0x00
+#define        OV9640_BLUE     0x01
+#define        OV9640_RED      0x02
+#define        OV9640_VFER     0x03
+#define        OV9640_COM1     0x04
+#define        OV9640_BAVE     0x05
+#define        OV9640_GEAVE    0x06
+#define        OV9640_RSID     0x07
+#define        OV9640_RAVE     0x08
+#define        OV9640_COM2     0x09
+#define        OV9640_PID      0x0a
+#define        OV9640_VER      0x0b
+#define        OV9640_COM3     0x0c
+#define        OV9640_COM4     0x0d
+#define        OV9640_COM5     0x0e
+#define        OV9640_COM6     0x0f
+#define        OV9640_AECH     0x10
+#define        OV9640_CLKRC    0x11
+#define        OV9640_COM7     0x12
+#define        OV9640_COM8     0x13
+#define        OV9640_COM9     0x14
+#define        OV9640_COM10    0x15
+/* 0x16 - RESERVED */
+#define        OV9640_HSTART   0x17
+#define        OV9640_HSTOP    0x18
+#define        OV9640_VSTART   0x19
+#define        OV9640_VSTOP    0x1a
+#define        OV9640_PSHFT    0x1b
+#define        OV9640_MIDH     0x1c
+#define        OV9640_MIDL     0x1d
+#define        OV9640_MVFP     0x1e
+#define        OV9640_LAEC     0x1f
+#define        OV9640_BOS      0x20
+#define        OV9640_GBOS     0x21
+#define        OV9640_GROS     0x22
+#define        OV9640_ROS      0x23
+#define        OV9640_AEW      0x24
+#define        OV9640_AEB      0x25
+#define        OV9640_VPT      0x26
+#define        OV9640_BBIAS    0x27
+#define        OV9640_GBBIAS   0x28
+/* 0x29 - RESERVED */
+#define        OV9640_EXHCH    0x2a
+#define        OV9640_EXHCL    0x2b
+#define        OV9640_RBIAS    0x2c
+#define        OV9640_ADVFL    0x2d
+#define        OV9640_ADVFH    0x2e
+#define        OV9640_YAVE     0x2f
+#define        OV9640_HSYST    0x30
+#define        OV9640_HSYEN    0x31
+#define        OV9640_HREF     0x32
+#define        OV9640_CHLF     0x33
+#define        OV9640_ARBLM    0x34
+/* 0x35..0x36 - RESERVED */
+#define        OV9640_ADC      0x37
+#define        OV9640_ACOM     0x38
+#define        OV9640_OFON     0x39
+#define        OV9640_TSLB     0x3a
+#define        OV9640_COM11    0x3b
+#define        OV9640_COM12    0x3c
+#define        OV9640_COM13    0x3d
+#define        OV9640_COM14    0x3e
+#define        OV9640_EDGE     0x3f
+#define        OV9640_COM15    0x40
+#define        OV9640_COM16    0x41
+#define        OV9640_COM17    0x42
+/* 0x43..0x4e - RESERVED */
+#define        OV9640_MTX1     0x4f
+#define        OV9640_MTX2     0x50
+#define        OV9640_MTX3     0x51
+#define        OV9640_MTX4     0x52
+#define        OV9640_MTX5     0x53
+#define        OV9640_MTX6     0x54
+#define        OV9640_MTX7     0x55
+#define        OV9640_MTX8     0x56
+#define        OV9640_MTX9     0x57
+#define        OV9640_MTXS     0x58
+/* 0x59..0x61 - RESERVED */
+#define        OV9640_LCC1     0x62
+#define        OV9640_LCC2     0x63
+#define        OV9640_LCC3     0x64
+#define        OV9640_LCC4     0x65
+#define        OV9640_LCC5     0x66
+#define        OV9640_MANU     0x67
+#define        OV9640_MANV     0x68
+#define        OV9640_HV       0x69
+#define        OV9640_MBD      0x6a
+#define        OV9640_DBLV     0x6b
+#define        OV9640_GSP      0x6c    /* ... till 0x7b */
+#define        OV9640_GST      0x7c    /* ... till 0x8a */
+
+#define        OV9640_CLKRC_DPLL_EN    0x80
+#define        OV9640_CLKRC_DIRECT     0x40
+#define        OV9640_CLKRC_DIV(x)     ((x) & 0x3f)
+
+#define        OV9640_PSHFT_VAL(x)     ((x) & 0xff)
+
+#define        OV9640_ACOM_2X_ANALOG   0x80
+#define        OV9640_ACOM_RSVD        0x12
+
+#define        OV9640_MVFP_V           0x10
+#define        OV9640_MVFP_H           0x20
+
+#define        OV9640_COM1_HREF_NOSKIP 0x00
+#define        OV9640_COM1_HREF_2SKIP  0x04
+#define        OV9640_COM1_HREF_3SKIP  0x08
+#define        OV9640_COM1_QQFMT       0x20
+
+#define        OV9640_COM2_SSM         0x10
+
+#define        OV9640_COM3_VP          0x04
+
+#define        OV9640_COM4_QQ_VP       0x80
+#define        OV9640_COM4_RSVD        0x40
+
+#define        OV9640_COM5_SYSCLK      0x80
+#define        OV9640_COM5_LONGEXP     0x01
+
+#define        OV9640_COM6_OPT_BLC     0x40
+#define        OV9640_COM6_ADBLC_BIAS  0x08
+#define        OV9640_COM6_FMT_RST     0x82
+#define        OV9640_COM6_ADBLC_OPTEN 0x01
+
+#define        OV9640_COM7_RAW_RGB     0x01
+#define        OV9640_COM7_RGB         0x04
+#define        OV9640_COM7_QCIF        0x08
+#define        OV9640_COM7_QVGA        0x10
+#define        OV9640_COM7_CIF         0x20
+#define        OV9640_COM7_VGA         0x40
+#define        OV9640_COM7_SCCB_RESET  0x80
+
+#define        OV9640_TSLB_YVYU_YUYV   0x04
+#define        OV9640_TSLB_YUYV_UYVY   0x08
+
+#define        OV9640_COM12_YUV_AVG    0x04
+#define        OV9640_COM12_RSVD       0x40
+
+#define        OV9640_COM13_GAMMA_NONE 0x00
+#define        OV9640_COM13_GAMMA_Y    0x40
+#define        OV9640_COM13_GAMMA_RAW  0x80
+#define        OV9640_COM13_RGB_AVG    0x20
+#define        OV9640_COM13_MATRIX_EN  0x10
+#define        OV9640_COM13_Y_DELAY_EN 0x08
+#define        OV9640_COM13_YUV_DLY(x) ((x) & 0x07)
+
+#define        OV9640_COM15_OR_00FF    0x00
+#define        OV9640_COM15_OR_01FE    0x40
+#define        OV9640_COM15_OR_10F0    0xc0
+#define        OV9640_COM15_RGB_NORM   0x00
+#define        OV9640_COM15_RGB_565    0x10
+#define        OV9640_COM15_RGB_555    0x30
+
+#define        OV9640_COM16_RB_AVG     0x01
+
+/* IDs */
+#define        OV9640_V2               0x9648
+#define        OV9640_V3               0x9649
+#define        VERSION(pid, ver)       (((pid) << 8) | ((ver) & 0xFF))
+
+/* supported resolutions */
+enum {
+       W_QQCIF = 88,
+       W_QQVGA = 160,
+       W_QCIF  = 176,
+       W_QVGA  = 320,
+       W_CIF   = 352,
+       W_VGA   = 640,
+       W_SXGA  = 1280
+};
+#define        H_SXGA  960
+
+/* Misc. structures */
+struct ov9640_reg_alt {
+       u8      com7;
+       u8      com12;
+       u8      com13;
+       u8      com15;
+};
+
+struct ov9640_reg {
+       u8      reg;
+       u8      val;
+};
+
+struct ov9640_priv {
+       struct v4l2_subdev              subdev;
+
+       int                             model;
+       int                             revision;
+
+       bool                            flag_vflip;
+       bool                            flag_hflip;
+};
+
+#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */
index a1ad38f..73ec970 100644 (file)
  *     unless the userspace driver also doesn't work for you...
  *
  *      Changes:
- *      08/07/2003        Daniele Bellucci <bellucda@tiscali.it>
- *                        - pms_capture: report back -EFAULT
+ *     25-11-2009      Hans Verkuil <hverkuil@xs4all.nl>
+ *                     - converted to version 2 of the V4L API.
+ *      08/07/2003      Daniele Bellucci <bellucda@tiscali.it>
+ *                      - pms_capture: report back -EFAULT
  */
 
 #include <linux/module.h>
 #include <linux/mm.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
 #include <asm/io.h>
-#include <linux/videodev.h>
+
+#include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
+#include <media/v4l2-device.h>
 
-#include <asm/uaccess.h>
+MODULE_LICENSE("GPL");
 
 
 #define MOTOROLA       1
-#define PHILIPS2       2
+#define PHILIPS2       2               /* SAA7191 */
 #define PHILIPS1       3
 #define MVVMEMORYWIDTH 0x40            /* 512 bytes */
 
-struct pms_device
-{
-       struct video_device v;
-       struct video_picture picture;
-       int height;
-       int width;
-       unsigned long in_use;
-       struct mutex lock;
-};
-
-struct i2c_info
-{
+struct i2c_info {
        u8 slave;
        u8 sub;
        u8 data;
        u8 hits;
 };
 
-static int i2c_count;
-static struct i2c_info i2cinfo[64];
+struct pms {
+       struct v4l2_device v4l2_dev;
+       struct video_device vdev;
+       int height;
+       int width;
+       int depth;
+       int input;
+       s32 brightness, saturation, hue, contrast;
+       unsigned long in_use;
+       struct mutex lock;
+       int i2c_count;
+       struct i2c_info i2cinfo[64];
+
+       int decoder;
+       int standard;   /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+       v4l2_std_id std;
+       int io;
+       int data;
+       void __iomem *mem;
+};
 
-static int decoder             = PHILIPS2;
-static int standard;   /* 0 - auto 1 - ntsc 2 - pal 3 - secam */
+static struct pms pms_card;
 
 /*
  *     I/O ports and Shared Memory
  */
 
-static int io_port             =       0x250;
-static int data_port           =       0x251;
-static int mem_base            =       0xC8000;
-static void __iomem *mem;
-static int video_nr             =       -1;
+static int io_port = 0x250;
+module_param(io_port, int, 0);
 
+static int mem_base = 0xc8000;
+module_param(mem_base, int, 0);
 
+static int video_nr = -1;
+module_param(video_nr, int, 0);
 
-static inline void mvv_write(u8 index, u8 value)
+
+static inline void mvv_write(struct pms *dev, u8 index, u8 value)
 {
-       outw(index|(value<<8), io_port);
+       outw(index | (value << 8), dev->io);
 }
 
-static inline u8 mvv_read(u8 index)
+static inline u8 mvv_read(struct pms *dev, u8 index)
 {
-       outb(index, io_port);
-       return inb(data_port);
+       outb(index, dev->io);
+       return inb(dev->data);
 }
 
-static int pms_i2c_stat(u8 slave)
+static int pms_i2c_stat(struct pms *dev, u8 slave)
 {
-       int counter;
+       int counter = 0;
        int i;
 
-       outb(0x28, io_port);
+       outb(0x28, dev->io);
 
-       counter=0;
-       while((inb(data_port)&0x01)==0)
-               if(counter++==256)
+       while ((inb(dev->data) & 0x01) == 0)
+               if (counter++ == 256)
                        break;
 
-       while((inb(data_port)&0x01)!=0)
-               if(counter++==256)
+       while ((inb(dev->data) & 0x01) != 0)
+               if (counter++ == 256)
                        break;
 
-       outb(slave, io_port);
+       outb(slave, dev->io);
 
-       counter=0;
-       while((inb(data_port)&0x01)==0)
-               if(counter++==256)
+       counter = 0;
+       while ((inb(dev->data) & 0x01) == 0)
+               if (counter++ == 256)
                        break;
 
-       while((inb(data_port)&0x01)!=0)
-               if(counter++==256)
+       while ((inb(dev->data) & 0x01) != 0)
+               if (counter++ == 256)
                        break;
 
-       for(i=0;i<12;i++)
-       {
-               char st=inb(data_port);
-               if((st&2)!=0)
+       for (i = 0; i < 12; i++) {
+               char st = inb(dev->data);
+
+               if ((st & 2) != 0)
                        return -1;
-               if((st&1)==0)
+               if ((st & 1) == 0)
                        break;
        }
-       outb(0x29, io_port);
-       return inb(data_port);
+       outb(0x29, dev->io);
+       return inb(dev->data);
 }
 
-static int pms_i2c_write(u16 slave, u16 sub, u16 data)
+static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data)
 {
-       int skip=0;
+       int skip = 0;
        int count;
        int i;
 
-       for(i=0;i<i2c_count;i++)
-       {
-               if((i2cinfo[i].slave==slave) &&
-                  (i2cinfo[i].sub == sub))
-               {
-                       if(i2cinfo[i].data==data)
-                               skip=1;
-                       i2cinfo[i].data=data;
-                       i=i2c_count+1;
+       for (i = 0; i < dev->i2c_count; i++) {
+               if ((dev->i2cinfo[i].slave == slave) &&
+                   (dev->i2cinfo[i].sub == sub)) {
+                       if (dev->i2cinfo[i].data == data)
+                               skip = 1;
+                       dev->i2cinfo[i].data = data;
+                       i = dev->i2c_count + 1;
                }
        }
 
-       if(i==i2c_count && i2c_count<64)
-       {
-               i2cinfo[i2c_count].slave=slave;
-               i2cinfo[i2c_count].sub=sub;
-               i2cinfo[i2c_count].data=data;
-               i2c_count++;
+       if (i == dev->i2c_count && dev->i2c_count < 64) {
+               dev->i2cinfo[dev->i2c_count].slave = slave;
+               dev->i2cinfo[dev->i2c_count].sub = sub;
+               dev->i2cinfo[dev->i2c_count].data = data;
+               dev->i2c_count++;
        }
 
-       if(skip)
+       if (skip)
                return 0;
 
-       mvv_write(0x29, sub);
-       mvv_write(0x2A, data);
-       mvv_write(0x28, slave);
+       mvv_write(dev, 0x29, sub);
+       mvv_write(dev, 0x2A, data);
+       mvv_write(dev, 0x28, slave);
 
-       outb(0x28, io_port);
+       outb(0x28, dev->io);
 
-       count=0;
-       while((inb(data_port)&1)==0)
-               if(count>255)
+       count = 0;
+       while ((inb(dev->data) & 1) == 0)
+               if (count > 255)
                        break;
-       while((inb(data_port)&1)!=0)
-               if(count>255)
+       while ((inb(dev->data) & 1) != 0)
+               if (count > 255)
                        break;
 
-       count=inb(data_port);
+       count = inb(dev->data);
 
-       if(count&2)
+       if (count & 2)
                return -1;
        return count;
 }
 
-static int pms_i2c_read(int slave, int sub)
+static int pms_i2c_read(struct pms *dev, int slave, int sub)
 {
-       int i=0;
-       for(i=0;i<i2c_count;i++)
-       {
-               if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub)
-                       return i2cinfo[i].data;
+       int i;
+
+       for (i = 0; i < dev->i2c_count; i++) {
+               if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub)
+                       return dev->i2cinfo[i].data;
        }
        return 0;
 }
 
 
-static void pms_i2c_andor(int slave, int sub, int and, int or)
+static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or)
 {
        u8 tmp;
 
-       tmp=pms_i2c_read(slave, sub);
-       tmp = (tmp&and)|or;
-       pms_i2c_write(slave, sub, tmp);
+       tmp = pms_i2c_read(dev, slave, sub);
+       tmp = (tmp & and) | or;
+       pms_i2c_write(dev, slave, sub, tmp);
 }
 
 /*
@@ -203,100 +213,108 @@ static void pms_i2c_andor(int slave, int sub, int and, int or)
  */
 
 
-static void pms_videosource(short source)
+static void pms_videosource(struct pms *dev, short source)
 {
-       mvv_write(0x2E, source?0x31:0x30);
+       switch (dev->decoder) {
+       case MOTOROLA:
+               break;
+       case PHILIPS2:
+               pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0);
+               break;
+       case PHILIPS1:
+               break;
+       }
+       mvv_write(dev, 0x2E, 0x31);
+       /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30);
+          But could not make this work correctly. Only Composite input
+          worked for me. */
 }
 
-static void pms_hue(short hue)
+static void pms_hue(struct pms *dev, short hue)
 {
-       switch(decoder)
-       {
-               case MOTOROLA:
-                       pms_i2c_write(0x8A, 0x00, hue);
-                       break;
-               case PHILIPS2:
-                       pms_i2c_write(0x8A, 0x07, hue);
-                       break;
-               case PHILIPS1:
-                       pms_i2c_write(0x42, 0x07, hue);
-                       break;
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, hue);
+               break;
+       case PHILIPS2:
+               pms_i2c_write(dev, 0x8a, 0x07, hue);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x07, hue);
+               break;
        }
 }
 
-static void pms_colour(short colour)
+static void pms_saturation(struct pms *dev, short sat)
 {
-       switch(decoder)
-       {
-               case MOTOROLA:
-                       pms_i2c_write(0x8A, 0x00, colour);
-                       break;
-               case PHILIPS1:
-                       pms_i2c_write(0x42, 0x12, colour);
-                       break;
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, sat);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x12, sat);
+               break;
        }
 }
 
 
-static void pms_contrast(short contrast)
+static void pms_contrast(struct pms *dev, short contrast)
 {
-       switch(decoder)
-       {
-               case MOTOROLA:
-                       pms_i2c_write(0x8A, 0x00, contrast);
-                       break;
-               case PHILIPS1:
-                       pms_i2c_write(0x42, 0x13, contrast);
-                       break;
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, contrast);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x13, contrast);
+               break;
        }
 }
 
-static void pms_brightness(short brightness)
+static void pms_brightness(struct pms *dev, short brightness)
 {
-       switch(decoder)
-       {
-               case MOTOROLA:
-                       pms_i2c_write(0x8A, 0x00, brightness);
-                       pms_i2c_write(0x8A, 0x00, brightness);
-                       pms_i2c_write(0x8A, 0x00, brightness);
-                       break;
-               case PHILIPS1:
-                       pms_i2c_write(0x42, 0x19, brightness);
-                       break;
+       switch (dev->decoder) {
+       case MOTOROLA:
+               pms_i2c_write(dev, 0x8a, 0x00, brightness);
+               pms_i2c_write(dev, 0x8a, 0x00, brightness);
+               pms_i2c_write(dev, 0x8a, 0x00, brightness);
+               break;
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x42, 0x19, brightness);
+               break;
        }
 }
 
 
-static void pms_format(short format)
+static void pms_format(struct pms *dev, short format)
 {
        int target;
-       standard = format;
 
-       if(decoder==PHILIPS1)
-               target=0x42;
-       else if(decoder==PHILIPS2)
-               target=0x8A;
+       dev->standard = format;
+
+       if (dev->decoder == PHILIPS1)
+               target = 0x42;
+       else if (dev->decoder == PHILIPS2)
+               target = 0x8a;
        else
                return;
 
-       switch(format)
-       {
-               case 0: /* Auto */
-                       pms_i2c_andor(target, 0x0D, 0xFE,0x00);
-                       pms_i2c_andor(target, 0x0F, 0x3F,0x80);
-                       break;
-               case 1: /* NTSC */
-                       pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
-                       pms_i2c_andor(target, 0x0F, 0x3F, 0x40);
-                       break;
-               case 2: /* PAL */
-                       pms_i2c_andor(target, 0x0D, 0xFE, 0x00);
-                       pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
-                       break;
-               case 3: /* SECAM */
-                       pms_i2c_andor(target, 0x0D, 0xFE, 0x01);
-                       pms_i2c_andor(target, 0x0F, 0x3F, 0x00);
-                       break;
+       switch (format) {
+       case 0: /* Auto */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80);
+               break;
+       case 1: /* NTSC */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40);
+               break;
+       case 2: /* PAL */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+               break;
+       case 3: /* SECAM */
+               pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01);
+               pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00);
+               break;
        }
 }
 
@@ -308,18 +326,17 @@ static void pms_format(short format)
  *     people need it. We also don't yet use the PMS interrupt.
  */
 
-static void pms_hstart(short start)
+static void pms_hstart(struct pms *dev, short start)
 {
-       switch(decoder)
-       {
-               case PHILIPS1:
-                       pms_i2c_write(0x8A, 0x05, start);
-                       pms_i2c_write(0x8A, 0x18, start);
-                       break;
-               case PHILIPS2:
-                       pms_i2c_write(0x42, 0x05, start);
-                       pms_i2c_write(0x42, 0x18, start);
-                       break;
+       switch (dev->decoder) {
+       case PHILIPS1:
+               pms_i2c_write(dev, 0x8a, 0x05, start);
+               pms_i2c_write(dev, 0x8a, 0x18, start);
+               break;
+       case PHILIPS2:
+               pms_i2c_write(dev, 0x42, 0x05, start);
+               pms_i2c_write(dev, 0x42, 0x18, start);
+               break;
        }
 }
 
@@ -327,293 +344,271 @@ static void pms_hstart(short start)
  *     Bandpass filters
  */
 
-static void pms_bandpass(short pass)
+static void pms_bandpass(struct pms *dev, short pass)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4);
 }
 
-static void pms_antisnow(short snow)
+static void pms_antisnow(struct pms *dev, short snow)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2);
 }
 
-static void pms_sharpness(short sharp)
+static void pms_sharpness(struct pms *dev, short sharp)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03);
 }
 
-static void pms_chromaagc(short agc)
+static void pms_chromaagc(struct pms *dev, short agc)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5);
 }
 
-static void pms_vertnoise(short noise)
+static void pms_vertnoise(struct pms *dev, short noise)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x10, 0xFC, noise&3);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3);
 }
 
-static void pms_forcecolour(short colour)
+static void pms_forcecolour(struct pms *dev, short colour)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7);
 }
 
-static void pms_antigamma(short gamma)
+static void pms_antigamma(struct pms *dev, short gamma)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7);
 }
 
-static void pms_prefilter(short filter)
+static void pms_prefilter(struct pms *dev, short filter)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6);
 }
 
-static void pms_hfilter(short filter)
+static void pms_hfilter(struct pms *dev, short filter)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5);
 }
 
-static void pms_vfilter(short filter)
+static void pms_vfilter(struct pms *dev, short filter)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5);
 }
 
-static void pms_killcolour(short colour)
+static void pms_killcolour(struct pms *dev, short colour)
 {
-       if(decoder==PHILIPS2)
-       {
-               pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3);
-               pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3);
-       }
-       else if(decoder==PHILIPS1)
-       {
-               pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3);
-               pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3);
+       if (dev->decoder == PHILIPS2) {
+               pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3);
+               pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3);
+       } else if (dev->decoder == PHILIPS1) {
+               pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3);
+               pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3);
        }
 }
 
-static void pms_chromagain(short chroma)
+static void pms_chromagain(struct pms *dev, short chroma)
 {
-       if(decoder==PHILIPS2)
-       {
-               pms_i2c_write(0x8A, 0x11, chroma);
-       }
-       else if(decoder==PHILIPS1)
-       {
-               pms_i2c_write(0x42, 0x11, chroma);
-       }
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_write(dev, 0x8a, 0x11, chroma);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_write(dev, 0x42, 0x11, chroma);
 }
 
 
-static void pms_spacialcompl(short data)
+static void pms_spacialcompl(struct pms *dev, short data)
 {
-       mvv_write(0x3B, data);
+       mvv_write(dev, 0x3b, data);
 }
 
-static void pms_spacialcomph(short data)
+static void pms_spacialcomph(struct pms *dev, short data)
 {
-       mvv_write(0x3A, data);
+       mvv_write(dev, 0x3a, data);
 }
 
-static void pms_vstart(short start)
+static void pms_vstart(struct pms *dev, short start)
 {
-       mvv_write(0x16, start);
-       mvv_write(0x17, (start>>8)&0x01);
+       mvv_write(dev, 0x16, start);
+       mvv_write(dev, 0x17, (start >> 8) & 0x01);
 }
 
 #endif
 
-static void pms_secamcross(short cross)
+static void pms_secamcross(struct pms *dev, short cross)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5);
 }
 
 
-static void pms_swsense(short sense)
+static void pms_swsense(struct pms *dev, short sense)
 {
-       if(decoder==PHILIPS2)
-       {
-               pms_i2c_write(0x8A, 0x0A, sense);
-               pms_i2c_write(0x8A, 0x0B, sense);
-       }
-       else if(decoder==PHILIPS1)
-       {
-               pms_i2c_write(0x42, 0x0A, sense);
-               pms_i2c_write(0x42, 0x0B, sense);
+       if (dev->decoder == PHILIPS2) {
+               pms_i2c_write(dev, 0x8a, 0x0a, sense);
+               pms_i2c_write(dev, 0x8a, 0x0b, sense);
+       } else if (dev->decoder == PHILIPS1) {
+               pms_i2c_write(dev, 0x42, 0x0a, sense);
+               pms_i2c_write(dev, 0x42, 0x0b, sense);
        }
 }
 
 
-static void pms_framerate(short frr)
+static void pms_framerate(struct pms *dev, short frr)
 {
-       int fps=(standard==1)?30:25;
-       if(frr==0)
+       int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25;
+
+       if (frr == 0)
                return;
-       fps=fps/frr;
-       mvv_write(0x14,0x80|fps);
-       mvv_write(0x15,1);
+       fps = fps/frr;
+       mvv_write(dev, 0x14, 0x80 | fps);
+       mvv_write(dev, 0x15, 1);
 }
 
-static void pms_vert(u8 deciden, u8 decinum)
+static void pms_vert(struct pms *dev, u8 deciden, u8 decinum)
 {
-       mvv_write(0x1C, deciden);       /* Denominator */
-       mvv_write(0x1D, decinum);       /* Numerator */
+       mvv_write(dev, 0x1c, deciden);  /* Denominator */
+       mvv_write(dev, 0x1d, decinum);  /* Numerator */
 }
 
 /*
  *     Turn 16bit ratios into best small ratio the chipset can grok
  */
 
-static void pms_vertdeci(unsigned short decinum, unsigned short deciden)
+static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden)
 {
-       /* Knock it down by /5 once */
-       if(decinum%5==0)
-       {
-               deciden/=5;
-               decinum/=5;
+       /* Knock it down by / 5 once */
+       if (decinum % 5 == 0) {
+               deciden /= 5;
+               decinum /= 5;
        }
        /*
         *      3's
         */
-       while(decinum%3==0 && deciden%3==0)
-       {
-               deciden/=3;
-               decinum/=3;
+       while (decinum % 3 == 0 && deciden % 3 == 0) {
+               deciden /= 3;
+               decinum /= 3;
        }
        /*
         *      2's
         */
-       while(decinum%2==0 && deciden%2==0)
-       {
-               decinum/=2;
-               deciden/=2;
+       while (decinum % 2 == 0 && deciden % 2 == 0) {
+               decinum /= 2;
+               deciden /= 2;
        }
        /*
         *      Fudgyify
         */
-       while(deciden>32)
-       {
-               deciden/=2;
-               decinum=(decinum+1)/2;
+       while (deciden > 32) {
+               deciden /= 2;
+               decinum = (decinum + 1) / 2;
        }
-       if(deciden==32)
+       if (deciden == 32)
                deciden--;
-       pms_vert(deciden,decinum);
+       pms_vert(dev, deciden, decinum);
 }
 
-static void pms_horzdeci(short decinum, short deciden)
+static void pms_horzdeci(struct pms *dev, short decinum, short deciden)
 {
-       if(decinum<=512)
-       {
-               if(decinum%5==0)
-               {
-                       decinum/=5;
-                       deciden/=5;
+       if (decinum <= 512) {
+               if (decinum % 5 == 0) {
+                       decinum /= 5;
+                       deciden /= 5;
                }
-       }
-       else
-       {
-               decinum=512;
-               deciden=640;    /* 768 would be ideal */
+       } else {
+               decinum = 512;
+               deciden = 640;  /* 768 would be ideal */
        }
 
-       while(((decinum|deciden)&1)==0)
-       {
-               decinum>>=1;
-               deciden>>=1;
+       while (((decinum | deciden) & 1) == 0) {
+               decinum >>= 1;
+               deciden >>= 1;
        }
-       while(deciden>32)
-       {
-               deciden>>=1;
-               decinum=(decinum+1)>>1;
+       while (deciden > 32) {
+               deciden >>= 1;
+               decinum = (decinum + 1) >> 1;
        }
-       if(deciden==32)
+       if (deciden == 32)
                deciden--;
 
-       mvv_write(0x24, 0x80|deciden);
-       mvv_write(0x25, decinum);
+       mvv_write(dev, 0x24, 0x80 | deciden);
+       mvv_write(dev, 0x25, decinum);
 }
 
-static void pms_resolution(short width, short height)
+static void pms_resolution(struct pms *dev, short width, short height)
 {
        int fg_height;
 
-       fg_height=height;
-       if(fg_height>280)
-               fg_height=280;
+       fg_height = height;
+       if (fg_height > 280)
+               fg_height = 280;
 
-       mvv_write(0x18, fg_height);
-       mvv_write(0x19, fg_height>>8);
+       mvv_write(dev, 0x18, fg_height);
+       mvv_write(dev, 0x19, fg_height >> 8);
 
-       if(standard==1)
-       {
-               mvv_write(0x1A, 0xFC);
-               mvv_write(0x1B, 0x00);
-               if(height>fg_height)
-                       pms_vertdeci(240,240);
+       if (dev->std & V4L2_STD_525_60) {
+               mvv_write(dev, 0x1a, 0xfc);
+               mvv_write(dev, 0x1b, 0x00);
+               if (height > fg_height)
+                       pms_vertdeci(dev, 240, 240);
                else
-                       pms_vertdeci(fg_height,240);
-       }
-       else
-       {
-               mvv_write(0x1A, 0x1A);
-               mvv_write(0x1B, 0x01);
-               if(fg_height>256)
-                       pms_vertdeci(270,270);
+                       pms_vertdeci(dev, fg_height, 240);
+       } else {
+               mvv_write(dev, 0x1a, 0x1a);
+               mvv_write(dev, 0x1b, 0x01);
+               if (fg_height > 256)
+                       pms_vertdeci(dev, 270, 270);
                else
-                       pms_vertdeci(fg_height, 270);
+                       pms_vertdeci(dev, fg_height, 270);
        }
-       mvv_write(0x12,0);
-       mvv_write(0x13, MVVMEMORYWIDTH);
-       mvv_write(0x42, 0x00);
-       mvv_write(0x43, 0x00);
-       mvv_write(0x44, MVVMEMORYWIDTH);
+       mvv_write(dev, 0x12, 0);
+       mvv_write(dev, 0x13, MVVMEMORYWIDTH);
+       mvv_write(dev, 0x42, 0x00);
+       mvv_write(dev, 0x43, 0x00);
+       mvv_write(dev, 0x44, MVVMEMORYWIDTH);
 
-       mvv_write(0x22, width+8);
-       mvv_write(0x23, (width+8)>> 8);
+       mvv_write(dev, 0x22, width + 8);
+       mvv_write(dev, 0x23, (width + 8) >> 8);
 
-       if(standard==1)
-               pms_horzdeci(width,640);
+       if (dev->std & V4L2_STD_525_60)
+               pms_horzdeci(dev, width, 640);
        else
-               pms_horzdeci(width+8, 768);
+               pms_horzdeci(dev, width + 8, 768);
 
-       mvv_write(0x30, mvv_read(0x30)&0xFE);
-       mvv_write(0x08, mvv_read(0x08)|0x01);
-       mvv_write(0x01, mvv_read(0x01)&0xFD);
-       mvv_write(0x32, 0x00);
-       mvv_write(0x33, MVVMEMORYWIDTH);
+       mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe);
+       mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01);
+       mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd);
+       mvv_write(dev, 0x32, 0x00);
+       mvv_write(dev, 0x33, MVVMEMORYWIDTH);
 }
 
 
@@ -621,52 +616,49 @@ static void pms_resolution(short width, short height)
  *     Set Input
  */
 
-static void pms_vcrinput(short input)
+static void pms_vcrinput(struct pms *dev, short input)
 {
-       if(decoder==PHILIPS2)
-               pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7);
-       else if(decoder==PHILIPS1)
-               pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7);
+       if (dev->decoder == PHILIPS2)
+               pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7);
+       else if (dev->decoder == PHILIPS1)
+               pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7);
 }
 
 
-static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count)
+static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count)
 {
        int y;
-       int dw = 2*dev->width;
-
-       char tmp[dw+32]; /* using a temp buffer is faster than direct  */
+       int dw = 2 * dev->width;
+       char tmp[dw + 32]; /* using a temp buffer is faster than direct  */
        int cnt = 0;
-       int len=0;
+       int len = 0;
        unsigned char r8 = 0x5;  /* value for reg8  */
 
        if (rgb555)
                r8 |= 0x20; /* else use untranslated rgb = 565 */
-       mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */
+       mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */
 
 /*     printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */
 
-       for (y = 0; y < dev->height; y++ )
-       {
-               writeb(0, mem);  /* synchronisiert neue Zeile */
+       for (y = 0; y < dev->height; y++) {
+               writeb(0, dev->mem);  /* synchronisiert neue Zeile */
 
                /*
                 *      This is in truth a fifo, be very careful as if you
                 *      forgot this odd things will occur 8)
                 */
 
-               memcpy_fromio(tmp, mem, dw+32); /* discard 16 word   */
+               memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word   */
                cnt -= dev->height;
-               while (cnt <= 0)
-               {
+               while (cnt <= 0) {
                        /*
                         *      Don't copy too far
                         */
-                       int dt=dw;
-                       if(dt+len>count)
-                               dt=count-len;
+                       int dt = dw;
+                       if (dt + len > count)
+                               dt = count - len;
                        cnt += dev->height;
-                       if (copy_to_user(buf, tmp+32, dt))
+                       if (copy_to_user(buf, tmp + 32, dt))
                                return len ? len : -EFAULT;
                        buf += dt;
                        len += dt;
@@ -680,221 +672,278 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int
  *     Video4linux interfacing
  */
 
-static long pms_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int pms_querycap(struct file *file, void  *priv,
+                                       struct v4l2_capability *vcap)
 {
-       struct video_device *dev = video_devdata(file);
-       struct pms_device *pd=(struct pms_device *)dev;
-
-       switch(cmd)
-       {
-               case VIDIOCGCAP:
-               {
-                       struct video_capability *b = arg;
-                       strcpy(b->name, "Mediavision PMS");
-                       b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES;
-                       b->channels = 4;
-                       b->audios = 0;
-                       b->maxwidth = 640;
-                       b->maxheight = 480;
-                       b->minwidth = 16;
-                       b->minheight = 16;
-                       return 0;
-               }
-               case VIDIOCGCHAN:
-               {
-                       struct video_channel *v = arg;
-                       if(v->channel<0 || v->channel>3)
-                               return -EINVAL;
-                       v->flags=0;
-                       v->tuners=1;
-                       /* Good question.. its composite or SVHS so.. */
-                       v->type = VIDEO_TYPE_CAMERA;
-                       switch(v->channel)
-                       {
-                               case 0:
-                                       strcpy(v->name, "Composite");break;
-                               case 1:
-                                       strcpy(v->name, "SVideo");break;
-                               case 2:
-                                       strcpy(v->name, "Composite(VCR)");break;
-                               case 3:
-                                       strcpy(v->name, "SVideo(VCR)");break;
-                       }
-                       return 0;
-               }
-               case VIDIOCSCHAN:
-               {
-                       struct video_channel *v = arg;
-                       if(v->channel<0 || v->channel>3)
-                               return -EINVAL;
-                       mutex_lock(&pd->lock);
-                       pms_videosource(v->channel&1);
-                       pms_vcrinput(v->channel>>1);
-                       mutex_unlock(&pd->lock);
-                       return 0;
-               }
-               case VIDIOCGTUNER:
-               {
-                       struct video_tuner *v = arg;
-                       if(v->tuner)
-                               return -EINVAL;
-                       strcpy(v->name, "Format");
-                       v->rangelow=0;
-                       v->rangehigh=0;
-                       v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
-                       switch(standard)
-                       {
-                               case 0:
-                                       v->mode = VIDEO_MODE_AUTO;
-                                       break;
-                               case 1:
-                                       v->mode = VIDEO_MODE_NTSC;
-                                       break;
-                               case 2:
-                                       v->mode = VIDEO_MODE_PAL;
-                                       break;
-                               case 3:
-                                       v->mode = VIDEO_MODE_SECAM;
-                                       break;
-                       }
-                       return 0;
-               }
-               case VIDIOCSTUNER:
-               {
-                       struct video_tuner *v = arg;
-                       if(v->tuner)
-                               return -EINVAL;
-                       mutex_lock(&pd->lock);
-                       switch(v->mode)
-                       {
-                               case VIDEO_MODE_AUTO:
-                                       pms_framerate(25);
-                                       pms_secamcross(0);
-                                       pms_format(0);
-                                       break;
-                               case VIDEO_MODE_NTSC:
-                                       pms_framerate(30);
-                                       pms_secamcross(0);
-                                       pms_format(1);
-                                       break;
-                               case VIDEO_MODE_PAL:
-                                       pms_framerate(25);
-                                       pms_secamcross(0);
-                                       pms_format(2);
-                                       break;
-                               case VIDEO_MODE_SECAM:
-                                       pms_framerate(25);
-                                       pms_secamcross(1);
-                                       pms_format(2);
-                                       break;
-                               default:
-                                       mutex_unlock(&pd->lock);
-                                       return -EINVAL;
-                       }
-                       mutex_unlock(&pd->lock);
-                       return 0;
-               }
-               case VIDIOCGPICT:
-               {
-                       struct video_picture *p = arg;
-                       *p = pd->picture;
-                       return 0;
-               }
-               case VIDIOCSPICT:
-               {
-                       struct video_picture *p = arg;
-                       if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16)
-                           ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15)))
-                               return -EINVAL;
-                       pd->picture= *p;
+       struct pms *dev = video_drvdata(file);
 
-                       /*
-                        *      Now load the card.
-                        */
+       strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
+       strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
+       strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
+       vcap->version = KERNEL_VERSION(0, 0, 3);
+       vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+       return 0;
+}
 
-                       mutex_lock(&pd->lock);
-                       pms_brightness(p->brightness>>8);
-                       pms_hue(p->hue>>8);
-                       pms_colour(p->colour>>8);
-                       pms_contrast(p->contrast>>8);
-                       mutex_unlock(&pd->lock);
-                       return 0;
-               }
-               case VIDIOCSWIN:
-               {
-                       struct video_window *vw = arg;
-                       if(vw->flags)
-                               return -EINVAL;
-                       if(vw->clipcount)
-                               return -EINVAL;
-                       if(vw->height<16||vw->height>480)
-                               return -EINVAL;
-                       if(vw->width<16||vw->width>640)
-                               return -EINVAL;
-                       pd->width=vw->width;
-                       pd->height=vw->height;
-                       mutex_lock(&pd->lock);
-                       pms_resolution(pd->width, pd->height);
-                       mutex_unlock(&pd->lock);                        /* Ok we figured out what to use from our wide choice */
-                       return 0;
-               }
-               case VIDIOCGWIN:
-               {
-                       struct video_window *vw = arg;
-                       memset(vw,0,sizeof(*vw));
-                       vw->width=pd->width;
-                       vw->height=pd->height;
-                       return 0;
-               }
-               case VIDIOCKEY:
-                       return 0;
-               case VIDIOCCAPTURE:
-               case VIDIOCGFBUF:
-               case VIDIOCSFBUF:
-               case VIDIOCGFREQ:
-               case VIDIOCSFREQ:
-               case VIDIOCGAUDIO:
-               case VIDIOCSAUDIO:
-                       return -EINVAL;
-               default:
-                       return -ENOIOCTLCMD;
+static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+       static const char *inputs[4] = {
+               "Composite",
+               "S-Video",
+               "Composite (VCR)",
+               "S-Video (VCR)"
+       };
+
+       if (vin->index > 3)
+               return -EINVAL;
+       strlcpy(vin->name, inputs[vin->index], sizeof(vin->name));
+       vin->type = V4L2_INPUT_TYPE_CAMERA;
+       vin->audioset = 0;
+       vin->tuner = 0;
+       vin->std = V4L2_STD_ALL;
+       vin->status = 0;
+       return 0;
+}
+
+static int pms_g_input(struct file *file, void *fh, unsigned int *inp)
+{
+       struct pms *dev = video_drvdata(file);
+
+       *inp = dev->input;
+       return 0;
+}
+
+static int pms_s_input(struct file *file, void *fh, unsigned int inp)
+{
+       struct pms *dev = video_drvdata(file);
+
+       if (inp > 3)
+               return -EINVAL;
+
+       mutex_lock(&dev->lock);
+       dev->input = inp;
+       pms_videosource(dev, inp & 1);
+       pms_vcrinput(dev, inp >> 1);
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct pms *dev = video_drvdata(file);
+
+       *std = dev->std;
+       return 0;
+}
+
+static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+       struct pms *dev = video_drvdata(file);
+       int ret = 0;
+
+       dev->std = *std;
+       mutex_lock(&dev->lock);
+       if (dev->std & V4L2_STD_NTSC) {
+               pms_framerate(dev, 30);
+               pms_secamcross(dev, 0);
+               pms_format(dev, 1);
+       } else if (dev->std & V4L2_STD_PAL) {
+               pms_framerate(dev, 25);
+               pms_secamcross(dev, 0);
+               pms_format(dev, 2);
+       } else if (dev->std & V4L2_STD_SECAM) {
+               pms_framerate(dev, 25);
+               pms_secamcross(dev, 1);
+               pms_format(dev, 2);
+       } else {
+               ret = -EINVAL;
+       }
+       /*
+       switch (v->mode) {
+       case VIDEO_MODE_AUTO:
+               pms_framerate(dev, 25);
+               pms_secamcross(dev, 0);
+               pms_format(dev, 0);
+               break;
+       }*/
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int pms_queryctrl(struct file *file, void *priv,
+                                       struct v4l2_queryctrl *qc)
+{
+       switch (qc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
+       case V4L2_CID_CONTRAST:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
+       case V4L2_CID_SATURATION:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
+       case V4L2_CID_HUE:
+               return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
+       }
+       return -EINVAL;
+}
+
+static int pms_g_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *ctrl)
+{
+       struct pms *dev = video_drvdata(file);
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               ctrl->value = dev->brightness;
+               break;
+       case V4L2_CID_CONTRAST:
+               ctrl->value = dev->contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               ctrl->value = dev->saturation;
+               break;
+       case V4L2_CID_HUE:
+               ctrl->value = dev->hue;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int pms_s_ctrl(struct file *file, void *priv,
+                                       struct v4l2_control *ctrl)
+{
+       struct pms *dev = video_drvdata(file);
+       int ret = 0;
+
+       mutex_lock(&dev->lock);
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               dev->brightness = ctrl->value;
+               pms_brightness(dev, dev->brightness);
+               break;
+       case V4L2_CID_CONTRAST:
+               dev->contrast = ctrl->value;
+               pms_contrast(dev, dev->contrast);
+               break;
+       case V4L2_CID_SATURATION:
+               dev->saturation = ctrl->value;
+               pms_saturation(dev, dev->saturation);
+               break;
+       case V4L2_CID_HUE:
+               dev->hue = ctrl->value;
+               pms_hue(dev, dev->hue);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
        }
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct pms *dev = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       pix->width = dev->width;
+       pix->height = dev->height;
+       pix->pixelformat = dev->width == 15 ?
+                           V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 2 * dev->width;
+       pix->sizeimage = 2 * dev->width * dev->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
        return 0;
 }
 
-static long pms_ioctl(struct file *file,
-                    unsigned int cmd, unsigned long arg)
+static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
 {
-       return video_usercopy(file, cmd, arg, pms_do_ioctl);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+       if (pix->height < 16 || pix->height > 480)
+               return -EINVAL;
+       if (pix->width < 16 || pix->width > 640)
+               return -EINVAL;
+       if (pix->pixelformat != V4L2_PIX_FMT_RGB555 &&
+           pix->pixelformat != V4L2_PIX_FMT_RGB565)
+               return -EINVAL;
+       pix->field = V4L2_FIELD_NONE;
+       pix->bytesperline = 2 * pix->width;
+       pix->sizeimage = 2 * pix->width * pix->height;
+       /* Just a guess */
+       pix->colorspace = V4L2_COLORSPACE_SRGB;
+       return 0;
+}
+
+static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+       struct pms *dev = video_drvdata(file);
+       struct v4l2_pix_format *pix = &fmt->fmt.pix;
+       int ret = pms_try_fmt_vid_cap(file, fh, fmt);
+
+       if (ret)
+               return ret;
+       mutex_lock(&dev->lock);
+       dev->width = pix->width;
+       dev->height = pix->height;
+       dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
+       pms_resolution(dev, dev->width, dev->height);
+       /* Ok we figured out what to use from our wide choice */
+       mutex_unlock(&dev->lock);
+       return 0;
+}
+
+static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+       static struct v4l2_fmtdesc formats[] = {
+               { 0, 0, 0,
+                 "RGB 5:5:5", V4L2_PIX_FMT_RGB555,
+                 { 0, 0, 0, 0 }
+               },
+               { 0, 0, 0,
+                 "RGB 5:6:5", V4L2_PIX_FMT_RGB565,
+                 { 0, 0, 0, 0 }
+               },
+       };
+       enum v4l2_buf_type type = fmt->type;
+
+       if (fmt->index > 1)
+               return -EINVAL;
+
+       *fmt = formats[fmt->index];
+       fmt->type = type;
+       return 0;
 }
 
 static ssize_t pms_read(struct file *file, char __user *buf,
                    size_t count, loff_t *ppos)
 {
-       struct video_device *v = video_devdata(file);
-       struct pms_device *pd=(struct pms_device *)v;
+       struct pms *dev = video_drvdata(file);
        int len;
 
-       mutex_lock(&pd->lock);
-       len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count);
-       mutex_unlock(&pd->lock);
+       mutex_lock(&dev->lock);
+       len = pms_capture(dev, buf, (dev->depth == 15), count);
+       mutex_unlock(&dev->lock);
        return len;
 }
 
 static int pms_exclusive_open(struct file *file)
 {
-       struct video_device *v = video_devdata(file);
-       struct pms_device *pd = (struct pms_device *)v;
+       struct pms *dev = video_drvdata(file);
 
-       return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0;
+       return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0;
 }
 
 static int pms_exclusive_release(struct file *file)
 {
-       struct video_device *v = video_devdata(file);
-       struct pms_device *pd = (struct pms_device *)v;
+       struct pms *dev = video_drvdata(file);
 
-       clear_bit(0, &pd->in_use);
+       clear_bit(0, &dev->in_use);
        return 0;
 }
 
@@ -902,78 +951,81 @@ static const struct v4l2_file_operations pms_fops = {
        .owner          = THIS_MODULE,
        .open           = pms_exclusive_open,
        .release        = pms_exclusive_release,
-       .ioctl          = pms_ioctl,
+       .ioctl          = video_ioctl2,
        .read           = pms_read,
 };
 
-static struct video_device pms_template=
-{
-       .name           = "Mediavision PMS",
-       .fops           = &pms_fops,
-       .release        = video_device_release_empty,
+static const struct v4l2_ioctl_ops pms_ioctl_ops = {
+       .vidioc_querycap                    = pms_querycap,
+       .vidioc_g_input                     = pms_g_input,
+       .vidioc_s_input                     = pms_s_input,
+       .vidioc_enum_input                  = pms_enum_input,
+       .vidioc_g_std                       = pms_g_std,
+       .vidioc_s_std                       = pms_s_std,
+       .vidioc_queryctrl                   = pms_queryctrl,
+       .vidioc_g_ctrl                      = pms_g_ctrl,
+       .vidioc_s_ctrl                      = pms_s_ctrl,
+       .vidioc_enum_fmt_vid_cap            = pms_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap               = pms_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap               = pms_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap             = pms_try_fmt_vid_cap,
 };
 
-static struct pms_device pms_device;
-
-
 /*
  *     Probe for and initialise the Mediavision PMS
  */
 
-static int init_mediavision(void)
+static int init_mediavision(struct pms *dev)
 {
        int id;
        int idec, decst;
        int i;
-
-       unsigned char i2c_defs[]={
-               0x4C,0x30,0x00,0xE8,
-               0xB6,0xE2,0x00,0x00,
-               0xFF,0xFF,0x00,0x00,
-               0x00,0x00,0x78,0x98,
-               0x00,0x00,0x00,0x00,
-               0x34,0x0A,0xF4,0xCE,
-               0xE4
+       static const unsigned char i2c_defs[] = {
+               0x4c, 0x30, 0x00, 0xe8,
+               0xb6, 0xe2, 0x00, 0x00,
+               0xff, 0xff, 0x00, 0x00,
+               0x00, 0x00, 0x78, 0x98,
+               0x00, 0x00, 0x00, 0x00,
+               0x34, 0x0a, 0xf4, 0xce,
+               0xe4
        };
 
-       mem = ioremap(mem_base, 0x800);
-       if (!mem)
+       dev->mem = ioremap(mem_base, 0x800);
+       if (!dev->mem)
                return -ENOMEM;
 
-       if (!request_region(0x9A01, 1, "Mediavision PMS config"))
-       {
-               printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n");
-               iounmap(mem);
+       if (!request_region(0x9a01, 1, "Mediavision PMS config")) {
+               printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n");
+               iounmap(dev->mem);
                return -EBUSY;
        }
-       if (!request_region(io_port, 3, "Mediavision PMS"))
-       {
-               printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port);
-               release_region(0x9A01, 1);
-               iounmap(mem);
+       if (!request_region(dev->io, 3, "Mediavision PMS")) {
+               printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io);
+               release_region(0x9a01, 1);
+               iounmap(dev->mem);
                return -EBUSY;
        }
-       outb(0xB8, 0x9A01);             /* Unlock */
-       outb(io_port>>4, 0x9A01);       /* Set IO port */
+       outb(0xb8, 0x9a01);             /* Unlock */
+       outb(dev->io >> 4, 0x9a01);     /* Set IO port */
 
 
-       id=mvv_read(3);
-       decst=pms_i2c_stat(0x43);
+       id = mvv_read(dev, 3);
+       decst = pms_i2c_stat(dev, 0x43);
 
-       if(decst!=-1)
-               idec=2;
-       else if(pms_i2c_stat(0xb9)!=-1)
-               idec=3;
-       else if(pms_i2c_stat(0x8b)!=-1)
-               idec=1;
+       if (decst != -1)
+               idec = 2;
+       else if (pms_i2c_stat(dev, 0xb9) != -1)
+               idec = 3;
+       else if (pms_i2c_stat(dev, 0x8b) != -1)
+               idec = 1;
        else
-               idec=0;
+               idec = 0;
 
        printk(KERN_INFO "PMS type is %d\n", idec);
-       if(idec == 0) {
-               release_region(io_port, 3);
-               release_region(0x9A01, 1);
-               iounmap(mem);
+       if (idec == 0) {
+               release_region(dev->io, 3);
+               release_region(0x9a01, 1);
+               iounmap(dev->mem);
                return -ENODEV;
        }
 
@@ -981,51 +1033,50 @@ static int init_mediavision(void)
         *      Ok we have a PMS of some sort
         */
 
-       mvv_write(0x04, mem_base>>12);  /* Set the memory area */
+       mvv_write(dev, 0x04, mem_base >> 12);   /* Set the memory area */
 
        /* Ok now load the defaults */
 
-       for(i=0;i<0x19;i++)
-       {
-               if(i2c_defs[i]==0xFF)
-                       pms_i2c_andor(0x8A, i, 0x07,0x00);
+       for (i = 0; i < 0x19; i++) {
+               if (i2c_defs[i] == 0xff)
+                       pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00);
                else
-                       pms_i2c_write(0x8A, i, i2c_defs[i]);
+                       pms_i2c_write(dev, 0x8a, i, i2c_defs[i]);
        }
 
-       pms_i2c_write(0xB8,0x00,0x12);
-       pms_i2c_write(0xB8,0x04,0x00);
-       pms_i2c_write(0xB8,0x07,0x00);
-       pms_i2c_write(0xB8,0x08,0x00);
-       pms_i2c_write(0xB8,0x09,0xFF);
-       pms_i2c_write(0xB8,0x0A,0x00);
-       pms_i2c_write(0xB8,0x0B,0x10);
-       pms_i2c_write(0xB8,0x10,0x03);
-
-       mvv_write(0x01, 0x00);
-       mvv_write(0x05, 0xA0);
-       mvv_write(0x08, 0x25);
-       mvv_write(0x09, 0x00);
-       mvv_write(0x0A, 0x20|MVVMEMORYWIDTH);
-
-       mvv_write(0x10, 0x02);
-       mvv_write(0x1E, 0x0C);
-       mvv_write(0x1F, 0x03);
-       mvv_write(0x26, 0x06);
-
-       mvv_write(0x2B, 0x00);
-       mvv_write(0x2C, 0x20);
-       mvv_write(0x2D, 0x00);
-       mvv_write(0x2F, 0x70);
-       mvv_write(0x32, 0x00);
-       mvv_write(0x33, MVVMEMORYWIDTH);
-       mvv_write(0x34, 0x00);
-       mvv_write(0x35, 0x00);
-       mvv_write(0x3A, 0x80);
-       mvv_write(0x3B, 0x10);
-       mvv_write(0x20, 0x00);
-       mvv_write(0x21, 0x00);
-       mvv_write(0x30, 0x22);
+       pms_i2c_write(dev, 0xb8, 0x00, 0x12);
+       pms_i2c_write(dev, 0xb8, 0x04, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x07, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x08, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x09, 0xff);
+       pms_i2c_write(dev, 0xb8, 0x0a, 0x00);
+       pms_i2c_write(dev, 0xb8, 0x0b, 0x10);
+       pms_i2c_write(dev, 0xb8, 0x10, 0x03);
+
+       mvv_write(dev, 0x01, 0x00);
+       mvv_write(dev, 0x05, 0xa0);
+       mvv_write(dev, 0x08, 0x25);
+       mvv_write(dev, 0x09, 0x00);
+       mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH);
+
+       mvv_write(dev, 0x10, 0x02);
+       mvv_write(dev, 0x1e, 0x0c);
+       mvv_write(dev, 0x1f, 0x03);
+       mvv_write(dev, 0x26, 0x06);
+
+       mvv_write(dev, 0x2b, 0x00);
+       mvv_write(dev, 0x2c, 0x20);
+       mvv_write(dev, 0x2d, 0x00);
+       mvv_write(dev, 0x2f, 0x70);
+       mvv_write(dev, 0x32, 0x00);
+       mvv_write(dev, 0x33, MVVMEMORYWIDTH);
+       mvv_write(dev, 0x34, 0x00);
+       mvv_write(dev, 0x35, 0x00);
+       mvv_write(dev, 0x3a, 0x80);
+       mvv_write(dev, 0x3b, 0x10);
+       mvv_write(dev, 0x20, 0x00);
+       mvv_write(dev, 0x21, 0x00);
+       mvv_write(dev, 0x30, 0x22);
        return 0;
 }
 
@@ -1038,53 +1089,77 @@ static int enable;
 module_param(enable, int, 0);
 #endif
 
-static int __init init_pms_cards(void)
+static int __init pms_init(void)
 {
-       printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n");
+       struct pms *dev = &pms_card;
+       struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+       int res;
+
+       strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
+
+       v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
 
 #ifndef MODULE
        if (!enable) {
-               printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to "
-                                "probe\n");
+               v4l2_err(v4l2_dev,
+                       "PMS: not enabled, use pms.enable=1 to probe\n");
                return -ENODEV;
        }
 #endif
 
-       data_port = io_port +1;
+       dev->decoder = PHILIPS2;
+       dev->io = io_port;
+       dev->data = io_port + 1;
 
-       if(init_mediavision())
-       {
-               printk(KERN_INFO "Board not found.\n");
+       if (init_mediavision(dev)) {
+               v4l2_err(v4l2_dev, "Board not found.\n");
                return -ENODEV;
        }
-       memcpy(&pms_device, &pms_template, sizeof(pms_template));
-       mutex_init(&pms_device.lock);
-       pms_device.height=240;
-       pms_device.width=320;
-       pms_swsense(75);
-       pms_resolution(320,240);
-       return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr);
-}
-
-module_param(io_port, int, 0);
-module_param(mem_base, int, 0);
-module_param(video_nr, int, 0);
-MODULE_LICENSE("GPL");
 
+       res = v4l2_device_register(NULL, v4l2_dev);
+       if (res < 0) {
+               v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+               return res;
+       }
 
-static void __exit shutdown_mediavision(void)
-{
-       release_region(io_port,3);
-       release_region(0x9A01, 1);
+       strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
+       dev->vdev.v4l2_dev = v4l2_dev;
+       dev->vdev.fops = &pms_fops;
+       dev->vdev.ioctl_ops = &pms_ioctl_ops;
+       dev->vdev.release = video_device_release_empty;
+       video_set_drvdata(&dev->vdev, dev);
+       mutex_init(&dev->lock);
+       dev->std = V4L2_STD_NTSC_M;
+       dev->height = 240;
+       dev->width = 320;
+       dev->depth = 15;
+       dev->brightness = 139;
+       dev->contrast = 70;
+       dev->hue = 0;
+       dev->saturation = 64;
+       pms_swsense(dev, 75);
+       pms_resolution(dev, 320, 240);
+       pms_videosource(dev, 0);
+       pms_vcrinput(dev, 0);
+       if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+               v4l2_device_unregister(&dev->v4l2_dev);
+               release_region(dev->io, 3);
+               release_region(0x9a01, 1);
+               iounmap(dev->mem);
+               return -EINVAL;
+       }
+       return 0;
 }
 
-static void __exit cleanup_pms_module(void)
+static void __exit pms_exit(void)
 {
-       shutdown_mediavision();
-       video_unregister_device((struct video_device *)&pms_device);
-       iounmap(mem);
-}
+       struct pms *dev = &pms_card;
 
-module_init(init_pms_cards);
-module_exit(cleanup_pms_module);
+       video_unregister_device(&dev->vdev);
+       release_region(dev->io, 3);
+       release_region(0x9a01, 1);
+       iounmap(dev->mem);
+}
 
+module_init(pms_init);
+module_exit(pms_exit);
index fbe3856..ae97766 100644 (file)
@@ -142,6 +142,9 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
 {
        int bcnt = 0;
        int ccnt;
+       ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n",
+                        pvr2_hdw_get_desc(hdw));
+       bcnt += ccnt; acnt -= ccnt; buf += ccnt;
        ccnt = scnprintf(buf,acnt,"Driver state info:\n");
        bcnt += ccnt; acnt -= ccnt; buf += ccnt;
        ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
@@ -249,11 +252,15 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
                        scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
                        if (scnt && wptr) {
                                count -= scnt; buf += scnt;
-                               if (debugifc_match_keyword(wptr,wlen,"prom")) {
-                                       pvr2_hdw_cpufw_set_enabled(hdw,!0,!0);
-                               } else if (debugifc_match_keyword(wptr,wlen,
-                                                                 "ram")) {
-                                       pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
+                               if (debugifc_match_keyword(wptr, wlen,
+                                                          "prom")) {
+                                       pvr2_hdw_cpufw_set_enabled(hdw, 2, !0);
+                               } else if (debugifc_match_keyword(wptr, wlen,
+                                                                 "ram8k")) {
+                                       pvr2_hdw_cpufw_set_enabled(hdw, 0, !0);
+                               } else if (debugifc_match_keyword(wptr, wlen,
+                                                                 "ram16k")) {
+                                       pvr2_hdw_cpufw_set_enabled(hdw, 1, !0);
                                } else {
                                        return -EINVAL;
                                }
index e4d7c13..6bc16c1 100644 (file)
@@ -58,7 +58,7 @@ static const char *pvr2_fw1_names_29xxx[] = {
 };
 
 static const struct pvr2_device_desc pvr2_device_29xxx = {
-               .description = "WinTV PVR USB2 Model Category 29xxx",
+               .description = "WinTV PVR USB2 Model 29xxx",
                .shortname = "29xxx",
                .client_table.lst = pvr2_cli_29xxx,
                .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
@@ -91,7 +91,7 @@ static const char *pvr2_fw1_names_24xxx[] = {
 };
 
 static const struct pvr2_device_desc pvr2_device_24xxx = {
-               .description = "WinTV PVR USB2 Model Category 24xxx",
+               .description = "WinTV PVR USB2 Model 24xxx",
                .shortname = "24xxx",
                .client_table.lst = pvr2_cli_24xxx,
                .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
@@ -340,7 +340,7 @@ static const char *pvr2_fw1_names_73xxx[] = {
 };
 
 static const struct pvr2_device_desc pvr2_device_73xxx = {
-               .description = "WinTV HVR-1900 Model Category 73xxx",
+               .description = "WinTV HVR-1900 Model 73xxx",
                .shortname = "73xxx",
                .client_table.lst = pvr2_cli_73xxx,
                .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
@@ -351,6 +351,7 @@ static const struct pvr2_device_desc pvr2_device_73xxx = {
                .flag_has_analogtuner = !0,
                .flag_has_composite = !0,
                .flag_has_svideo = !0,
+               .flag_fx2_16kb = !0,
                .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
                .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
                .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
@@ -445,7 +446,7 @@ static const char *pvr2_fw1_names_75xxx[] = {
 };
 
 static const struct pvr2_device_desc pvr2_device_750xx = {
-               .description = "WinTV HVR-1950 Model Category 750xx",
+               .description = "WinTV HVR-1950 Model 750xx",
                .shortname = "750xx",
                .client_table.lst = pvr2_cli_73xxx,
                .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
@@ -456,6 +457,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = {
                .flag_has_analogtuner = !0,
                .flag_has_composite = !0,
                .flag_has_svideo = !0,
+               .flag_fx2_16kb = !0,
                .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
                .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
                .default_std_mask = V4L2_STD_NTSC_M,
@@ -467,7 +469,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = {
 };
 
 static const struct pvr2_device_desc pvr2_device_751xx = {
-               .description = "WinTV HVR-1950 Model Category 751xx",
+               .description = "WinTV HVR-1950 Model 751xx",
                .shortname = "751xx",
                .client_table.lst = pvr2_cli_73xxx,
                .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
@@ -478,6 +480,7 @@ static const struct pvr2_device_desc pvr2_device_751xx = {
                .flag_has_analogtuner = !0,
                .flag_has_composite = !0,
                .flag_has_svideo = !0,
+               .flag_fx2_16kb = !0,
                .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
                .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
                .default_std_mask = V4L2_STD_NTSC_M,
index ea04ecf..e5b9594 100644 (file)
@@ -176,6 +176,7 @@ struct pvr2_device_desc {
        unsigned int flag_has_analogtuner:1;   /* Has analog tuner */
        unsigned int flag_has_composite:1;     /* Has composite input */
        unsigned int flag_has_svideo:1;        /* Has s-video input */
+       unsigned int flag_fx2_16kb:1;          /* 16KB FX2 firmware OK here */
 };
 
 extern struct usb_device_id pvr2_device_table[];
index 54ac534..e046fda 100644 (file)
@@ -294,7 +294,10 @@ static int pvr2_encoder_cmd(void *ctxt,
                        pvr2_trace(
                                PVR2_TRACE_ERROR_LEGS,
                                "Giving up on command."
-                               "  This is normally recovered by the driver.");
+                               "  This is normally recovered via a firmware"
+                               " reload and re-initialization; concern"
+                               " is only warranted if this happens repeatedly"
+                               " and rapidly.");
                        break;
                }
                wrData[0] = 0x7;
index 5b152ff..de5485f 100644 (file)
@@ -270,6 +270,7 @@ struct pvr2_hdw {
 
        int force_dirty;        /* consider all controls dirty if true */
        int flag_ok;            /* device in known good state */
+       int flag_modulefail;    /* true if at least one module failed to load */
        int flag_disconnected;  /* flag_ok == 0 due to disconnect */
        int flag_init_ok;       /* true if structure is fully initialized */
        int fw1_state;          /* current situation with fw1 */
@@ -332,7 +333,7 @@ struct pvr2_hdw {
 
        /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */
        unsigned int input_avail_mask;
-       /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */
+       /* Bit mask of PVR2_CVAL_INPUT choices which are currently allowed */
        unsigned int input_allowed_mask;
 
        /* Location of eeprom or a negative number if none */
index 13639b3..1bbdab0 100644 (file)
@@ -1447,6 +1447,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
        const struct firmware *fw_entry = NULL;
        void  *fw_ptr;
        unsigned int pipe;
+       unsigned int fwsize;
        int ret;
        u16 address;
 
@@ -1473,9 +1474,21 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
        usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
 
        pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+       fwsize = fw_entry->size;
 
-       if (fw_entry->size != 0x2000){
-               pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size");
+       if ((fwsize != 0x2000) &&
+           (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
+               if (hdw->hdw_desc->flag_fx2_16kb) {
+                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                                  "Wrong fx2 firmware size"
+                                  " (expected 8192 or 16384, got %u)",
+                                  fwsize);
+               } else {
+                       pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+                                  "Wrong fx2 firmware size"
+                                  " (expected 8192, got %u)",
+                                  fwsize);
+               }
                release_firmware(fw_entry);
                return -ENOMEM;
        }
@@ -1493,7 +1506,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
           chunk. */
 
        ret = 0;
-       for(address = 0; address < fw_entry->size; address += 0x800) {
+       for (address = 0; address < fwsize; address += 0x800) {
                memcpy(fw_ptr, fw_entry->data + address, 0x800);
                ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
                                       0, fw_ptr, 0x800, HZ);
@@ -1509,8 +1522,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
 
        trace_firmware("Upload done (%d bytes sent)",ret);
 
-       /* We should have written 8192 bytes */
-       if (ret == 8192) {
+       /* We should have written fwsize bytes */
+       if (ret == fwsize) {
                hdw->fw1_state = FW1_STATE_RELOAD;
                return 0;
        }
@@ -2030,7 +2043,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
        fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
        if (!fname) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                          "Module ID %u for device %s has no name",
+                          "Module ID %u for device %s has no name?"
+                          "  The driver might have a configuration problem.",
                           mid,
                           hdw->hdw_desc->description);
                return -EINVAL;
@@ -2058,7 +2072,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
        if (!i2ccnt) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
                           "Module ID %u (%s) for device %s:"
-                          " No i2c addresses",
+                          " No i2c addresses."
+                          "  The driver might have a configuration problem.",
                           mid, fname, hdw->hdw_desc->description);
                return -EINVAL;
        }
@@ -2090,7 +2105,9 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
 
        if (!sd) {
                pvr2_trace(PVR2_TRACE_ERROR_LEGS,
-                          "Module ID %u (%s) for device %s failed to load",
+                          "Module ID %u (%s) for device %s failed to load."
+                          "  Possible missing sub-device kernel module or"
+                          " initialization failure within module.",
                           mid, fname, hdw->hdw_desc->description);
                return -EIO;
        }
@@ -2132,7 +2149,10 @@ static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
        for (idx = 0; idx < ct->cnt; idx++) {
                if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
        }
-       if (!okFl) pvr2_hdw_render_useless(hdw);
+       if (!okFl) {
+               hdw->flag_modulefail = !0;
+               pvr2_hdw_render_useless(hdw);
+       }
 }
 
 
@@ -2334,6 +2354,20 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
                                break;
                        }
                }
+               if (hdw->flag_modulefail) {
+                       pvr2_trace(
+                               PVR2_TRACE_ERROR_LEGS,
+                               "***WARNING*** pvrusb2 driver initialization"
+                               " failed due to the failure of one or more"
+                               " sub-device kernel modules.");
+                       pvr2_trace(
+                               PVR2_TRACE_ERROR_LEGS,
+                               "You need to resolve the failing condition"
+                               " before this driver can function.  There"
+                               " should be some earlier messages giving more"
+                               " information about the problem.");
+                       break;
+               }
                if (procreload) {
                        pvr2_trace(
                                PVR2_TRACE_ERROR_LEGS,
@@ -2419,6 +2453,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
        hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
        pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
                   hdw,hdw_desc->description);
+       pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
+               hdw_desc->description);
        if (!hdw) goto fail;
 
        init_timer(&hdw->quiescent_timer);
@@ -3480,7 +3516,7 @@ static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
 
 
 void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
-                               int prom_flag,
+                               int mode,
                                int enable_flag)
 {
        int ret;
@@ -3503,11 +3539,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
                        break;
                }
 
-               hdw->fw_cpu_flag = (prom_flag == 0);
+               hdw->fw_cpu_flag = (mode != 2);
                if (hdw->fw_cpu_flag) {
+                       hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
                        pvr2_trace(PVR2_TRACE_FIRMWARE,
-                                  "Preparing to suck out CPU firmware");
-                       hdw->fw_size = 0x2000;
+                                  "Preparing to suck out CPU firmware"
+                                  " (size=%u)", hdw->fw_size);
                        hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
                        if (!hdw->fw_buffer) {
                                hdw->fw_size = 0;
index 7b69405..56e70ea 100644 (file)
@@ -219,7 +219,7 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
    this may prevent the device from running (and leaving this mode may
    imply a device reset). */
 void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *,
-                               int prom_flag,
+                               int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */
                                int enable_flag);
 
 /* Return true if we're in a mode for retrieval CPU firmware */
index a334b1a..7cbe18c 100644 (file)
@@ -50,6 +50,7 @@ MODULE_PARM_DESC(disable_autoload_ir_video,
 
 /* Mapping of IR schemes to known I2C addresses - if any */
 static const unsigned char ir_video_addresses[] = {
+       [PVR2_IR_SCHEME_ZILOG] = 0x71,
        [PVR2_IR_SCHEME_29XXX] = 0x18,
        [PVR2_IR_SCHEME_24XXX] = 0x18,
 };
index 2d8825e..6aa48e0 100644 (file)
@@ -913,6 +913,15 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
 }
 
 
+static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
+{
+       if (!dip) return;
+       if (!dip->devbase.parent) return;
+       dip->devbase.parent = NULL;
+       device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
+}
+
+
 static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
 {
        if (vp->dev_video) {
@@ -943,6 +952,8 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
        struct pvr2_v4l2 *vp;
        vp = container_of(chp,struct pvr2_v4l2,channel);
        if (!vp->channel.mc_head->disconnect_flag) return;
+       pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
+       pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
        if (vp->vfirst) return;
        pvr2_v4l2_destroy_no_lock(vp);
 }
@@ -1250,12 +1261,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
                               struct pvr2_v4l2 *vp,
                               int v4l_type)
 {
+       struct usb_device *usbdev;
        int mindevnum;
        int unit_number;
        int *nr_ptr = NULL;
        dip->v4lp = vp;
 
-
+       usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
        dip->v4l_type = v4l_type;
        switch (v4l_type) {
        case VFL_TYPE_GRABBER:
@@ -1296,6 +1308,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
        if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
                mindevnum = nr_ptr[unit_number];
        }
+       dip->devbase.parent = &usbdev->dev;
        if ((video_register_device(&dip->devbase,
                                   dip->v4l_type, mindevnum) < 0) &&
            (video_register_device(&dip->devbase,
index f976df4..89b620f 100644 (file)
@@ -68,6 +68,7 @@
 #endif
 #include <linux/vmalloc.h>
 #include <asm/io.h>
+#include <linux/kernel.h>              /* simple_strtol() */
 
 #include "pwc.h"
 #include "pwc-kiara.h"
@@ -1916,19 +1917,6 @@ disconnect_out:
        unlock_kernel();
 }
 
-/* *grunt* We have to do atoi ourselves :-( */
-static int pwc_atoi(const char *s)
-{
-       int k = 0;
-
-       k = 0;
-       while (*s != '\0' && *s >= '0' && *s <= '9') {
-               k = 10 * k + (*s - '0');
-               s++;
-       }
-       return k;
-}
-
 
 /*
  * Initialization code & module stuff
@@ -2078,13 +2066,16 @@ static int __init usb_pwc_init(void)
                                }
                                else {
                                        /* No type or serial number specified, just a number. */
-                                       device_hint[i].device_node = pwc_atoi(s);
+                                       device_hint[i].device_node =
+                                               simple_strtol(s, NULL, 10);
                                }
                        }
                        else {
                                /* There's a colon, so we have at least a type and a device node */
-                               device_hint[i].type = pwc_atoi(s);
-                               device_hint[i].device_node = pwc_atoi(colon + 1);
+                               device_hint[i].type =
+                                       simple_strtol(s, NULL, 10);
+                               device_hint[i].device_node =
+                                       simple_strtol(colon + 1, NULL, 10);
                                if (*dot != '\0') {
                                        /* There's a serial number as well */
                                        int k;
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
new file mode 100644 (file)
index 0000000..373f2a3
--- /dev/null
@@ -0,0 +1,1219 @@
+/*
+ * Driver for RJ54N1CB0C CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/soc_camera.h>
+
+#define RJ54N1_DEV_CODE                        0x0400
+#define RJ54N1_DEV_CODE2               0x0401
+#define RJ54N1_OUT_SEL                 0x0403
+#define RJ54N1_XY_OUTPUT_SIZE_S_H      0x0404
+#define RJ54N1_X_OUTPUT_SIZE_S_L       0x0405
+#define RJ54N1_Y_OUTPUT_SIZE_S_L       0x0406
+#define RJ54N1_XY_OUTPUT_SIZE_P_H      0x0407
+#define RJ54N1_X_OUTPUT_SIZE_P_L       0x0408
+#define RJ54N1_Y_OUTPUT_SIZE_P_L       0x0409
+#define RJ54N1_LINE_LENGTH_PCK_S_H     0x040a
+#define RJ54N1_LINE_LENGTH_PCK_S_L     0x040b
+#define RJ54N1_LINE_LENGTH_PCK_P_H     0x040c
+#define RJ54N1_LINE_LENGTH_PCK_P_L     0x040d
+#define RJ54N1_RESIZE_N                        0x040e
+#define RJ54N1_RESIZE_N_STEP           0x040f
+#define RJ54N1_RESIZE_STEP             0x0410
+#define RJ54N1_RESIZE_HOLD_H           0x0411
+#define RJ54N1_RESIZE_HOLD_L           0x0412
+#define RJ54N1_H_OBEN_OFS              0x0413
+#define RJ54N1_V_OBEN_OFS              0x0414
+#define RJ54N1_RESIZE_CONTROL          0x0415
+#define RJ54N1_INC_USE_SEL_H           0x0425
+#define RJ54N1_INC_USE_SEL_L           0x0426
+#define RJ54N1_MIRROR_STILL_MODE       0x0427
+#define RJ54N1_INIT_START              0x0428
+#define RJ54N1_SCALE_1_2_LEV           0x0429
+#define RJ54N1_SCALE_4_LEV             0x042a
+#define RJ54N1_Y_GAIN                  0x04d8
+#define RJ54N1_APT_GAIN_UP             0x04fa
+#define RJ54N1_RA_SEL_UL               0x0530
+#define RJ54N1_BYTE_SWAP               0x0531
+#define RJ54N1_OUT_SIGPO               0x053b
+#define RJ54N1_FRAME_LENGTH_S_H                0x0595
+#define RJ54N1_FRAME_LENGTH_S_L                0x0596
+#define RJ54N1_FRAME_LENGTH_P_H                0x0597
+#define RJ54N1_FRAME_LENGTH_P_L                0x0598
+#define RJ54N1_IOC                     0x05ef
+#define RJ54N1_TG_BYPASS               0x0700
+#define RJ54N1_PLL_L                   0x0701
+#define RJ54N1_PLL_N                   0x0702
+#define RJ54N1_PLL_EN                  0x0704
+#define RJ54N1_RATIO_TG                        0x0706
+#define RJ54N1_RATIO_T                 0x0707
+#define RJ54N1_RATIO_R                 0x0708
+#define RJ54N1_RAMP_TGCLK_EN           0x0709
+#define RJ54N1_OCLK_DSP                        0x0710
+#define RJ54N1_RATIO_OP                        0x0711
+#define RJ54N1_RATIO_O                 0x0712
+#define RJ54N1_OCLK_SEL_EN             0x0713
+#define RJ54N1_CLK_RST                 0x0717
+#define RJ54N1_RESET_STANDBY           0x0718
+
+#define E_EXCLK                                (1 << 7)
+#define SOFT_STDBY                     (1 << 4)
+#define SEN_RSTX                       (1 << 2)
+#define TG_RSTX                                (1 << 1)
+#define DSP_RSTX                       (1 << 0)
+
+#define RESIZE_HOLD_SEL                        (1 << 2)
+#define RESIZE_GO                      (1 << 1)
+
+#define RJ54N1_COLUMN_SKIP             0
+#define RJ54N1_ROW_SKIP                        0
+#define RJ54N1_MAX_WIDTH               1600
+#define RJ54N1_MAX_HEIGHT              1200
+
+/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
+
+static const struct soc_camera_data_format rj54n1_colour_formats[] = {
+       {
+               .name           = "YUYV",
+               .depth          = 16,
+               .fourcc         = V4L2_PIX_FMT_YUYV,
+               .colorspace     = V4L2_COLORSPACE_JPEG,
+       }, {
+               .name           = "RGB565",
+               .depth          = 16,
+               .fourcc         = V4L2_PIX_FMT_RGB565,
+               .colorspace     = V4L2_COLORSPACE_SRGB,
+       }
+};
+
+struct rj54n1_clock_div {
+       u8 ratio_tg;
+       u8 ratio_t;
+       u8 ratio_r;
+       u8 ratio_op;
+       u8 ratio_o;
+};
+
+struct rj54n1 {
+       struct v4l2_subdev subdev;
+       struct v4l2_rect rect;  /* Sensor window */
+       unsigned short width;   /* Output window */
+       unsigned short height;
+       unsigned short resize;  /* Sensor * 1024 / resize = Output */
+       struct rj54n1_clock_div clk_div;
+       u32 fourcc;
+       unsigned short scale;
+       u8 bank;
+};
+
+struct rj54n1_reg_val {
+       u16 reg;
+       u8 val;
+};
+
+const static struct rj54n1_reg_val bank_4[] = {
+       {0x417, 0},
+       {0x42c, 0},
+       {0x42d, 0xf0},
+       {0x42e, 0},
+       {0x42f, 0x50},
+       {0x430, 0xf5},
+       {0x431, 0x16},
+       {0x432, 0x20},
+       {0x433, 0},
+       {0x434, 0xc8},
+       {0x43c, 8},
+       {0x43e, 0x90},
+       {0x445, 0x83},
+       {0x4ba, 0x58},
+       {0x4bb, 4},
+       {0x4bc, 0x20},
+       {0x4db, 4},
+       {0x4fe, 2},
+};
+
+const static struct rj54n1_reg_val bank_5[] = {
+       {0x514, 0},
+       {0x516, 0},
+       {0x518, 0},
+       {0x51a, 0},
+       {0x51d, 0xff},
+       {0x56f, 0x28},
+       {0x575, 0x40},
+       {0x5bc, 0x48},
+       {0x5c1, 6},
+       {0x5e5, 0x11},
+       {0x5e6, 0x43},
+       {0x5e7, 0x33},
+       {0x5e8, 0x21},
+       {0x5e9, 0x30},
+       {0x5ea, 0x0},
+       {0x5eb, 0xa5},
+       {0x5ec, 0xff},
+       {0x5fe, 2},
+};
+
+const static struct rj54n1_reg_val bank_7[] = {
+       {0x70a, 0},
+       {0x714, 0xff},
+       {0x715, 0xff},
+       {0x716, 0x1f},
+       {0x7FE, 0x02},
+};
+
+const static struct rj54n1_reg_val bank_8[] = {
+       {0x800, 0x00},
+       {0x801, 0x01},
+       {0x802, 0x61},
+       {0x805, 0x00},
+       {0x806, 0x00},
+       {0x807, 0x00},
+       {0x808, 0x00},
+       {0x809, 0x01},
+       {0x80A, 0x61},
+       {0x80B, 0x00},
+       {0x80C, 0x01},
+       {0x80D, 0x00},
+       {0x80E, 0x00},
+       {0x80F, 0x00},
+       {0x810, 0x00},
+       {0x811, 0x01},
+       {0x812, 0x61},
+       {0x813, 0x00},
+       {0x814, 0x11},
+       {0x815, 0x00},
+       {0x816, 0x41},
+       {0x817, 0x00},
+       {0x818, 0x51},
+       {0x819, 0x01},
+       {0x81A, 0x1F},
+       {0x81B, 0x00},
+       {0x81C, 0x01},
+       {0x81D, 0x00},
+       {0x81E, 0x11},
+       {0x81F, 0x00},
+       {0x820, 0x41},
+       {0x821, 0x00},
+       {0x822, 0x51},
+       {0x823, 0x00},
+       {0x824, 0x00},
+       {0x825, 0x00},
+       {0x826, 0x47},
+       {0x827, 0x01},
+       {0x828, 0x4F},
+       {0x829, 0x00},
+       {0x82A, 0x00},
+       {0x82B, 0x00},
+       {0x82C, 0x30},
+       {0x82D, 0x00},
+       {0x82E, 0x40},
+       {0x82F, 0x00},
+       {0x830, 0xB3},
+       {0x831, 0x00},
+       {0x832, 0xE3},
+       {0x833, 0x00},
+       {0x834, 0x00},
+       {0x835, 0x00},
+       {0x836, 0x00},
+       {0x837, 0x00},
+       {0x838, 0x00},
+       {0x839, 0x01},
+       {0x83A, 0x61},
+       {0x83B, 0x00},
+       {0x83C, 0x01},
+       {0x83D, 0x00},
+       {0x83E, 0x00},
+       {0x83F, 0x00},
+       {0x840, 0x00},
+       {0x841, 0x01},
+       {0x842, 0x61},
+       {0x843, 0x00},
+       {0x844, 0x1D},
+       {0x845, 0x00},
+       {0x846, 0x00},
+       {0x847, 0x00},
+       {0x848, 0x00},
+       {0x849, 0x01},
+       {0x84A, 0x1F},
+       {0x84B, 0x00},
+       {0x84C, 0x05},
+       {0x84D, 0x00},
+       {0x84E, 0x19},
+       {0x84F, 0x01},
+       {0x850, 0x21},
+       {0x851, 0x01},
+       {0x852, 0x5D},
+       {0x853, 0x00},
+       {0x854, 0x00},
+       {0x855, 0x00},
+       {0x856, 0x19},
+       {0x857, 0x01},
+       {0x858, 0x21},
+       {0x859, 0x00},
+       {0x85A, 0x00},
+       {0x85B, 0x00},
+       {0x85C, 0x00},
+       {0x85D, 0x00},
+       {0x85E, 0x00},
+       {0x85F, 0x00},
+       {0x860, 0xB3},
+       {0x861, 0x00},
+       {0x862, 0xE3},
+       {0x863, 0x00},
+       {0x864, 0x00},
+       {0x865, 0x00},
+       {0x866, 0x00},
+       {0x867, 0x00},
+       {0x868, 0x00},
+       {0x869, 0xE2},
+       {0x86A, 0x00},
+       {0x86B, 0x01},
+       {0x86C, 0x06},
+       {0x86D, 0x00},
+       {0x86E, 0x00},
+       {0x86F, 0x00},
+       {0x870, 0x60},
+       {0x871, 0x8C},
+       {0x872, 0x10},
+       {0x873, 0x00},
+       {0x874, 0xE0},
+       {0x875, 0x00},
+       {0x876, 0x27},
+       {0x877, 0x01},
+       {0x878, 0x00},
+       {0x879, 0x00},
+       {0x87A, 0x00},
+       {0x87B, 0x03},
+       {0x87C, 0x00},
+       {0x87D, 0x00},
+       {0x87E, 0x00},
+       {0x87F, 0x00},
+       {0x880, 0x00},
+       {0x881, 0x00},
+       {0x882, 0x00},
+       {0x883, 0x00},
+       {0x884, 0x00},
+       {0x885, 0x00},
+       {0x886, 0xF8},
+       {0x887, 0x00},
+       {0x888, 0x03},
+       {0x889, 0x00},
+       {0x88A, 0x64},
+       {0x88B, 0x00},
+       {0x88C, 0x03},
+       {0x88D, 0x00},
+       {0x88E, 0xB1},
+       {0x88F, 0x00},
+       {0x890, 0x03},
+       {0x891, 0x01},
+       {0x892, 0x1D},
+       {0x893, 0x00},
+       {0x894, 0x03},
+       {0x895, 0x01},
+       {0x896, 0x4B},
+       {0x897, 0x00},
+       {0x898, 0xE5},
+       {0x899, 0x00},
+       {0x89A, 0x01},
+       {0x89B, 0x00},
+       {0x89C, 0x01},
+       {0x89D, 0x04},
+       {0x89E, 0xC8},
+       {0x89F, 0x00},
+       {0x8A0, 0x01},
+       {0x8A1, 0x01},
+       {0x8A2, 0x61},
+       {0x8A3, 0x00},
+       {0x8A4, 0x01},
+       {0x8A5, 0x00},
+       {0x8A6, 0x00},
+       {0x8A7, 0x00},
+       {0x8A8, 0x00},
+       {0x8A9, 0x00},
+       {0x8AA, 0x7F},
+       {0x8AB, 0x03},
+       {0x8AC, 0x00},
+       {0x8AD, 0x00},
+       {0x8AE, 0x00},
+       {0x8AF, 0x00},
+       {0x8B0, 0x00},
+       {0x8B1, 0x00},
+       {0x8B6, 0x00},
+       {0x8B7, 0x01},
+       {0x8B8, 0x00},
+       {0x8B9, 0x00},
+       {0x8BA, 0x02},
+       {0x8BB, 0x00},
+       {0x8BC, 0xFF},
+       {0x8BD, 0x00},
+       {0x8FE, 0x02},
+};
+
+const static struct rj54n1_reg_val bank_10[] = {
+       {0x10bf, 0x69}
+};
+
+/* Clock dividers - these are default register values, divider = register + 1 */
+const static struct rj54n1_clock_div clk_div = {
+       .ratio_tg       = 3 /* default: 5 */,
+       .ratio_t        = 4 /* default: 1 */,
+       .ratio_r        = 4 /* default: 0 */,
+       .ratio_op       = 1 /* default: 5 */,
+       .ratio_o        = 9 /* default: 0 */,
+};
+
+static struct rj54n1 *to_rj54n1(const struct i2c_client *client)
+{
+       return container_of(i2c_get_clientdata(client), struct rj54n1, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u16 reg)
+{
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+       int ret;
+
+       /* set bank */
+       if (rj54n1->bank != reg >> 8) {
+               dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+               ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+               if (ret < 0)
+                       return ret;
+               rj54n1->bank = reg >> 8;
+       }
+       return i2c_smbus_read_byte_data(client, reg & 0xff);
+}
+
+static int reg_write(struct i2c_client *client, const u16 reg,
+                    const u8 data)
+{
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+       int ret;
+
+       /* set bank */
+       if (rj54n1->bank != reg >> 8) {
+               dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+               ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+               if (ret < 0)
+                       return ret;
+               rj54n1->bank = reg >> 8;
+       }
+       dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data);
+       return i2c_smbus_write_byte_data(client, reg & 0xff, data);
+}
+
+static int reg_set(struct i2c_client *client, const u16 reg,
+                  const u8 data, const u8 mask)
+{
+       int ret;
+
+       ret = reg_read(client, reg);
+       if (ret < 0)
+               return ret;
+       return reg_write(client, reg, (ret & ~mask) | (data & mask));
+}
+
+static int reg_write_multiple(struct i2c_client *client,
+                             const struct rj54n1_reg_val *rv, const int n)
+{
+       int i, ret;
+
+       for (i = 0; i < n; i++) {
+               ret = reg_write(client, rv->reg, rv->val);
+               if (ret < 0)
+                       return ret;
+               rv++;
+       }
+
+       return 0;
+}
+
+static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       /* TODO: start / stop streaming */
+       return 0;
+}
+
+static int rj54n1_set_bus_param(struct soc_camera_device *icd,
+                               unsigned long flags)
+{
+       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+       struct i2c_client *client = sd->priv;
+       /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+
+       if (flags & SOCAM_PCLK_SAMPLE_RISING)
+               return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+       else
+               return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
+static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd)
+{
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+       const unsigned long flags =
+               SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
+               SOCAM_MASTER | SOCAM_DATAWIDTH_8 |
+               SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+               SOCAM_DATA_ACTIVE_HIGH;
+
+       return soc_camera_apply_sensor_flags(icl, flags);
+}
+
+static int rj54n1_set_rect(struct i2c_client *client,
+                          u16 reg_x, u16 reg_y, u16 reg_xy,
+                          u32 width, u32 height)
+{
+       int ret;
+
+       ret = reg_write(client, reg_xy,
+                       ((width >> 4) & 0x70) |
+                       ((height >> 8) & 7));
+
+       if (!ret)
+               ret = reg_write(client, reg_x, width & 0xff);
+       if (!ret)
+               ret = reg_write(client, reg_y, height & 0xff);
+
+       return ret;
+}
+
+/*
+ * Some commands, specifically certain initialisation sequences, require
+ * a commit operation.
+ */
+static int rj54n1_commit(struct i2c_client *client)
+{
+       int ret = reg_write(client, RJ54N1_INIT_START, 1);
+       msleep(10);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_INIT_START, 0);
+       return ret;
+}
+
+static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+       struct i2c_client *client = sd->priv;
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+
+       a->c    = rj54n1->rect;
+       a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+       return 0;
+}
+
+static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+       a->bounds.left                  = RJ54N1_COLUMN_SKIP;
+       a->bounds.top                   = RJ54N1_ROW_SKIP;
+       a->bounds.width                 = RJ54N1_MAX_WIDTH;
+       a->bounds.height                = RJ54N1_MAX_HEIGHT;
+       a->defrect                      = a->bounds;
+       a->type                         = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       a->pixelaspect.numerator        = 1;
+       a->pixelaspect.denominator      = 1;
+
+       return 0;
+}
+
+static int rj54n1_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+       struct i2c_client *client = sd->priv;
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+
+       pix->pixelformat        = rj54n1->fourcc;
+       pix->field              = V4L2_FIELD_NONE;
+       pix->width              = rj54n1->width;
+       pix->height             = rj54n1->height;
+
+       return 0;
+}
+
+/*
+ * The actual geometry configuration routine. It scales the input window into
+ * the output one, updates the window sizes and returns an error or the resize
+ * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
+ */
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h,
+                              u32 *out_w, u32 *out_h)
+{
+       struct i2c_client *client = sd->priv;
+       unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
+               output_w = *out_w, output_h = *out_h;
+       u16 inc_sel;
+       int ret;
+
+       ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
+                             RJ54N1_Y_OUTPUT_SIZE_S_L,
+                             RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
+       if (!ret)
+               ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
+                             RJ54N1_Y_OUTPUT_SIZE_P_L,
+                             RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
+
+       if (ret < 0)
+               return ret;
+
+       if (output_w > input_w || output_h > input_h) {
+               input_w = output_w;
+               input_h = output_h;
+
+               resize = 1024;
+       } else {
+               unsigned int resize_x, resize_y;
+               resize_x = input_w * 1024 / output_w;
+               resize_y = input_h * 1024 / output_h;
+
+               resize = min(resize_x, resize_y);
+
+               /* Prohibited value ranges */
+               switch (resize) {
+               case 2040 ... 2047:
+                       resize = 2039;
+                       break;
+               case 4080 ... 4095:
+                       resize = 4079;
+                       break;
+               case 8160 ... 8191:
+                       resize = 8159;
+                       break;
+               case 16320 ... 16383:
+                       resize = 16319;
+               }
+
+               input_w = output_w * resize / 1024;
+               input_h = output_h * resize / 1024;
+       }
+
+       /* Set scaling */
+       ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
+
+       if (ret < 0)
+               return ret;
+
+       /*
+        * Configure a skipping bitmask. The sensor will select a skipping value
+        * among set bits automatically.
+        */
+       skip = min(resize / 1024, (unsigned)15);
+       inc_sel = 1 << skip;
+
+       if (inc_sel <= 2)
+               inc_sel = 0xc;
+       else if (resize & 1023 && skip < 15)
+               inc_sel |= 1 << (skip + 1);
+
+       ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
+
+       /* Start resizing */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+                               RESIZE_HOLD_SEL | RESIZE_GO | 1);
+
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(&client->dev, "resize %u, skip %u\n", resize, skip);
+
+       /* Constant taken from manufacturer's example */
+       msleep(230);
+
+       ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1);
+       if (ret < 0)
+               return ret;
+
+       *in_w = input_w;
+       *in_h = input_h;
+       *out_w = output_w;
+       *out_h = output_h;
+
+       return resize;
+}
+
+static int rj54n1_set_clock(struct i2c_client *client)
+{
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+       int ret;
+
+       /* Enable external clock */
+       ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
+       /* Leave stand-by */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
+
+       if (!ret)
+               ret = reg_write(client, RJ54N1_PLL_L, 2);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_PLL_N, 0x31);
+
+       /* TGCLK dividers */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RATIO_TG,
+                               rj54n1->clk_div.ratio_tg);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RATIO_T,
+                               rj54n1->clk_div.ratio_t);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RATIO_R,
+                               rj54n1->clk_div.ratio_r);
+
+       /* Enable TGCLK & RAMP */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
+
+       /* Disable clock output */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
+
+       /* Set divisors */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RATIO_OP,
+                               rj54n1->clk_div.ratio_op);
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RATIO_O,
+                               rj54n1->clk_div.ratio_o);
+
+       /* Enable OCLK */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+       /* Use PLL for Timing Generator, write 2 to reserved bits */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
+
+       /* Take sensor out of reset */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESET_STANDBY,
+                               E_EXCLK | SEN_RSTX);
+       /* Enable PLL */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_PLL_EN, 1);
+
+       /* Wait for PLL to stabilise */
+       msleep(10);
+
+       /* Enable clock to frequency divider */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_CLK_RST, 1);
+
+       if (!ret)
+               ret = reg_read(client, RJ54N1_CLK_RST);
+       if (ret != 1) {
+               dev_err(&client->dev,
+                       "Resetting RJ54N1CB0C clock failed: %d!\n", ret);
+               return -EIO;
+       }
+       /* Start the PLL */
+       ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
+
+       /* Enable OCLK */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+       return ret;
+}
+
+static int rj54n1_reg_init(struct i2c_client *client)
+{
+       int ret = rj54n1_set_clock(client);
+
+       if (!ret)
+               ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7));
+       if (!ret)
+               ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
+
+       /* Set binning divisors */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4));
+       if (!ret)
+               ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
+
+       /* Switch to fixed resize mode */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+                               RESIZE_HOLD_SEL | 1);
+
+       /* Set gain */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
+
+       /* Mirror the image back: default is upside down and left-to-right... */
+       if (!ret)
+               ret = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 3, 3);
+
+       if (!ret)
+               ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
+       if (!ret)
+               ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
+       if (!ret)
+               ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
+
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESET_STANDBY,
+                               E_EXCLK | DSP_RSTX | SEN_RSTX);
+
+       /* Commit init */
+       if (!ret)
+               ret = rj54n1_commit(client);
+
+       /* Take DSP, TG, sensor out of reset */
+       if (!ret)
+               ret = reg_write(client, RJ54N1_RESET_STANDBY,
+                               E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
+
+       if (!ret)
+               ret = reg_write(client, 0x7fe, 2);
+
+       /* Constant taken from manufacturer's example */
+       msleep(700);
+
+       return ret;
+}
+
+/* FIXME: streaming output only up to 800x600 is functional */
+static int rj54n1_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+
+       pix->field = V4L2_FIELD_NONE;
+
+       if (pix->width > 800)
+               pix->width = 800;
+       if (pix->height > 600)
+               pix->height = 600;
+
+       return 0;
+}
+
+static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+       struct i2c_client *client = sd->priv;
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+       struct v4l2_pix_format *pix = &f->fmt.pix;
+       unsigned int output_w, output_h,
+               input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
+       int ret;
+
+       /*
+        * The host driver can call us without .try_fmt(), so, we have to take
+        * care ourseleves
+        */
+       ret = rj54n1_try_fmt(sd, f);
+
+       /*
+        * Verify if the sensor has just been powered on. TODO: replace this
+        * with proper PM, when a suitable API is available.
+        */
+       if (!ret)
+               ret = reg_read(client, RJ54N1_RESET_STANDBY);
+       if (ret < 0)
+               return ret;
+
+       if (!(ret & E_EXCLK)) {
+               ret = rj54n1_reg_init(client);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
+       switch (pix->pixelformat) {
+       case V4L2_PIX_FMT_YUYV:
+               ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+               if (!ret)
+                       ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+               break;
+       case V4L2_PIX_FMT_RGB565:
+               ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+               if (!ret)
+                       ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       /* Supported scales 1:1 - 1:16 */
+       if (pix->width < input_w / 16)
+               pix->width = input_w / 16;
+       if (pix->height < input_h / 16)
+               pix->height = input_h / 16;
+
+       output_w = pix->width;
+       output_h = pix->height;
+
+       ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+       if (ret < 0)
+               return ret;
+
+       rj54n1->fourcc          = pix->pixelformat;
+       rj54n1->resize          = ret;
+       rj54n1->rect.width      = input_w;
+       rj54n1->rect.height     = input_h;
+       rj54n1->width           = output_w;
+       rj54n1->height          = output_h;
+
+       pix->width              = output_w;
+       pix->height             = output_h;
+       pix->field              = V4L2_FIELD_NONE;
+
+       return ret;
+}
+
+static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
+                              struct v4l2_dbg_chip_ident *id)
+{
+       struct i2c_client *client = sd->priv;
+
+       if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+               return -EINVAL;
+
+       if (id->match.addr != client->addr)
+               return -ENODEV;
+
+       id->ident       = V4L2_IDENT_RJ54N1CB0C;
+       id->revision    = 0;
+
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int rj54n1_g_register(struct v4l2_subdev *sd,
+                            struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = sd->priv;
+
+       if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+           reg->reg < 0x400 || reg->reg > 0x1fff)
+               /* Registers > 0x0800 are only available from Sharp support */
+               return -EINVAL;
+
+       if (reg->match.addr != client->addr)
+               return -ENODEV;
+
+       reg->size = 1;
+       reg->val = reg_read(client, reg->reg);
+
+       if (reg->val > 0xff)
+               return -EIO;
+
+       return 0;
+}
+
+static int rj54n1_s_register(struct v4l2_subdev *sd,
+                            struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = sd->priv;
+
+       if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+           reg->reg < 0x400 || reg->reg > 0x1fff)
+               /* Registers >= 0x0800 are only available from Sharp support */
+               return -EINVAL;
+
+       if (reg->match.addr != client->addr)
+               return -ENODEV;
+
+       if (reg_write(client, reg->reg, reg->val) < 0)
+               return -EIO;
+
+       return 0;
+}
+#endif
+
+static const struct v4l2_queryctrl rj54n1_controls[] = {
+       {
+               .id             = V4L2_CID_VFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Flip Vertically",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+       }, {
+               .id             = V4L2_CID_HFLIP,
+               .type           = V4L2_CTRL_TYPE_BOOLEAN,
+               .name           = "Flip Horizontally",
+               .minimum        = 0,
+               .maximum        = 1,
+               .step           = 1,
+               .default_value  = 0,
+       }, {
+               .id             = V4L2_CID_GAIN,
+               .type           = V4L2_CTRL_TYPE_INTEGER,
+               .name           = "Gain",
+               .minimum        = 0,
+               .maximum        = 127,
+               .step           = 1,
+               .default_value  = 66,
+               .flags          = V4L2_CTRL_FLAG_SLIDER,
+       },
+};
+
+static struct soc_camera_ops rj54n1_ops = {
+       .set_bus_param          = rj54n1_set_bus_param,
+       .query_bus_param        = rj54n1_query_bus_param,
+       .controls               = rj54n1_controls,
+       .num_controls           = ARRAY_SIZE(rj54n1_controls),
+};
+
+static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct i2c_client *client = sd->priv;
+       int data;
+
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
+               if (data < 0)
+                       return -EIO;
+               ctrl->value = !(data & 1);
+               break;
+       case V4L2_CID_HFLIP:
+               data = reg_read(client, RJ54N1_MIRROR_STILL_MODE);
+               if (data < 0)
+                       return -EIO;
+               ctrl->value = !(data & 2);
+               break;
+       case V4L2_CID_GAIN:
+               data = reg_read(client, RJ54N1_Y_GAIN);
+               if (data < 0)
+                       return -EIO;
+
+               ctrl->value = data / 2;
+               break;
+       }
+
+       return 0;
+}
+
+static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       int data;
+       struct i2c_client *client = sd->priv;
+       const struct v4l2_queryctrl *qctrl;
+
+       qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id);
+       if (!qctrl)
+               return -EINVAL;
+
+       switch (ctrl->id) {
+       case V4L2_CID_VFLIP:
+               if (ctrl->value)
+                       data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
+               else
+                       data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
+               if (data < 0)
+                       return -EIO;
+               break;
+       case V4L2_CID_HFLIP:
+               if (ctrl->value)
+                       data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
+               else
+                       data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
+               if (data < 0)
+                       return -EIO;
+               break;
+       case V4L2_CID_GAIN:
+               if (ctrl->value > qctrl->maximum ||
+                   ctrl->value < qctrl->minimum)
+                       return -EINVAL;
+               else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0)
+                       return -EIO;
+               break;
+       }
+
+       return 0;
+}
+
+static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
+       .g_ctrl         = rj54n1_g_ctrl,
+       .s_ctrl         = rj54n1_s_ctrl,
+       .g_chip_ident   = rj54n1_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .g_register     = rj54n1_g_register,
+       .s_register     = rj54n1_s_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
+       .s_stream       = rj54n1_s_stream,
+       .s_fmt          = rj54n1_s_fmt,
+       .g_fmt          = rj54n1_g_fmt,
+       .try_fmt        = rj54n1_try_fmt,
+       .g_crop         = rj54n1_g_crop,
+       .cropcap        = rj54n1_cropcap,
+};
+
+static struct v4l2_subdev_ops rj54n1_subdev_ops = {
+       .core   = &rj54n1_subdev_core_ops,
+       .video  = &rj54n1_subdev_video_ops,
+};
+
+static int rj54n1_pin_config(struct i2c_client *client)
+{
+       /*
+        * Experimentally found out IOCTRL wired to 0. TODO: add to platform
+        * data: 0 or 1 << 7.
+        */
+       return reg_write(client, RJ54N1_IOC, 0);
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int rj54n1_video_probe(struct soc_camera_device *icd,
+                             struct i2c_client *client)
+{
+       int data1, data2;
+       int ret;
+
+       /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */
+       if (!icd->dev.parent ||
+           to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+               return -ENODEV;
+
+       /* Read out the chip version register */
+       data1 = reg_read(client, RJ54N1_DEV_CODE);
+       data2 = reg_read(client, RJ54N1_DEV_CODE2);
+
+       if (data1 != 0x51 || data2 != 0x10) {
+               ret = -ENODEV;
+               dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
+                        data1, data2);
+               goto ei2c;
+       }
+
+       ret = rj54n1_pin_config(client);
+       if (ret < 0)
+               goto ei2c;
+
+       dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
+                data1, data2);
+
+ei2c:
+       return ret;
+}
+
+static int rj54n1_probe(struct i2c_client *client,
+                       const struct i2c_device_id *did)
+{
+       struct rj54n1 *rj54n1;
+       struct soc_camera_device *icd = client->dev.platform_data;
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct soc_camera_link *icl;
+       int ret;
+
+       if (!icd) {
+               dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n");
+               return -EINVAL;
+       }
+
+       icl = to_soc_camera_link(icd);
+       if (!icl) {
+               dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
+               return -EINVAL;
+       }
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_warn(&adapter->dev,
+                        "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+               return -EIO;
+       }
+
+       rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL);
+       if (!rj54n1)
+               return -ENOMEM;
+
+       v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+
+       icd->ops                = &rj54n1_ops;
+
+       rj54n1->clk_div         = clk_div;
+       rj54n1->rect.left       = RJ54N1_COLUMN_SKIP;
+       rj54n1->rect.top        = RJ54N1_ROW_SKIP;
+       rj54n1->rect.width      = RJ54N1_MAX_WIDTH;
+       rj54n1->rect.height     = RJ54N1_MAX_HEIGHT;
+       rj54n1->width           = RJ54N1_MAX_WIDTH;
+       rj54n1->height          = RJ54N1_MAX_HEIGHT;
+       rj54n1->fourcc          = V4L2_PIX_FMT_YUYV;
+       rj54n1->resize          = 1024;
+
+       ret = rj54n1_video_probe(icd, client);
+       if (ret < 0) {
+               icd->ops = NULL;
+               i2c_set_clientdata(client, NULL);
+               kfree(rj54n1);
+               return ret;
+       }
+
+       icd->formats            = rj54n1_colour_formats;
+       icd->num_formats        = ARRAY_SIZE(rj54n1_colour_formats);
+
+       return ret;
+}
+
+static int rj54n1_remove(struct i2c_client *client)
+{
+       struct rj54n1 *rj54n1 = to_rj54n1(client);
+       struct soc_camera_device *icd = client->dev.platform_data;
+       struct soc_camera_link *icl = to_soc_camera_link(icd);
+
+       icd->ops = NULL;
+       if (icl->free_bus)
+               icl->free_bus(icl);
+       i2c_set_clientdata(client, NULL);
+       client->driver = NULL;
+       kfree(rj54n1);
+
+       return 0;
+}
+
+static const struct i2c_device_id rj54n1_id[] = {
+       { "rj54n1cb0c", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rj54n1_id);
+
+static struct i2c_driver rj54n1_i2c_driver = {
+       .driver = {
+               .name = "rj54n1cb0c",
+       },
+       .probe          = rj54n1_probe,
+       .remove         = rj54n1_remove,
+       .id_table       = rj54n1_id,
+};
+
+static int __init rj54n1_mod_init(void)
+{
+       return i2c_add_driver(&rj54n1_i2c_driver);
+}
+
+static void __exit rj54n1_mod_exit(void)
+{
+       i2c_del_driver(&rj54n1_i2c_driver);
+}
+
+module_init(rj54n1_mod_init);
+module_exit(rj54n1_mod_exit);
+
+MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
index 2c0bb06..41765f3 100644 (file)
@@ -1958,7 +1958,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
                                if (pdword[1] >= MAX_CHANNELS)
                                        break;
                                cc = G_chnmap[pdword[1]];
-                               if (!(cc >= 0 && cc < MAX_CHANNELS))
+                               if (cc >= MAX_CHANNELS)
                                        break;
                                switch (pdword[2]) {
                                case S2255_RESPONSE_SETMODE:
@@ -1980,7 +1980,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
                                        wake_up(&dev->fw_data->wait_fw);
                                        break;
                                default:
-                                       printk(KERN_INFO "s2255 unknwn resp\n");
+                                       printk(KERN_INFO "s2255 unknown resp\n");
                                }
                        default:
                                pdata++;
index 5c24c99..3bca744 100644 (file)
@@ -304,7 +304,7 @@ static int saa7110_s_routing(struct v4l2_subdev *sd,
 {
        struct saa7110 *decoder = to_saa7110(sd);
 
-       if (input < 0 || input >= SAA7110_MAX_INPUT) {
+       if (input >= SAA7110_MAX_INPUT) {
                v4l2_dbg(1, debug, sd, "input=%d not available\n", input);
                return -EINVAL;
        }
index 0901322..7e40d6d 100644 (file)
@@ -5239,6 +5239,7 @@ struct saa7134_board saa7134_boards[] = {
                .radio_type     = UNSET,
                .tuner_addr     = ADDR_UNSET,
                .radio_addr     = ADDR_UNSET,
+               .mpeg           = SAA7134_MPEG_DVB,
                .inputs         = { {
                        .name = name_tv,
                        .vmux = 2,
@@ -5279,6 +5280,46 @@ struct saa7134_board saa7134_boards[] = {
                        .amux = TV,
                },
        },
+       [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = {
+               .name           = "Asus Europa Hybrid OEM",
+               .audio_clock    = 0x00187de7,
+               .tuner_type     = TUNER_PHILIPS_TD1316,
+               .radio_type     = UNSET,
+               .tuner_addr     = 0x61,
+               .radio_addr     = ADDR_UNSET,
+               .tda9887_conf   = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE,
+               .mpeg           = SAA7134_MPEG_DVB,
+               .inputs = { {
+                       .name   = name_tv,
+                       .vmux   = 3,
+                       .amux   = TV,
+                       .tv     = 1,
+               }, {
+                       .name   = name_comp1,
+                       .vmux   = 4,
+                       .amux   = LINE2,
+               }, {
+                       .name   = name_svideo,
+                       .vmux   = 8,
+                       .amux   = LINE2,
+               } },
+       },
+       [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = {
+               .name           = "Leadtek Winfast DTV1000S",
+               .audio_clock    = 0x00187de7,
+               .tuner_type     = TUNER_PHILIPS_TDA8290,
+               .radio_type     = UNSET,
+               .tuner_addr     = ADDR_UNSET,
+               .radio_addr     = ADDR_UNSET,
+               .mpeg           = SAA7134_MPEG_DVB,
+               .inputs         = { {
+                       .name = name_comp1,
+                       .vmux = 3,
+               }, {
+                       .name = name_svideo,
+                       .vmux = 8,
+               } },
+       },
 
 };
 
@@ -6417,6 +6458,18 @@ struct pci_device_id saa7134_pci_tbl[] = {
                .subvendor    = PCI_VENDOR_ID_PHILIPS,
                .subdevice    = 0x2004,
                .driver_data  = SAA7134_BOARD_ZOLID_HYBRID_PCI,
+       }, {
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7134,
+               .subvendor    = 0x1043,
+               .subdevice    = 0x4847,
+               .driver_data  = SAA7134_BOARD_ASUS_EUROPA_HYBRID,
+       }, {
+               .vendor       = PCI_VENDOR_ID_PHILIPS,
+               .device       = PCI_DEVICE_ID_PHILIPS_SAA7130,
+               .subvendor    = 0x107d,
+               .subdevice    = 0x6655,
+               .driver_data  = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S,
        }, {
                /* --- boards without eeprom + subsystem ID --- */
                .vendor       = PCI_VENDOR_ID_PHILIPS,
@@ -6748,6 +6801,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
        case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
        case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
        case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+       case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
                dev->has_remote = SAA7134_REMOTE_GPIO;
                break;
        case SAA7134_BOARD_FLYDVBS_LR300:
@@ -7079,6 +7133,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
                /* break intentionally omitted */
        case SAA7134_BOARD_VIDEOMATE_DVBT_300:
        case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+       case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
        {
 
                /* The Philips EUROPA based hybrid boards have the tuner
index f87757f..0ba7f5a 100644 (file)
@@ -1032,7 +1032,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
        saa7134_irq_video_signalchange(dev);
 
        if (TUNER_ABSENT != dev->tuner_type)
-               saa_call_all(dev, tuner, s_standby);
+               saa_call_all(dev, core, s_power, 0);
 
        /* register v4l devices */
        if (saa7134_no_overlay > 0)
@@ -1319,7 +1319,7 @@ static struct pci_driver saa7134_pci_driver = {
 #endif
 };
 
-static int saa7134_init(void)
+static int __init saa7134_init(void)
 {
        INIT_LIST_HEAD(&saa7134_devlist);
        printk(KERN_INFO "saa7130/34: v4l2 driver version %d.%d.%d loaded\n",
@@ -1333,7 +1333,7 @@ static int saa7134_init(void)
        return pci_register_driver(&saa7134_pci_driver);
 }
 
-static void saa7134_fini(void)
+static void __exit saa7134_fini(void)
 {
        pci_unregister_driver(&saa7134_pci_driver);
 }
index a26e997..73739d2 100644 (file)
@@ -40,6 +40,7 @@
 #include "tda1004x.h"
 #include "nxt200x.h"
 #include "tuner-xc2028.h"
+#include "xc5000.h"
 
 #include "tda10086.h"
 #include "tda826x.h"
@@ -871,6 +872,20 @@ static struct zl10353_config behold_h6_config = {
        .disable_i2c_gate_ctrl = 1,
 };
 
+static struct xc5000_config behold_x7_tunerconfig = {
+       .i2c_address      = 0xc2>>1,
+       .if_khz           = 4560,
+       .radio_input      = XC5000_RADIO_FM1,
+};
+
+static struct zl10353_config behold_x7_config = {
+       .demod_address = 0x1e>>1,
+       .if2           = 45600,
+       .no_tuner      = 1,
+       .parallel_ts   = 1,
+       .disable_i2c_gate_ctrl = 1,
+};
+
 /* ==================================================================
  * tda10086 based DVB-S cards, helper functions
  */
@@ -1030,6 +1045,32 @@ static struct tda18271_config zolid_tda18271_config = {
        .gate    = TDA18271_GATE_ANALOG,
 };
 
+static struct tda10048_config dtv1000s_tda10048_config = {
+       .demod_address    = 0x10 >> 1,
+       .output_mode      = TDA10048_PARALLEL_OUTPUT,
+       .fwbulkwritelen   = TDA10048_BULKWRITE_200,
+       .inversion        = TDA10048_INVERSION_ON,
+       .dtv6_if_freq_khz = TDA10048_IF_3300,
+       .dtv7_if_freq_khz = TDA10048_IF_3800,
+       .dtv8_if_freq_khz = TDA10048_IF_4300,
+       .clk_freq_khz     = TDA10048_CLK_16000,
+       .disable_gate_access = 1,
+};
+
+static struct tda18271_std_map dtv1000s_tda18271_std_map = {
+       .dvbt_6   = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+                     .if_lvl = 1, .rfagc_top = 0x37, },
+       .dvbt_7   = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+                     .if_lvl = 1, .rfagc_top = 0x37, },
+       .dvbt_8   = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+                     .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config dtv1000s_tda18271_config = {
+       .std_map = &dtv1000s_tda18271_std_map,
+       .gate    = TDA18271_GATE_ANALOG,
+};
+
 /* ==================================================================
  * Core code
  */
@@ -1116,6 +1157,7 @@ static int dvb_init(struct saa7134_dev *dev)
                break;
        case SAA7134_BOARD_PHILIPS_EUROPA:
        case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+       case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
                fe0->dvb.frontend = dvb_attach(tda10046_attach,
                                               &philips_europa_config,
                                               &dev->i2c_adap);
@@ -1482,6 +1524,15 @@ static int dvb_init(struct saa7134_dev *dev)
                                   TUNER_PHILIPS_FMD1216MEX_MK3);
                }
                break;
+       case SAA7134_BOARD_BEHOLD_X7:
+               fe0->dvb.frontend = dvb_attach(zl10353_attach,
+                                               &behold_x7_config,
+                                               &dev->i2c_adap);
+               if (fe0->dvb.frontend) {
+                       dvb_attach(xc5000_attach, fe0->dvb.frontend,
+                                  &dev->i2c_adap, &behold_x7_tunerconfig);
+               }
+               break;
        case SAA7134_BOARD_AVERMEDIA_A700_PRO:
        case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
                /* Zarlink ZL10313 */
@@ -1518,6 +1569,19 @@ static int dvb_init(struct saa7134_dev *dev)
                                   &zolid_tda18271_config);
                }
                break;
+       case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+               fe0->dvb.frontend = dvb_attach(tda10048_attach,
+                                              &dtv1000s_tda10048_config,
+                                              &dev->i2c_adap);
+               if (fe0->dvb.frontend != NULL) {
+                       dvb_attach(tda829x_attach, fe0->dvb.frontend,
+                                  &dev->i2c_adap, 0x4b,
+                                  &tda829x_no_probe);
+                       dvb_attach(tda18271_attach, fe0->dvb.frontend,
+                                  0x60, &dev->i2c_adap,
+                                  &dtv1000s_tda18271_config);
+               }
+               break;
        default:
                wprintk("Huh? unknown DVB card?\n");
                break;
@@ -1550,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev)
 
        /* register everything else */
        ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
-               &dev->pci->dev, adapter_nr, 0);
+                                       &dev->pci->dev, adapter_nr, 0, NULL);
 
        /* this sequence is necessary to make the tda1004x load its firmware
         * and to enter analog mode of hybrid boards
index a0e8c62..744918b 100644 (file)
@@ -102,14 +102,14 @@ static int build_key(struct saa7134_dev *dev)
                if (data == ir->mask_keycode)
                        ir_input_nokey(ir->dev, &ir->ir);
                else
-                       ir_input_keydown(ir->dev, &ir->ir, data, data);
+                       ir_input_keydown(ir->dev, &ir->ir, data);
                return 0;
        }
 
        if (ir->polling) {
                if ((ir->mask_keydown  &&  (0 != (gpio & ir->mask_keydown))) ||
                    (ir->mask_keyup    &&  (0 == (gpio & ir->mask_keyup)))) {
-                       ir_input_keydown(ir->dev, &ir->ir, data, data);
+                       ir_input_keydown(ir->dev, &ir->ir, data);
                } else {
                        ir_input_nokey(ir->dev, &ir->ir);
                }
@@ -117,7 +117,7 @@ static int build_key(struct saa7134_dev *dev)
        else {  /* IRQ driven mode - handle key press and release in one go */
                if ((ir->mask_keydown  &&  (0 != (gpio & ir->mask_keydown))) ||
                    (ir->mask_keyup    &&  (0 == (gpio & ir->mask_keyup)))) {
-                       ir_input_keydown(ir->dev, &ir->ir, data, data);
+                       ir_input_keydown(ir->dev, &ir->ir, data);
                        ir_input_nokey(ir->dev, &ir->ir);
                }
        }
@@ -616,6 +616,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
                mask_keycode = 0x003f00;
                mask_keydown = 0x040000;
                break;
+       case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+               ir_codes     = &ir_codes_winfast_table;
+               mask_keycode = 0x5f00;
+               mask_keyup   = 0x020000;
+               polling      = 50; /* ms */
+               break;
        }
        if (NULL == ir_codes) {
                printk("%s: Oops: IR config error [card=%d]\n",
@@ -646,7 +652,10 @@ int saa7134_input_init1(struct saa7134_dev *dev)
        snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
                 pci_name(dev->pci));
 
-       ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes);
+       if (err < 0)
+               goto err_out_free;
+
        input_dev->name = ir->name;
        input_dev->phys = ir->phys;
        input_dev->id.bustype = BUS_PCI;
@@ -677,6 +686,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
        saa7134_ir_stop(dev);
        dev->remote = NULL;
  err_out_free:
+       ir_input_free(input_dev);
        input_free_device(input_dev);
        kfree(ir);
        return err;
@@ -688,6 +698,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
                return;
 
        saa7134_ir_stop(dev);
+       ir_input_free(dev->remote->dev);
        input_unregister_device(dev->remote->dev);
        kfree(dev->remote);
        dev->remote = NULL;
@@ -695,10 +706,7 @@ void saa7134_input_fini(struct saa7134_dev *dev)
 
 void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
 {
-       const unsigned short addr_list[] = {
-               0x7a, 0x47, 0x71, 0x2d,
-               I2C_CLIENT_END
-       };
+       struct i2c_board_info info;
 
        struct i2c_msg msg_msi = {
                .addr = 0x50,
@@ -714,9 +722,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
                return;
        }
 
-       memset(&dev->info, 0, sizeof(dev->info));
+       memset(&info, 0, sizeof(struct i2c_board_info));
        memset(&dev->init_data, 0, sizeof(dev->init_data));
-       strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE);
+       strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
 
        switch (dev->board) {
        case SAA7134_BOARD_PINNACLE_PCTV_110i:
@@ -725,23 +733,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
                if (pinnacle_remote == 0) {
                        dev->init_data.get_key = get_key_pinnacle_color;
                        dev->init_data.ir_codes = &ir_codes_pinnacle_color_table;
-                       dev->info.addr = 0x47;
+                       info.addr = 0x47;
                } else {
                        dev->init_data.get_key = get_key_pinnacle_grey;
                        dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table;
-                       dev->info.addr = 0x47;
+                       info.addr = 0x47;
                }
                break;
        case SAA7134_BOARD_UPMOST_PURPLE_TV:
                dev->init_data.name = "Purple TV";
                dev->init_data.get_key = get_key_purpletv;
                dev->init_data.ir_codes = &ir_codes_purpletv_table;
+               info.addr = 0x7a;
                break;
        case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
                dev->init_data.name = "MSI TV@nywhere Plus";
                dev->init_data.get_key = get_key_msi_tvanywhere_plus;
                dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table;
-               dev->info.addr = 0x30;
+               info.addr = 0x30;
                /* MSI TV@nywhere Plus controller doesn't seem to
                   respond to probes unless we read something from
                   an existing device. Weird...
@@ -755,6 +764,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
                dev->init_data.name = "HVR 1110";
                dev->init_data.get_key = get_key_hvr1110;
                dev->init_data.ir_codes = &ir_codes_hauppauge_new_table;
+               info.addr = 0x71;
                break;
        case SAA7134_BOARD_BEHOLD_607FM_MK3:
        case SAA7134_BOARD_BEHOLD_607FM_MK5:
@@ -772,23 +782,20 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
                dev->init_data.name = "BeholdTV";
                dev->init_data.get_key = get_key_beholdm6xx;
                dev->init_data.ir_codes = &ir_codes_behold_table;
+               info.addr = 0x2d;
                break;
        case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
        case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
-               dev->info.addr = 0x40;
+               info.addr = 0x40;
                break;
-       }
-
-       if (dev->init_data.name)
-               dev->info.platform_data = &dev->init_data;
-       /* No need to probe if address is known */
-       if (dev->info.addr) {
-               i2c_new_device(&dev->i2c_adap, &dev->info);
+       default:
+               dprintk("No I2C IR support for board %x\n", dev->board);
                return;
        }
 
-       /* Address not known, fallback to probing */
-       i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list);
+       if (dev->init_data.name)
+               info.platform_data = &dev->init_data;
+       i2c_new_device(&dev->i2c_adap, &info);
 }
 
 static int saa7134_rc5_irq(struct saa7134_dev *dev)
@@ -936,7 +943,7 @@ static void nec_task(unsigned long data)
                dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n",
                         ir->code, ircode, not_code);
 
-               ir_input_keydown(ir->dev, &ir->ir, ir->code, ir->code);
+               ir_input_keydown(ir->dev, &ir->ir, ir->code);
        } else
                dprintk("Repeat last key\n");
 
index da26f47..35f8daa 100644 (file)
@@ -1499,7 +1499,7 @@ static int video_release(struct file *file)
        saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
        saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
 
-       saa_call_all(dev, tuner, s_standby);
+       saa_call_all(dev, core, s_power, 0);
        if (fh->radio)
                saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd);
 
index f8697d4..53b7e0b 100644 (file)
@@ -297,6 +297,8 @@ struct saa7134_format {
 #define SAA7134_BOARD_BEHOLD_X7             171
 #define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172
 #define SAA7134_BOARD_ZOLID_HYBRID_PCI         173
+#define SAA7134_BOARD_ASUS_EUROPA_HYBRID       174
+#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
 
 #define SAA7134_MAXBOARDS 32
 #define SAA7134_INPUT_MAX 8
@@ -592,7 +594,6 @@ struct saa7134_dev {
        unsigned int               insuspend;
 
        /* I2C keyboard data */
-       struct i2c_board_info      info;
        struct IR_i2c_init_data    init_data;
 
        /* SAA7134_MPEG_* */
index 709affc..e6aa0fb 100644 (file)
@@ -724,13 +724,13 @@ static struct pci_driver saa7164_pci_driver = {
        .resume   = NULL,
 };
 
-static int saa7164_init(void)
+static int __init saa7164_init(void)
 {
        printk(KERN_INFO "saa7164 driver loaded\n");
        return pci_register_driver(&saa7164_pci_driver);
 }
 
-static void saa7164_fini(void)
+static void __exit saa7164_fini(void)
 {
        pci_unregister_driver(&saa7164_pci_driver);
 }
index 6a2d847..cf099c5 100644 (file)
@@ -68,6 +68,7 @@ static struct tda18271_config hauppauge_hvr22x0s_tuner_config = {
        .std_map        = &hauppauge_tda18271_std_map,
        .gate           = TDA18271_GATE_ANALOG,
        .role           = TDA18271_SLAVE,
+       .output_opt     = TDA18271_OUTPUT_LT_OFF,
        .rf_cal_on_startup = 1
 };
 
index b15c409..6818df5 100644 (file)
@@ -1115,7 +1115,7 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd,
        v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input);
        /* inputs from 0-9 are available*/
        /* saa717x have mode0-mode9 but mode5 is reserved. */
-       if (input < 0 || input > 9 || input == 5)
+       if (input > 9 || input == 5)
                return -EINVAL;
 
        if (decoder->input != input) {
@@ -1312,7 +1312,7 @@ static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
                "MONO", "STEREO", "LANG1", "LANG2/SAP"
        };
 
-       audio_mode = V4L2_TUNER_MODE_STEREO;
+       audio_mode = TUNER_AUDIO_STEREO;
 
        switch (vt->audmode) {
                case V4L2_TUNER_MODE_MONO:
index 9c8b7c7..a4f3472 100644 (file)
@@ -153,6 +153,40 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
        return ioread32(priv->base + reg_offs);
 }
 
+static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+       int i, success = 0;
+       struct soc_camera_device *icd = pcdev->icd;
+
+       ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+
+       /* wait CSTSR.CPTON bit */
+       for (i = 0; i < 1000; i++) {
+               if (!(ceu_read(pcdev, CSTSR) & 1)) {
+                       success++;
+                       break;
+               }
+               udelay(1);
+       }
+
+       /* wait CAPSR.CPKIL bit */
+       for (i = 0; i < 1000; i++) {
+               if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) {
+                       success++;
+                       break;
+               }
+               udelay(1);
+       }
+
+
+       if (2 != success) {
+               dev_warn(&icd->dev, "soft reset time out\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
 /*
  *  Videobuf operations
  */
@@ -202,26 +236,45 @@ static void free_buffer(struct videobuf_queue *vq,
 #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */
 #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */
 #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */
+#define CEU_CEIER_VBP   (1 << 20) /* vbp error */
 #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
+#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP)
 
 
-static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+/*
+ * return value doesn't reflex the success/failure to queue the new buffer,
+ * but rather the status of the previous buffer.
+ */
+static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 {
        struct soc_camera_device *icd = pcdev->icd;
        dma_addr_t phys_addr_top, phys_addr_bottom;
+       u32 status;
+       int ret = 0;
 
        /* The hardware is _very_ picky about this sequence. Especially
         * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
         * several not-so-well documented interrupt sources in CETCR.
         */
-       ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE);
-       ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC);
-       ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE);
+       ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK);
+       status = ceu_read(pcdev, CETCR);
+       ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC);
+       ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK);
        ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
        ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
 
+       /*
+        * When a VBP interrupt occurs, a capture end interrupt does not occur
+        * and the image of that frame is not captured correctly. So, soft reset
+        * is needed here.
+        */
+       if (status & CEU_CEIER_VBP) {
+               sh_mobile_ceu_soft_reset(pcdev);
+               ret = -EIO;
+       }
+
        if (!pcdev->active)
-               return;
+               return ret;
 
        phys_addr_top = videobuf_to_dma_contig(pcdev->active);
        ceu_write(pcdev, CDAYR, phys_addr_top);
@@ -247,6 +300,8 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
 
        pcdev->active->state = VIDEOBUF_ACTIVE;
        ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+
+       return ret;
 }
 
 static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
@@ -319,6 +374,11 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
        list_add_tail(&vb->queue, &pcdev->capture);
 
        if (!pcdev->active) {
+               /*
+                * Because there were no active buffer at this moment,
+                * we are not interested in the return value of
+                * sh_mobile_ceu_capture here.
+                */
                pcdev->active = vb;
                sh_mobile_ceu_capture(pcdev);
        }
@@ -379,9 +439,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
        else
                pcdev->active = NULL;
 
-       sh_mobile_ceu_capture(pcdev);
-
-       vb->state = VIDEOBUF_DONE;
+       vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ?
+               VIDEOBUF_ERROR : VIDEOBUF_DONE;
        do_gettimeofday(&vb->ts);
        vb->field_count++;
        wake_up(&vb->done);
@@ -407,13 +466,9 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
 
        pm_runtime_get_sync(ici->v4l2_dev.dev);
 
-       ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
-       while (ceu_read(pcdev, CSTSR) & 1)
-               msleep(1);
-
        pcdev->icd = icd;
 
-       return 0;
+       return sh_mobile_ceu_soft_reset(pcdev);
 }
 
 /* Called with .video_lock held */
@@ -427,7 +482,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
 
        /* disable capture, disable interrupts */
        ceu_write(pcdev, CEIER, 0);
-       ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+       sh_mobile_ceu_soft_reset(pcdev);
 
        /* make sure active buffer is canceled */
        spin_lock_irqsave(&pcdev->lock, flags);
index aba92e2..5b3eaa1 100644 (file)
@@ -320,6 +320,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
        struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
        struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
        unsigned char buffer[4];
+       int tune_now = 1;
 
        if (type == UNSET || type == TUNER_ABSENT) {
                tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr);
@@ -328,7 +329,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
 
        t->type = type;
        /* prevent invalid config values */
-       t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0;
+       t->config = new_config < 256 ? new_config : 0;
        if (tuner_callback != NULL) {
                tuner_dbg("defining GPIO callback\n");
                t->fe.callback = tuner_callback;
@@ -404,6 +405,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
                };
                if (!dvb_attach(xc2028_attach, &t->fe, &cfg))
                        goto attach_failed;
+               tune_now = 0;
                break;
        }
        case TUNER_TDA9887:
@@ -419,6 +421,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
                if (!dvb_attach(xc5000_attach,
                                &t->fe, t->i2c->adapter, &xc5000_cfg))
                        goto attach_failed;
+               tune_now = 0;
                break;
        }
        case TUNER_NXP_TDA18271:
@@ -430,6 +433,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
                if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr,
                                t->i2c->adapter, &cfg))
                        goto attach_failed;
+               tune_now = 0;
                break;
        }
        default:
@@ -458,12 +462,13 @@ static void set_type(struct i2c_client *c, unsigned int type,
        if (t->mode_mask == T_UNINITIALIZED)
                t->mode_mask = new_mode_mask;
 
-       /* xc2028/3028 and xc5000 requires a firmware to be set-up later
+       /* Some tuners require more initialization setup before use,
+          such as firmware download or device calibration.
           trying to set a frequency here will just fail
           FIXME: better to move set_freq to the tuner code. This is needed
           on analog tuners for PLL to properly work
         */
-       if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000)
+       if (tune_now)
                set_freq(c, (V4L2_TUNER_RADIO == t->mode) ?
                            t->radio_freq : t->tv_freq);
 
@@ -752,14 +757,17 @@ static int tuner_s_radio(struct v4l2_subdev *sd)
        return 0;
 }
 
-static int tuner_s_standby(struct v4l2_subdev *sd)
+static int tuner_s_power(struct v4l2_subdev *sd, int on)
 {
        struct tuner *t = to_tuner(sd);
        struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
 
+       if (on)
+               return 0;
+
        tuner_dbg("Putting tuner to sleep\n");
 
-       if (check_mode(t, "s_standby") == -EINVAL)
+       if (check_mode(t, "s_power") == -EINVAL)
                return 0;
        t->mode = T_STANDBY;
        if (analog_ops->standby)
@@ -961,6 +969,7 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
 static const struct v4l2_subdev_core_ops tuner_core_ops = {
        .log_status = tuner_log_status,
        .s_std = tuner_s_std,
+       .s_power = tuner_s_power,
 };
 
 static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
@@ -971,7 +980,6 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
        .g_frequency = tuner_g_frequency,
        .s_type_addr = tuner_s_type_addr,
        .s_config = tuner_s_config,
-       .s_standby = tuner_s_standby,
 };
 
 static const struct v4l2_subdev_ops tuner_ops = {
index 0869baf..800fc1b 100644 (file)
@@ -1919,7 +1919,7 @@ static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
        .s_radio = tvaudio_s_radio,
        .s_frequency = tvaudio_s_frequency,
        .s_tuner = tvaudio_s_tuner,
-       .s_tuner = tvaudio_g_tuner,
+       .g_tuner = tvaudio_g_tuner,
 };
 
 static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
index 2443726..26b4e71 100644 (file)
@@ -272,7 +272,7 @@ static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg)
 read_again:
 
        err = i2c_smbus_read_byte_data(client, reg);
-       if (err == -1) {
+       if (err < 0) {
                if (retry <= I2C_RETRY_COUNT) {
                        v4l2_warn(sd, "Read: retry ... %d\n", retry);
                        retry++;
index 31d57f2..a0addcb 100644 (file)
@@ -225,7 +225,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev
        int error;
 
        usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
-       strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
+       strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname));
 
        cam->input = input_dev = input_allocate_device();
        if (!input_dev) {
index 803d3e4..c4d1b96 100644 (file)
@@ -89,7 +89,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev)
        int error;
 
        usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
-       strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
+       strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname));
 
        cam->input = input_dev = input_allocate_device();
        if (!input_dev) {
index a2a50d6..c07b0ac 100644 (file)
@@ -601,7 +601,7 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input)
 {
        struct usb_usbvision *usbvision = video_drvdata(file);
 
-       if ((input >= usbvision->video_inputs) || (input < 0) )
+       if (input >= usbvision->video_inputs)
                return -EINVAL;
 
        mutex_lock(&usbvision->lock);
index 1b89735..0469d7a 100644 (file)
@@ -742,17 +742,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
        v4l2_id &= V4L2_CTRL_ID_MASK;
 
        /* Find the control. */
-       __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next);
-       if (ctrl && !next)
-               return ctrl;
-
-       list_for_each_entry(entity, &chain->iterms, chain) {
-               __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
-               if (ctrl && !next)
-                       return ctrl;
-       }
-
-       list_for_each_entry(entity, &chain->extensions, chain) {
+       list_for_each_entry(entity, &chain->entities, chain) {
                __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
                if (ctrl && !next)
                        return ctrl;
@@ -826,6 +816,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
                ret = 0;
                goto out;
 
+       case V4L2_CTRL_TYPE_BUTTON:
+               v4l2_ctrl->minimum = 0;
+               v4l2_ctrl->maximum = 0;
+               v4l2_ctrl->step = 0;
+               ret = 0;
+               goto out;
+
        default:
                break;
        }
@@ -944,17 +941,7 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
        int ret = 0;
 
        /* Find the control. */
-       ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback);
-       if (ret < 0)
-               goto done;
-
-       list_for_each_entry(entity, &chain->iterms, chain) {
-               ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
-               if (ret < 0)
-                       goto done;
-       }
-
-       list_for_each_entry(entity, &chain->extensions, chain) {
+       list_for_each_entry(entity, &chain->entities, chain) {
                ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback);
                if (ret < 0)
                        goto done;
@@ -1068,8 +1055,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
        int ret;
 
        /* Find the extension unit. */
-       list_for_each_entry(entity, &chain->extensions, chain) {
-               if (entity->id == xctrl->unit)
+       list_for_each_entry(entity, &chain->entities, chain) {
+               if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT &&
+                   entity->id == xctrl->unit)
                        break;
        }
 
index 8756be5..c31bc50 100644 (file)
@@ -46,6 +46,7 @@
 unsigned int uvc_no_drop_param;
 static unsigned int uvc_quirks_param;
 unsigned int uvc_trace_param;
+unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
 
 /* ------------------------------------------------------------------------
  * Video formats
@@ -248,29 +249,9 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
                entity = list_entry(&dev->entities, struct uvc_entity, list);
 
        list_for_each_entry_continue(entity, &dev->entities, list) {
-               switch (UVC_ENTITY_TYPE(entity)) {
-               case UVC_TT_STREAMING:
-                       if (entity->output.bSourceID == id)
-                               return entity;
-                       break;
-
-               case UVC_VC_PROCESSING_UNIT:
-                       if (entity->processing.bSourceID == id)
+               for (i = 0; i < entity->bNrInPins; ++i)
+                       if (entity->baSourceID[i] == id)
                                return entity;
-                       break;
-
-               case UVC_VC_SELECTOR_UNIT:
-                       for (i = 0; i < entity->selector.bNrInPins; ++i)
-                               if (entity->selector.baSourceID[i] == id)
-                                       return entity;
-                       break;
-
-               case UVC_VC_EXTENSION_UNIT:
-                       for (i = 0; i < entity->extension.bNrInPins; ++i)
-                               if (entity->extension.baSourceID[i] == id)
-                                       return entity;
-                       break;
-               }
        }
 
        return NULL;
@@ -426,7 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev,
        /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
         * based formats have frame descriptors.
         */
-       while (buflen > 2 && buffer[2] == ftype) {
+       while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+              buffer[2] == ftype) {
                frame = &format->frame[format->nframes];
                if (ftype != UVC_VS_FRAME_FRAME_BASED)
                        n = buflen > 25 ? buffer[25] : 0;
@@ -503,12 +485,14 @@ static int uvc_parse_format(struct uvc_device *dev,
                buffer += buffer[0];
        }
 
-       if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
+       if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+           buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
                buflen -= buffer[0];
                buffer += buffer[0];
        }
 
-       if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) {
+       if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+           buffer[2] == UVC_VS_COLORFORMAT) {
                if (buflen < 6) {
                        uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
                               "interface %d COLORFORMAT error\n",
@@ -749,6 +733,11 @@ static int uvc_parse_streaming(struct uvc_device *dev,
                buffer += buffer[0];
        }
 
+       if (buflen)
+               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+                       "%d has %u bytes of trailing descriptor garbage.\n",
+                       dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
+
        /* Parse the alternate settings to find the maximum bandwidth. */
        for (i = 0; i < intf->num_altsetting; ++i) {
                struct usb_host_endpoint *ep;
@@ -776,6 +765,28 @@ error:
        return ret;
 }
 
+static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
+               unsigned int num_pads, unsigned int extra_size)
+{
+       struct uvc_entity *entity;
+       unsigned int num_inputs;
+       unsigned int size;
+
+       num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
+       size = sizeof(*entity) + extra_size + num_inputs;
+       entity = kzalloc(size, GFP_KERNEL);
+       if (entity == NULL)
+               return NULL;
+
+       entity->id = id;
+       entity->type = type;
+
+       entity->bNrInPins = num_inputs;
+       entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size;
+
+       return entity;
+}
+
 /* Parse vendor-specific extensions. */
 static int uvc_parse_vendor_control(struct uvc_device *dev,
        const unsigned char *buffer, int buflen)
@@ -827,21 +838,18 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
                        break;
                }
 
-               unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL);
+               unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
+                                       p + 1, 2*n);
                if (unit == NULL)
                        return -ENOMEM;
 
-               unit->id = buffer[3];
-               unit->type = UVC_VC_EXTENSION_UNIT;
                memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
                unit->extension.bNumControls = buffer[20];
-               unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
-               unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
-               memcpy(unit->extension.baSourceID, &buffer[22], p);
+               memcpy(unit->baSourceID, &buffer[22], p);
                unit->extension.bControlSize = buffer[22+p];
-               unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
-               unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit
-                                              + p + n;
+               unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
+               unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
+                                              + n;
                memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
 
                if (buffer[24+p+2*n] != 0)
@@ -938,13 +946,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return -EINVAL;
                }
 
-               term = kzalloc(sizeof *term + n + p, GFP_KERNEL);
+               term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
+                                       1, n + p);
                if (term == NULL)
                        return -ENOMEM;
 
-               term->id = buffer[3];
-               term->type = type | UVC_TERM_INPUT;
-
                if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
                        term->camera.bControlSize = n;
                        term->camera.bmControls = (__u8 *)term + sizeof *term;
@@ -999,13 +1005,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return 0;
                }
 
-               term = kzalloc(sizeof *term, GFP_KERNEL);
+               term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
+                                       1, 0);
                if (term == NULL)
                        return -ENOMEM;
 
-               term->id = buffer[3];
-               term->type = type | UVC_TERM_OUTPUT;
-               term->output.bSourceID = buffer[7];
+               memcpy(term->baSourceID, &buffer[7], 1);
 
                if (buffer[8] != 0)
                        usb_string(udev, buffer[8], term->name,
@@ -1026,15 +1031,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return -EINVAL;
                }
 
-               unit = kzalloc(sizeof *unit + p, GFP_KERNEL);
+               unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
                if (unit == NULL)
                        return -ENOMEM;
 
-               unit->id = buffer[3];
-               unit->type = buffer[2];
-               unit->selector.bNrInPins = buffer[4];
-               unit->selector.baSourceID = (__u8 *)unit + sizeof *unit;
-               memcpy(unit->selector.baSourceID, &buffer[5], p);
+               memcpy(unit->baSourceID, &buffer[5], p);
 
                if (buffer[5+p] != 0)
                        usb_string(udev, buffer[5+p], unit->name,
@@ -1056,13 +1057,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return -EINVAL;
                }
 
-               unit = kzalloc(sizeof *unit + n, GFP_KERNEL);
+               unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
                if (unit == NULL)
                        return -ENOMEM;
 
-               unit->id = buffer[3];
-               unit->type = buffer[2];
-               unit->processing.bSourceID = buffer[4];
+               memcpy(unit->baSourceID, &buffer[4], 1);
                unit->processing.wMaxMultiplier =
                        get_unaligned_le16(&buffer[5]);
                unit->processing.bControlSize = buffer[7];
@@ -1091,19 +1090,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return -EINVAL;
                }
 
-               unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL);
+               unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
                if (unit == NULL)
                        return -ENOMEM;
 
-               unit->id = buffer[3];
-               unit->type = buffer[2];
                memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
                unit->extension.bNumControls = buffer[20];
-               unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
-               unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
-               memcpy(unit->extension.baSourceID, &buffer[22], p);
+               memcpy(unit->baSourceID, &buffer[22], p);
                unit->extension.bControlSize = buffer[22+p];
-               unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
+               unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
                memcpy(unit->extension.bmControls, &buffer[23+p], n);
 
                if (buffer[23+p+n] != 0)
@@ -1209,13 +1204,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
                if (uvc_trace_param & UVC_TRACE_PROBE)
                        printk(" <- XU %d", entity->id);
 
-               if (entity->extension.bNrInPins != 1) {
+               if (entity->bNrInPins != 1) {
                        uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
                                "than 1 input pin.\n", entity->id);
                        return -1;
                }
 
-               list_add_tail(&entity->chain, &chain->extensions);
                break;
 
        case UVC_VC_PROCESSING_UNIT:
@@ -1236,7 +1230,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
                        printk(" <- SU %d", entity->id);
 
                /* Single-input selector units are ignored. */
-               if (entity->selector.bNrInPins == 1)
+               if (entity->bNrInPins == 1)
                        break;
 
                if (chain->selector != NULL) {
@@ -1254,20 +1248,17 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
                if (uvc_trace_param & UVC_TRACE_PROBE)
                        printk(" <- IT %d\n", entity->id);
 
-               list_add_tail(&entity->chain, &chain->iterms);
                break;
 
        case UVC_TT_STREAMING:
-               if (uvc_trace_param & UVC_TRACE_PROBE)
-                       printk(" <- IT %d\n", entity->id);
-
-               if (!UVC_ENTITY_IS_ITERM(entity)) {
-                       uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
-                               "terminal %u.\n", entity->id);
-                       return -1;
+               if (UVC_ENTITY_IS_ITERM(entity)) {
+                       if (uvc_trace_param & UVC_TRACE_PROBE)
+                               printk(" <- IT %d\n", entity->id);
+               } else {
+                       if (uvc_trace_param & UVC_TRACE_PROBE)
+                               printk(" OT %d", entity->id);
                }
 
-               list_add_tail(&entity->chain, &chain->iterms);
                break;
 
        default:
@@ -1276,6 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
                return -1;
        }
 
+       list_add_tail(&entity->chain, &chain->entities);
        return 0;
 }
 
@@ -1299,14 +1291,14 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
 
                switch (UVC_ENTITY_TYPE(forward)) {
                case UVC_VC_EXTENSION_UNIT:
-                       if (forward->extension.bNrInPins != 1) {
+                       if (forward->bNrInPins != 1) {
                                uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
                                          "has more than 1 input pin.\n",
                                          entity->id);
                                return -EINVAL;
                        }
 
-                       list_add_tail(&forward->chain, &chain->extensions);
+                       list_add_tail(&forward->chain, &chain->entities);
                        if (uvc_trace_param & UVC_TRACE_PROBE) {
                                if (!found)
                                        printk(" (->");
@@ -1326,7 +1318,7 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
                                return -EINVAL;
                        }
 
-                       list_add_tail(&forward->chain, &chain->oterms);
+                       list_add_tail(&forward->chain, &chain->entities);
                        if (uvc_trace_param & UVC_TRACE_PROBE) {
                                if (!found)
                                        printk(" (->");
@@ -1344,24 +1336,22 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
 }
 
 static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
-       struct uvc_entity *entity)
+       struct uvc_entity **_entity)
 {
+       struct uvc_entity *entity = *_entity;
        struct uvc_entity *term;
-       int id = -1, i;
+       int id = -EINVAL, i;
 
        switch (UVC_ENTITY_TYPE(entity)) {
        case UVC_VC_EXTENSION_UNIT:
-               id = entity->extension.baSourceID[0];
-               break;
-
        case UVC_VC_PROCESSING_UNIT:
-               id = entity->processing.bSourceID;
+               id = entity->baSourceID[0];
                break;
 
        case UVC_VC_SELECTOR_UNIT:
                /* Single-input selector units are ignored. */
-               if (entity->selector.bNrInPins == 1) {
-                       id = entity->selector.baSourceID[0];
+               if (entity->bNrInPins == 1) {
+                       id = entity->baSourceID[0];
                        break;
                }
 
@@ -1369,8 +1359,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
                        printk(" <- IT");
 
                chain->selector = entity;
-               for (i = 0; i < entity->selector.bNrInPins; ++i) {
-                       id = entity->selector.baSourceID[i];
+               for (i = 0; i < entity->bNrInPins; ++i) {
+                       id = entity->baSourceID[i];
                        term = uvc_entity_by_id(chain->dev, id);
                        if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
                                uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
@@ -1382,7 +1372,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
                        if (uvc_trace_param & UVC_TRACE_PROBE)
                                printk(" %d", term->id);
 
-                       list_add_tail(&term->chain, &chain->iterms);
+                       list_add_tail(&term->chain, &chain->entities);
                        uvc_scan_chain_forward(chain, term, entity);
                }
 
@@ -1391,34 +1381,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
 
                id = 0;
                break;
+
+       case UVC_ITT_VENDOR_SPECIFIC:
+       case UVC_ITT_CAMERA:
+       case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+       case UVC_OTT_VENDOR_SPECIFIC:
+       case UVC_OTT_DISPLAY:
+       case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+       case UVC_TT_STREAMING:
+               id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
+               break;
+       }
+
+       if (id <= 0) {
+               *_entity = NULL;
+               return id;
        }
 
-       return id;
+       entity = uvc_entity_by_id(chain->dev, id);
+       if (entity == NULL) {
+               uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+                       "unknown entity %d.\n", id);
+               return -EINVAL;
+       }
+
+       *_entity = entity;
+       return 0;
 }
 
 static int uvc_scan_chain(struct uvc_video_chain *chain,
-                         struct uvc_entity *oterm)
+                         struct uvc_entity *term)
 {
        struct uvc_entity *entity, *prev;
-       int id;
 
-       entity = oterm;
-       list_add_tail(&entity->chain, &chain->oterms);
-       uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+       uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
 
-       id = entity->output.bSourceID;
-       while (id != 0) {
-               prev = entity;
-               entity = uvc_entity_by_id(chain->dev, id);
-               if (entity == NULL) {
-                       uvc_trace(UVC_TRACE_DESCR, "Found reference to "
-                               "unknown entity %d.\n", id);
-                       return -EINVAL;
-               }
+       entity = term;
+       prev = NULL;
 
+       while (entity != NULL) {
+               /* Entity must not be part of an existing chain */
                if (entity->chain.next || entity->chain.prev) {
                        uvc_trace(UVC_TRACE_DESCR, "Found reference to "
-                               "entity %d already in chain.\n", id);
+                               "entity %d already in chain.\n", entity->id);
                        return -EINVAL;
                }
 
@@ -1430,34 +1435,34 @@ static int uvc_scan_chain(struct uvc_video_chain *chain,
                if (uvc_scan_chain_forward(chain, entity, prev) < 0)
                        return -EINVAL;
 
-               /* Stop when a terminal is found. */
-               if (UVC_ENTITY_IS_TERM(entity))
-                       break;
-
                /* Backward scan */
-               id = uvc_scan_chain_backward(chain, entity);
-               if (id < 0)
-                       return id;
+               prev = entity;
+               if (uvc_scan_chain_backward(chain, &entity) < 0)
+                       return -EINVAL;
        }
 
        return 0;
 }
 
-static unsigned int uvc_print_terms(struct list_head *terms, char *buffer)
+static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
+               char *buffer)
 {
        struct uvc_entity *term;
        unsigned int nterms = 0;
        char *p = buffer;
 
        list_for_each_entry(term, terms, chain) {
-               p += sprintf(p, "%u", term->id);
-               if (term->chain.next != terms) {
+               if (!UVC_ENTITY_IS_TERM(term) ||
+                   UVC_TERM_DIRECTION(term) != dir)
+                       continue;
+
+               if (nterms)
                        p += sprintf(p, ",");
-                       if (++nterms >= 4) {
-                               p += sprintf(p, "...");
-                               break;
-                       }
+               if (++nterms >= 4) {
+                       p += sprintf(p, "...");
+                       break;
                }
+               p += sprintf(p, "%u", term->id);
        }
 
        return p - buffer;
@@ -1468,9 +1473,9 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain)
        static char buffer[43];
        char *p = buffer;
 
-       p += uvc_print_terms(&chain->iterms, p);
+       p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
        p += sprintf(p, " -> ");
-       uvc_print_terms(&chain->oterms, p);
+       uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
 
        return buffer;
 }
@@ -1501,9 +1506,7 @@ static int uvc_scan_device(struct uvc_device *dev)
                if (chain == NULL)
                        return -ENOMEM;
 
-               INIT_LIST_HEAD(&chain->iterms);
-               INIT_LIST_HEAD(&chain->oterms);
-               INIT_LIST_HEAD(&chain->extensions);
+               INIT_LIST_HEAD(&chain->entities);
                mutex_init(&chain->ctrl_mutex);
                chain->dev = dev;
 
@@ -1530,6 +1533,66 @@ static int uvc_scan_device(struct uvc_device *dev)
  * Video device registration and unregistration
  */
 
+/*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+static void uvc_delete(struct uvc_device *dev)
+{
+       struct list_head *p, *n;
+
+       usb_put_intf(dev->intf);
+       usb_put_dev(dev->udev);
+
+       uvc_status_cleanup(dev);
+       uvc_ctrl_cleanup_device(dev);
+
+       list_for_each_safe(p, n, &dev->chains) {
+               struct uvc_video_chain *chain;
+               chain = list_entry(p, struct uvc_video_chain, list);
+               kfree(chain);
+       }
+
+       list_for_each_safe(p, n, &dev->entities) {
+               struct uvc_entity *entity;
+               entity = list_entry(p, struct uvc_entity, list);
+               kfree(entity);
+       }
+
+       list_for_each_safe(p, n, &dev->streams) {
+               struct uvc_streaming *streaming;
+               streaming = list_entry(p, struct uvc_streaming, list);
+               usb_driver_release_interface(&uvc_driver.driver,
+                       streaming->intf);
+               usb_put_intf(streaming->intf);
+               kfree(streaming->format);
+               kfree(streaming->header.bmaControls);
+               kfree(streaming);
+       }
+
+       kfree(dev);
+}
+
+static void uvc_release(struct video_device *vdev)
+{
+       struct uvc_streaming *stream = video_get_drvdata(vdev);
+       struct uvc_device *dev = stream->dev;
+
+       video_device_release(vdev);
+
+       /* Decrement the registered streams count and delete the device when it
+        * reaches zero.
+        */
+       if (atomic_dec_and_test(&dev->nstreams))
+               uvc_delete(dev);
+}
+
 /*
  * Unregister the video devices.
  */
@@ -1537,16 +1600,26 @@ static void uvc_unregister_video(struct uvc_device *dev)
 {
        struct uvc_streaming *stream;
 
+       /* Unregistering all video devices might result in uvc_delete() being
+        * called from inside the loop if there's no open file handle. To avoid
+        * that, increment the stream count before iterating over the streams
+        * and decrement it when done.
+        */
+       atomic_inc(&dev->nstreams);
+
        list_for_each_entry(stream, &dev->streams, list) {
                if (stream->vdev == NULL)
                        continue;
 
-               if (stream->vdev->minor == -1)
-                       video_device_release(stream->vdev);
-               else
-                       video_unregister_device(stream->vdev);
+               video_unregister_device(stream->vdev);
                stream->vdev = NULL;
        }
+
+       /* Decrement the stream count and call uvc_delete explicitly if there
+        * are no stream left.
+        */
+       if (atomic_dec_and_test(&dev->nstreams))
+               uvc_delete(dev);
 }
 
 static int uvc_register_video(struct uvc_device *dev,
@@ -1580,7 +1653,7 @@ static int uvc_register_video(struct uvc_device *dev,
        vdev->parent = &dev->intf->dev;
        vdev->minor = -1;
        vdev->fops = &uvc_fops;
-       vdev->release = video_device_release;
+       vdev->release = uvc_release;
        strlcpy(vdev->name, dev->name, sizeof vdev->name);
 
        /* Set the driver data before calling video_register_device, otherwise
@@ -1598,6 +1671,7 @@ static int uvc_register_video(struct uvc_device *dev,
                return ret;
        }
 
+       atomic_inc(&dev->nstreams);
        return 0;
 }
 
@@ -1605,13 +1679,13 @@ static int uvc_register_video(struct uvc_device *dev,
  * Register all video devices in all chains.
  */
 static int uvc_register_terms(struct uvc_device *dev,
-       struct uvc_video_chain *chain, struct list_head *terms)
+       struct uvc_video_chain *chain)
 {
        struct uvc_streaming *stream;
        struct uvc_entity *term;
        int ret;
 
-       list_for_each_entry(term, terms, chain) {
+       list_for_each_entry(term, &chain->entities, chain) {
                if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
                        continue;
 
@@ -1637,11 +1711,7 @@ static int uvc_register_chains(struct uvc_device *dev)
        int ret;
 
        list_for_each_entry(chain, &dev->chains, list) {
-               ret = uvc_register_terms(dev, chain, &chain->iterms);
-               if (ret < 0)
-                       return ret;
-
-               ret = uvc_register_terms(dev, chain, &chain->oterms);
+               ret = uvc_register_terms(dev, chain);
                if (ret < 0)
                        return ret;
        }
@@ -1653,61 +1723,6 @@ static int uvc_register_chains(struct uvc_device *dev)
  * USB probe, disconnect, suspend and resume
  */
 
-/*
- * Delete the UVC device.
- *
- * Called by the kernel when the last reference to the uvc_device structure
- * is released.
- *
- * Unregistering the video devices is done here because every opened instance
- * must be closed before the device can be unregistered. An alternative would
- * have been to use another reference count for uvc_v4l2_open/uvc_release, and
- * unregister the video devices on disconnect when that reference count drops
- * to zero.
- *
- * As this function is called after or during disconnect(), all URBs have
- * already been canceled by the USB core. There is no need to kill the
- * interrupt URB manually.
- */
-void uvc_delete(struct kref *kref)
-{
-       struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
-       struct list_head *p, *n;
-
-       /* Unregister the video devices. */
-       uvc_unregister_video(dev);
-       usb_put_intf(dev->intf);
-       usb_put_dev(dev->udev);
-
-       uvc_status_cleanup(dev);
-       uvc_ctrl_cleanup_device(dev);
-
-       list_for_each_safe(p, n, &dev->chains) {
-               struct uvc_video_chain *chain;
-               chain = list_entry(p, struct uvc_video_chain, list);
-               kfree(chain);
-       }
-
-       list_for_each_safe(p, n, &dev->entities) {
-               struct uvc_entity *entity;
-               entity = list_entry(p, struct uvc_entity, list);
-               kfree(entity);
-       }
-
-       list_for_each_safe(p, n, &dev->streams) {
-               struct uvc_streaming *streaming;
-               streaming = list_entry(p, struct uvc_streaming, list);
-               usb_driver_release_interface(&uvc_driver.driver,
-                       streaming->intf);
-               usb_put_intf(streaming->intf);
-               kfree(streaming->format);
-               kfree(streaming->header.bmaControls);
-               kfree(streaming);
-       }
-
-       kfree(dev);
-}
-
 static int uvc_probe(struct usb_interface *intf,
                     const struct usb_device_id *id)
 {
@@ -1730,7 +1745,7 @@ static int uvc_probe(struct usb_interface *intf,
        INIT_LIST_HEAD(&dev->entities);
        INIT_LIST_HEAD(&dev->chains);
        INIT_LIST_HEAD(&dev->streams);
-       kref_init(&dev->kref);
+       atomic_set(&dev->nstreams, 0);
        atomic_set(&dev->users, 0);
 
        dev->udev = usb_get_dev(udev);
@@ -1792,7 +1807,7 @@ static int uvc_probe(struct usb_interface *intf,
        return 0;
 
 error:
-       kref_put(&dev->kref, uvc_delete);
+       uvc_unregister_video(dev);
        return -ENODEV;
 }
 
@@ -1809,21 +1824,9 @@ static void uvc_disconnect(struct usb_interface *intf)
            UVC_SC_VIDEOSTREAMING)
                return;
 
-       /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide
-        * lock is needed to prevent uvc_disconnect from releasing its
-        * reference to the uvc_device instance after uvc_v4l2_open() received
-        * the pointer to the device (video_devdata) but before it got the
-        * chance to increase the reference count (kref_get).
-        *
-        * Note that the reference can't be released with the lock held,
-        * otherwise a AB-BA deadlock can occur with videodev_lock that
-        * videodev acquires in videodev_open() and video_unregister_device().
-        */
-       mutex_lock(&uvc_driver.open_mutex);
        dev->state |= UVC_DEV_DISCONNECTED;
-       mutex_unlock(&uvc_driver.open_mutex);
 
-       kref_put(&dev->kref, uvc_delete);
+       uvc_unregister_video(dev);
 }
 
 static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
@@ -1899,6 +1902,15 @@ static int uvc_reset_resume(struct usb_interface *intf)
  * though they are compliant.
  */
 static struct usb_device_id uvc_ids[] = {
+       /* Genius eFace 2025 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x0458,
+         .idProduct            = 0x706e,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* Microsoft Lifecam NX-6000 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2123,6 +2135,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_STATUS_INTERVAL },
+       /* MSI StarCam 370i */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x1b3b,
+         .idProduct            = 0x2951,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* SiGma Micro USB Web Camera */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -2159,7 +2180,6 @@ static int __init uvc_init(void)
 
        INIT_LIST_HEAD(&uvc_driver.devices);
        INIT_LIST_HEAD(&uvc_driver.controls);
-       mutex_init(&uvc_driver.open_mutex);
        mutex_init(&uvc_driver.ctrl_mutex);
 
        uvc_ctrl_init();
@@ -2184,6 +2204,8 @@ module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(quirks, "Forced device quirks");
 module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(trace, "Trace level bitmask");
+module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
index a2bdd80..23239a4 100644 (file)
@@ -364,37 +364,30 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
  * unprivileged state. Only a single instance can be in a privileged state at
  * a given time. Trying to perform an operation that requires privileges will
  * automatically acquire the required privileges if possible, or return -EBUSY
- * otherwise. Privileges are dismissed when closing the instance.
+ * otherwise. Privileges are dismissed when closing the instance or when
+ * freeing the video buffers using VIDIOC_REQBUFS.
  *
  * Operations that require privileges are:
  *
  * - VIDIOC_S_INPUT
  * - VIDIOC_S_PARM
  * - VIDIOC_S_FMT
- * - VIDIOC_TRY_FMT
  * - VIDIOC_REQBUFS
  */
 static int uvc_acquire_privileges(struct uvc_fh *handle)
 {
-       int ret = 0;
-
        /* Always succeed if the handle is already privileged. */
        if (handle->state == UVC_HANDLE_ACTIVE)
                return 0;
 
        /* Check if the device already has a privileged handle. */
-       mutex_lock(&uvc_driver.open_mutex);
        if (atomic_inc_return(&handle->stream->active) != 1) {
                atomic_dec(&handle->stream->active);
-               ret = -EBUSY;
-               goto done;
+               return -EBUSY;
        }
 
        handle->state = UVC_HANDLE_ACTIVE;
-
-done:
-       mutex_unlock(&uvc_driver.open_mutex);
-       return ret;
+       return 0;
 }
 
 static void uvc_dismiss_privileges(struct uvc_fh *handle)
@@ -421,24 +414,20 @@ static int uvc_v4l2_open(struct file *file)
        int ret = 0;
 
        uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
-       mutex_lock(&uvc_driver.open_mutex);
        stream = video_drvdata(file);
 
-       if (stream->dev->state & UVC_DEV_DISCONNECTED) {
-               ret = -ENODEV;
-               goto done;
-       }
+       if (stream->dev->state & UVC_DEV_DISCONNECTED)
+               return -ENODEV;
 
        ret = usb_autopm_get_interface(stream->dev->intf);
        if (ret < 0)
-               goto done;
+               return ret;
 
        /* Create the device handle. */
        handle = kzalloc(sizeof *handle, GFP_KERNEL);
        if (handle == NULL) {
                usb_autopm_put_interface(stream->dev->intf);
-               ret = -ENOMEM;
-               goto done;
+               return -ENOMEM;
        }
 
        if (atomic_inc_return(&stream->dev->users) == 1) {
@@ -447,7 +436,7 @@ static int uvc_v4l2_open(struct file *file)
                        usb_autopm_put_interface(stream->dev->intf);
                        atomic_dec(&stream->dev->users);
                        kfree(handle);
-                       goto done;
+                       return ret;
                }
        }
 
@@ -456,11 +445,7 @@ static int uvc_v4l2_open(struct file *file)
        handle->state = UVC_HANDLE_PASSIVE;
        file->private_data = handle;
 
-       kref_get(&stream->dev->kref);
-
-done:
-       mutex_unlock(&uvc_driver.open_mutex);
-       return ret;
+       return 0;
 }
 
 static int uvc_v4l2_release(struct file *file)
@@ -490,7 +475,6 @@ static int uvc_v4l2_release(struct file *file)
                uvc_status_stop(stream->dev);
 
        usb_autopm_put_interface(stream->dev->intf);
-       kref_put(&stream->dev->kref, uvc_delete);
        return 0;
 }
 
@@ -636,12 +620,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
                        if (index != 0)
                                return -EINVAL;
-                       iterm = list_first_entry(&chain->iterms,
-                                       struct uvc_entity, chain);
+                       list_for_each_entry(iterm, &chain->entities, chain) {
+                               if (UVC_ENTITY_IS_ITERM(iterm))
+                                       break;
+                       }
                        pin = iterm->id;
-               } else if (pin < selector->selector.bNrInPins) {
-                       pin = selector->selector.baSourceID[index];
-                       list_for_each_entry(iterm, chain->iterms.next, chain) {
+               } else if (pin < selector->bNrInPins) {
+                       pin = selector->baSourceID[index];
+                       list_for_each_entry(iterm, &chain->entities, chain) {
+                               if (!UVC_ENTITY_IS_ITERM(iterm))
+                                       continue;
                                if (iterm->id == pin)
                                        break;
                        }
@@ -692,7 +680,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                        break;
                }
 
-               if (input == 0 || input > chain->selector->selector.bNrInPins)
+               if (input == 0 || input > chain->selector->bNrInPins)
                        return -EINVAL;
 
                return uvc_query_ctrl(chain->dev, UVC_SET_CUR,
@@ -731,9 +719,6 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        {
                struct uvc_streaming_control probe;
 
-               if ((ret = uvc_acquire_privileges(handle)) < 0)
-                       return ret;
-
                return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
        }
 
@@ -888,6 +873,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                if (ret < 0)
                        return ret;
 
+               if (ret == 0)
+                       uvc_dismiss_privileges(handle);
+
                rb->count = ret;
                ret = 0;
                break;
@@ -1051,7 +1039,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
                    size_t count, loff_t *ppos)
 {
        uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
-       return -ENODEV;
+       return -EINVAL;
 }
 
 /*
index a6e41d1..05139a4 100644 (file)
@@ -135,7 +135,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream,
 
        ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
                probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
-               size, UVC_CTRL_STREAMING_TIMEOUT);
+               size, uvc_timeout_param);
 
        if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) {
                /* Some cameras, mostly based on Bison Electronics chipsets,
@@ -239,7 +239,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream,
 
        ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
                probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
-               size, UVC_CTRL_STREAMING_TIMEOUT);
+               size, uvc_timeout_param);
        if (ret != size) {
                uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
                        "%d (exp. %u).\n", probe ? "probe" : "commit",
@@ -770,8 +770,9 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
        /* Retry allocations until one succeed. */
        for (; npackets > 1; npackets /= 2) {
                for (i = 0; i < UVC_URBS; ++i) {
+                       stream->urb_size = psize * npackets;
                        stream->urb_buffer[i] = usb_buffer_alloc(
-                               stream->dev->udev, psize * npackets,
+                               stream->dev->udev, stream->urb_size,
                                gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
                        if (!stream->urb_buffer[i]) {
                                uvc_free_urb_buffers(stream);
@@ -780,11 +781,15 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
                }
 
                if (i == UVC_URBS) {
-                       stream->urb_size = psize * npackets;
+                       uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
+                               "of %ux%u bytes each.\n", UVC_URBS, npackets,
+                               psize);
                        return npackets;
                }
        }
 
+       uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
+               "per packet).\n", psize);
        return 0;
 }
 
@@ -935,10 +940,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
                bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
 
                if (bandwidth == 0) {
-                       uvc_printk(KERN_WARNING, "device %s requested null "
-                               "bandwidth, defaulting to lowest.\n",
-                               stream->dev->name);
+                       uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
+                               "bandwidth, defaulting to lowest.\n");
                        bandwidth = 1;
+               } else {
+                       uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
+                               "B/frame bandwidth.\n", bandwidth);
                }
 
                for (i = 0; i < intf->num_altsetting; ++i) {
@@ -955,8 +962,11 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
                                break;
                }
 
-               if (i >= intf->num_altsetting)
+               if (i >= intf->num_altsetting) {
+                       uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
+                               "for requested bandwidth.\n");
                        return -EIO;
+               }
 
                ret = usb_set_interface(stream->dev->udev, intfnum, i);
                if (ret < 0)
index e7958aa..7ec9a04 100644 (file)
@@ -75,6 +75,7 @@ struct uvc_xu_control {
 
 #define UVC_TERM_INPUT                 0x0000
 #define UVC_TERM_OUTPUT                        0x8000
+#define UVC_TERM_DIRECTION(term)       ((term)->type & 0x8000)
 
 #define UVC_ENTITY_TYPE(entity)                ((entity)->type & 0x7fff)
 #define UVC_ENTITY_IS_UNIT(entity)     (((entity)->type & 0xff00) == 0)
@@ -148,7 +149,7 @@ struct uvc_xu_control {
 #define UVC_MAX_STATUS_SIZE    16
 
 #define UVC_CTRL_CONTROL_TIMEOUT       300
-#define UVC_CTRL_STREAMING_TIMEOUT     1000
+#define UVC_CTRL_STREAMING_TIMEOUT     3000
 
 /* Devices quirks */
 #define UVC_QUIRK_STATUS_INTERVAL      0x00000001
@@ -292,11 +293,9 @@ struct uvc_entity {
                } media;
 
                struct {
-                       __u8  bSourceID;
                } output;
 
                struct {
-                       __u8  bSourceID;
                        __u16 wMaxMultiplier;
                        __u8  bControlSize;
                        __u8  *bmControls;
@@ -304,21 +303,20 @@ struct uvc_entity {
                } processing;
 
                struct {
-                       __u8  bNrInPins;
-                       __u8  *baSourceID;
                } selector;
 
                struct {
                        __u8  guidExtensionCode[16];
                        __u8  bNumControls;
-                       __u8  bNrInPins;
-                       __u8  *baSourceID;
                        __u8  bControlSize;
                        __u8  *bmControls;
                        __u8  *bmControlsType;
                } extension;
        };
 
+       __u8 bNrInPins;
+       __u8 *baSourceID;
+
        unsigned int ncontrols;
        struct uvc_control *controls;
 };
@@ -408,11 +406,9 @@ struct uvc_video_chain {
        struct uvc_device *dev;
        struct list_head list;
 
-       struct list_head iterms;                /* Input terminals */
-       struct list_head oterms;                /* Output terminals */
+       struct list_head entities;              /* All entities */
        struct uvc_entity *processing;          /* Processing unit */
        struct uvc_entity *selector;            /* Selector unit */
-       struct list_head extensions;            /* Extension units */
 
        struct mutex ctrl_mutex;
 };
@@ -475,7 +471,6 @@ struct uvc_device {
        char name[32];
 
        enum uvc_device_state state;
-       struct kref kref;
        struct list_head list;
        atomic_t users;
 
@@ -488,6 +483,7 @@ struct uvc_device {
 
        /* Video Streaming interfaces */
        struct list_head streams;
+       atomic_t nstreams;
 
        /* Status Interrupt Endpoint */
        struct usb_host_endpoint *int_ep;
@@ -511,8 +507,6 @@ struct uvc_fh {
 struct uvc_driver {
        struct usb_driver driver;
 
-       struct mutex open_mutex;        /* protects from open/disconnect race */
-
        struct list_head devices;       /* struct uvc_device list */
        struct list_head controls;      /* struct uvc_control_info list */
        struct mutex ctrl_mutex;        /* protects controls and devices
@@ -533,12 +527,14 @@ struct uvc_driver {
 #define UVC_TRACE_FRAME                (1 << 7)
 #define UVC_TRACE_SUSPEND      (1 << 8)
 #define UVC_TRACE_STATUS       (1 << 9)
+#define UVC_TRACE_VIDEO                (1 << 10)
 
 #define UVC_WARN_MINMAX                0
 #define UVC_WARN_PROBE_DEF     1
 
 extern unsigned int uvc_no_drop_param;
 extern unsigned int uvc_trace_param;
+extern unsigned int uvc_timeout_param;
 
 #define uvc_trace(flag, msg...) \
        do { \
@@ -571,7 +567,6 @@ extern unsigned int uvc_trace_param;
 
 /* Core driver */
 extern struct uvc_driver uvc_driver;
-extern void uvc_delete(struct kref *kref);
 
 /* Video buffers queue management. */
 extern void uvc_queue_init(struct uvc_video_queue *queue,
index f5a93ae..e8e5aff 100644 (file)
@@ -431,6 +431,8 @@ const char *v4l2_ctrl_get_name(u32 id)
        case V4L2_CID_CHROMA_AGC:               return "Chroma AGC";
        case V4L2_CID_COLOR_KILLER:             return "Color Killer";
        case V4L2_CID_COLORFX:                  return "Color Effects";
+       case V4L2_CID_ROTATE:                   return "Rotate";
+       case V4L2_CID_BG_COLOR:                 return "Background color";
 
        /* MPEG controls */
        case V4L2_CID_MPEG_CLASS:               return "MPEG Encoder Controls";
@@ -587,6 +589,13 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
                qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
                min = max = step = def = 0;
                break;
+       case V4L2_CID_BG_COLOR:
+               qctrl->type = V4L2_CTRL_TYPE_INTEGER;
+               step = 1;
+               min = 0;
+               /* Max is calculated as RGB888 that is 2^24 */
+               max = 0xFFFFFF;
+               break;
        default:
                qctrl->type = V4L2_CTRL_TYPE_INTEGER;
                break;
index 8e93c6f..bb0a1c8 100644 (file)
@@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc);
 
 
 void videobuf_queue_core_init(struct videobuf_queue *q,
-                        struct videobuf_queue_ops *ops,
+                        const struct videobuf_queue_ops *ops,
                         struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
@@ -360,7 +360,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q,
                q->bufs[i]->bsize  = bsize;
                switch (memory) {
                case V4L2_MEMORY_MMAP:
-                       q->bufs[i]->boff  = bsize * i;
+                       q->bufs[i]->boff = PAGE_ALIGN(bsize) * i;
                        break;
                case V4L2_MEMORY_USERPTR:
                case V4L2_MEMORY_OVERLAY:
@@ -430,9 +430,9 @@ int videobuf_reqbufs(struct videobuf_queue *q,
                count = VIDEO_MAX_FRAME;
        size = 0;
        q->ops->buf_setup(q, &count, &size);
-       size = PAGE_ALIGN(size);
-       dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n",
-               count, size, (count*size)>>PAGE_SHIFT);
+       dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n",
+               count, size,
+               (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) );
 
        retval = __videobuf_mmap_setup(q, count, size, req->memory);
        if (retval < 0) {
@@ -1099,7 +1099,7 @@ int videobuf_cgmbuf(struct videobuf_queue *q,
        mbuf->size   = 0;
        for (i = 0; i < mbuf->frames; i++) {
                mbuf->offsets[i]  = q->bufs[i]->boff;
-               mbuf->size       += q->bufs[i]->bsize;
+               mbuf->size       += PAGE_ALIGN(q->bufs[i]->bsize);
        }
 
        return 0;
index c3065c4..d25f284 100644 (file)
@@ -429,7 +429,7 @@ static struct videobuf_qtype_ops qops = {
 };
 
 void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
-                                   struct videobuf_queue_ops *ops,
+                                   const struct videobuf_queue_ops *ops,
                                    struct device *dev,
                                    spinlock_t *irqlock,
                                    enum v4l2_buf_type type,
index 032ebae..fa78555 100644 (file)
@@ -588,7 +588,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
                        retval = -EBUSY;
                        goto done;
                }
-               size += q->bufs[last]->bsize;
+               size += PAGE_ALIGN(q->bufs[last]->bsize);
                if (size == (vma->vm_end - vma->vm_start))
                        break;
        }
@@ -610,7 +610,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
                        continue;
                q->bufs[i]->map   = map;
                q->bufs[i]->baddr = vma->vm_start + size;
-               size += q->bufs[i]->bsize;
+               size += PAGE_ALIGN(q->bufs[i]->bsize);
        }
 
        map->count    = 1;
@@ -702,7 +702,7 @@ void *videobuf_sg_alloc(size_t size)
 }
 
 void videobuf_queue_sg_init(struct videobuf_queue* q,
-                        struct videobuf_queue_ops *ops,
+                        const struct videobuf_queue_ops *ops,
                         struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
index 0e7dcba..a56cf0d 100644 (file)
@@ -139,7 +139,9 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
                          struct device *device,
                          char *adapter_name,
                          short *adapter_nr,
-                         int mfe_shared)
+                         int mfe_shared,
+                         int (*fe_ioctl_override)(struct dvb_frontend *,
+                                       unsigned int, void *, unsigned int))
 {
        int result;
 
@@ -154,6 +156,7 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
        }
        fe->adapter.priv = adapter_priv;
        fe->adapter.mfe_shared = mfe_shared;
+       fe->adapter.fe_ioctl_override = fe_ioctl_override;
 
        return result;
 }
@@ -253,7 +256,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
                          void *adapter_priv,
                          struct device *device,
                          short *adapter_nr,
-                         int mfe_shared)
+                         int mfe_shared,
+                         int (*fe_ioctl_override)(struct dvb_frontend *,
+                                       unsigned int, void *, unsigned int))
 {
        struct list_head *list, *q;
        struct videobuf_dvb_frontend *fe;
@@ -267,7 +272,7 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
 
        /* Bring up the adapter */
        res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
-               fe->dvb.name, adapter_nr, mfe_shared);
+               fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override);
        if (res < 0) {
                printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
                return res;
index 35f3900..d6e6a28 100644 (file)
@@ -391,8 +391,8 @@ static struct videobuf_qtype_ops qops = {
 };
 
 void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
-                        struct videobuf_queue_ops *ops,
-                        void *dev,
+                        const struct videobuf_queue_ops *ops,
+                        struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
                         enum v4l2_field field,
index 97e0ce2..33205d7 100644 (file)
@@ -391,7 +391,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd,
                {0x0e, 1}
        };
 
-       if (input < 0 || input > 2)
+       if (input > 2)
                return -EINVAL;
 
        v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
index d439c76..cb1de7e 100644 (file)
@@ -106,7 +106,7 @@ struct zoran_params {
        unsigned long jpeg_markers;     /* Which markers should go into the JPEG output.
                                         * Unless you exactly know what you do, leave them untouched.
                                         * Inluding less markers will make the resulting code
-                                        * smaller, but there will be fewer aplications
+                                        * smaller, but there will be fewer applications
                                         * which can read it.
                                         * The presence of the APP and COM marker is
                                         * influenced by APP0_len and COM_len ONLY! */
index 47137de..e9f72ca 100644 (file)
@@ -2764,7 +2764,7 @@ static int zoran_enum_input(struct file *file, void *__fh,
        struct zoran_fh *fh = __fh;
        struct zoran *zr = fh->zr;
 
-       if (inp->index < 0 || inp->index >= zr->card.inputs)
+       if (inp->index >= zr->card.inputs)
                return -EINVAL;
        else {
                int id = inp->index;
index 9aae011..2ef110b 100644 (file)
@@ -115,6 +115,7 @@ static struct usb_device_id device_table[] = {
        {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 },
        {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 },
        {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 },
+       {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 },
        {}                      /* Terminating entry */
 };
 
index 8dd4d21..b494867 100644 (file)
@@ -76,8 +76,8 @@
 #define COPYRIGHT      "Copyright (c) 1999-2008 " MODULEAUTHOR
 #endif
 
-#define MPT_LINUX_VERSION_COMMON       "3.04.12"
-#define MPT_LINUX_PACKAGE_NAME         "@(#)mptlinux-3.04.12"
+#define MPT_LINUX_VERSION_COMMON       "3.04.13"
+#define MPT_LINUX_PACKAGE_NAME         "@(#)mptlinux-3.04.13"
 #define WHAT_MAGIC_STRING              "@" "(" "#" ")"
 
 #define show_mptmod_ver(s,ver)  \
index 9b2e219..352acd0 100644 (file)
@@ -621,11 +621,8 @@ __mptctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
         */
        iocnumX = khdr.iocnum & 0xFF;
        if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
-           (iocp == NULL)) {
-               printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - ioc%d not found!\n",
-                               __FILE__, __LINE__, iocnumX);
+           (iocp == NULL))
                return -ENODEV;
-       }
 
        if (!iocp->active) {
                printk(KERN_DEBUG MYNAM "%s::mptctl_ioctl() @%d - Controller disabled.\n",
index c295786..5775275 100644 (file)
@@ -792,11 +792,36 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
                         *  precedence!
                         */
                        sc->result = (DID_OK << 16) | scsi_status;
-                       if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
-                               /* Have already saved the status and sense data
+                       if (!(scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)) {
+
+                               /*
+                                * For an Errata on LSI53C1030
+                                * When the length of request data
+                                * and transfer data are different
+                                * with result of command (READ or VERIFY),
+                                * DID_SOFT_ERROR is set.
                                 */
-                               ;
-                       } else {
+                               if (ioc->bus_type == SPI) {
+                                       if (pScsiReq->CDB[0] == READ_6  ||
+                                           pScsiReq->CDB[0] == READ_10 ||
+                                           pScsiReq->CDB[0] == READ_12 ||
+                                           pScsiReq->CDB[0] == READ_16 ||
+                                           pScsiReq->CDB[0] == VERIFY  ||
+                                           pScsiReq->CDB[0] == VERIFY_16) {
+                                               if (scsi_bufflen(sc) !=
+                                                       xfer_cnt) {
+                                                       sc->result =
+                                                       DID_SOFT_ERROR << 16;
+                                                   printk(KERN_WARNING "Errata"
+                                                   "on LSI53C1030 occurred."
+                                                   "sc->req_bufflen=0x%02x,"
+                                                   "xfer_cnt=0x%02x\n",
+                                                   scsi_bufflen(sc),
+                                                   xfer_cnt);
+                                               }
+                                       }
+                               }
+
                                if (xfer_cnt < sc->underflow) {
                                        if (scsi_status == SAM_STAT_BUSY)
                                                sc->result = SAM_STAT_BUSY;
@@ -835,7 +860,58 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
                        sc->result = (DID_OK << 16) | scsi_status;
                        if (scsi_state == 0) {
                                ;
-                       } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
+                       } else if (scsi_state &
+                           MPI_SCSI_STATE_AUTOSENSE_VALID) {
+
+                               /*
+                                * For potential trouble on LSI53C1030.
+                                * (date:2007.xx.)
+                                * It is checked whether the length of
+                                * request data is equal to
+                                * the length of transfer and residual.
+                                * MEDIUM_ERROR is set by incorrect data.
+                                */
+                               if ((ioc->bus_type == SPI) &&
+                                       (sc->sense_buffer[2] & 0x20)) {
+                                       u32      difftransfer;
+                                       difftransfer =
+                                       sc->sense_buffer[3] << 24 |
+                                       sc->sense_buffer[4] << 16 |
+                                       sc->sense_buffer[5] << 8 |
+                                       sc->sense_buffer[6];
+                                       if (((sc->sense_buffer[3] & 0x80) ==
+                                               0x80) && (scsi_bufflen(sc)
+                                               != xfer_cnt)) {
+                                               sc->sense_buffer[2] =
+                                                   MEDIUM_ERROR;
+                                               sc->sense_buffer[12] = 0xff;
+                                               sc->sense_buffer[13] = 0xff;
+                                               printk(KERN_WARNING"Errata"
+                                               "on LSI53C1030 occurred."
+                                               "sc->req_bufflen=0x%02x,"
+                                               "xfer_cnt=0x%02x\n" ,
+                                               scsi_bufflen(sc),
+                                               xfer_cnt);
+                                       }
+                                       if (((sc->sense_buffer[3] & 0x80)
+                                               != 0x80) &&
+                                               (scsi_bufflen(sc) !=
+                                               xfer_cnt + difftransfer)) {
+                                               sc->sense_buffer[2] =
+                                                       MEDIUM_ERROR;
+                                               sc->sense_buffer[12] = 0xff;
+                                               sc->sense_buffer[13] = 0xff;
+                                               printk(KERN_WARNING
+                                               "Errata on LSI53C1030 occurred"
+                                               "sc->req_bufflen=0x%02x,"
+                                               " xfer_cnt=0x%02x,"
+                                               "difftransfer=0x%02x\n",
+                                               scsi_bufflen(sc),
+                                               xfer_cnt,
+                                               difftransfer);
+                                       }
+                               }
+
                                /*
                                 * If running against circa 200003dd 909 MPT f/w,
                                 * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
@@ -2275,11 +2351,12 @@ mptscsih_slave_destroy(struct scsi_device *sdev)
  *     mptscsih_change_queue_depth - This function will set a devices queue depth
  *     @sdev: per scsi_device pointer
  *     @qdepth: requested queue depth
+ *     @reason: calling context
  *
  *     Adding support for new 'change_queue_depth' api.
 */
 int
-mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
+mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
 {
        MPT_SCSI_HOST           *hd = shost_priv(sdev->host);
        VirtTarget              *vtarget;
@@ -2291,6 +2368,9 @@ mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
        starget = scsi_target(sdev);
        vtarget = starget->hostdata;
 
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (ioc->bus_type == SPI) {
                if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
                        max_depth = 1;
@@ -2357,7 +2437,8 @@ 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);
+       mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH,
+                                   SCSI_QDEPTH_DEFAULT);
        dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
                "tagged %d, simple %d, ordered %d\n",
                ioc->name,sdev->tagged_supported, sdev->simple_tags,
index e0b33e0..45a5ff3 100644 (file)
@@ -128,7 +128,8 @@ extern int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_F
 extern int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
 extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
 extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
-extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
+extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                      int reason);
 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[];
index d505b68..e39986a 100644 (file)
@@ -940,7 +940,7 @@ static const struct block_device_operations i2o_block_fops = {
  *     Allocate memory for the i2o_block_device struct, gendisk and request
  *     queue and initialize them as far as no additional information is needed.
  *
- *     Returns a pointer to the allocated I2O Block device on succes or a
+ *     Returns a pointer to the allocated I2O Block device on success or a
  *     negative error code on failure.
  */
 static struct i2o_block_device *i2o_block_device_alloc(void)
index 27cf4af..e5ab621 100644 (file)
@@ -132,7 +132,7 @@ u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
  *     Removes a previously added pointer from the context list and returns
  *     the matching context id.
  *
- *     Returns context id on succes or 0 on failure.
+ *     Returns context id on success or 0 on failure.
  */
 u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
 {
@@ -198,7 +198,7 @@ void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
  *     @c: controller to which the context list belong
  *     @ptr: pointer to which the context id should be fetched
  *
- *     Returns context id which matches to the pointer on succes or 0 on
+ *     Returns context id which matches to the pointer on success or 0 on
  *     failure.
  */
 u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr)
index 08f2d07..a296e71 100644 (file)
@@ -35,6 +35,14 @@ config MFD_ASIC3
          This driver supports the ASIC3 multifunction chip found on many
          PDAs (mainly iPAQ and HTC based ones)
 
+config MFD_SH_MOBILE_SDHI
+       bool "Support for SuperH Mobile SDHI"
+       depends on SUPERH
+       select MFD_CORE
+        ---help---
+         This driver supports the SDHI hardware block found in many
+         SuperH Mobile SoCs.
+
 config MFD_DM355EVM_MSP
        bool "DaVinci DM355 EVM microcontroller"
        depends on I2C && MACH_DAVINCI_DM355_EVM
index af0fc90..11350c1 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_MFD_SM501)                += sm501.o
 obj-$(CONFIG_MFD_ASIC3)                += asic3.o
+obj-$(CONFIG_MFD_SH_MOBILE_SDHI)               += sh_mobile_sdhi.o
 
 obj-$(CONFIG_HTC_EGPIO)                += htc-egpio.o
 obj-$(CONFIG_HTC_PASIC3)       += htc-pasic3.o
diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c
new file mode 100644 (file)
index 0000000..03efae8
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * SuperH Mobile SDHI
+ *
+ * Copyright (C) 2009 Magnus Damm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on "Compaq ASIC3 support":
+ *
+ * Copyright 2001 Compaq Computer Corporation.
+ * Copyright 2004-2005 Phil Blundell
+ * Copyright 2007-2008 OpenedHand Ltd.
+ *
+ * Authors: Phil Blundell <pb@handhelds.org>,
+ *         Samuel Ortiz <sameo@openedhand.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+#include <linux/mfd/sh_mobile_sdhi.h>
+
+struct sh_mobile_sdhi {
+       struct clk *clk;
+       struct tmio_mmc_data mmc_data;
+       struct mfd_cell cell_mmc;
+};
+
+static struct resource sh_mobile_sdhi_resources[] = {
+       {
+               .start = 0x000,
+               .end   = 0x1ff,
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = 0,
+               .end   = 0,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct mfd_cell sh_mobile_sdhi_cell = {
+       .name          = "tmio-mmc",
+       .num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources),
+       .resources     = sh_mobile_sdhi_resources,
+};
+
+static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state)
+{
+       struct platform_device *pdev = to_platform_device(tmio->dev.parent);
+       struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
+
+       if (p && p->set_pwr)
+               p->set_pwr(pdev, state);
+}
+
+static int __init sh_mobile_sdhi_probe(struct platform_device *pdev)
+{
+       struct sh_mobile_sdhi *priv;
+       struct resource *mem;
+       char clk_name[8];
+       int ret, irq;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem)
+               dev_err(&pdev->dev, "missing MEM resource\n");
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               dev_err(&pdev->dev, "missing IRQ resource\n");
+
+       if (!mem || (irq < 0))
+               return -EINVAL;
+
+       priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
+       if (priv == NULL) {
+               dev_err(&pdev->dev, "kzalloc failed\n");
+               return -ENOMEM;
+       }
+
+       snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
+       priv->clk = clk_get(&pdev->dev, clk_name);
+       if (IS_ERR(priv->clk)) {
+               dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+               ret = PTR_ERR(priv->clk);
+               kfree(priv);
+               return ret;
+       }
+
+       clk_enable(priv->clk);
+
+       /* FIXME: silly const unsigned int hclk */
+       *(unsigned int *)&priv->mmc_data.hclk = clk_get_rate(priv->clk);
+       priv->mmc_data.set_pwr = sh_mobile_sdhi_set_pwr;
+
+       memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc));
+       priv->cell_mmc.driver_data = &priv->mmc_data;
+       priv->cell_mmc.platform_data = &priv->cell_mmc;
+       priv->cell_mmc.data_size = sizeof(priv->cell_mmc);
+
+       platform_set_drvdata(pdev, priv);
+
+       ret = mfd_add_devices(&pdev->dev, pdev->id,
+                             &priv->cell_mmc, 1, mem, irq);
+       if (ret) {
+               clk_disable(priv->clk);
+               clk_put(priv->clk);
+               kfree(priv);
+       }
+
+       return ret;
+}
+
+static int sh_mobile_sdhi_remove(struct platform_device *pdev)
+{
+       struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev);
+
+       mfd_remove_devices(&pdev->dev);
+       clk_disable(priv->clk);
+       clk_put(priv->clk);
+       kfree(priv);
+
+       return 0;
+}
+
+static struct platform_driver sh_mobile_sdhi_driver = {
+       .driver         = {
+               .name   = "sh_mobile_sdhi",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sh_mobile_sdhi_probe,
+       .remove         = __devexit_p(sh_mobile_sdhi_remove),
+};
+
+static int __init sh_mobile_sdhi_init(void)
+{
+       return platform_driver_register(&sh_mobile_sdhi_driver);
+}
+
+static void __exit sh_mobile_sdhi_exit(void)
+{
+       platform_driver_unregister(&sh_mobile_sdhi_driver);
+}
+
+module_init(sh_mobile_sdhi_init);
+module_exit(sh_mobile_sdhi_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
index fa294b6..85fd942 100644 (file)
@@ -51,6 +51,7 @@ static int ucb1400_core_probe(struct device *dev)
        struct ucb1400_ts ucb_ts;
        struct ucb1400_gpio ucb_gpio;
        struct snd_ac97 *ac97;
+       struct ucb1400_pdata *pdata = dev->platform_data;
 
        memset(&ucb_ts, 0, sizeof(ucb_ts));
        memset(&ucb_gpio, 0, sizeof(ucb_gpio));
@@ -88,6 +89,12 @@ static int ucb1400_core_probe(struct device *dev)
 
        /* TOUCHSCREEN */
        ucb_ts.ac97 = ac97;
+
+       if (pdata != NULL && pdata->irq >= 0)
+               ucb_ts.irq = pdata->irq;
+       else
+               ucb_ts.irq = -1;
+
        ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1);
        if (!ucb->ucb1400_ts) {
                err = -ENOMEM;
index 41c8fe2..ce5eda9 100644 (file)
@@ -92,7 +92,7 @@ static void gru_vma_close(struct vm_area_struct *vma)
 /*
  * gru_file_mmap
  *
- * Called when mmaping the device.  Initializes the vma with a fault handler
+ * Called when mmapping the device.  Initializes the vma with a fault handler
  * and private data structure necessary to allocate, track, and free the
  * underlying pages.
  */
index 36a8d53..b8e7c5a 100644 (file)
@@ -231,7 +231,8 @@ static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
        return ret;
 }
 
-static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
+static void sdio_uart_write_mctrl(struct sdio_uart_port *port,
+                                 unsigned int mctrl)
 {
        unsigned char mcr = 0;
 
@@ -387,7 +388,8 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port)
        sdio_out(port, UART_IER, port->ier);
 }
 
-static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status)
+static void sdio_uart_receive_chars(struct sdio_uart_port *port,
+                                   unsigned int *status)
 {
        struct tty_struct *tty = port->tty;
        unsigned int ch, flag;
@@ -399,7 +401,7 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s
                port->icount.rx++;
 
                if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
-                                       UART_LSR_FE | UART_LSR_OE))) {
+                                       UART_LSR_FE | UART_LSR_OE))) {
                        /*
                         * For statistics only
                         */
@@ -417,9 +419,9 @@ static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *s
                         * Mask off conditions which should be ignored.
                         */
                        *status &= port->read_status_mask;
-                       if (*status & UART_LSR_BI) {
+                       if (*status & UART_LSR_BI)
                                flag = TTY_BREAK;
-                       else if (*status & UART_LSR_PE)
+                       else if (*status & UART_LSR_PE)
                                flag = TTY_PARITY;
                        else if (*status & UART_LSR_FE)
                                flag = TTY_FRAME;
@@ -574,7 +576,7 @@ static int sdio_uart_startup(struct sdio_uart_port *port)
         */
        sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
        sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
-                       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+                      UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
        sdio_out(port, UART_FCR, 0);
 
        /*
@@ -635,7 +637,7 @@ static void sdio_uart_shutdown(struct sdio_uart_port *port)
        if (port->tty->termios->c_cflag & HUPCL)
                sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
 
-        /* Disable interrupts from this port */
+       /* Disable interrupts from this port */
        sdio_release_irq(port->func);
        port->ier = 0;
        sdio_out(port, UART_IER, 0);
@@ -659,7 +661,7 @@ skip:
        free_page((unsigned long)port->xmit.buf);
 }
 
-static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
+static int sdio_uart_open(struct tty_struct *tty, struct file *filp)
 {
        struct sdio_uart_port *port;
        int ret;
@@ -846,7 +848,7 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t
        struct sdio_uart_port *port = tty->driver_data;
        unsigned int cflag = tty->termios->c_cflag;
 
-#define RELEVANT_IFLAG(iflag)   ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+#define RELEVANT_IFLAG(iflag)  ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 
        if ((cflag ^ old_termios->c_cflag) == 0 &&
            RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
@@ -925,7 +927,7 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
        struct sdio_uart_port *port = tty->driver_data;
        int result;
 
-       result =sdio_uart_claim_func(port);
+       result = sdio_uart_claim_func(port);
        if(!result) {
                sdio_uart_update_mctrl(port, set, clear);
                sdio_uart_release_func(port);
@@ -946,31 +948,31 @@ static int sdio_uart_proc_show(struct seq_file *m, void *v)
                        seq_printf(m, "%d: uart:SDIO", i);
                        if(capable(CAP_SYS_ADMIN)) {
                                seq_printf(m, " tx:%d rx:%d",
-                                              port->icount.tx, port->icount.rx);
+                                             port->icount.tx, port->icount.rx);
                                if (port->icount.frame)
                                        seq_printf(m, " fe:%d",
-                                                      port->icount.frame);
+                                                     port->icount.frame);
                                if (port->icount.parity)
                                        seq_printf(m, " pe:%d",
-                                                      port->icount.parity);
+                                                     port->icount.parity);
                                if (port->icount.brk)
                                        seq_printf(m, " brk:%d",
-                                                      port->icount.brk);
+                                                     port->icount.brk);
                                if (port->icount.overrun)
                                        seq_printf(m, " oe:%d",
-                                                      port->icount.overrun);
+                                                     port->icount.overrun);
                                if (port->icount.cts)
                                        seq_printf(m, " cts:%d",
-                                                      port->icount.cts);
+                                                     port->icount.cts);
                                if (port->icount.dsr)
                                        seq_printf(m, " dsr:%d",
-                                                      port->icount.dsr);
+                                                     port->icount.dsr);
                                if (port->icount.rng)
                                        seq_printf(m, " rng:%d",
-                                                      port->icount.rng);
+                                                     port->icount.rng);
                                if (port->icount.dcd)
                                        seq_printf(m, " dcd:%d",
-                                                      port->icount.dcd);
+                                                     port->icount.dcd);
                        }
                        sdio_uart_port_put(port);
                        seq_putc(m, '\n');
index 432ae83..e04b751 100644 (file)
@@ -329,7 +329,7 @@ config MMC_SDRICOH_CS
 
 config MMC_TMIO
        tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
-       depends on MFD_TMIO || MFD_ASIC3
+       depends on MFD_TMIO || MFD_ASIC3 || SUPERH
        help
          This provides support for the SD/MMC cell found in TC6393XB,
          T7L66XB and also HTC ASIC3
index 99b74a3..941a4d3 100644 (file)
@@ -1360,7 +1360,7 @@ static struct mmc_host_ops s3cmci_ops = {
 
 static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
        /* This is currently here to avoid a number of if (host->pdata)
-        * checks. Any zero fields to ensure reaonable defaults are picked. */
+        * checks. Any zero fields to ensure reasonable defaults are picked. */
 };
 
 #ifdef CONFIG_CPU_FREQ
index 9408099..35c6a23 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/mtd/chips/Kconfig
-
 menu "RAM/ROM/Flash chip drivers"
        depends on MTD!=n
 
@@ -242,4 +240,3 @@ config MTD_XIP
          then say N.
 
 endmenu
-
index c222514..35081ce 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/mtd/maps/Kconfig
-
 menu "Self-contained MTD device drivers"
        depends on MTD!=n
 
@@ -308,4 +306,3 @@ config MTD_DOCPROBE_55AA
          you have managed to wipe the first block.
 
 endmenu
-
index 3aa05cd..592016a 100644 (file)
@@ -18,7 +18,7 @@
                 to specify the offset instead of the absolute address
 
   NOTE:
-  With slram it's only possible to map a contigous memory region. Therfore
+  With slram it's only possible to map a contiguous memory region. Therefore
   if there's a device mapped somewhere in the region specified slram will
   fail to load (see kernel log if modprobe fails).
 
index 5a401d8..265f969 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/mtd/chips/Kconfig
-
 menu "LPDDR flash memory drivers"
        depends on MTD!=n
 
@@ -20,4 +18,3 @@ config MTD_QINFO_PROBE
            families of devices. This serves similar purpose of CFI on legacy
            Flash products
 endmenu
-
index 14be075..847e214 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/mtd/maps/Kconfig
-
 menu "Mapping drivers for chip access"
        depends on MTD!=n
 
@@ -389,9 +387,9 @@ config MTD_IXP2000
        depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000
        help
          This enables MTD access to flash devices on platforms based
-         on Intel's IXP2000 family of network processors such as the
-         IXDP425 and Coyote. If you have an IXP2000 based board and
-         would like to use the flash chips on it, say 'Y'.
+         on Intel's IXP2000 family of network processors. If you have an
+         IXP2000 based board and would like to use the flash chips on it,
+         say 'Y'.
 
 config MTD_FORTUNET
        tristate "CFI Flash device mapped on the FortuNet board"
index 8f8e87b..0e35e1a 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/mtd/nand/Kconfig
-
 menuconfig MTD_NAND
        tristate "NAND Device Support"
        depends on MTD
index e51c1ed..b126cf8 100644 (file)
@@ -1056,7 +1056,7 @@ static struct nand_ecclayout doc200x_oobinfo = {
 };
 
 /* Find the (I)NFTL Media Header, and optionally also the mirror media header.
-   On sucessful return, buf will contain a copy of the media header for
+   On successful return, buf will contain a copy of the media header for
    further processing.  id is the string to scan for, and will presumably be
    either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
    header.  The page #s of the found media headers are placed in mh0_page and
index db7ae9d..92320a6 100644 (file)
@@ -475,7 +475,7 @@ int __nand_correct_data(unsigned char *buf,
                 *
                 * The b2 shift is there to get rid of the lowest two bits.
                 * We could also do addressbits[b2] >> 1 but for the
-                * performace it does not make any difference
+                * performance it does not make any difference
                 */
                if (eccsize_mult == 1)
                        byte_addr = (addressbits[b1] << 4) + addressbits[b0];
index 11dc7e6..68b5b3a 100644 (file)
@@ -875,7 +875,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
  * @info: The controller instance.
  * @nmtd: The driver version of the MTD instance.
  *
- * This routine is called after the chip probe has succesfully completed
+ * This routine is called after the chip probe has successfully completed
  * and the relevant per-chip information updated. This call ensure that
  * we update the internal state accordingly.
  *
index a38f580..3a9f157 100644 (file)
@@ -1,7 +1,3 @@
-#
-# linux/drivers/mtd/onenand/Kconfig
-#
-
 menuconfig MTD_ONENAND
        tristate "OneNAND Device Support"
        depends on MTD
index b1cd7a1..0a8c7ea 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/mtd/ubi/Kconfig
-
 menu "UBI - Unsorted block images"
        depends on MTD
 
index 88a72e9..277786e 100644 (file)
@@ -22,6 +22,8 @@
 
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
 #include <asm/div64.h>
 #include "ubi.h"
 
@@ -279,6 +281,44 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
 }
 EXPORT_SYMBOL_GPL(ubi_open_volume_nm);
 
+/**
+ * ubi_open_volume_path - open UBI volume by its character device node path.
+ * @pathname: volume character device node path
+ * @mode: open mode
+ *
+ * This function is similar to 'ubi_open_volume()', but opens a volume the path
+ * to its character device node.
+ */
+struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode)
+{
+       int error, ubi_num, vol_id;
+       struct ubi_volume_desc *ret;
+       struct inode *inode;
+       struct path path;
+
+       dbg_gen("open volume %s, mode %d", pathname, mode);
+
+       if (!pathname || !*pathname)
+               return ERR_PTR(-EINVAL);
+
+       error = kern_path(pathname, LOOKUP_FOLLOW, &path);
+       if (error)
+               return ERR_PTR(error);
+
+       inode = path.dentry->d_inode;
+       ubi_num = ubi_major2num(imajor(inode));
+       vol_id = iminor(inode) - 1;
+
+       if (vol_id >= 0 && ubi_num >= 0)
+               ret = ubi_open_volume(ubi_num, vol_id, mode);
+       else
+               ret = ERR_PTR(-ENODEV);
+
+       path_put(&path);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ubi_open_volume_path);
+
 /**
  * ubi_close_volume - close UBI volume.
  * @desc: volume descriptor
index 74fdc40..c1d7b88 100644 (file)
@@ -147,12 +147,14 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol,
        }
 
        if (bytes == 0) {
+               err = ubi_wl_flush(ubi);
+               if (err)
+                       return err;
+
                err = clear_update_marker(ubi, vol, 0);
                if (err)
                        return err;
-               err = ubi_wl_flush(ubi);
-               if (!err)
-                       vol->updating = 0;
+               vol->updating = 0;
        }
 
        vol->upd_buf = vmalloc(ubi->leb_size);
@@ -362,16 +364,16 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol,
 
        ubi_assert(vol->upd_received <= vol->upd_bytes);
        if (vol->upd_received == vol->upd_bytes) {
+               err = ubi_wl_flush(ubi);
+               if (err)
+                       return err;
                /* The update is finished, clear the update marker */
                err = clear_update_marker(ubi, vol, vol->upd_bytes);
                if (err)
                        return err;
-               err = ubi_wl_flush(ubi);
-               if (err == 0) {
-                       vol->updating = 0;
-                       err = to_write;
-                       vfree(vol->upd_buf);
-               }
+               vol->updating = 0;
+               err = to_write;
+               vfree(vol->upd_buf);
        }
 
        return err;
index ea6b139..1663bc9 100644 (file)
@@ -19,7 +19,7 @@
    TBD:
    * look at deferring rx frames rather than discarding (as per tulip)
    * handle tx ring full as per tulip
-   * performace test to tune rx_copybreak
+   * performance test to tune rx_copybreak
 
    Most of my modifications relate to the braindead big-endian
    implementation by Intel.  When the i596 is operating in
index 4e6359f..766aabf 100644 (file)
@@ -1633,8 +1633,13 @@ static int amd8111e_enable_link_change(struct amd8111e_priv* lp)
        readl(lp->mmio + CMD7);
        return 0;
 }
-/* This function is called when a packet transmission fails to complete within a  resonable period, on the assumption that an interrupts have been failed or the  interface is locked up. This function will reinitialize the hardware */
 
+/*
+ * This function is called when a packet transmission fails to complete
+ * within a reasonable period, on the assumption that an interrupt have
+ * failed or the interface is locked up. This function will reinitialize
+ * the hardware.
+ */
 static void amd8111e_tx_timeout(struct net_device *dev)
 {
        struct amd8111e_priv* lp = netdev_priv(dev);
index 50cecf4..73b38c2 100644 (file)
@@ -120,7 +120,7 @@ static int irq = 5;         /* Default IRQ */
  *      DAYNA driver mode:
  *              Dayna DL2000/DaynaTalk PC (Half Length), COPS LT-95, 
  *             Farallon PhoneNET PC III, Farallon PhoneNET PC II
- *     Other cards possibly supported mode unkown though:
+ *     Other cards possibly supported mode unknown though:
  *             Dayna DL2000 (Full length), COPS LT/M (Micro-Channel)
  *
  *     Cards NOT supported by this driver but supported by the ltpc.c
index bb613f2..727be5c 100644 (file)
@@ -244,7 +244,7 @@ struct Am79C960 {
 #define DLNKTST                0x0010  /* Disable Link Status */
 #define DAPC           0x0008  /* Disable Automatic Polarity Correction */
 #define MENDECL                0x0004  /* MENDEC Loopback Mode */
-#define LRTTSEL                0x0002  /* Low Receive Treshold/Transmit Mode Select */
+#define LRTTSEL                0x0002  /* Low Receive Threshold/Transmit Mode Select */
 #define PORTSEL1       0x0001  /* Port Select Bits */
 #define PORTSEL2       0x8000  /* Port Select Bits */
 #define INTL           0x4000  /* Internal Loopback */
index 1e2f57d..6eb9241 100644 (file)
@@ -1540,7 +1540,7 @@ static irqreturn_t atl1c_intr(int irq, void *data)
                if (status & ISR_OVER)
                        if (netif_msg_intr(adapter))
                                dev_warn(&pdev->dev,
-                                       "TX/RX over flow (status = 0x%x)\n",
+                                       "TX/RX overflow (status = 0x%x)\n",
                                        status & ISR_OVER);
 
                /* link event */
index e7323be..92b87ef 100644 (file)
@@ -444,7 +444,7 @@ enum be_if_flags {
  * filtering capabilities. */
 struct be_cmd_req_if_create {
        struct be_cmd_req_hdr hdr;
-       u32 version;            /* ignore currntly */
+       u32 version;            /* ignore currently */
        u32 capability_flags;
        u32 enable_flags;
        u8 mac_addr[ETH_ALEN];
index 957a0f7..24c7d99 100644 (file)
@@ -1982,7 +1982,7 @@ int be_load_fw(struct be_adapter *adapter, u8 *func)
                goto fw_exit;
        }
 
-       dev_info(&adapter->pdev->dev, "Firmware flashed succesfully\n");
+       dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n");
 
 fw_exit:
        release_firmware(fw);
index b668173..944964e 100644 (file)
 /* [RC 1] A flag to indicate that overflow error occurred in one of the
    queues. */
 #define QM_REG_OVFERROR                                         0x16805c
-/* [RC 7] the Q were the qverflow occurs */
+/* [RC 7] the Q where the overflow occurs */
 #define QM_REG_OVFQNUM                                          0x168058
 /* [R 16] Pause state for physical queues 15-0 */
 #define QM_REG_PAUSESTATE0                                      0x168410
index 0d30d1e..00ab51e 100644 (file)
@@ -556,7 +556,7 @@ static void rlb_update_rx_clients(struct bonding *bond)
                }
        }
 
-       /* do not update the entries again untill this counter is zero so that
+       /* do not update the entries again until this counter is zero so that
         * not to confuse the clients.
         */
        bond_info->rlb_update_delay_counter = RLB_UPDATE_DELAY;
index 49f3de7..bdbd147 100644 (file)
@@ -1285,7 +1285,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev)
 
        /*
         * We do not use Tx completion interrupts to free DMAd Tx packets.
-        * This is good for performamce but means that we rely on new Tx
+        * This is good for performance but means that we rely on new Tx
         * packets arriving to run the destructors of completed packets,
         * which open up space in their sockets' send queues.  Sometimes
         * we do not get such new packets causing Tx to stall.  A single
index d768852..75b099c 100644 (file)
@@ -118,7 +118,7 @@ doit:
        ret = ehea_set_portspeed(port, sp);
 
        if (!ret)
-               ehea_info("%s: Port speed succesfully set: %dMbps "
+               ehea_info("%s: Port speed successfully set: %dMbps "
                          "%s Duplex",
                          port->netdev->name, port->port_speed,
                          port->full_duplex == 1 ? "Full" : "Half");
@@ -134,7 +134,7 @@ static int ehea_nway_reset(struct net_device *dev)
        ret = ehea_set_portspeed(port, EHEA_SPEED_AUTONEG);
 
        if (!ret)
-               ehea_info("%s: Port speed succesfully set: %dMbps "
+               ehea_info("%s: Port speed successfully set: %dMbps "
                          "%s Duplex",
                          port->netdev->name, port->port_speed,
                          port->full_duplex == 1 ? "Full" : "Half");
index ed60fd6..0cab992 100644 (file)
@@ -35,7 +35,7 @@
  *          driver only supports standard serial hardware (8250, 16450, 16550A)
  *
  *          This modem usually draws its supply current out of the otherwise unused
- *          TXD pin of the serial port. Thus a contignuous stream of 0x00-bytes
+ *          TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes
  *          is transmitted to achieve a positive supply voltage.
  *
  *  hsk:    This is a 4800 baud FSK modem, designed for TNC use. It works fine
index 1e8dd8c..8f7d0d1 100644 (file)
@@ -115,7 +115,7 @@ struct smsc_ircc_subsystem_configuration {
        unsigned short vendor; /* PCI vendor ID */
        unsigned short device; /* PCI vendor ID */
        unsigned short subvendor; /* PCI subsystem vendor ID */
-       unsigned short subdevice; /* PCI sybsystem device ID */
+       unsigned short subdevice; /* PCI subsystem device ID */
        unsigned short sir_io; /* I/O port for SIR */
        unsigned short fir_io; /* I/O port for FIR */
        unsigned char  fir_irq; /* FIR IRQ */
index 4999719..16c9191 100644 (file)
@@ -1384,7 +1384,7 @@ static inline void veth_build_dma_list(struct dma_chunk *list,
        unsigned long done;
        int i = 1;
 
-       /* FIXME: skbs are continguous in real addresses.  Do we
+       /* FIXME: skbs are contiguous in real addresses.  Do we
         * really need to break it into PAGE_SIZE chunks, or can we do
         * it just at the granularity of iSeries real->absolute
         * mapping?  Indeed, given the way the allocator works, can we
index a0c5785..b77238d 100644 (file)
@@ -47,7 +47,7 @@
    TBD:
    * look at deferring rx frames rather than discarding (as per tulip)
    * handle tx ring full as per tulip
-   * performace test to tune rx_copybreak
+   * performance test to tune rx_copybreak
 
    Most of my modifications relate to the braindead big-endian
    implementation by Intel.  When the i596 is operating in
index 7a07430..b117f7f 100644 (file)
@@ -47,7 +47,7 @@
    TBD:
    * look at deferring rx frames rather than discarding (as per tulip)
    * handle tx ring full as per tulip
-   * performace test to tune rx_copybreak
+   * performance test to tune rx_copybreak
 
    Most of my modifications relate to the braindead big-endian
    implementation by Intel.  When the i596 is operating in
index 03b781a..829b9ec 100644 (file)
@@ -204,7 +204,7 @@ static void mlx4_en_free_rx_desc(struct mlx4_en_priv *priv,
                en_dbg(DRV, priv, "Freeing fragment:%d\n", nr);
                dma = be64_to_cpu(rx_desc->data[nr].addr);
 
-               en_dbg(DRV, priv, "Unmaping buffer at dma:0x%llx\n", (u64) dma);
+               en_dbg(DRV, priv, "Unmapping buffer at dma:0x%llx\n", (u64) dma);
                pci_unmap_single(mdev->pdev, dma, skb_frags[nr].size,
                                 PCI_DMA_FROMDEVICE);
                put_page(skb_frags[nr].page);
index 8c72799..3d1396a 100644 (file)
@@ -47,7 +47,7 @@ enum {
 static int inline_thold __read_mostly = MAX_INLINE;
 
 module_param_named(inline_thold, inline_thold, int, 0444);
-MODULE_PARM_DESC(inline_thold, "treshold for using inline data");
+MODULE_PARM_DESC(inline_thold, "threshold for using inline data");
 
 int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv,
                           struct mlx4_en_tx_ring *ring, u32 size,
index 4376147..82c3ebc 100644 (file)
@@ -162,7 +162,7 @@ enum {
 #define MLX4_EN_DEF_RX_PAUSE   1
 #define MLX4_EN_DEF_TX_PAUSE   1
 
-/* Interval between sucessive polls in the Tx routine when polling is used
+/* Interval between successive polls in the Tx routine when polling is used
    instead of interrupts (in per-core Tx rings) - should be power of 2 */
 #define MLX4_EN_TX_POLL_MODER  16
 #define MLX4_EN_TX_POLL_TIMEOUT        (HZ / 4)
index 89c4948..0c76859 100644 (file)
@@ -314,7 +314,7 @@ static void gelic_card_reset_chain(struct gelic_card *card,
  * @card: card structure
  * @descr: descriptor to re-init
  *
- * return 0 on succes, <0 on failure
+ * return 0 on success, <0 on failure
  *
  * allocates a new rx skb, iommu-maps it and attaches it to the descriptor.
  * Activate the descriptor state-wise
index 5b631c6..0a88b53 100644 (file)
@@ -199,7 +199,7 @@ struct gelic_eurus_rssi_info {
 /* for 'stat' member of gelic_wl_info */
 enum gelic_wl_info_status_bit {
        GELIC_WL_STAT_CONFIGURED,
-       GELIC_WL_STAT_CH_INFO,   /* ch info aquired */
+       GELIC_WL_STAT_CH_INFO,   /* ch info acquired */
        GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */
        GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */
        GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */
index e3e6bc9..dd35066 100644 (file)
@@ -3651,7 +3651,7 @@ static int ql_adapter_up(struct ql3_adapter *qdev)
                ql_sem_unlock(qdev, QL_DRVR_SEM_MASK);
        } else {
                printk(KERN_ERR PFX
-                      "%s: Could not aquire driver lock.\n",
+                      "%s: Could not acquire driver lock.\n",
                       ndev->name);
                goto err_lock;
        }
index 0dd7839..cc42186 100644 (file)
@@ -3238,7 +3238,7 @@ static u64 s2io_mdio_read(u32 mmd_type, u64 addr, struct net_device *dev)
 
 /**
  *  s2io_chk_xpak_counter - Function to check the status of the xpak counters
- *  @counter      : couter value to be updated
+ *  @counter      : counter value to be updated
  *  @flag         : flag to indicate the status
  *  @type         : counter type
  *  Description:
index 9a12d88..7360d4b 100644 (file)
@@ -1760,7 +1760,7 @@ static int sis900_rx(struct net_device *net_dev)
                                sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE,
                                PCI_DMA_FROMDEVICE);
 
-                       /* refill the Rx buffer, what if there is not enought
+                       /* refill the Rx buffer, what if there is not enough
                         * memory for new socket buffer ?? */
                        if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
                                /*
@@ -1775,7 +1775,7 @@ static int sis900_rx(struct net_device *net_dev)
                        }
 
                        /* This situation should never happen, but due to
-                          some unknow bugs, it is possible that
+                          some unknown bugs, it is possible that
                           we are working on NULL sk_buff :-( */
                        if (sis_priv->rx_skbuff[entry] == NULL) {
                                if (netif_msg_rx_err(sis_priv))
index 1758d95..026a83b 100644 (file)
@@ -393,10 +393,10 @@ struct smt_config {
                                         */
        u_long  mac_d_max ;             /* MAC : D_Max timer value */
 
-       u_long lct_short ;              /* LCT : error threshhold */
-       u_long lct_medium ;             /* LCT : error threshhold */
-       u_long lct_long ;               /* LCT : error threshhold */
-       u_long lct_extended ;           /* LCT : error threshhold */
+       u_long lct_short ;              /* LCT : error threshold */
+       u_long lct_medium ;             /* LCT : error threshold */
+       u_long lct_long ;               /* LCT : error threshold */
+       u_long lct_extended ;           /* LCT : error threshold */
 } ;
 
 #ifdef DEBUG
index b27156e..db216a7 100644 (file)
@@ -1002,7 +1002,7 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                }
                break;
        default:
-               printk("ioctl for %s: unknow cmd: %04x\n", dev->name, ioc.cmd);
+               printk("ioctl for %s: unknown cmd: %04x\n", dev->name, ioc.cmd);
                status = -EOPNOTSUPP;
 
        }                       // switch
index 3943d89..044e681 100644 (file)
@@ -2017,7 +2017,7 @@ static void sky2_link_down(struct sky2_port *sky2)
 
        netif_carrier_off(sky2->netdev);
 
-       /* Turn on link LED */
+       /* Turn off link LED */
        sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
 
        if (netif_msg_link(sky2))
index 4d0d5c5..20d6095 100644 (file)
@@ -816,7 +816,7 @@ static int smsc911x_mii_probe(struct net_device *dev)
        SMSC_TRACE(HW, "Passed Loop Back Test");
 #endif                         /* USE_PHY_WORK_AROUND */
 
-       SMSC_TRACE(HW, "phy initialised succesfully");
+       SMSC_TRACE(HW, "phy initialised successfully");
        return 0;
 }
 
index b5716bd..016360c 100644 (file)
@@ -30,7 +30,7 @@
 #define SMSC_NAPI_WEIGHT       16
 
 /* implements a PHY loopback test at initialisation time, to ensure a packet
- * can be succesfully looped back */
+ * can be successfully looped back */
 #define USE_PHY_WORK_AROUND
 
 #define DPRINTK(nlevel, klevel, fmt, args...) \
index 782910c..2185248 100644 (file)
@@ -410,7 +410,7 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card)
  * @card: card structure
  * @descr: descriptor to re-init
  *
- * Return 0 on succes, <0 on failure.
+ * Return 0 on success, <0 on failure.
  *
  * Allocates a new rx skb, iommu-maps it and attaches it to the
  * descriptor. Mark the descriptor as activated, ready-to-use.
index b624bb5..52586ee 100644 (file)
@@ -112,7 +112,7 @@ static void gmac_dma_operation_mode(unsigned long ioaddr, int txmode,
                              " (threshold = %d)\n", txmode);
                csr6 &= ~DMA_CONTROL_TSF;
                csr6 &= DMA_CONTROL_TC_TX_MASK;
-               /* Set the transmit threashold */
+               /* Set the transmit threshold */
                if (txmode <= 32)
                        csr6 |= DMA_CONTROL_TTC_32;
                else if (txmode <= 64)
index 684a363..2e82d6c 100644 (file)
@@ -154,14 +154,14 @@ enum rx_tx_priority_ratio {
 #define DMA_CONTROL_DT         0x04000000 /* Disable Drop TCP/IP csum error */
 #define DMA_CONTROL_RSF                0x02000000 /* Receive Store and Forward */
 #define DMA_CONTROL_DFF                0x01000000 /* Disaable flushing */
-/* Theshold for Activating the FC */
+/* Threshold for Activating the FC */
 enum rfa {
        act_full_minus_1 = 0x00800000,
        act_full_minus_2 = 0x00800200,
        act_full_minus_3 = 0x00800400,
        act_full_minus_4 = 0x00800600,
 };
-/* Theshold for Deactivating the FC */
+/* Threshold for Deactivating the FC */
 enum rfd {
        deac_full_minus_1 = 0x00400000,
        deac_full_minus_2 = 0x00400800,
index 5db0270..66272f2 100644 (file)
@@ -96,7 +96,7 @@
  *
  *      Change by Mike Sullivan et al.:
  *      + added turbo card support. No need to use lanaid to configure
- *      the adapter into isa compatiblity mode.
+ *      the adapter into isa compatibility mode.
  *
  *      Changes by Burt Silverman to allow the computer to behave nicely when
  *     a cable is pulled or not in place, or a PCMCIA card is removed hot.
index 427a897..5401d86 100644 (file)
@@ -426,7 +426,7 @@ static int smctr_alloc_shared_memory(struct net_device *dev)
         smctr_malloc(dev, 1L);
 
         /* Allocate Non-MAC receive data buffers.
-         * To guarantee a minimum of 256 contigous memory to
+         * To guarantee a minimum of 256 contiguous memory to
          * UM_Receive_Packet's lookahead pointer, before a page
          * change or ring end is encountered, place each rx buffer on
          * a 256 byte boundary.
index 9f44c99..afaf088 100644 (file)
@@ -3798,7 +3798,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
                prop = of_get_property(np, "tx-clock", NULL);
                if (!prop) {
                        printk(KERN_ERR
-                               "ucc_geth: mising tx-clock-name property\n");
+                               "ucc_geth: missing tx-clock-name property\n");
                        return -EINVAL;
                }
                if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
index 03a6ca0..a007e2a 100644 (file)
@@ -80,16 +80,16 @@ struct ucc_geth {
                                   frames) received that were between 128
                                   (Including FCS length==4) and 255 octets */
        u32 txok;               /* Total number of octets residing in frames
-                                  that where involved in succesfull
+                                  that where involved in successfull
                                   transmission */
        u16 txcf;               /* Total number of PAUSE control frames
                                   transmitted by this MAC */
        u8 res4[0x2];
        u32 tmca;               /* Total number of frames that were transmitted
-                                  succesfully with the group address bit set
+                                  successfully with the group address bit set
                                   that are not broadcast frames */
        u32 tbca;               /* Total number of frames transmitted
-                                  succesfully that had destination address
+                                  successfully that had destination address
                                   field equal to the broadcast address */
        u32 rxfok;              /* Total number of frames received OK */
        u32 rxbok;              /* Total number of octets received OK */
@@ -98,9 +98,9 @@ struct ucc_geth {
                                   HW because it includes octets in frames that
                                   never even reach the UCC */
        u32 rmca;               /* Total number of frames that were received
-                                  succesfully with the group address bit set
+                                  successfully with the group address bit set
                                   that are not broadcast frames */
-       u32 rbca;               /* Total number of frames received succesfully
+       u32 rbca;               /* Total number of frames received successfully
                                   that had destination address equal to the
                                   broadcast address */
        u32 scar;               /* Statistics carry register */
@@ -759,15 +759,15 @@ struct ucc_geth_hardware_statistics {
                                   frames) received that were between 128
                                   (Including FCS length==4) and 255 octets */
        u32 txok;               /* Total number of octets residing in frames
-                                  that where involved in succesfull
+                                  that where involved in successfull
                                   transmission */
        u16 txcf;               /* Total number of PAUSE control frames
                                   transmitted by this MAC */
        u32 tmca;               /* Total number of frames that were transmitted
-                                  succesfully with the group address bit set
+                                  successfully with the group address bit set
                                   that are not broadcast frames */
        u32 tbca;               /* Total number of frames transmitted
-                                  succesfully that had destination address
+                                  successfully that had destination address
                                   field equal to the broadcast address */
        u32 rxfok;              /* Total number of frames received OK */
        u32 rxbok;              /* Total number of octets received OK */
@@ -776,9 +776,9 @@ struct ucc_geth_hardware_statistics {
                                   HW because it includes octets in frames that
                                   never even reach the UCC */
        u32 rmca;               /* Total number of frames that were received
-                                  succesfully with the group address bit set
+                                  successfully with the group address bit set
                                   that are not broadcast frames */
-       u32 rbca;               /* Total number of frames received succesfully
+       u32 rbca;               /* Total number of frames received successfully
                                   that had destination address equal to the
                                   broadcast address */
 } __attribute__ ((packed));
index c6c9222..0c3c738 100644 (file)
@@ -748,7 +748,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
        mii_nway_restart(&dev->mii);
 
        if (netif_msg_ifup(dev))
-               devdbg(dev, "phy initialised succesfully");
+               devdbg(dev, "phy initialised successfully");
        return 0;
 }
 
index a7d4fc1..0e52993 100644 (file)
@@ -182,7 +182,7 @@ static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
                if (pvc->dlci == dlci)
                        return pvc;
                if (pvc->dlci > dlci)
-                       return NULL; /* the listed is sorted */
+                       return NULL; /* the list is sorted */
                pvc = pvc->next;
        }
 
index 2ebe935..4b6f27e 100644 (file)
@@ -927,7 +927,7 @@ static int __devinit lmc_init_one(struct pci_dev *pdev,
         sc->lmc_media = &lmc_t1_media;
         break;
     default:
-       printk(KERN_WARNING "%s: LMC UNKOWN CARD!\n", dev->name);
+       printk(KERN_WARNING "%s: LMC UNKNOWN CARD!\n", dev->name);
         break;
     }
 
index e3d2a9d..7ddb173 100644 (file)
@@ -1194,7 +1194,7 @@ error:
  * device. See the file header for the format. Run all checks on the
  * buffer header, then run over each payload's descriptors, verify
  * their consistency and act on each payload's contents.  If
- * everything is succesful, update the device's statistics.
+ * everything is successful, update the device's statistics.
  *
  * Note: You need to set the skb to contain only the length of the
  * received buffer; for that, use skb_trim(skb, RECEIVED_SIZE).
index b72338c..952b3a2 100644 (file)
@@ -36,7 +36,7 @@
  */
 
 /*
- * Defintions for the Atheros Wireless LAN controller driver.
+ * Definitions for the Atheros Wireless LAN controller driver.
  */
 #ifndef _DEV_ATH_ATHVAR_H
 #define _DEV_ATH_ATHVAR_H
@@ -190,7 +190,7 @@ struct ath5k_softc {
        struct ath5k_txq        *cabq;          /* content after beacon */
 
        int                     power_level;    /* Requested tx power in dbm */
-       bool                    assoc;          /* assocate state */
+       bool                    assoc;          /* associate state */
        bool                    enable_beacon;  /* true if beacons are on */
 };
 
index 644962a..81ea52c 100644 (file)
@@ -1492,7 +1492,7 @@ ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
  * This info is used to calibrate the baseband power table. Imagine
  * that for each channel there is a power curve that's hw specific
  * (depends on amplifier etc) and we try to "correct" this curve using
- * offests we pass on to phy chip (baseband -> before amplifier) so that
+ * offsets we pass on to phy chip (baseband -> before amplifier) so that
  * it can use accurate power values when setting tx power (takes amplifier's
  * performance on each channel into account).
  *
index bbfdcd5..72474c0 100644 (file)
@@ -117,7 +117,7 @@ static unsigned int ath5k_hw_rfb_op(struct ath5k_hw *ah,
 
 /*
  * This code is used to optimize rf gain on different environments
- * (temprature mostly) based on feedback from a power detector.
+ * (temperature mostly) based on feedback from a power detector.
  *
  * It's only used on RF5111 and RF5112, later RF chips seem to have
  * auto adjustment on hw -notice they have a much smaller BANK 7 and
@@ -2746,7 +2746,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
                /* Fill curves in reverse order
                 * from lower power (max gain)
                 * to higher power. Use curve -> idx
-                * backmaping we did on eeprom init */
+                * backmapping we did on eeprom init */
                u8 idx = pdg_curve_to_idx[pdg];
 
                /* Grab the needed curves by index */
@@ -2848,7 +2848,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah,
        /* Now we have a set of curves for this
         * channel on tmpL (x range is table_max - table_min
         * and y values are tmpL[pdg][]) sorted in the same
-        * order as EEPROM (because we've used the backmaping).
+        * order as EEPROM (because we've used the backmapping).
         * So for RF5112 it's from higher power to lower power
         * and for RF2413 it's from lower power to higher power.
         * For RF5111 we only have one curve. */
index c915954..70fdb9d 100644 (file)
@@ -880,7 +880,7 @@ static bool ath_rc_update_per(struct ath_softc *sc,
                                 * Since this probe succeeded, we allow the next
                                 * probe twice as soon.  This allows the maxRate
                                 * to move up faster if the probes are
-                                * succesful.
+                                * successful.
                                 */
                                ath_rc_priv->probe_time =
                                        now_msec - rate_table->probe_interval / 2;
index 077480c..71e5c99 100644 (file)
@@ -2955,7 +2955,7 @@ static void do_periodic_work(struct b43_wldev *dev)
 /* Periodic work locking policy:
  *     The whole periodic work handler is protected by
  *     wl->mutex. If another lock is needed somewhere in the
- *     pwork callchain, it's aquired in-place, where it's needed.
+ *     pwork callchain, it's acquired in-place, where it's needed.
  */
 static void b43_periodic_work_handler(struct work_struct *work)
 {
index ab6a18c..4a905b6 100644 (file)
@@ -2277,7 +2277,7 @@ static void do_periodic_work(struct b43legacy_wldev *dev)
 /* Periodic work locking policy:
  *     The whole periodic work handler is protected by
  *     wl->mutex. If another lock is needed somewhere in the
- *     pwork callchain, it's aquired in-place, where it's needed.
+ *     pwork callchain, it's acquired in-place, where it's needed.
  */
 static void b43legacy_periodic_work_handler(struct work_struct *work)
 {
index 17a9cb3..56afcf0 100644 (file)
@@ -578,7 +578,7 @@ static int ipw2100_get_ordinal(struct ipw2100_priv *priv, u32 ord,
                /* get number of entries */
                field_count = *(((u16 *) & field_info) + 1);
 
-               /* abort if no enought memory */
+               /* abort if no enough memory */
                total_length = field_len * field_count;
                if (total_length > *len) {
                        *len = total_length;
@@ -3127,7 +3127,7 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv)
                             IPW_MAX_BDS)) {
                        /* TODO: Support merging buffers if more than
                         * IPW_MAX_BDS are used */
-                       IPW_DEBUG_INFO("%s: Maximum BD theshold exceeded.  "
+                       IPW_DEBUG_INFO("%s: Maximum BD threshold exceeded.  "
                                       "Increase fragmentation level.\n",
                                       priv->net_dev->name);
                }
@@ -6897,7 +6897,7 @@ static int ipw2100_wx_get_range(struct net_device *dev,
        range->max_qual.updated = 7;    /* Updated all three */
 
        range->avg_qual.qual = 70;      /* > 8% missed beacons is 'bad' */
-       /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+       /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
        range->avg_qual.level = 20 + IPW2100_RSSI_TO_DBM;
        range->avg_qual.noise = 0;
        range->avg_qual.updated = 7;    /* Updated all three */
index c28984a..09ddd3e 100644 (file)
@@ -792,7 +792,7 @@ static int ipw_get_ordinal(struct ipw_priv *priv, u32 ord, void *val, u32 * len)
                /* get number of entries */
                field_count = *(((u16 *) & field_info) + 1);
 
-               /* abort if not enought memory */
+               /* abort if not enough memory */
                total_len = field_len * field_count;
                if (total_len > *len) {
                        *len = total_len;
@@ -7760,7 +7760,7 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv,
        case SEC_LEVEL_0:
                break;
        default:
-               printk(KERN_ERR "Unknow security level %d\n",
+               printk(KERN_ERR "Unknown security level %d\n",
                       priv->ieee->sec.level);
                break;
        }
@@ -8926,7 +8926,7 @@ static int ipw_wx_get_range(struct net_device *dev,
        range->max_qual.updated = 7;    /* Updated all three */
 
        range->avg_qual.qual = 70;
-       /* TODO: Find real 'good' to 'bad' threshol value for RSSI */
+       /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
        range->avg_qual.level = 0;      /* FIXME to real average level */
        range->avg_qual.noise = 0;
        range->avg_qual.updated = 7;    /* Updated all three */
@@ -10299,7 +10299,7 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb,
                case SEC_LEVEL_0:
                        break;
                default:
-                       printk(KERN_ERR "Unknow security level %d\n",
+                       printk(KERN_ERR "Unknown security level %d\n",
                               priv->ieee->sec.level);
                        break;
                }
index bf21eb3..1ae0b2b 100644 (file)
@@ -199,7 +199,7 @@ struct net_device *alloc_ieee80211(int sizeof_priv, int monitor)
        ieee->host_decrypt = 1;
        ieee->host_mc_decrypt = 1;
 
-       /* Host fragementation in Open mode. Default is enabled.
+       /* Host fragmentation in Open mode. Default is enabled.
         * Note: host fragmentation is always enabled if host encryption
         * is enabled. For cards can do hardware encryption, they must do
         * hardware fragmentation as well. So we don't need a variable
index c430418..d13c885 100644 (file)
@@ -411,7 +411,7 @@ static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
 /*
  * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
  * Sending command to the LMAC is equivalent to sending a
- * regular UMAC command with the LMAC passtrough or the LMAC
+ * regular UMAC command with the LMAC passthrough or the LMAC
  * wrapper UMAC command IDs.
  */
 int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
index 72c27a3..1c57c1f 100644 (file)
@@ -1578,7 +1578,7 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm,
                kfree_skb(packet->skb);
                break;
        default:
-               IWM_ERR(iwm, "Unknow ticket action: %d\n",
+               IWM_ERR(iwm, "Unknown ticket action: %d\n",
                        le16_to_cpu(ticket_node->ticket->action));
                kfree_skb(packet->skb);
        }
index 09fcfad..7a73f62 100644 (file)
@@ -993,7 +993,7 @@ static int if_sdio_probe(struct sdio_func *func,
        }
 
        if (i == ARRAY_SIZE(if_sdio_models)) {
-               lbs_pr_err("unkown card model 0x%x\n", card->model);
+               lbs_pr_err("unknown card model 0x%x\n", card->model);
                ret = -ENODEV;
                goto free;
        }
index a3eefe1..84200da 100644 (file)
@@ -550,7 +550,7 @@ static const struct {                                                       \
 
 #define DEFAULT_PDR(pid) default_pdr_data_##pid
 
-/*  HWIF Compatiblity */
+/*  HWIF Compatibility */
 DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
 
 /* PPPPSign */
@@ -656,7 +656,7 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
                                         record_id + 1, pdi);
                        }
                        break;
-               case 0x5: /*  HWIF Compatiblity */
+               case 0x5: /*  HWIF Compatibility */
                        default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
                        break;
                case 0x108: /* PPPPSign */
index bc08464..f7f5c79 100644 (file)
@@ -1897,7 +1897,7 @@ prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
        return 0;
 }
 
-/* Setting policy also clears the MAC acl, even if we don't change the defaut
+/* Setting policy also clears the MAC acl, even if we don't change the default
  * policy
  */
 
@@ -2323,7 +2323,7 @@ prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
 
        case DOT11_OID_BEACON:
                send_formatted_event(priv,
-                                    "Received a beacon from an unkown AP",
+                                    "Received a beacon from an unknown AP",
                                     mlme, 0);
                break;
 
index 6c21ef6..c3dea69 100644 (file)
@@ -35,7 +35,7 @@
 
 /*
  * Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
  */
 #define DEFAULT_RSSI_OFFSET            100
 
index b007567..c6bd1fc 100644 (file)
@@ -46,7 +46,7 @@
 
 /*
  * Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
  */
 #define DEFAULT_RSSI_OFFSET            121
 
index 341a704..b493306 100644 (file)
@@ -46,7 +46,7 @@
 
 /*
  * Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
  */
 #define DEFAULT_RSSI_OFFSET            120
 
index 6f33f7f..8f13810 100644 (file)
@@ -37,7 +37,7 @@
 
 /*
  * Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
  */
 #define DEFAULT_RSSI_OFFSET            120
 
index e783a09..7942f81 100644 (file)
@@ -37,7 +37,7 @@
 
 /*
  * Signal information.
- * Defaul offset is required for RSSI <-> dBm conversion.
+ * Default offset is required for RSSI <-> dBm conversion.
  */
 #define DEFAULT_RSSI_OFFSET            120
 
index 8a24373..cf51e8f 100644 (file)
@@ -374,7 +374,7 @@ static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
  * zd_mac_tx_failed - callback for failed frames
  * @dev: the mac80211 wireless device
  *
- * This function is called if a frame couldn't be succesfully be
+ * This function is called if a frame couldn't be successfully be
  * transferred. The first frame from the tx queue, will be selected and
  * reported as error to the upper layers.
  */
index a6b4a5a..f511e70 100644 (file)
@@ -650,7 +650,7 @@ ccio_clear_io_tlb(struct ioc *ioc, dma_addr_t iovp, size_t byte_cnt)
  * Mark the I/O Pdir entries invalid and blow away the corresponding I/O
  * TLB entries.
  *
- * FIXME: at some threshhold it might be "cheaper" to just blow
+ * FIXME: at some threshold it might be "cheaper" to just blow
  *        away the entire I/O TLB instead of individual entries.
  *
  * FIXME: Uturn has 256 TLB entries. We don't need to purge every
index a73028e..0f32571 100644 (file)
@@ -471,7 +471,7 @@ int acpi_pci_detect_ejectable(acpi_handle handle)
                return found;
 
        acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
-                           check_hotplug, (void *)&found, NULL);
+                           check_hotplug, NULL, (void *)&found, NULL);
        return found;
 }
 EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable);
index 58d25a1..df1b0ea 100644 (file)
@@ -266,7 +266,7 @@ static int detect_ejectable_slots(acpi_handle handle)
        int found = acpi_pci_detect_ejectable(handle);
        if (!found) {
                acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                   is_pci_dock_device, (void *)&found, NULL);
+                                   is_pci_dock_device, NULL, (void *)&found, NULL);
        }
        return found;
 }
@@ -281,7 +281,7 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge)
 
        /* register all slot objects under this bridge */
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge->handle, (u32)1,
-                                    register_slot, bridge, NULL);
+                                    register_slot, NULL, bridge, NULL);
        if (ACPI_FAILURE(status)) {
                list_del(&bridge->list);
                return;
@@ -447,7 +447,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
 
        /* search P2P bridges under this p2p bridge */
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                    find_p2p_bridge, NULL, NULL);
+                                    find_p2p_bridge, NULL, NULL, NULL);
        if (ACPI_FAILURE(status))
                warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
 
@@ -485,7 +485,7 @@ static int add_bridge(acpi_handle handle)
 
        /* search P2P bridges under this host bridge */
        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                                    find_p2p_bridge, NULL, NULL);
+                                    find_p2p_bridge, NULL, NULL, NULL);
 
        if (ACPI_FAILURE(status))
                warn("find_p2p_bridge failed (error code = 0x%x)\n", status);
@@ -573,7 +573,7 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
        /* cleanup p2p bridges under this P2P bridge
           in a depth-first manner */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
-                               cleanup_p2p_bridge, NULL, NULL);
+                               cleanup_p2p_bridge, NULL, NULL, NULL);
 
        bridge = acpiphp_handle_to_bridge(handle);
        if (bridge)
@@ -589,7 +589,7 @@ static void remove_bridge(acpi_handle handle)
        /* cleanup p2p bridges under this host bridge
           in a depth-first manner */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-                               (u32)1, cleanup_p2p_bridge, NULL, NULL);
+                               (u32)1, cleanup_p2p_bridge, NULL, NULL, NULL);
 
        /*
         * On root bridges with hotplug slots directly underneath (ie,
@@ -778,7 +778,7 @@ static int acpiphp_configure_ioapics(acpi_handle handle)
 {
        ioapic_add(handle, 0, NULL, NULL);
        acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-                           ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
+                           ACPI_UINT32_MAX, ioapic_add, NULL, NULL, NULL);
        return 0;
 }
 
@@ -786,7 +786,7 @@ static int acpiphp_unconfigure_ioapics(acpi_handle handle)
 {
        ioapic_remove(handle, 0, NULL, NULL);
        acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-                           ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
+                           ACPI_UINT32_MAX, ioapic_remove, NULL, NULL, NULL);
        return 0;
 }
 
@@ -1367,7 +1367,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont
        bridge = acpiphp_handle_to_bridge(handle);
        if (type == ACPI_NOTIFY_BUS_CHECK) {
                acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX,
-                       count_sub_bridges, &num_sub_bridges, NULL);
+                       count_sub_bridges, NULL, &num_sub_bridges, NULL);
        }
 
        if (!bridge && !num_sub_bridges) {
@@ -1388,7 +1388,7 @@ static void handle_hotplug_event_bridge(acpi_handle handle, u32 type, void *cont
                }
                if (num_sub_bridges)
                        acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-                               ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL);
+                               ACPI_UINT32_MAX, check_sub_bridges, NULL, NULL, NULL);
                break;
 
        case ACPI_NOTIFY_DEVICE_CHECK:
@@ -1512,7 +1512,7 @@ int __init acpiphp_glue_init(void)
        int num = 0;
 
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                       ACPI_UINT32_MAX, find_root_bridges, &num, NULL);
+                       ACPI_UINT32_MAX, find_root_bridges, NULL, &num, NULL);
 
        if (num <= 0)
                return -1;
index e7be66d..aa5df48 100644 (file)
@@ -434,7 +434,7 @@ static int __init ibm_acpiphp_init(void)
        dbg("%s\n", __func__);
 
        if (acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
-                       ACPI_UINT32_MAX, ibm_find_acpi_device,
+                       ACPI_UINT32_MAX, ibm_find_acpi_device, NULL,
                        &ibm_acpi_handle, NULL) != FOUND_APCI) {
                err("%s: acpi_walk_namespace failed\n", __func__);
                retval = -ENODEV;
index 9652c3f..8390dca 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/platform/Kconfig
-
 if X86
 source "drivers/platform/x86/Kconfig"
 endif
index 29432a5..f0a90a6 100644 (file)
@@ -510,7 +510,7 @@ static int __init intel_menlow_module_init(void)
        /* Looking for sensors in each ACPI thermal zone */
        status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
                                     ACPI_UINT32_MAX,
-                                    intel_menlow_register_sensor, NULL, NULL);
+                                    intel_menlow_register_sensor, NULL, NULL, NULL);
        if (ACPI_FAILURE(status))
                return -ENODEV;
 
index a2a742c..7a2cc8a 100644 (file)
@@ -1203,7 +1203,7 @@ static int sony_nc_add(struct acpi_device *device)
 
        if (debug) {
                status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
-                                            1, sony_walk_callback, NULL, NULL);
+                                            1, sony_walk_callback, NULL, NULL, NULL);
                if (ACPI_FAILURE(status)) {
                        printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
                        result = -ENODEV;
index a848c7e..0ed8480 100644 (file)
@@ -1087,7 +1087,7 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
         */
 
        status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
-                                    tpacpi_acpi_walk_find_bcl, NULL,
+                                    tpacpi_acpi_walk_find_bcl, NULL, NULL,
                                     &bcl_ptr);
 
        if (ACPI_SUCCESS(status) && bcl_levels > 2) {
@@ -6545,7 +6545,7 @@ static struct ibm_struct volume_driver_data = {
  *     The speeds are stored on handles
  *     (FANA:FAN9), (FANC:FANB), (FANE:FAND).
  *
- *     There are three default speed sets, acessible as handles:
+ *     There are three default speed sets, accessible as handles:
  *     FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
  *
  *     ACPI DSDT switches which set is in use depending on various
index 87b4f49..a5135eb 100644 (file)
@@ -191,7 +191,7 @@ static unsigned char *pnpbios_parse_allocated_resource_data(struct pnp_dev *dev,
                        return (unsigned char *)p;
                        break;
 
-               default:        /* an unkown tag */
+               default:        /* an unknown tag */
 len_err:
                        dev_err(&dev->dev, "unknown tag %#x length %d\n",
                                tag, len);
@@ -405,7 +405,7 @@ pnpbios_parse_resource_option_data(unsigned char *p, unsigned char *end,
                case SMALL_TAG_END:
                        return p + 2;
 
-               default:        /* an unkown tag */
+               default:        /* an unknown tag */
 len_err:
                        dev_err(&dev->dev, "unknown tag %#x length %d\n",
                                tag, len);
@@ -475,7 +475,7 @@ static unsigned char *pnpbios_parse_compatible_ids(unsigned char *p,
                        return (unsigned char *)p;
                        break;
 
-               default:        /* an unkown tag */
+               default:        /* an unknown tag */
 len_err:
                        dev_err(&dev->dev, "unknown tag %#x length %d\n",
                                tag, len);
@@ -744,7 +744,7 @@ static unsigned char *pnpbios_encode_allocated_resource_data(struct pnp_dev
                        return (unsigned char *)p;
                        break;
 
-               default:        /* an unkown tag */
+               default:        /* an unknown tag */
 len_err:
                        dev_err(&dev->dev, "unknown tag %#x length %d\n",
                                tag, len);
index 88cb740..3cbaf18 100644 (file)
@@ -46,7 +46,7 @@
 /**
  * struct ps3_sys_manager_header - System manager message header.
  * @version: Header version, currently 1.
- * @size: Header size in bytes, curently 16.
+ * @size: Header size in bytes, currently 16.
  * @payload_size: Message payload size in bytes.
  * @service_id: Message type, one of enum ps3_sys_manager_service_id.
  * @request_tag: Unique number to identify reply.
index d490628..1e73c8f 100644 (file)
@@ -201,7 +201,7 @@ static struct platform_driver ds1302_platform_driver = {
                .name   = DRV_NAME,
                .owner  = THIS_MODULE,
        },
-       .remove         = __exit_p(ds1302_rtc_remove),
+       .remove         = __devexit_p(ds1302_rtc_remove),
 };
 
 static int __init ds1302_rtc_init(void)
index 0b6b773..539676e 100644 (file)
@@ -2,7 +2,7 @@
  * An rtc driver for the Dallas DS1511
  *
  * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
- * Copyright (C) 2007 Andrew Sharp <andy.sharp@onstor.com>
+ * Copyright (C) 2007 Andrew Sharp <andy.sharp@lsi.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
@@ -636,7 +636,7 @@ ds1511_rtc_exit(void)
 module_init(ds1511_rtc_init);
 module_exit(ds1511_rtc_exit);
 
-MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>");
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>");
 MODULE_DESCRIPTION("Dallas DS1511 RTC driver");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(DRV_VERSION);
index 7d1547b..d491eb2 100644 (file)
@@ -286,7 +286,7 @@ static struct bin_attribute stk17ta8_nvram_attr = {
        .write = stk17ta8_nvram_write,
 };
 
-static int __init stk17ta8_rtc_probe(struct platform_device *pdev)
+static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev)
 {
        struct rtc_device *rtc;
        struct resource *res;
index 423cd5a..ad741af 100644 (file)
@@ -335,7 +335,7 @@ static int rtc_probe(struct platform_device *pdev)
                goto err_io;
        }
 
-       /* Make sure frequency measurment mode, test modes, and lock
+       /* Make sure frequency measurement mode, test modes, and lock
         * are all disabled */
        v3020_set_reg(chip, V3020_STATUS_0, 0x0);
 
index aaccc8e..fdb2e7c 100644 (file)
@@ -24,7 +24,6 @@
 #include <asm/ccwdev.h>
 #include <asm/ebcdic.h>
 #include <asm/idals.h>
-#include <asm/todclk.h>
 #include <asm/itcw.h>
 
 /* This is ugly... */
@@ -64,6 +63,7 @@ static void do_restore_device(struct work_struct *);
 static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
 static void dasd_device_timeout(unsigned long);
 static void dasd_block_timeout(unsigned long);
+static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
 
 /*
  * SECTION: Operations on the device structure.
@@ -960,7 +960,7 @@ static void dasd_device_timeout(unsigned long ptr)
        device = (struct dasd_device *) ptr;
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
        /* re-activate request queue */
-        device->stopped &= ~DASD_STOPPED_PENDING;
+       dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        dasd_schedule_device_bh(device);
 }
@@ -994,10 +994,9 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
                return;
        cqr = (struct dasd_ccw_req *) intparm;
        if (cqr->status != DASD_CQR_IN_IO) {
-               DBF_EVENT(DBF_DEBUG,
-                       "invalid status in handle_killed_request: "
-                       "bus_id %s, status %02x",
-                       dev_name(&cdev->dev), cqr->status);
+               DBF_EVENT_DEVID(DBF_DEBUG, cdev,
+                               "invalid status in handle_killed_request: "
+                               "%02x", cqr->status);
                return;
        }
 
@@ -1023,7 +1022,7 @@ void dasd_generic_handle_state_change(struct dasd_device *device)
        /* First of all start sense subsystem status request. */
        dasd_eer_snss(device);
 
-       device->stopped &= ~DASD_STOPPED_PENDING;
+       dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
        dasd_schedule_device_bh(device);
        if (device->block)
                dasd_schedule_block_bh(device->block);
@@ -1045,12 +1044,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
                case -EIO:
                        break;
                case -ETIMEDOUT:
-                       DBF_EVENT(DBF_WARNING, "%s(%s): request timed out\n",
-                              __func__, dev_name(&cdev->dev));
+                       DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
+                                       "request timed out\n", __func__);
                        break;
                default:
-                       DBF_EVENT(DBF_WARNING, "%s(%s): unknown error %ld\n",
-                              __func__, dev_name(&cdev->dev), PTR_ERR(irb));
+                       DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
+                                       "unknown error %ld\n", __func__,
+                                       PTR_ERR(irb));
                }
                dasd_handle_killed_request(cdev, intparm);
                return;
@@ -1405,6 +1405,20 @@ void dasd_schedule_device_bh(struct dasd_device *device)
        tasklet_hi_schedule(&device->tasklet);
 }
 
+void dasd_device_set_stop_bits(struct dasd_device *device, int bits)
+{
+       device->stopped |= bits;
+}
+EXPORT_SYMBOL_GPL(dasd_device_set_stop_bits);
+
+void dasd_device_remove_stop_bits(struct dasd_device *device, int bits)
+{
+       device->stopped &= ~bits;
+       if (!device->stopped)
+               wake_up(&generic_waitq);
+}
+EXPORT_SYMBOL_GPL(dasd_device_remove_stop_bits);
+
 /*
  * Queue a request to the head of the device ccw_queue.
  * Start the I/O if possible.
@@ -1465,58 +1479,135 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
 }
 
 /*
- * Queue a request to the tail of the device ccw_queue and wait for
- * it's completion.
+ * checks if error recovery is necessary, returns 1 if yes, 0 otherwise.
  */
-int dasd_sleep_on(struct dasd_ccw_req *cqr)
+static int __dasd_sleep_on_erp(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
-       int rc;
+       dasd_erp_fn_t erp_fn;
 
+       if (cqr->status == DASD_CQR_FILLED)
+               return 0;
        device = cqr->startdev;
+       if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
+               if (cqr->status == DASD_CQR_TERMINATED) {
+                       device->discipline->handle_terminated_request(cqr);
+                       return 1;
+               }
+               if (cqr->status == DASD_CQR_NEED_ERP) {
+                       erp_fn = device->discipline->erp_action(cqr);
+                       erp_fn(cqr);
+                       return 1;
+               }
+               if (cqr->status == DASD_CQR_FAILED)
+                       dasd_log_sense(cqr, &cqr->irb);
+               if (cqr->refers) {
+                       __dasd_process_erp(device, cqr);
+                       return 1;
+               }
+       }
+       return 0;
+}
 
-       cqr->callback = dasd_wakeup_cb;
-       cqr->callback_data = (void *) &generic_waitq;
-       dasd_add_request_tail(cqr);
-       wait_event(generic_waitq, _wait_for_wakeup(cqr));
+static int __dasd_sleep_on_loop_condition(struct dasd_ccw_req *cqr)
+{
+       if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
+               if (cqr->refers) /* erp is not done yet */
+                       return 1;
+               return ((cqr->status != DASD_CQR_DONE) &&
+                       (cqr->status != DASD_CQR_FAILED));
+       } else
+               return (cqr->status == DASD_CQR_FILLED);
+}
 
-       if (cqr->status == DASD_CQR_DONE)
+static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
+{
+       struct dasd_device *device;
+       int rc;
+       struct list_head ccw_queue;
+       struct dasd_ccw_req *cqr;
+
+       INIT_LIST_HEAD(&ccw_queue);
+       maincqr->status = DASD_CQR_FILLED;
+       device = maincqr->startdev;
+       list_add(&maincqr->blocklist, &ccw_queue);
+       for (cqr = maincqr;  __dasd_sleep_on_loop_condition(cqr);
+            cqr = list_first_entry(&ccw_queue,
+                                   struct dasd_ccw_req, blocklist)) {
+
+               if (__dasd_sleep_on_erp(cqr))
+                       continue;
+               if (cqr->status != DASD_CQR_FILLED) /* could be failed */
+                       continue;
+
+               /* Non-temporary stop condition will trigger fail fast */
+               if (device->stopped & ~DASD_STOPPED_PENDING &&
+                   test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
+                   (!dasd_eer_enabled(device))) {
+                       cqr->status = DASD_CQR_FAILED;
+                       continue;
+               }
+
+               /* Don't try to start requests if device is stopped */
+               if (interruptible) {
+                       rc = wait_event_interruptible(
+                               generic_waitq, !(device->stopped));
+                       if (rc == -ERESTARTSYS) {
+                               cqr->status = DASD_CQR_FAILED;
+                               maincqr->intrc = rc;
+                               continue;
+                       }
+               } else
+                       wait_event(generic_waitq, !(device->stopped));
+
+               cqr->callback = dasd_wakeup_cb;
+               cqr->callback_data = (void *) &generic_waitq;
+               dasd_add_request_tail(cqr);
+               if (interruptible) {
+                       rc = wait_event_interruptible(
+                               generic_waitq, _wait_for_wakeup(cqr));
+                       if (rc == -ERESTARTSYS) {
+                               dasd_cancel_req(cqr);
+                               /* wait (non-interruptible) for final status */
+                               wait_event(generic_waitq,
+                                          _wait_for_wakeup(cqr));
+                               cqr->status = DASD_CQR_FAILED;
+                               maincqr->intrc = rc;
+                               continue;
+                       }
+               } else
+                       wait_event(generic_waitq, _wait_for_wakeup(cqr));
+       }
+
+       maincqr->endclk = get_clock();
+       if ((maincqr->status != DASD_CQR_DONE) &&
+           (maincqr->intrc != -ERESTARTSYS))
+               dasd_log_sense(maincqr, &maincqr->irb);
+       if (maincqr->status == DASD_CQR_DONE)
                rc = 0;
-       else if (cqr->intrc)
-               rc = cqr->intrc;
+       else if (maincqr->intrc)
+               rc = maincqr->intrc;
        else
                rc = -EIO;
        return rc;
 }
 
+/*
+ * Queue a request to the tail of the device ccw_queue and wait for
+ * it's completion.
+ */
+int dasd_sleep_on(struct dasd_ccw_req *cqr)
+{
+       return _dasd_sleep_on(cqr, 0);
+}
+
 /*
  * Queue a request to the tail of the device ccw_queue and wait
  * interruptible for it's completion.
  */
 int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
 {
-       struct dasd_device *device;
-       int rc;
-
-       device = cqr->startdev;
-       cqr->callback = dasd_wakeup_cb;
-       cqr->callback_data = (void *) &generic_waitq;
-       dasd_add_request_tail(cqr);
-       rc = wait_event_interruptible(generic_waitq, _wait_for_wakeup(cqr));
-       if (rc == -ERESTARTSYS) {
-               dasd_cancel_req(cqr);
-               /* wait (non-interruptible) for final status */
-               wait_event(generic_waitq, _wait_for_wakeup(cqr));
-               cqr->intrc = rc;
-       }
-
-       if (cqr->status == DASD_CQR_DONE)
-               rc = 0;
-       else if (cqr->intrc)
-               rc = cqr->intrc;
-       else
-               rc = -EIO;
-       return rc;
+       return _dasd_sleep_on(cqr, 1);
 }
 
 /*
@@ -1630,7 +1721,7 @@ static void dasd_block_timeout(unsigned long ptr)
        block = (struct dasd_block *) ptr;
        spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags);
        /* re-activate request queue */
-       block->base->stopped &= ~DASD_STOPPED_PENDING;
+       dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING);
        spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
        dasd_schedule_block_bh(block);
 }
@@ -1657,11 +1748,10 @@ void dasd_block_clear_timer(struct dasd_block *block)
 /*
  * Process finished error recovery ccw.
  */
-static inline void __dasd_block_process_erp(struct dasd_block *block,
-                                           struct dasd_ccw_req *cqr)
+static void __dasd_process_erp(struct dasd_device *device,
+                              struct dasd_ccw_req *cqr)
 {
        dasd_erp_fn_t erp_fn;
-       struct dasd_device *device = block->base;
 
        if (cqr->status == DASD_CQR_DONE)
                DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
@@ -1725,9 +1815,12 @@ static void __dasd_process_request_queue(struct dasd_block *block)
                                 */
                                if (!list_empty(&block->ccw_queue))
                                        break;
-                               spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags);
-                               basedev->stopped |= DASD_STOPPED_PENDING;
-                               spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags);
+                               spin_lock_irqsave(
+                                       get_ccwdev_lock(basedev->cdev), flags);
+                               dasd_device_set_stop_bits(basedev,
+                                                         DASD_STOPPED_PENDING);
+                               spin_unlock_irqrestore(
+                                       get_ccwdev_lock(basedev->cdev), flags);
                                dasd_block_set_timer(block, HZ/2);
                                break;
                        }
@@ -1813,7 +1906,7 @@ restart:
                        cqr->status = DASD_CQR_FILLED;
                        cqr->retries = 255;
                        spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
-                       base->stopped |= DASD_STOPPED_QUIESCE;
+                       dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
                        spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
                                               flags);
                        goto restart;
@@ -1821,7 +1914,7 @@ restart:
 
                /* Process finished ERP request. */
                if (cqr->refers) {
-                       __dasd_block_process_erp(block, cqr);
+                       __dasd_process_erp(base, cqr);
                        goto restart;
                }
 
@@ -1952,7 +2045,7 @@ restart_cb:
                /* Process finished ERP request. */
                if (cqr->refers) {
                        spin_lock_bh(&block->queue_lock);
-                       __dasd_block_process_erp(block, cqr);
+                       __dasd_process_erp(block->base, cqr);
                        spin_unlock_bh(&block->queue_lock);
                        /* restart list_for_xx loop since dasd_process_erp
                         * might remove multiple elements */
@@ -2208,18 +2301,11 @@ int dasd_generic_probe(struct ccw_device *cdev,
 {
        int ret;
 
-       ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
-       if (ret) {
-               DBF_EVENT(DBF_WARNING,
-                      "dasd_generic_probe: could not set ccw-device options "
-                      "for %s\n", dev_name(&cdev->dev));
-               return ret;
-       }
        ret = dasd_add_sysfs_files(cdev);
        if (ret) {
-               DBF_EVENT(DBF_WARNING,
-                      "dasd_generic_probe: could not add sysfs entries "
-                      "for %s\n", dev_name(&cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
+                               "dasd_generic_probe: could not add "
+                               "sysfs entries");
                return ret;
        }
        cdev->handler = &dasd_int_handler;
@@ -2418,16 +2504,16 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
                                cqr->status = DASD_CQR_QUEUED;
                                cqr->retries++;
                        }
-               device->stopped |= DASD_STOPPED_DC_WAIT;
+               dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
                dasd_device_clear_timer(device);
                dasd_schedule_device_bh(device);
                ret = 1;
                break;
        case CIO_OPER:
                /* FIXME: add a sanity check. */
-               device->stopped &= ~DASD_STOPPED_DC_WAIT;
+               dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
                if (device->stopped & DASD_UNRESUMED_PM) {
-                       device->stopped &= ~DASD_UNRESUMED_PM;
+                       dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
                        dasd_restore_device(device);
                        ret = 1;
                        break;
@@ -2452,7 +2538,7 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
        if (IS_ERR(device))
                return PTR_ERR(device);
        /* disallow new I/O  */
-       device->stopped |= DASD_STOPPED_PM;
+       dasd_device_set_stop_bits(device, DASD_STOPPED_PM);
        /* clear active requests */
        INIT_LIST_HEAD(&freeze_queue);
        spin_lock_irq(get_ccwdev_lock(cdev));
@@ -2504,14 +2590,18 @@ int dasd_generic_restore_device(struct ccw_device *cdev)
                return PTR_ERR(device);
 
        /* allow new IO again */
-       device->stopped &= ~DASD_STOPPED_PM;
-       device->stopped &= ~DASD_UNRESUMED_PM;
+       dasd_device_remove_stop_bits(device,
+                                    (DASD_STOPPED_PM | DASD_UNRESUMED_PM));
 
        dasd_schedule_device_bh(device);
 
-       if (device->discipline->restore)
+       /*
+        * call discipline restore function
+        * if device is stopped do nothing e.g. for disconnected devices
+        */
+       if (device->discipline->restore && !(device->stopped))
                rc = device->discipline->restore(device);
-       if (rc)
+       if (rc || device->stopped)
                /*
                 * if the resume failed for the DASD we put it in
                 * an UNRESUMED stop state
@@ -2561,8 +2651,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->expires = 10*HZ;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
-       cqr->retries = 2;
+       cqr->retries = 256;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        return cqr;
index e8ff7b0..44796ba 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/timer.h>
 #include <linux/slab.h>
 #include <asm/idals.h>
-#include <asm/todclk.h>
 
 #define PRINTK_HEADER "dasd_erp(3990): "
 
@@ -70,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
  *   processing until the started timer has expired or an related
  *   interrupt was received.
  */
-static void
-dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
+static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires)
 {
 
        struct dasd_device *device = erp->startdev;
@@ -81,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
                    "blocking request queue for %is", expires/HZ);
 
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-       device->stopped |= DASD_STOPPED_PENDING;
+       dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING);
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        erp->status = DASD_CQR_FILLED;
-       dasd_block_set_timer(device->block, expires);
+       if (erp->block)
+               dasd_block_set_timer(erp->block, expires);
+       else
+               dasd_device_set_timer(device, expires);
 }
 
 /*
@@ -243,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
  * DESCRIPTION
  *   Setup ERP to do the ERP action 1 (see Reference manual).
  *   Repeat the operation on a different channel path.
- *   If all alternate paths have been tried, the request is posted with a
- *   permanent error.
- *   Note: duplex handling is not implemented (yet).
+ *   As deviation from the recommended recovery action, we reset the path mask
+ *   after we have tried each path and go through all paths a second time.
+ *   This will cover situations where only one path at a time is actually down,
+ *   but all paths fail and recover just with the same sequence and timing as
+ *   we try to use them (flapping links).
+ *   If all alternate paths have been tried twice, the request is posted with
+ *   a permanent error.
  *
  *  PARAMETER
  *   erp               pointer to the current ERP
@@ -254,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
  *   erp               pointer to the ERP
  *
  */
-static struct dasd_ccw_req *
-dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
+static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp)
 {
+       erp->function = dasd_3990_erp_action_1_sec;
+       dasd_3990_erp_alternate_path(erp);
+       return erp;
+}
 
+static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
+{
        erp->function = dasd_3990_erp_action_1;
-
        dasd_3990_erp_alternate_path(erp);
-
+       if (erp->status == DASD_CQR_FAILED) {
+               erp->status = DASD_CQR_FILLED;
+               erp->retries = 10;
+               erp->lpm = LPM_ANYPATH;
+               erp->function = dasd_3990_erp_action_1_sec;
+       }
        return erp;
-
-}                              /* end dasd_3990_erp_action_1 */
+}                              /* end dasd_3990_erp_action_1(b) */
 
 /*
  * DASD_3990_ERP_ACTION_4
@@ -2295,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
                return cqr;
        }
 
+       ccw = cqr->cpaddr;
        if (cqr->cpmode == 1) {
                /* make a shallow copy of the original tcw but set new tsb */
                erp->cpmode = 1;
@@ -2303,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
                tsb = (struct tsb *) &tcw[1];
                *tcw = *((struct tcw *)cqr->cpaddr);
                tcw->tsb = (long)tsb;
+       } else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) {
+               /* PSF cannot be chained from NOOP/TIC */
+               erp->cpaddr = cqr->cpaddr;
        } else {
                /* initialize request with default TIC to current ERP/CQR */
                ccw = erp->cpaddr;
@@ -2487,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
 
                erp = dasd_3990_erp_action_1(erp);
 
+       } else if (erp->function == dasd_3990_erp_action_1_sec) {
+               erp = dasd_3990_erp_action_1_sec(erp);
        } else if (erp->function == dasd_3990_erp_action_5) {
 
                /* retries have not been successful */
index 70a008c..fd12317 100644 (file)
@@ -152,6 +152,7 @@ static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
        INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
        INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
        spin_lock_init(&lcu->lock);
+       init_completion(&lcu->lcu_setup);
        return lcu;
 
 out_err4:
@@ -239,6 +240,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
        return is_lcu_known;
 }
 
+/*
+ * The first device to be registered on an LCU will have to do
+ * some additional setup steps to configure that LCU on the
+ * storage server. All further devices should wait with their
+ * initialization until the first device is done.
+ * To synchronize this work, the first device will call
+ * dasd_alias_lcu_setup_complete when it is done, and all
+ * other devices will wait for it with dasd_alias_wait_for_lcu_setup.
+ */
+void dasd_alias_lcu_setup_complete(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       unsigned long flags;
+       struct alias_server *server;
+       struct alias_lcu *lcu;
+       struct dasd_uid *uid;
+
+       private = (struct dasd_eckd_private *) device->private;
+       uid = &private->uid;
+       lcu = NULL;
+       spin_lock_irqsave(&aliastree.lock, flags);
+       server = _find_server(uid);
+       if (server)
+               lcu = _find_lcu(server, uid);
+       spin_unlock_irqrestore(&aliastree.lock, flags);
+       if (!lcu) {
+               DBF_EVENT_DEVID(DBF_ERR, device->cdev,
+                               "could not find lcu for %04x %02x",
+                               uid->ssid, uid->real_unit_addr);
+               WARN_ON(1);
+               return;
+       }
+       complete_all(&lcu->lcu_setup);
+}
+
+void dasd_alias_wait_for_lcu_setup(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       unsigned long flags;
+       struct alias_server *server;
+       struct alias_lcu *lcu;
+       struct dasd_uid *uid;
+
+       private = (struct dasd_eckd_private *) device->private;
+       uid = &private->uid;
+       lcu = NULL;
+       spin_lock_irqsave(&aliastree.lock, flags);
+       server = _find_server(uid);
+       if (server)
+               lcu = _find_lcu(server, uid);
+       spin_unlock_irqrestore(&aliastree.lock, flags);
+       if (!lcu) {
+               DBF_EVENT_DEVID(DBF_ERR, device->cdev,
+                               "could not find lcu for %04x %02x",
+                               uid->ssid, uid->real_unit_addr);
+               WARN_ON(1);
+               return;
+       }
+       wait_for_completion(&lcu->lcu_setup);
+}
+
 /*
  * This function removes a device from the scope of alias management.
  * The complicated part is to make sure that it is not in use by
@@ -755,11 +817,11 @@ static void __stop_device_on_lcu(struct dasd_device *device,
 {
        /* If pos == device then device is already locked! */
        if (pos == device) {
-               pos->stopped |= DASD_STOPPED_SU;
+               dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
                return;
        }
        spin_lock(get_ccwdev_lock(pos->cdev));
-       pos->stopped |= DASD_STOPPED_SU;
+       dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
        spin_unlock(get_ccwdev_lock(pos->cdev));
 }
 
@@ -793,26 +855,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
 
        list_for_each_entry(device, &lcu->active_devices, alias_list) {
                spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-               device->stopped &= ~DASD_STOPPED_SU;
+               dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
                spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        }
 
        list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
                spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-               device->stopped &= ~DASD_STOPPED_SU;
+               dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
                spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        }
 
        list_for_each_entry(pavgroup, &lcu->grouplist, group) {
                list_for_each_entry(device, &pavgroup->baselist, alias_list) {
                        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-                       device->stopped &= ~DASD_STOPPED_SU;
+                       dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
                        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
                                               flags);
                }
                list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
                        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-                       device->stopped &= ~DASD_STOPPED_SU;
+                       dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
                        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
                                               flags);
                }
@@ -836,7 +898,8 @@ static void summary_unit_check_handling_work(struct work_struct *work)
 
        /* 2. reset summary unit check */
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-       device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING);
+       dasd_device_remove_stop_bits(device,
+                                    (DASD_STOPPED_SU | DASD_STOPPED_PENDING));
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        reset_summary_unit_check(lcu, device, suc_data->reason);
 
index 4e49b4a..f64d0db 100644 (file)
@@ -24,7 +24,6 @@
 #include <asm/ebcdic.h>
 #include <asm/io.h>
 #include <asm/s390_ext.h>
-#include <asm/todclk.h>
 #include <asm/vtoc.h>
 #include <asm/diag.h>
 
@@ -145,6 +144,15 @@ dasd_diag_erp(struct dasd_device *device)
 
        mdsk_term_io(device);
        rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
+       if (rc == 4) {
+               if (!(device->features & DASD_FEATURE_READONLY)) {
+                       dev_warn(&device->cdev->dev,
+                                "The access mode of a DIAG device changed"
+                                " to read-only");
+                       device->features |= DASD_FEATURE_READONLY;
+               }
+               rc = 0;
+       }
        if (rc)
                dev_warn(&device->cdev->dev, "DIAG ERP failed with "
                            "rc=%d\n", rc);
@@ -433,16 +441,20 @@ dasd_diag_check_device(struct dasd_device *device)
        for (sb = 512; sb < bsize; sb = sb << 1)
                block->s2b_shift++;
        rc = mdsk_init_io(device, block->bp_block, 0, NULL);
-       if (rc) {
+       if (rc && (rc != 4)) {
                dev_warn(&device->cdev->dev, "DIAG initialization "
                        "failed with rc=%d\n", rc);
                rc = -EIO;
        } else {
+               if (rc == 4)
+                       device->features |= DASD_FEATURE_READONLY;
                dev_info(&device->cdev->dev,
-                        "New DASD with %ld byte/block, total size %ld KB\n",
+                        "New DASD with %ld byte/block, total size %ld KB%s\n",
                         (unsigned long) block->bp_block,
                         (unsigned long) (block->blocks <<
-                                         block->s2b_shift) >> 1);
+                                         block->s2b_shift) >> 1,
+                        (rc == 4) ? ", read-only device" : "");
+               rc = 0;
        }
 out_label:
        free_page((long) label);
index 417b97c..5819dc0 100644 (file)
@@ -24,7 +24,6 @@
 #include <asm/idals.h>
 #include <asm/ebcdic.h>
 #include <asm/io.h>
-#include <asm/todclk.h>
 #include <asm/uaccess.h>
 #include <asm/cio.h>
 #include <asm/ccwdev.h>
@@ -78,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
 
 static struct ccw_driver dasd_eckd_driver; /* see below */
 
+#define INIT_CQR_OK 0
+#define INIT_CQR_UNFORMATTED 1
+#define INIT_CQR_ERROR 2
+
+
 /* initial attempt at a probe function. this can be simplified once
  * the other detection code is gone */
 static int
@@ -86,11 +90,12 @@ dasd_eckd_probe (struct ccw_device *cdev)
        int ret;
 
        /* set ECKD specific ccw-device options */
-       ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
+       ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
+                                    CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH);
        if (ret) {
-               DBF_EVENT(DBF_WARNING,
-                      "dasd_eckd_probe: could not set ccw-device options "
-                      "for %s\n", dev_name(&cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
+                               "dasd_eckd_probe: could not set "
+                               "ccw-device options");
                return ret;
        }
        ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
@@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
        cqr->block = NULL;
        cqr->expires = 10*HZ;
        cqr->lpm = lpm;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
-       cqr->retries = 2;
+       cqr->retries = 256;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        return cqr;
@@ -885,16 +889,15 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
                        rc = dasd_eckd_read_conf_lpm(device, &conf_data,
                                                     &conf_len, lpm);
                        if (rc && rc != -EOPNOTSUPP) {  /* -EOPNOTSUPP is ok */
-                               DBF_EVENT(DBF_WARNING,
+                               DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
                                          "Read configuration data returned "
-                                         "error %d for device: %s", rc,
-                                         dev_name(&device->cdev->dev));
+                                         "error %d", rc);
                                return rc;
                        }
                        if (conf_data == NULL) {
-                               DBF_EVENT(DBF_WARNING, "No configuration "
-                                         "data retrieved for device: %s",
-                                         dev_name(&device->cdev->dev));
+                               DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+                                               "No configuration data "
+                                               "retrieved");
                                continue;       /* no error */
                        }
                        /* save first valid configuration data */
@@ -941,16 +944,14 @@ static int dasd_eckd_read_features(struct dasd_device *device)
                                    sizeof(struct dasd_rssd_features)),
                                   device);
        if (IS_ERR(cqr)) {
-               DBF_EVENT(DBF_WARNING, "Could not allocate initialization "
-                         "request for device: %s",
-                         dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
+                               "allocate initialization request");
                return PTR_ERR(cqr);
        }
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->block = NULL;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
-       cqr->retries = 5;
+       cqr->retries = 256;
        cqr->expires = 10 * HZ;
 
        /* Prepare for Read Subsystem Data */
@@ -1012,9 +1013,9 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
        }
        psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
        psf_ssc_data->order = PSF_ORDER_SSC;
-       psf_ssc_data->suborder = 0x40;
+       psf_ssc_data->suborder = 0xc0;
        if (enable_pav) {
-               psf_ssc_data->suborder |= 0x88;
+               psf_ssc_data->suborder |= 0x08;
                psf_ssc_data->reserved[0] = 0x88;
        }
        ccw = cqr->cpaddr;
@@ -1025,6 +1026,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->block = NULL;
+       cqr->retries = 256;
        cqr->expires = 10*HZ;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
@@ -1057,7 +1059,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
 /*
  * Valide storage server of current device.
  */
-static int dasd_eckd_validate_server(struct dasd_device *device)
+static void dasd_eckd_validate_server(struct dasd_device *device)
 {
        int rc;
        struct dasd_eckd_private *private;
@@ -1068,15 +1070,12 @@ static int dasd_eckd_validate_server(struct dasd_device *device)
        else
                enable_pav = 1;
        rc = dasd_eckd_psf_ssc(device, enable_pav);
+
        /* may be requested feature is not available on server,
         * therefore just report error and go ahead */
        private = (struct dasd_eckd_private *) device->private;
-       DBF_EVENT(DBF_WARNING, "PSF-SSC on storage subsystem %s.%s.%04x "
-                 "returned rc=%d for device: %s",
-                 private->uid.vendor, private->uid.serial,
-                 private->uid.ssid, rc, dev_name(&device->cdev->dev));
-       /* RE-Read Configuration Data */
-       return dasd_eckd_read_conf(device);
+       DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
+                       "returned rc=%d", private->uid.ssid, rc);
 }
 
 /*
@@ -1090,6 +1089,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        struct dasd_block *block;
        int is_known, rc;
 
+       if (!ccw_device_is_pathgroup(device->cdev)) {
+               dev_warn(&device->cdev->dev,
+                        "A channel path group could not be established\n");
+               return -EIO;
+       }
+       if (!ccw_device_is_multipath(device->cdev)) {
+               dev_info(&device->cdev->dev,
+                        "The DASD is not operating in multipath mode\n");
+       }
        private = (struct dasd_eckd_private *) device->private;
        if (!private) {
                private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
@@ -1123,9 +1131,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        if (private->uid.type == UA_BASE_DEVICE) {
                block = dasd_alloc_block();
                if (IS_ERR(block)) {
-                       DBF_EVENT(DBF_WARNING, "could not allocate dasd "
-                                 "block structure for device: %s",
-                                 dev_name(&device->cdev->dev));
+                       DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+                                       "could not allocate dasd "
+                                       "block structure");
                        rc = PTR_ERR(block);
                        goto out_err1;
                }
@@ -1139,12 +1147,21 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                rc = is_known;
                goto out_err2;
        }
+       /*
+        * dasd_eckd_vaildate_server is done on the first device that
+        * is found for an LCU. All later other devices have to wait
+        * for it, so they will read the correct feature codes.
+        */
        if (!is_known) {
-               /* new lcu found */
-               rc = dasd_eckd_validate_server(device); /* will switch pav on */
-               if (rc)
-                       goto out_err3;
-       }
+               dasd_eckd_validate_server(device);
+               dasd_alias_lcu_setup_complete(device);
+       } else
+               dasd_alias_wait_for_lcu_setup(device);
+
+       /* device may report different configuration data after LCU setup */
+       rc = dasd_eckd_read_conf(device);
+       if (rc)
+               goto out_err3;
 
        /* Read Feature Codes */
        dasd_eckd_read_features(device);
@@ -1153,9 +1170,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
                                         &private->rdc_data, 64);
        if (rc) {
-               DBF_EVENT(DBF_WARNING,
-                         "Read device characteristics failed, rc=%d for "
-                         "device: %s", rc, dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+                               "Read device characteristic failed, rc=%d", rc);
                goto out_err3;
        }
        /* find the vaild cylinder size */
@@ -1256,12 +1272,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
        cqr->block = NULL;
        cqr->startdev = device;
        cqr->memdev = device;
-       cqr->retries = 0;
+       cqr->retries = 255;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        return cqr;
 }
 
+/* differentiate between 'no record found' and any other error */
+static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
+{
+       char *sense;
+       if (init_cqr->status == DASD_CQR_DONE)
+               return INIT_CQR_OK;
+       else if (init_cqr->status == DASD_CQR_NEED_ERP ||
+                init_cqr->status == DASD_CQR_FAILED) {
+               sense = dasd_get_sense(&init_cqr->irb);
+               if (sense && (sense[1] & SNS1_NO_REC_FOUND))
+                       return INIT_CQR_UNFORMATTED;
+               else
+                       return INIT_CQR_ERROR;
+       } else
+               return INIT_CQR_ERROR;
+}
+
 /*
  * This is the callback function for the init_analysis cqr. It saves
  * the status of the initial analysis ccw before it frees it and kicks
@@ -1269,21 +1302,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
  * dasd_eckd_do_analysis again (if the devices has not been marked
  * for deletion in the meantime).
  */
-static void
-dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
+static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
+                                       void *data)
 {
        struct dasd_eckd_private *private;
        struct dasd_device *device;
 
        device = init_cqr->startdev;
        private = (struct dasd_eckd_private *) device->private;
-       private->init_cqr_status = init_cqr->status;
+       private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
        dasd_sfree_request(init_cqr, device);
        dasd_kick_device(device);
 }
 
-static int
-dasd_eckd_start_analysis(struct dasd_block *block)
+static int dasd_eckd_start_analysis(struct dasd_block *block)
 {
        struct dasd_eckd_private *private;
        struct dasd_ccw_req *init_cqr;
@@ -1295,27 +1327,44 @@ dasd_eckd_start_analysis(struct dasd_block *block)
        init_cqr->callback = dasd_eckd_analysis_callback;
        init_cqr->callback_data = NULL;
        init_cqr->expires = 5*HZ;
+       /* first try without ERP, so we can later handle unformatted
+        * devices as special case
+        */
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
+       init_cqr->retries = 0;
        dasd_add_request_head(init_cqr);
        return -EAGAIN;
 }
 
-static int
-dasd_eckd_end_analysis(struct dasd_block *block)
+static int dasd_eckd_end_analysis(struct dasd_block *block)
 {
        struct dasd_device *device;
        struct dasd_eckd_private *private;
        struct eckd_count *count_area;
        unsigned int sb, blk_per_trk;
        int status, i;
+       struct dasd_ccw_req *init_cqr;
 
        device = block->base;
        private = (struct dasd_eckd_private *) device->private;
        status = private->init_cqr_status;
        private->init_cqr_status = -1;
-       if (status != DASD_CQR_DONE) {
-               dev_warn(&device->cdev->dev,
-                           "The DASD is not formatted\n");
+       if (status == INIT_CQR_ERROR) {
+               /* try again, this time with full ERP */
+               init_cqr = dasd_eckd_analysis_ccw(device);
+               dasd_sleep_on(init_cqr);
+               status = dasd_eckd_analysis_evaluation(init_cqr);
+               dasd_sfree_request(init_cqr, device);
+       }
+
+       if (status == INIT_CQR_UNFORMATTED) {
+               dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
                return -EMEDIUMTYPE;
+       } else if (status == INIT_CQR_ERROR) {
+               dev_err(&device->cdev->dev,
+                       "Detecting the DASD disk layout failed because "
+                       "of an I/O error\n");
+               return -EIO;
        }
 
        private->uses_cdl = 1;
@@ -1607,8 +1656,7 @@ dasd_eckd_format_device(struct dasd_device * device,
        }
        fcp->startdev = device;
        fcp->memdev = device;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags);
-       fcp->retries = 5;       /* set retry counter to enable default ERP */
+       fcp->retries = 256;
        fcp->buildclk = get_clock();
        fcp->status = DASD_CQR_FILLED;
        return fcp;
@@ -2690,6 +2738,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->retries = 0;
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        cqr->expires = 10 * HZ;
 
        /* Prepare for Read Subsystem Data */
@@ -3240,11 +3289,15 @@ int dasd_eckd_restore_device(struct dasd_device *device)
        if (is_known < 0)
                return is_known;
        if (!is_known) {
-               /* new lcu found */
-               rc = dasd_eckd_validate_server(device); /* will switch pav on */
-               if (rc)
-                       goto out_err;
-       }
+               dasd_eckd_validate_server(device);
+               dasd_alias_lcu_setup_complete(device);
+       } else
+               dasd_alias_wait_for_lcu_setup(device);
+
+       /* RE-Read Configuration Data */
+       rc = dasd_eckd_read_conf(device);
+       if (rc)
+               goto out_err;
 
        /* Read Feature Codes */
        dasd_eckd_read_features(device);
@@ -3253,9 +3306,8 @@ int dasd_eckd_restore_device(struct dasd_device *device)
        rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
                                         &temp_rdc_data, 64);
        if (rc) {
-               DBF_EVENT(DBF_WARNING,
-                         "Read device characteristics failed, rc=%d for "
-                         "device: %s", rc, dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+                               "Read device characteristic failed, rc=%d", rc);
                goto out_err;
        }
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
index ad45bca..864d53c 100644 (file)
@@ -414,6 +414,7 @@ struct alias_lcu {
        struct summary_unit_check_work_data suc_data;
        struct read_uac_work_data ruac_data;
        struct dasd_ccw_req *rsu_cqr;
+       struct completion lcu_setup;
 };
 
 struct alias_pav_group {
@@ -460,5 +461,6 @@ int dasd_alias_remove_device(struct dasd_device *);
 struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
 void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
 void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
-
+void dasd_alias_lcu_setup_complete(struct dasd_device *);
+void dasd_alias_wait_for_lcu_setup(struct dasd_device *);
 #endif                         /* DASD_ECKD_H */
index d96039e..1f3e967 100644 (file)
@@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
        eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
        if (!eerb)
                return -ENOMEM;
-       lock_kernel();
        eerb->buffer_page_count = eer_pages;
        if (eerb->buffer_page_count < 1 ||
            eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
@@ -544,7 +543,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
                DBF_EVENT(DBF_WARNING, "can't open device since module "
                        "parameter eer_pages is smaller than 1 or"
                        " bigger than %d", (int)(INT_MAX / PAGE_SIZE));
-               unlock_kernel();
                return -EINVAL;
        }
        eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;
@@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
                               GFP_KERNEL);
         if (!eerb->buffer) {
                kfree(eerb);
-               unlock_kernel();
                 return -ENOMEM;
        }
        if (dasd_eer_allocate_buffer_pages(eerb->buffer,
                                           eerb->buffer_page_count)) {
                kfree(eerb->buffer);
                kfree(eerb);
-               unlock_kernel();
                return -ENOMEM;
        }
        filp->private_data = eerb;
@@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
        list_add(&eerb->list, &bufferlist);
        spin_unlock_irqrestore(&bufferlock, flags);
 
-       unlock_kernel();
        return nonseekable_open(inp,filp);
 }
 
index f245377..0f15244 100644 (file)
@@ -20,7 +20,6 @@
 #include <asm/idals.h>
 #include <asm/ebcdic.h>
 #include <asm/io.h>
-#include <asm/todclk.h>
 #include <asm/ccwdev.h>
 
 #include "dasd_int.h"
@@ -141,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
        }
        block = dasd_alloc_block();
        if (IS_ERR(block)) {
-               DBF_EVENT(DBF_WARNING, "could not allocate dasd block "
-                         "structure for device: %s",
-                         dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate "
+                               "dasd block structure");
                device->private = NULL;
                kfree(private);
                return PTR_ERR(block);
@@ -155,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
        rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC,
                                         &private->rdc_data, 32);
        if (rc) {
-               DBF_EVENT(DBF_WARNING, "Read device characteristics returned "
-                         "error %d for device: %s",
-                         rc, dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device "
+                               "characteristics returned error %d", rc);
                device->block = NULL;
                dasd_free_block(block);
                device->private = NULL;
index 8afd9fa..e4c2143 100644 (file)
@@ -108,6 +108,16 @@ do { \
                            d_data); \
 } while(0)
 
+#define DBF_EVENT_DEVID(d_level, d_cdev, d_str, d_data...)     \
+do { \
+       struct ccw_dev_id __dev_id;                     \
+       ccw_device_get_id(d_cdev, &__dev_id);           \
+       debug_sprintf_event(dasd_debug_area,            \
+                           d_level,                                    \
+                           "0.%x.%04x " d_str "\n",                    \
+                           __dev_id.ssid, __dev_id.devno, d_data);     \
+} while (0)
+
 #define DBF_EXC(d_level, d_str, d_data...)\
 do { \
        debug_sprintf_exception(dasd_debug_area, \
@@ -595,6 +605,9 @@ int dasd_generic_restore_device(struct ccw_device *);
 int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
 char *dasd_get_sense(struct irb *);
 
+void dasd_device_set_stop_bits(struct dasd_device *, int);
+void dasd_device_remove_stop_bits(struct dasd_device *, int);
+
 /* externals in dasd_devmap.c */
 extern int dasd_max_devindex;
 extern int dasd_probeonly;
index f756a1b..478bcdb 100644 (file)
@@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block)
        pr_info("%s: The DASD has been put in the quiesce "
                "state\n", dev_name(&base->cdev->dev));
        spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
-       base->stopped |= DASD_STOPPED_QUIESCE;
+       dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
        spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
        return 0;
 }
@@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block)
        pr_info("%s: I/O operations have been resumed "
                "on the DASD\n", dev_name(&base->cdev->dev));
        spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
-       base->stopped &= ~DASD_STOPPED_QUIESCE;
+       dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE);
        spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
 
        dasd_schedule_block_bh(block);
index 654daa3..5f23eca 100644 (file)
@@ -215,7 +215,7 @@ dasd_statistics_read(char *page, char **start, off_t off,
        }
 
        prof = &dasd_global_profile;
-       /* prevent couter 'overflow' on output */
+       /* prevent counter 'overflow' on output */
        for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
             factor *= 10);
 
index 21639d6..9d61683 100644 (file)
@@ -857,7 +857,6 @@ static struct console con3215 = {
 
 /*
  * 3215 console initialization code called from console_init().
- * NOTE: This is called before kmalloc is available.
  */
 static int __init con3215_init(void)
 {
index bb838bd..6bca81a 100644 (file)
@@ -572,7 +572,6 @@ static struct console con3270 = {
 
 /*
  * 3270 console initialization code called from console_init().
- * NOTE: This is called before kmalloc is available.
  */
 static int __init
 con3270_init(void)
index 097d384..28e4649 100644 (file)
@@ -38,6 +38,8 @@ struct fs3270 {
        size_t rdbuf_size;              /* size of data returned by RDBUF */
 };
 
+static DEFINE_MUTEX(fs3270_mutex);
+
 static void
 fs3270_wake_up(struct raw3270_request *rq, void *data)
 {
@@ -74,7 +76,7 @@ fs3270_do_io(struct raw3270_view *view, struct raw3270_request *rq)
                }
                rc = raw3270_start(view, rq);
                if (rc == 0) {
-                       /* Started sucessfully. Now wait for completion. */
+                       /* Started successfully. Now wait for completion. */
                        wait_event(fp->wait, raw3270_request_final(rq));
                }
        } while (rc == -EACCES);
@@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        if (!fp)
                return -ENODEV;
        rc = 0;
-       lock_kernel();
+       mutex_lock(&fs3270_mutex);
        switch (cmd) {
        case TUBICMD:
                fp->read_command = arg;
@@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                        rc = -EFAULT;
                break;
        }
-       unlock_kernel();
+       mutex_unlock(&fs3270_mutex);
        return rc;
 }
 
@@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp)
                minor = tty->index + RAW3270_FIRSTMINOR;
                tty_kref_put(tty);
        }
-       lock_kernel();
+       mutex_lock(&fs3270_mutex);
        /* Check if some other program is already using fullscreen mode. */
        fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
        if (!IS_ERR(fp)) {
@@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp)
        }
        filp->private_data = fp;
 out:
-       unlock_kernel();
+       mutex_unlock(&fs3270_mutex);
        return rc;
 }
 
index 66e21dd..60473f8 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
-#include <linux/smp_lock.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp)
        /*
         * only one user allowed
         */
-       lock_kernel();
        rc = -EBUSY;
        if (test_and_set_bit(MON_IN_USE, &mon_in_use))
                goto out;
@@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp)
        }
        filp->private_data = monpriv;
        dev_set_drvdata(monreader_device, monpriv);
-       unlock_kernel();
        return nonseekable_open(inode, filp);
 
 out_path:
@@ -331,7 +328,6 @@ out_priv:
 out_use:
        clear_bit(MON_IN_USE, &mon_in_use);
 out:
-       unlock_kernel();
        return rc;
 }
 
@@ -607,6 +603,10 @@ static int __init mon_init(void)
        }
        dcss_mkname(mon_dcss_name, &user_data_connect[8]);
 
+       /*
+        * misc_register() has to be the last action in module_init(), because
+        * file operations will be available right after this.
+        */
        rc = misc_register(&mon_dev);
        if (rc < 0 )
                goto out;
index 66fb8eb..6532ed8 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/errno.h>
-#include <linux/smp_lock.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
@@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp)
        monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
        if (!monpriv)
                return -ENOMEM;
-       lock_kernel();
        INIT_LIST_HEAD(&monpriv->list);
        monpriv->hdr_to_read = sizeof(monpriv->hdr);
        mutex_init(&monpriv->thread_mutex);
        filp->private_data = monpriv;
        list_add_tail(&monpriv->priv_list, &mon_priv_list);
-       unlock_kernel();
        return nonseekable_open(inode, filp);
 }
 
@@ -364,6 +361,10 @@ static int __init mon_init(void)
                goto out_driver;
        }
 
+       /*
+        * misc_register() has to be the last action in module_init(), because
+        * file operations will be available right after this.
+        */
        rc = misc_register(&mon_dev);
        if (rc)
                goto out_device;
index 5cc11c6..28b5afc 100644 (file)
@@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void)
                do {
                        memset(sccb, 0, sizeof(*sccb));
                        sccb->header.length = sizeof(*sccb);
+                       sccb->header.function_code = 0x80;
                        sccb->header.control_mask[2] = 0x80;
                        rc = sclp_cmd_sync_early(commands[i], sccb);
                } while (rc == -EBUSY);
index a263337..7a242f0 100644 (file)
@@ -212,6 +212,9 @@ struct tape_device {
        struct tape_class_device *      nt;
        struct tape_class_device *      rt;
 
+       /* Device mutex to serialize tape commands. */
+       struct mutex                    mutex;
+
        /* Device discipline information. */
        struct tape_discipline *        discipline;
        void *                          discdata;
@@ -292,9 +295,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *);
 extern int tape_generic_probe(struct ccw_device *);
 extern void tape_generic_remove(struct ccw_device *);
 
-extern struct tape_device *tape_get_device(int devindex);
-extern struct tape_device *tape_get_device_reference(struct tape_device *);
-extern struct tape_device *tape_put_device(struct tape_device *);
+extern struct tape_device *tape_find_device(int devindex);
+extern struct tape_device *tape_get_device(struct tape_device *);
+extern void tape_put_device(struct tape_device *);
 
 /* Externals from tape_char.c */
 extern int tapechar_init(void);
index 2fe45ff..3657fe1 100644 (file)
@@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work)
 {
        struct tape_34xx_work *p =
                container_of(work, struct tape_34xx_work, work);
+       struct tape_device *device = p->device;
 
        switch(p->op) {
                case TO_MSEN:
-                       tape_34xx_medium_sense(p->device);
+                       tape_34xx_medium_sense(device);
                        break;
                default:
                        DBF_EVENT(3, "T34XX: internal error: unknown work\n");
        }
-
-       p->device = tape_put_device(p->device);
+       tape_put_device(device);
        kfree(p);
 }
 
@@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
 
        INIT_WORK(&p->work, tape_34xx_work_handler);
 
-       p->device = tape_get_device_reference(device);
+       p->device = tape_get_device(device);
        p->op     = op;
 
        schedule_work(&p->work);
index e4cc3aa..0c72aad 100644 (file)
@@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
 
        INIT_WORK(&p->work, tape_3590_work_handler);
 
-       p->device = tape_get_device_reference(device);
+       p->device = tape_get_device(device);
        p->op = op;
 
        schedule_work(&p->work);
index 0c0705b..4799cc2 100644 (file)
@@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = {
        .owner           = THIS_MODULE,
        .open            = tapeblock_open,
        .release         = tapeblock_release,
-       .locked_ioctl           = tapeblock_ioctl,
+       .ioctl           = tapeblock_ioctl,
        .media_changed   = tapeblock_medium_changed,
        .revalidate_disk = tapeblock_revalidate_disk,
 };
@@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device)
        disk->major = tapeblock_major;
        disk->first_minor = device->first_minor;
        disk->fops = &tapeblock_fops;
-       disk->private_data = tape_get_device_reference(device);
+       disk->private_data = tape_get_device(device);
        disk->queue = blkdat->request_queue;
        set_capacity(disk, 0);
        sprintf(disk->disk_name, "btibm%d",
@@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device)
 
        blkdat->disk = disk;
        blkdat->medium_changed = 1;
-       blkdat->request_queue->queuedata = tape_get_device_reference(device);
+       blkdat->request_queue->queuedata = tape_get_device(device);
 
        add_disk(disk);
 
-       tape_get_device_reference(device);
+       tape_get_device(device);
        INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
 
        return 0;
@@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device)
        }
 
        del_gendisk(device->blk_data.disk);
-       device->blk_data.disk->private_data =
-               tape_put_device(device->blk_data.disk->private_data);
+       device->blk_data.disk->private_data = NULL;
+       tape_put_device(device);
        put_disk(device->blk_data.disk);
 
        device->blk_data.disk = NULL;
 cleanup_queue:
-       device->blk_data.request_queue->queuedata = tape_put_device(device);
+       device->blk_data.request_queue->queuedata = NULL;
+       tape_put_device(device);
 
        blk_cleanup_queue(device->blk_data.request_queue);
        device->blk_data.request_queue = NULL;
@@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
        struct tape_device *    device;
        int                     rc;
 
-       device = tape_get_device_reference(disk->private_data);
+       device = tape_get_device(disk->private_data);
 
        if (device->required_tapemarks) {
                DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
index 31566c5..23d773a 100644 (file)
@@ -33,8 +33,7 @@ static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
 static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
 static int tapechar_open(struct inode *,struct file *);
 static int tapechar_release(struct inode *,struct file *);
-static int tapechar_ioctl(struct inode *, struct file *, unsigned int,
-                         unsigned long);
+static long tapechar_ioctl(struct file *, unsigned int, unsigned long);
 static long tapechar_compat_ioctl(struct file *, unsigned int,
                          unsigned long);
 
@@ -43,7 +42,7 @@ static const struct file_operations tape_fops =
        .owner = THIS_MODULE,
        .read = tapechar_read,
        .write = tapechar_write,
-       .ioctl = tapechar_ioctl,
+       .unlocked_ioctl = tapechar_ioctl,
        .compat_ioctl = tapechar_compat_ioctl,
        .open = tapechar_open,
        .release = tapechar_release,
@@ -170,7 +169,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
        if (rc == 0) {
                rc = block_size - request->rescnt;
                DBF_EVENT(6, "TCHAR:rbytes:  %x\n", rc);
-               filp->f_pos += rc;
                /* Copy data from idal buffer to user space. */
                if (idal_buffer_to_user(device->char_data.idal_buf,
                                        data, rc) != 0)
@@ -238,7 +236,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t
                        break;
                DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
                          block_size - request->rescnt);
-               filp->f_pos += block_size - request->rescnt;
                written += block_size - request->rescnt;
                if (request->rescnt != 0)
                        break;
@@ -286,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp)
        if (imajor(filp->f_path.dentry->d_inode) != tapechar_major)
                return -ENODEV;
 
-       lock_kernel();
        minor = iminor(filp->f_path.dentry->d_inode);
-       device = tape_get_device(minor / TAPE_MINORS_PER_DEV);
+       device = tape_find_device(minor / TAPE_MINORS_PER_DEV);
        if (IS_ERR(device)) {
-               DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n");
-               rc = PTR_ERR(device);
-               goto out;
+               DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n");
+               return PTR_ERR(device);
        }
 
-
        rc = tape_open(device);
        if (rc == 0) {
                filp->private_data = device;
-               rc = nonseekable_open(inode, filp);
-       }
-       else
+               nonseekable_open(inode, filp);
+       } else
                tape_put_device(device);
 
-out:
-       unlock_kernel();
        return rc;
 }
 
@@ -342,7 +333,8 @@ tapechar_release(struct inode *inode, struct file *filp)
                device->char_data.idal_buf = NULL;
        }
        tape_release(device);
-       filp->private_data = tape_put_device(device);
+       filp->private_data = NULL;
+       tape_put_device(device);
 
        return 0;
 }
@@ -351,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp)
  * Tape device io controls.
  */
 static int
-tapechar_ioctl(struct inode *inp, struct file *filp,
-              unsigned int no, unsigned long data)
+__tapechar_ioctl(struct tape_device *device,
+                unsigned int no, unsigned long data)
 {
-       struct tape_device *device;
        int rc;
 
-       DBF_EVENT(6, "TCHAR:ioct\n");
-
-       device = (struct tape_device *) filp->private_data;
-
        if (no == MTIOCTOP) {
                struct mtop op;
 
@@ -452,6 +439,21 @@ tapechar_ioctl(struct inode *inp, struct file *filp,
        return device->discipline->ioctl_fn(device, no, data);
 }
 
+static long
+tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data)
+{
+       struct tape_device *device;
+       long rc;
+
+       DBF_EVENT(6, "TCHAR:ioct\n");
+
+       device = (struct tape_device *) filp->private_data;
+       mutex_lock(&device->mutex);
+       rc = __tapechar_ioctl(device, no, data);
+       mutex_unlock(&device->mutex);
+       return rc;
+}
+
 static long
 tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
 {
@@ -459,9 +461,9 @@ tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
        int rval = -ENOIOCTLCMD;
 
        if (device->discipline->ioctl_fn) {
-               lock_kernel();
+               mutex_lock(&device->mutex);
                rval = device->discipline->ioctl_fn(device, no, data);
-               unlock_kernel();
+               mutex_unlock(&device->mutex);
                if (rval == -EINVAL)
                        rval = -ENOIOCTLCMD;
        }
index 5cd31e0..f5d6802 100644 (file)
@@ -492,6 +492,7 @@ tape_alloc_device(void)
                kfree(device);
                return ERR_PTR(-ENOMEM);
        }
+       mutex_init(&device->mutex);
        INIT_LIST_HEAD(&device->req_queue);
        INIT_LIST_HEAD(&device->node);
        init_waitqueue_head(&device->state_change_wq);
@@ -511,11 +512,12 @@ tape_alloc_device(void)
  * increment the reference count.
  */
 struct tape_device *
-tape_get_device_reference(struct tape_device *device)
+tape_get_device(struct tape_device *device)
 {
-       DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device,
-               atomic_inc_return(&device->ref_count));
+       int count;
 
+       count = atomic_inc_return(&device->ref_count);
+       DBF_EVENT(4, "tape_get_device(%p) = %i\n", device, count);
        return device;
 }
 
@@ -525,32 +527,25 @@ tape_get_device_reference(struct tape_device *device)
  * The function returns a NULL pointer to be used by the caller
  * for clearing reference pointers.
  */
-struct tape_device *
+void
 tape_put_device(struct tape_device *device)
 {
-       int remain;
+       int count;
 
-       remain = atomic_dec_return(&device->ref_count);
-       if (remain > 0) {
-               DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain);
-       } else {
-               if (remain < 0) {
-                       DBF_EVENT(4, "put device without reference\n");
-               } else {
-                       DBF_EVENT(4, "tape_free_device(%p)\n", device);
-                       kfree(device->modeset_byte);
-                       kfree(device);
-               }
+       count = atomic_dec_return(&device->ref_count);
+       DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, count);
+       BUG_ON(count < 0);
+       if (count == 0) {
+               kfree(device->modeset_byte);
+               kfree(device);
        }
-
-       return NULL;                    
 }
 
 /*
  * Find tape device by a device index.
  */
 struct tape_device *
-tape_get_device(int devindex)
+tape_find_device(int devindex)
 {
        struct tape_device *device, *tmp;
 
@@ -558,7 +553,7 @@ tape_get_device(int devindex)
        read_lock(&tape_device_lock);
        list_for_each_entry(tmp, &tape_device_list, node) {
                if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) {
-                       device = tape_get_device_reference(tmp);
+                       device = tape_get_device(tmp);
                        break;
                }
        }
@@ -579,7 +574,8 @@ tape_generic_probe(struct ccw_device *cdev)
        device = tape_alloc_device();
        if (IS_ERR(device))
                return -ENODEV;
-       ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
+       ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP |
+                                    CCWDEV_DO_MULTIPATH);
        ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
        if (ret) {
                tape_put_device(device);
@@ -606,7 +602,8 @@ __tape_discard_requests(struct tape_device *device)
                list_del(&request->list);
 
                /* Decrease ref_count for removed request. */
-               request->device = tape_put_device(device);
+               request->device = NULL;
+               tape_put_device(device);
                request->rc = -EIO;
                if (request->callback != NULL)
                        request->callback(request, request->callback_data);
@@ -664,9 +661,11 @@ tape_generic_remove(struct ccw_device *cdev)
                        tape_cleanup_device(device);
        }
 
-       if (!dev_get_drvdata(&cdev->dev)) {
+       device = dev_get_drvdata(&cdev->dev);
+       if (device) {
                sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group);
-               dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev)));
+               dev_set_drvdata(&cdev->dev, NULL);
+               tape_put_device(device);
        }
 }
 
@@ -721,9 +720,8 @@ tape_free_request (struct tape_request * request)
 {
        DBF_LH(6, "Free request %p\n", request);
 
-       if (request->device != NULL) {
-               request->device = tape_put_device(request->device);
-       }
+       if (request->device)
+               tape_put_device(request->device);
        kfree(request->cpdata);
        kfree(request->cpaddr);
        kfree(request);
@@ -838,7 +836,8 @@ static void tape_long_busy_timeout(unsigned long data)
        BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY);
        DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id);
        __tape_start_next_request(device);
-       device->lb_timeout.data = (unsigned long) tape_put_device(device);
+       device->lb_timeout.data = 0UL;
+       tape_put_device(device);
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
 }
 
@@ -918,7 +917,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *request)
        }
 
        /* Increase use count of device for the added request. */
-       request->device = tape_get_device_reference(device);
+       request->device = tape_get_device(device);
 
        if (list_empty(&device->req_queue)) {
                /* No other requests are on the queue. Start this one. */
@@ -1117,8 +1116,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
                if (req->status == TAPE_REQUEST_LONG_BUSY) {
                        DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id);
                        if (del_timer(&device->lb_timeout)) {
-                               device->lb_timeout.data = (unsigned long)
-                                       tape_put_device(device);
+                               device->lb_timeout.data = 0UL;
+                               tape_put_device(device);
                                __tape_start_next_request(device);
                        }
                        return;
@@ -1173,7 +1172,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
                        break;
                case TAPE_IO_LONG_BUSY:
                        device->lb_timeout.data =
-                               (unsigned long)tape_get_device_reference(device);
+                               (unsigned long) tape_get_device(device);
                        device->lb_timeout.expires = jiffies +
                                LONG_BUSY_TIMEOUT * HZ;
                        DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id);
@@ -1326,7 +1325,7 @@ EXPORT_SYMBOL(tape_generic_online);
 EXPORT_SYMBOL(tape_generic_offline);
 EXPORT_SYMBOL(tape_generic_pm_suspend);
 EXPORT_SYMBOL(tape_put_device);
-EXPORT_SYMBOL(tape_get_device_reference);
+EXPORT_SYMBOL(tape_get_device);
 EXPORT_SYMBOL(tape_state_verbose);
 EXPORT_SYMBOL(tape_op_verbose);
 EXPORT_SYMBOL(tape_state_set);
index 202f421..ebd820c 100644 (file)
@@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v)
                seq_printf(m, "TapeNo\tBusID      CuType/Model\t"
                        "DevType/Model\tBlkSize\tState\tOp\tMedState\n");
        }
-       device = tape_get_device(n);
+       device = tape_find_device(n);
        if (IS_ERR(device))
                return 0;
        spin_lock_irq(get_ccwdev_lock(device->cdev));
index 3838567..911822d 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <linux/slab.h>
 #include <linux/bootmem.h>
+#include <linux/compat.h>
 
 #include <asm/ccwdev.h>
 #include <asm/cio.h>
@@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file,
        return kbd_ioctl(tp->kbd, file, cmd, arg);
 }
 
+#ifdef CONFIG_COMPAT
+static long
+tty3270_compat_ioctl(struct tty_struct *tty, struct file *file,
+             unsigned int cmd, unsigned long arg)
+{
+       struct tty3270 *tp;
+
+       tp = tty->driver_data;
+       if (!tp)
+               return -ENODEV;
+       if (tty->flags & (1 << TTY_IO_ERROR))
+               return -EIO;
+       return kbd_ioctl(tp->kbd, file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
 static const struct tty_operations tty3270_ops = {
        .open = tty3270_open,
        .close = tty3270_close,
@@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = {
        .hangup = tty3270_hangup,
        .wait_until_sent = tty3270_wait_until_sent,
        .ioctl = tty3270_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl = tty3270_compat_ioctl,
+#endif
        .set_termios = tty3270_set_termios
 };
 
index d1a142f..899aa79 100644 (file)
@@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
                return -ENOSYS;
 
        /* Besure this device hasn't already been opened */
-       lock_kernel();
        spin_lock_bh(&logptr->priv_lock);
        if (logptr->dev_in_use) {
                spin_unlock_bh(&logptr->priv_lock);
-               unlock_kernel();
                return -EBUSY;
        }
        logptr->dev_in_use = 1;
@@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
                   || (logptr->iucv_path_severed));
        if (logptr->iucv_path_severed)
                goto out_record;
-       ret = nonseekable_open(inode, filp);
-       unlock_kernel();
-       return ret;
+       nonseekable_open(inode, filp);
+       return 0;
 
 out_record:
        if (logptr->autorecording)
@@ -372,7 +369,6 @@ out_path:
        logptr->path = NULL;
 out_dev:
        logptr->dev_in_use = 0;
-       unlock_kernel();
        return -EIO;
 }
 
index 77571b6..cc56fc7 100644 (file)
@@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file)
 
        if (accmode == O_RDWR)
                return -EACCES;
-       lock_kernel();
        /*
         * We treat the minor number as the devno of the ur device
         * to find in the driver tree.
@@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file)
                goto fail_urfile_free;
        urf->file_reclen = rc;
        file->private_data = urf;
-       unlock_kernel();
        return 0;
 
 fail_urfile_free:
@@ -761,7 +759,6 @@ fail_unlock:
 fail_put:
        urdev_put(urd);
 out:
-       unlock_kernel();
        return rc;
 }
 
index f2bc287..c974058 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/moduleparam.h>
 #include <linux/suspend.h>
 #include <linux/watchdog.h>
-#include <linux/smp_lock.h>
 
 #include <asm/ebcdic.h>
 #include <asm/io.h>
@@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60;
 static unsigned long vmwdt_is_open;
 static int vmwdt_expect_close;
 
+static DEFINE_MUTEX(vmwdt_mutex);
+
 #define VMWDT_OPEN     0       /* devnode is open or suspend in progress */
 #define VMWDT_RUNNING  1       /* The watchdog is armed */
 
@@ -133,15 +134,11 @@ static int __init vmwdt_probe(void)
 static int vmwdt_open(struct inode *i, struct file *f)
 {
        int ret;
-       lock_kernel();
-       if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
-               unlock_kernel();
+       if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open))
                return -EBUSY;
-       }
        ret = vmwdt_keepalive();
        if (ret)
                clear_bit(VMWDT_OPEN, &vmwdt_is_open);
-       unlock_kernel();
        return ret ? ret : nonseekable_open(i, f);
 }
 
@@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = {
        .identity = "z/VM Watchdog Timer",
 };
 
-static int vmwdt_ioctl(struct inode *i, struct file *f,
-                         unsigned int cmd, unsigned long arg)
+static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg)
 {
        switch (cmd) {
        case WDIOC_GETSUPPORT:
@@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f,
        case WDIOC_KEEPALIVE:
                return vmwdt_keepalive();
        }
-
        return -EINVAL;
 }
 
+static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+       int rc;
+
+       mutex_lock(&vmwdt_mutex);
+       rc = __vmwdt_ioctl(cmd, arg);
+       mutex_unlock(&vmwdt_mutex);
+       return (long) rc;
+}
+
 static ssize_t vmwdt_write(struct file *f, const char __user *buf,
                                size_t count, loff_t *ppos)
 {
@@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = {
 static const struct file_operations vmwdt_fops = {
        .open    = &vmwdt_open,
        .release = &vmwdt_close,
-       .ioctl   = &vmwdt_ioctl,
+       .unlocked_ioctl = &vmwdt_ioctl,
        .write   = &vmwdt_write,
        .owner   = THIS_MODULE,
 };
@@ -309,6 +314,10 @@ static int __init vmwdt_init(void)
        ret = register_pm_notifier(&vmwdt_power_notifier);
        if (ret)
                return ret;
+       /*
+        * misc_register() has to be the last action in module_init(), because
+        * file operations will be available right after this.
+        */
        ret = misc_register(&vmwdt_dev);
        if (ret) {
                unregister_pm_notifier(&vmwdt_power_notifier);
index fa4c966..d033414 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \
-       fcx.o itcw.o crw.o
+       fcx.o itcw.o crw.o ccwreq.o
 ccw_device-objs += device.o device_fsm.o device_ops.o
 ccw_device-objs += device_id.o device_pgid.o device_status.o
 obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c
new file mode 100644 (file)
index 0000000..9509e38
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ *  Handling of internal CCW device requests.
+ *
+ *    Copyright IBM Corp. 2009
+ *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/err.h>
+#include <asm/ccwdev.h>
+#include <asm/cio.h>
+
+#include "io_sch.h"
+#include "cio.h"
+#include "device.h"
+#include "cio_debug.h"
+
+/**
+ * lpm_adjust - adjust path mask
+ * @lpm: path mask to adjust
+ * @mask: mask of available paths
+ *
+ * Shift @lpm right until @lpm and @mask have at least one bit in common or
+ * until @lpm is zero. Return the resulting lpm.
+ */
+int lpm_adjust(int lpm, int mask)
+{
+       while (lpm && ((lpm & mask) == 0))
+               lpm >>= 1;
+       return lpm;
+}
+
+/*
+ * Adjust path mask to use next path and reset retry count. Return resulting
+ * path mask.
+ */
+static u16 ccwreq_next_path(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+
+       req->retries    = req->maxretries;
+       req->mask       = lpm_adjust(req->mask >>= 1, req->lpm);
+
+       return req->mask;
+}
+
+/*
+ * Clean up device state and report to callback.
+ */
+static void ccwreq_stop(struct ccw_device *cdev, int rc)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+
+       if (req->done)
+               return;
+       req->done = 1;
+       ccw_device_set_timeout(cdev, 0);
+       memset(&cdev->private->irb, 0, sizeof(struct irb));
+       sch->lpm = sch->schib.pmcw.pam;
+       if (rc && rc != -ENODEV && req->drc)
+               rc = req->drc;
+       req->callback(cdev, req->data, rc);
+}
+
+/*
+ * (Re-)Start the operation until retries and paths are exhausted.
+ */
+static void ccwreq_do(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw1 *cp = req->cp;
+       int rc = -EACCES;
+
+       while (req->mask) {
+               if (req->retries-- == 0) {
+                       /* Retries exhausted, try next path. */
+                       ccwreq_next_path(cdev);
+                       continue;
+               }
+               /* Perform start function. */
+               sch->lpm = 0xff;
+               memset(&cdev->private->irb, 0, sizeof(struct irb));
+               rc = cio_start(sch, cp, (u8) req->mask);
+               if (rc == 0) {
+                       /* I/O started successfully. */
+                       ccw_device_set_timeout(cdev, req->timeout);
+                       return;
+               }
+               if (rc == -ENODEV) {
+                       /* Permanent device error. */
+                       break;
+               }
+               if (rc == -EACCES) {
+                       /* Permant path error. */
+                       ccwreq_next_path(cdev);
+                       continue;
+               }
+               /* Temporary improper status. */
+               rc = cio_clear(sch);
+               if (rc)
+                       break;
+               return;
+       }
+       ccwreq_stop(cdev, rc);
+}
+
+/**
+ * ccw_request_start - perform I/O request
+ * @cdev: ccw device
+ *
+ * Perform the I/O request specified by cdev->req.
+ */
+void ccw_request_start(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+
+       /* Try all paths twice to counter link flapping. */
+       req->mask       = 0x8080;
+       req->retries    = req->maxretries;
+       req->mask       = lpm_adjust(req->mask, req->lpm);
+       req->drc        = 0;
+       req->done       = 0;
+       req->cancel     = 0;
+       if (!req->mask)
+               goto out_nopath;
+       ccwreq_do(cdev);
+       return;
+
+out_nopath:
+       ccwreq_stop(cdev, -EACCES);
+}
+
+/**
+ * ccw_request_cancel - cancel running I/O request
+ * @cdev: ccw device
+ *
+ * Cancel the I/O request specified by cdev->req. Return non-zero if request
+ * has already finished, zero otherwise.
+ */
+int ccw_request_cancel(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+       int rc;
+
+       if (req->done)
+               return 1;
+       req->cancel = 1;
+       rc = cio_clear(sch);
+       if (rc)
+               ccwreq_stop(cdev, rc);
+       return 0;
+}
+
+/*
+ * Return the status of the internal I/O started on the specified ccw device.
+ * Perform BASIC SENSE if required.
+ */
+static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
+{
+       struct irb *irb = &cdev->private->irb;
+       struct cmd_scsw *scsw = &irb->scsw.cmd;
+
+       /* Perform BASIC SENSE if needed. */
+       if (ccw_device_accumulate_and_sense(cdev, lcirb))
+               return IO_RUNNING;
+       /* Check for halt/clear interrupt. */
+       if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+               return IO_KILLED;
+       /* Check for path error. */
+       if (scsw->cc == 3 || scsw->pno)
+               return IO_PATH_ERROR;
+       /* Handle BASIC SENSE data. */
+       if (irb->esw.esw0.erw.cons) {
+               CIO_TRACE_EVENT(2, "sensedata");
+               CIO_HEX_EVENT(2, &cdev->private->dev_id,
+                             sizeof(struct ccw_dev_id));
+               CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT);
+               /* Check for command reject. */
+               if (irb->ecw[0] & SNS0_CMD_REJECT)
+                       return IO_REJECTED;
+               /* Assume that unexpected SENSE data implies an error. */
+               return IO_STATUS_ERROR;
+       }
+       /* Check for channel errors. */
+       if (scsw->cstat != 0)
+               return IO_STATUS_ERROR;
+       /* Check for device errors. */
+       if (scsw->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+               return IO_STATUS_ERROR;
+       /* Check for final state. */
+       if (!(scsw->dstat & DEV_STAT_DEV_END))
+               return IO_RUNNING;
+       /* Check for other improper status. */
+       if (scsw->cc == 1 && (scsw->stctl & SCSW_STCTL_ALERT_STATUS))
+               return IO_STATUS_ERROR;
+       return IO_DONE;
+}
+
+/*
+ * Log ccw request status.
+ */
+static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status)
+{
+       struct ccw_request *req = &cdev->private->req;
+       struct {
+               struct ccw_dev_id dev_id;
+               u16 retries;
+               u8 lpm;
+               u8 status;
+       }  __attribute__ ((packed)) data;
+       data.dev_id     = cdev->private->dev_id;
+       data.retries    = req->retries;
+       data.lpm        = (u8) req->mask;
+       data.status     = (u8) status;
+       CIO_TRACE_EVENT(2, "reqstat");
+       CIO_HEX_EVENT(2, &data, sizeof(data));
+}
+
+/**
+ * ccw_request_handler - interrupt handler for I/O request procedure.
+ * @cdev: ccw device
+ *
+ * Handle interrupt during I/O request procedure.
+ */
+void ccw_request_handler(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+       struct irb *irb = (struct irb *) __LC_IRB;
+       enum io_status status;
+       int rc = -EOPNOTSUPP;
+
+       /* Check status of I/O request. */
+       status = ccwreq_status(cdev, irb);
+       if (req->filter)
+               status = req->filter(cdev, req->data, irb, status);
+       if (status != IO_RUNNING)
+               ccw_device_set_timeout(cdev, 0);
+       if (status != IO_DONE && status != IO_RUNNING)
+               ccwreq_log_status(cdev, status);
+       switch (status) {
+       case IO_DONE:
+               break;
+       case IO_RUNNING:
+               return;
+       case IO_REJECTED:
+               goto err;
+       case IO_PATH_ERROR:
+               goto out_next_path;
+       case IO_STATUS_ERROR:
+               goto out_restart;
+       case IO_KILLED:
+               /* Check if request was cancelled on purpose. */
+               if (req->cancel) {
+                       rc = -EIO;
+                       goto err;
+               }
+               goto out_restart;
+       }
+       /* Check back with request initiator. */
+       if (!req->check)
+               goto out;
+       switch (req->check(cdev, req->data)) {
+       case 0:
+               break;
+       case -EAGAIN:
+               goto out_restart;
+       case -EACCES:
+               goto out_next_path;
+       default:
+               goto err;
+       }
+out:
+       ccwreq_stop(cdev, 0);
+       return;
+
+out_next_path:
+       /* Try next path and restart I/O. */
+       if (!ccwreq_next_path(cdev)) {
+               rc = -EACCES;
+               goto err;
+       }
+out_restart:
+       /* Restart. */
+       ccwreq_do(cdev);
+       return;
+err:
+       ccwreq_stop(cdev, rc);
+}
+
+
+/**
+ * ccw_request_timeout - timeout handler for I/O request procedure
+ * @cdev: ccw device
+ *
+ * Handle timeout during I/O request procedure.
+ */
+void ccw_request_timeout(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+       int rc;
+
+       if (!ccwreq_next_path(cdev)) {
+               /* set the final return code for this request */
+               req->drc = -ETIME;
+       }
+       rc = cio_clear(sch);
+       if (rc)
+               goto err;
+       return;
+
+err:
+       ccwreq_stop(cdev, rc);
+}
+
+/**
+ * ccw_request_notoper - notoper handler for I/O request procedure
+ * @cdev: ccw device
+ *
+ * Handle timeout during I/O request procedure.
+ */
+void ccw_request_notoper(struct ccw_device *cdev)
+{
+       ccwreq_stop(cdev, -ENODEV);
+}
index 8ab5160..c268a2e 100644 (file)
@@ -65,7 +65,7 @@ static void set_chp_logically_online(struct chp_id chpid, int onoff)
        chpid_to_chp(chpid)->state = onoff;
 }
 
-/* On succes return 0 if channel-path is varied offline, 1 if it is varied
+/* On success return 0 if channel-path is varied offline, 1 if it is varied
  * online. Return -ENODEV if channel-path is not registered. */
 int chp_get_status(struct chp_id chpid)
 {
index 2e43558..bf7f80f 100644 (file)
@@ -68,6 +68,11 @@ struct schib {
        __u8 mda[4];             /* model dependent area */
 } __attribute__ ((packed,aligned(4)));
 
+enum sch_todo {
+       SCH_TODO_NOTHING,
+       SCH_TODO_UNREG,
+};
+
 /* subchannel data structure used by I/O subroutines */
 struct subchannel {
        struct subchannel_id schid;
@@ -95,7 +100,8 @@ struct subchannel {
        struct device dev;      /* entry in device tree */
        struct css_driver *driver;
        void *private; /* private per subchannel type data */
-       struct work_struct work;
+       enum sch_todo todo;
+       struct work_struct todo_work;
        struct schib_config config;
 } __attribute__ ((aligned(8)));
 
index 30f5161..2985eb4 100644 (file)
@@ -462,7 +462,7 @@ static struct cmb_area cmb_area = {
  * block of memory, which can not be moved as long as any channel
  * is active. Therefore, a maximum number of subchannels needs to
  * be defined somewhere. This is a module parameter, defaulting to
- * a resonable value of 1024, or 32 kb of memory.
+ * a reasonable value of 1024, or 32 kb of memory.
  * Current kernels don't allow kmalloc with more than 128kb, so the
  * maximum is 4096.
  */
index 91c2570..92ff88a 100644 (file)
@@ -133,6 +133,8 @@ out:
        return rc;
 }
 
+static void css_sch_todo(struct work_struct *work);
+
 static struct subchannel *
 css_alloc_subchannel(struct subchannel_id schid)
 {
@@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid)
                kfree(sch);
                return ERR_PTR(ret);
        }
+       INIT_WORK(&sch->todo_work, css_sch_todo);
        return sch;
 }
 
@@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch)
 }
 EXPORT_SYMBOL_GPL(css_sch_device_unregister);
 
+static void css_sch_todo(struct work_struct *work)
+{
+       struct subchannel *sch;
+       enum sch_todo todo;
+
+       sch = container_of(work, struct subchannel, todo_work);
+       /* Find out todo. */
+       spin_lock_irq(sch->lock);
+       todo = sch->todo;
+       CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid,
+                     sch->schid.sch_no, todo);
+       sch->todo = SCH_TODO_NOTHING;
+       spin_unlock_irq(sch->lock);
+       /* Perform todo. */
+       if (todo == SCH_TODO_UNREG)
+               css_sch_device_unregister(sch);
+       /* Release workqueue ref. */
+       put_device(&sch->dev);
+}
+
+/**
+ * css_sched_sch_todo - schedule a subchannel operation
+ * @sch: subchannel
+ * @todo: todo
+ *
+ * Schedule the operation identified by @todo to be performed on the slow path
+ * workqueue. Do nothing if another operation with higher priority is already
+ * scheduled. Needs to be called with subchannel lock held.
+ */
+void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
+{
+       CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n",
+                     sch->schid.ssid, sch->schid.sch_no, todo);
+       if (sch->todo >= todo)
+               return;
+       /* Get workqueue ref. */
+       if (!get_device(&sch->dev))
+               return;
+       sch->todo = todo;
+       if (!queue_work(slow_path_wq, &sch->todo_work)) {
+               /* Already queued, release workqueue ref. */
+               put_device(&sch->dev);
+       }
+}
+
 static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
 {
        int i;
@@ -376,8 +424,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
                /* Unusable - ignore. */
                return 0;
        }
-       CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
-                        "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
+       CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid,
+                     schid.sch_no);
 
        return css_probe_device(schid);
 }
@@ -394,6 +442,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
                                "Got subchannel machine check but "
                                "no sch_event handler provided.\n");
        }
+       if (ret != 0 && ret != -EAGAIN) {
+               CIO_MSG_EVENT(2, "eval: sch 0.%x.%04x, rc=%d\n",
+                             sch->schid.ssid, sch->schid.sch_no, ret);
+       }
        return ret;
 }
 
@@ -684,6 +736,7 @@ static int __init setup_css(int nr)
        css->pseudo_subchannel->dev.parent = &css->device;
        css->pseudo_subchannel->dev.release = css_subchannel_release;
        dev_set_name(&css->pseudo_subchannel->dev, "defunct");
+       mutex_init(&css->pseudo_subchannel->reg_mutex);
        ret = cio_create_sch_lock(css->pseudo_subchannel);
        if (ret) {
                kfree(css->pseudo_subchannel);
index 68d6b0b..fe84b92 100644 (file)
@@ -11,6 +11,8 @@
 #include <asm/chpid.h>
 #include <asm/schid.h>
 
+#include "cio.h"
+
 /*
  * path grouping stuff
  */
@@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *);
 
 extern struct workqueue_struct *slow_path_wq;
 void css_wait_for_slow_path(void);
+void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo);
 #endif
index 2490b74..9fecfb4 100644 (file)
@@ -7,6 +7,10 @@
  *              Cornelia Huck (cornelia.huck@de.ibm.com)
  *              Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
+
+#define KMSG_COMPONENT "cio"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
@@ -299,53 +303,18 @@ int ccw_device_is_orphan(struct ccw_device *cdev)
 
 static void ccw_device_unregister(struct ccw_device *cdev)
 {
-       if (test_and_clear_bit(1, &cdev->private->registered)) {
+       if (device_is_registered(&cdev->dev)) {
+               /* Undo device_add(). */
                device_del(&cdev->dev);
+       }
+       if (cdev->private->flags.initialized) {
+               cdev->private->flags.initialized = 0;
                /* Release reference from device_initialize(). */
                put_device(&cdev->dev);
        }
 }
 
-static void ccw_device_remove_orphan_cb(struct work_struct *work)
-{
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
-
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
-       ccw_device_unregister(cdev);
-       /* Release cdev reference for workqueue processing. */
-       put_device(&cdev->dev);
-}
-
-static void
-ccw_device_remove_disconnected(struct ccw_device *cdev)
-{
-       unsigned long flags;
-
-       /*
-        * Forced offline in disconnected state means
-        * 'throw away device'.
-        */
-       if (ccw_device_is_orphan(cdev)) {
-               /*
-                * Deregister ccw device.
-                * Unfortunately, we cannot do this directly from the
-                * attribute method.
-                */
-               /* Get cdev reference for workqueue processing. */
-               if (!get_device(&cdev->dev))
-                       return;
-               spin_lock_irqsave(cdev->ccwlock, flags);
-               cdev->private->state = DEV_STATE_NOT_OPER;
-               spin_unlock_irqrestore(cdev->ccwlock, flags);
-               PREPARE_WORK(&cdev->private->kick_work,
-                               ccw_device_remove_orphan_cb);
-               queue_work(slow_path_wq, &cdev->private->kick_work);
-       } else
-               /* Deregister subchannel, which will kill the ccw device. */
-               ccw_device_schedule_sch_unregister(cdev);
-}
+static void io_subchannel_quiesce(struct subchannel *);
 
 /**
  * ccw_device_set_offline() - disable a ccw device for I/O
@@ -360,7 +329,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
  */
 int ccw_device_set_offline(struct ccw_device *cdev)
 {
-       int ret;
+       struct subchannel *sch;
+       int ret, state;
 
        if (!cdev)
                return -ENODEV;
@@ -374,6 +344,7 @@ int ccw_device_set_offline(struct ccw_device *cdev)
        }
        cdev->online = 0;
        spin_lock_irq(cdev->ccwlock);
+       sch = to_subchannel(cdev->dev.parent);
        /* Wait until a final state or DISCONNECTED is reached */
        while (!dev_fsm_final_state(cdev) &&
               cdev->private->state != DEV_STATE_DISCONNECTED) {
@@ -382,20 +353,37 @@ int ccw_device_set_offline(struct ccw_device *cdev)
                           cdev->private->state == DEV_STATE_DISCONNECTED));
                spin_lock_irq(cdev->ccwlock);
        }
-       ret = ccw_device_offline(cdev);
-       if (ret)
-               goto error;
+       do {
+               ret = ccw_device_offline(cdev);
+               if (!ret)
+                       break;
+               CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device "
+                             "0.%x.%04x\n", ret, cdev->private->dev_id.ssid,
+                             cdev->private->dev_id.devno);
+               if (ret != -EBUSY)
+                       goto error;
+               state = cdev->private->state;
+               spin_unlock_irq(cdev->ccwlock);
+               io_subchannel_quiesce(sch);
+               spin_lock_irq(cdev->ccwlock);
+               cdev->private->state = state;
+       } while (ret == -EBUSY);
        spin_unlock_irq(cdev->ccwlock);
        wait_event(cdev->private->wait_q, (dev_fsm_final_state(cdev) ||
                   cdev->private->state == DEV_STATE_DISCONNECTED));
+       /* Inform the user if set offline failed. */
+       if (cdev->private->state == DEV_STATE_BOXED) {
+               pr_warning("%s: The device entered boxed state while "
+                          "being set offline\n", dev_name(&cdev->dev));
+       } else if (cdev->private->state == DEV_STATE_NOT_OPER) {
+               pr_warning("%s: The device stopped operating while "
+                          "being set offline\n", dev_name(&cdev->dev));
+       }
        /* Give up reference from ccw_device_set_online(). */
        put_device(&cdev->dev);
        return 0;
 
 error:
-       CIO_MSG_EVENT(0, "ccw_device_offline returned %d, device 0.%x.%04x\n",
-                     ret, cdev->private->dev_id.ssid,
-                     cdev->private->dev_id.devno);
        cdev->private->state = DEV_STATE_OFFLINE;
        dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
        spin_unlock_irq(cdev->ccwlock);
@@ -448,6 +436,16 @@ int ccw_device_set_online(struct ccw_device *cdev)
        if ((cdev->private->state != DEV_STATE_ONLINE) &&
            (cdev->private->state != DEV_STATE_W4SENSE)) {
                spin_unlock_irq(cdev->ccwlock);
+               /* Inform the user that set online failed. */
+               if (cdev->private->state == DEV_STATE_BOXED) {
+                       pr_warning("%s: Setting the device online failed "
+                                  "because it is boxed\n",
+                                  dev_name(&cdev->dev));
+               } else if (cdev->private->state == DEV_STATE_NOT_OPER) {
+                       pr_warning("%s: Setting the device online failed "
+                                  "because it is not operational\n",
+                                  dev_name(&cdev->dev));
+               }
                /* Give up online reference since onlining failed. */
                put_device(&cdev->dev);
                return -ENODEV;
@@ -494,27 +492,22 @@ error:
 
 static int online_store_handle_offline(struct ccw_device *cdev)
 {
-       if (cdev->private->state == DEV_STATE_DISCONNECTED)
-               ccw_device_remove_disconnected(cdev);
-       else if (cdev->online && cdev->drv && cdev->drv->set_offline)
+       if (cdev->private->state == DEV_STATE_DISCONNECTED) {
+               spin_lock_irq(cdev->ccwlock);
+               ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
+               spin_unlock_irq(cdev->ccwlock);
+       } else if (cdev->online && cdev->drv && cdev->drv->set_offline)
                return ccw_device_set_offline(cdev);
        return 0;
 }
 
 static int online_store_recog_and_online(struct ccw_device *cdev)
 {
-       int ret;
-
        /* Do device recognition, if needed. */
        if (cdev->private->state == DEV_STATE_BOXED) {
-               ret = ccw_device_recognition(cdev);
-               if (ret) {
-                       CIO_MSG_EVENT(0, "Couldn't start recognition "
-                                     "for device 0.%x.%04x (ret=%d)\n",
-                                     cdev->private->dev_id.ssid,
-                                     cdev->private->dev_id.devno, ret);
-                       return ret;
-               }
+               spin_lock_irq(cdev->ccwlock);
+               ccw_device_recognition(cdev);
+               spin_unlock_irq(cdev->ccwlock);
                wait_event(cdev->private->wait_q,
                           cdev->private->flags.recog_done);
                if (cdev->private->state != DEV_STATE_OFFLINE)
@@ -553,11 +546,10 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr,
        int force, ret;
        unsigned long i;
 
-       if ((cdev->private->state != DEV_STATE_OFFLINE &&
-            cdev->private->state != DEV_STATE_ONLINE &&
-            cdev->private->state != DEV_STATE_BOXED &&
-            cdev->private->state != DEV_STATE_DISCONNECTED) ||
-           atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
+       if (!dev_fsm_final_state(cdev) &&
+           cdev->private->state != DEV_STATE_DISCONNECTED)
+               return -EAGAIN;
+       if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
                return -EAGAIN;
 
        if (cdev->drv && !try_module_get(cdev->drv->owner)) {
@@ -665,81 +657,31 @@ static int ccw_device_register(struct ccw_device *cdev)
                           cdev->private->dev_id.devno);
        if (ret)
                return ret;
-       ret = device_add(dev);
-       if (ret)
-               return ret;
-
-       set_bit(1, &cdev->private->registered);
-       return ret;
+       return device_add(dev);
 }
 
-struct match_data {
-       struct ccw_dev_id dev_id;
-       struct ccw_device * sibling;
-};
-
-static int
-match_devno(struct device * dev, void * data)
-{
-       struct match_data * d = data;
-       struct ccw_device * cdev;
-
-       cdev = to_ccwdev(dev);
-       if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
-           !ccw_device_is_orphan(cdev) &&
-           ccw_dev_id_is_equal(&cdev->private->dev_id, &d->dev_id) &&
-           (cdev != d->sibling))
-               return 1;
-       return 0;
-}
-
-static struct ccw_device * get_disc_ccwdev_by_dev_id(struct ccw_dev_id *dev_id,
-                                                    struct ccw_device *sibling)
-{
-       struct device *dev;
-       struct match_data data;
-
-       data.dev_id = *dev_id;
-       data.sibling = sibling;
-       dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
-
-       return dev ? to_ccwdev(dev) : NULL;
-}
-
-static int match_orphan(struct device *dev, void *data)
+static int match_dev_id(struct device *dev, void *data)
 {
-       struct ccw_dev_id *dev_id;
-       struct ccw_device *cdev;
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct ccw_dev_id *dev_id = data;
 
-       dev_id = data;
-       cdev = to_ccwdev(dev);
        return ccw_dev_id_is_equal(&cdev->private->dev_id, dev_id);
 }
 
-static struct ccw_device *
-get_orphaned_ccwdev_by_dev_id(struct channel_subsystem *css,
-                             struct ccw_dev_id *dev_id)
+static struct ccw_device *get_ccwdev_by_dev_id(struct ccw_dev_id *dev_id)
 {
        struct device *dev;
 
-       dev = device_find_child(&css->pseudo_subchannel->dev, dev_id,
-                               match_orphan);
+       dev = bus_find_device(&ccw_bus_type, NULL, dev_id, match_dev_id);
 
        return dev ? to_ccwdev(dev) : NULL;
 }
 
-void ccw_device_do_unbind_bind(struct work_struct *work)
+static void ccw_device_do_unbind_bind(struct ccw_device *cdev)
 {
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
-       struct subchannel *sch;
        int ret;
 
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
-       sch = to_subchannel(cdev->dev.parent);
-
-       if (test_bit(1, &cdev->private->registered)) {
+       if (device_is_registered(&cdev->dev)) {
                device_release_driver(&cdev->dev);
                ret = device_attach(&cdev->dev);
                WARN_ON(ret == -ENODEV);
@@ -773,6 +715,8 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
        return ERR_PTR(-ENOMEM);
 }
 
+static void ccw_device_todo(struct work_struct *work);
+
 static int io_subchannel_initialize_dev(struct subchannel *sch,
                                        struct ccw_device *cdev)
 {
@@ -780,7 +724,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
        atomic_set(&cdev->private->onoff, 0);
        cdev->dev.parent = &sch->dev;
        cdev->dev.release = ccw_device_release;
-       INIT_WORK(&cdev->private->kick_work, NULL);
+       INIT_WORK(&cdev->private->todo_work, ccw_device_todo);
        cdev->dev.groups = ccwdev_attr_groups;
        /* Do first half of device_register. */
        device_initialize(&cdev->dev);
@@ -789,6 +733,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
                put_device(&cdev->dev);
                return -ENODEV;
        }
+       cdev->private->flags.initialized = 1;
        return 0;
 }
 
@@ -806,76 +751,7 @@ static struct ccw_device * io_subchannel_create_ccwdev(struct subchannel *sch)
        return cdev;
 }
 
-static int io_subchannel_recog(struct ccw_device *, struct subchannel *);
-
-static void sch_attach_device(struct subchannel *sch,
-                             struct ccw_device *cdev)
-{
-       css_update_ssd_info(sch);
-       spin_lock_irq(sch->lock);
-       sch_set_cdev(sch, cdev);
-       cdev->private->schid = sch->schid;
-       cdev->ccwlock = sch->lock;
-       ccw_device_trigger_reprobe(cdev);
-       spin_unlock_irq(sch->lock);
-}
-
-static void sch_attach_disconnected_device(struct subchannel *sch,
-                                          struct ccw_device *cdev)
-{
-       struct subchannel *other_sch;
-       int ret;
-
-       /* Get reference for new parent. */
-       if (!get_device(&sch->dev))
-               return;
-       other_sch = to_subchannel(cdev->dev.parent);
-       /* Note: device_move() changes cdev->dev.parent */
-       ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
-                             "(ret=%d)!\n", cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
-               /* Put reference for new parent. */
-               put_device(&sch->dev);
-               return;
-       }
-       sch_set_cdev(other_sch, NULL);
-       /* No need to keep a subchannel without ccw device around. */
-       css_sch_device_unregister(other_sch);
-       sch_attach_device(sch, cdev);
-       /* Put reference for old parent. */
-       put_device(&other_sch->dev);
-}
-
-static void sch_attach_orphaned_device(struct subchannel *sch,
-                                      struct ccw_device *cdev)
-{
-       int ret;
-       struct subchannel *pseudo_sch;
-
-       /* Get reference for new parent. */
-       if (!get_device(&sch->dev))
-               return;
-       pseudo_sch = to_subchannel(cdev->dev.parent);
-       /*
-        * Try to move the ccw device to its new subchannel.
-        * Note: device_move() changes cdev->dev.parent
-        */
-       ret = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
-                             "failed (ret=%d)!\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
-               /* Put reference for new parent. */
-               put_device(&sch->dev);
-               return;
-       }
-       sch_attach_device(sch, cdev);
-       /* Put reference on pseudo subchannel. */
-       put_device(&pseudo_sch->dev);
-}
+static void io_subchannel_recog(struct ccw_device *, struct subchannel *);
 
 static void sch_create_and_recog_new_device(struct subchannel *sch)
 {
@@ -888,100 +764,19 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
                css_sch_device_unregister(sch);
                return;
        }
-       spin_lock_irq(sch->lock);
-       sch_set_cdev(sch, cdev);
-       spin_unlock_irq(sch->lock);
        /* Start recognition for the new ccw device. */
-       if (io_subchannel_recog(cdev, sch)) {
-               spin_lock_irq(sch->lock);
-               sch_set_cdev(sch, NULL);
-               spin_unlock_irq(sch->lock);
-               css_sch_device_unregister(sch);
-               /* Put reference from io_subchannel_create_ccwdev(). */
-               put_device(&sch->dev);
-               /* Give up initial reference. */
-               put_device(&cdev->dev);
-       }
-}
-
-
-void ccw_device_move_to_orphanage(struct work_struct *work)
-{
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
-       struct ccw_device *replacing_cdev;
-       struct subchannel *sch;
-       int ret;
-       struct channel_subsystem *css;
-       struct ccw_dev_id dev_id;
-
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
-       sch = to_subchannel(cdev->dev.parent);
-       css = to_css(sch->dev.parent);
-       dev_id.devno = sch->schib.pmcw.dev;
-       dev_id.ssid = sch->schid.ssid;
-
-       /* Increase refcount for pseudo subchannel. */
-       get_device(&css->pseudo_subchannel->dev);
-       /*
-        * Move the orphaned ccw device to the orphanage so the replacing
-        * ccw device can take its place on the subchannel.
-        * Note: device_move() changes cdev->dev.parent
-        */
-       ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev,
-               DPM_ORDER_NONE);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
-                             "(ret=%d)!\n", cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
-               /* Decrease refcount for pseudo subchannel again. */
-               put_device(&css->pseudo_subchannel->dev);
-               return;
-       }
-       cdev->ccwlock = css->pseudo_subchannel->lock;
-       /*
-        * Search for the replacing ccw device
-        * - among the disconnected devices
-        * - in the orphanage
-        */
-       replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
-       if (replacing_cdev) {
-               sch_attach_disconnected_device(sch, replacing_cdev);
-               /* Release reference from get_disc_ccwdev_by_dev_id() */
-               put_device(&replacing_cdev->dev);
-               /* Release reference of subchannel from old cdev. */
-               put_device(&sch->dev);
-               return;
-       }
-       replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
-       if (replacing_cdev) {
-               sch_attach_orphaned_device(sch, replacing_cdev);
-               /* Release reference from get_orphaned_ccwdev_by_dev_id() */
-               put_device(&replacing_cdev->dev);
-               /* Release reference of subchannel from old cdev. */
-               put_device(&sch->dev);
-               return;
-       }
-       sch_create_and_recog_new_device(sch);
-       /* Release reference of subchannel from old cdev. */
-       put_device(&sch->dev);
+       io_subchannel_recog(cdev, sch);
 }
 
 /*
  * Register recognized device.
  */
-static void
-io_subchannel_register(struct work_struct *work)
+static void io_subchannel_register(struct ccw_device *cdev)
 {
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
        struct subchannel *sch;
        int ret;
        unsigned long flags;
 
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
        /*
         * Check if subchannel is still registered. It may have become
@@ -1033,41 +828,23 @@ out:
        cdev->private->flags.recog_done = 1;
        wake_up(&cdev->private->wait_q);
 out_err:
-       /* Release reference for workqueue processing. */
-       put_device(&cdev->dev);
        if (atomic_dec_and_test(&ccw_device_init_count))
                wake_up(&ccw_device_init_wq);
 }
 
-static void ccw_device_call_sch_unregister(struct work_struct *work)
+static void ccw_device_call_sch_unregister(struct ccw_device *cdev)
 {
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
        struct subchannel *sch;
 
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
        /* Get subchannel reference for local processing. */
        if (!get_device(cdev->dev.parent))
                return;
        sch = to_subchannel(cdev->dev.parent);
        css_sch_device_unregister(sch);
-       /* Release cdev reference for workqueue processing.*/
-       put_device(&cdev->dev);
        /* Release subchannel reference for local processing. */
        put_device(&sch->dev);
 }
 
-void ccw_device_schedule_sch_unregister(struct ccw_device *cdev)
-{
-       /* Get cdev reference for workqueue processing. */
-       if (!get_device(&cdev->dev))
-               return;
-       PREPARE_WORK(&cdev->private->kick_work,
-                    ccw_device_call_sch_unregister);
-       queue_work(slow_path_wq, &cdev->private->kick_work);
-}
-
 /*
  * subchannel recognition done. Called from the state machine.
  */
@@ -1083,7 +860,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                /* Device did not respond in time. */
        case DEV_STATE_NOT_OPER:
                cdev->private->flags.recog_done = 1;
-               ccw_device_schedule_sch_unregister(cdev);
+               /* Remove device found not operational. */
+               ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
                if (atomic_dec_and_test(&ccw_device_init_count))
                        wake_up(&ccw_device_init_wq);
                break;
@@ -1092,22 +870,15 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                 * We can't register the device in interrupt context so
                 * we schedule a work item.
                 */
-               if (!get_device(&cdev->dev))
-                       break;
-               PREPARE_WORK(&cdev->private->kick_work,
-                            io_subchannel_register);
-               queue_work(slow_path_wq, &cdev->private->kick_work);
+               ccw_device_sched_todo(cdev, CDEV_TODO_REGISTER);
                break;
        }
 }
 
-static int
-io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
+static void io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 {
-       int rc;
        struct ccw_device_private *priv;
 
-       sch_set_cdev(sch, cdev);
        cdev->ccwlock = sch->lock;
 
        /* Init private data. */
@@ -1125,62 +896,81 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 
        /* Start async. device sensing. */
        spin_lock_irq(sch->lock);
-       rc = ccw_device_recognition(cdev);
+       sch_set_cdev(sch, cdev);
+       ccw_device_recognition(cdev);
        spin_unlock_irq(sch->lock);
-       if (rc) {
-               if (atomic_dec_and_test(&ccw_device_init_count))
-                       wake_up(&ccw_device_init_wq);
-       }
-       return rc;
 }
 
-static void ccw_device_move_to_sch(struct work_struct *work)
+static int ccw_device_move_to_sch(struct ccw_device *cdev,
+                                 struct subchannel *sch)
 {
-       struct ccw_device_private *priv;
-       int rc;
-       struct subchannel *sch;
-       struct ccw_device *cdev;
-       struct subchannel *former_parent;
+       struct subchannel *old_sch;
+       int rc, old_enabled = 0;
 
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       sch = priv->sch;
-       cdev = priv->cdev;
-       former_parent = to_subchannel(cdev->dev.parent);
-       /* Get reference for new parent. */
+       old_sch = to_subchannel(cdev->dev.parent);
+       /* Obtain child reference for new parent. */
        if (!get_device(&sch->dev))
-               return;
+               return -ENODEV;
+
+       if (!sch_is_pseudo_sch(old_sch)) {
+               spin_lock_irq(old_sch->lock);
+               old_enabled = old_sch->schib.pmcw.ena;
+               rc = 0;
+               if (old_enabled)
+                       rc = cio_disable_subchannel(old_sch);
+               spin_unlock_irq(old_sch->lock);
+               if (rc == -EBUSY) {
+                       /* Release child reference for new parent. */
+                       put_device(&sch->dev);
+                       return rc;
+               }
+       }
+
        mutex_lock(&sch->reg_mutex);
-       /*
-        * Try to move the ccw device to its new subchannel.
-        * Note: device_move() changes cdev->dev.parent
-        */
        rc = device_move(&cdev->dev, &sch->dev, DPM_ORDER_PARENT_BEFORE_DEV);
        mutex_unlock(&sch->reg_mutex);
        if (rc) {
-               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to subchannel "
-                             "0.%x.%04x failed (ret=%d)!\n",
+               CIO_MSG_EVENT(0, "device_move(0.%x.%04x,0.%x.%04x)=%d\n",
                              cdev->private->dev_id.ssid,
                              cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no, rc);
-               css_sch_device_unregister(sch);
-               /* Put reference for new parent again. */
+                             sch->schib.pmcw.dev, rc);
+               if (old_enabled) {
+                       /* Try to reenable the old subchannel. */
+                       spin_lock_irq(old_sch->lock);
+                       cio_enable_subchannel(old_sch, (u32)(addr_t)old_sch);
+                       spin_unlock_irq(old_sch->lock);
+               }
+               /* Release child reference for new parent. */
                put_device(&sch->dev);
-               goto out;
+               return rc;
        }
-       if (!sch_is_pseudo_sch(former_parent)) {
-               spin_lock_irq(former_parent->lock);
-               sch_set_cdev(former_parent, NULL);
-               spin_unlock_irq(former_parent->lock);
-               css_sch_device_unregister(former_parent);
-               /* Reset intparm to zeroes. */
-               former_parent->config.intparm = 0;
-               cio_commit_config(former_parent);
+       /* Clean up old subchannel. */
+       if (!sch_is_pseudo_sch(old_sch)) {
+               spin_lock_irq(old_sch->lock);
+               sch_set_cdev(old_sch, NULL);
+               spin_unlock_irq(old_sch->lock);
+               css_schedule_eval(old_sch->schid);
        }
-       sch_attach_device(sch, cdev);
-out:
-       /* Put reference for old parent. */
-       put_device(&former_parent->dev);
-       put_device(&cdev->dev);
+       /* Release child reference for old parent. */
+       put_device(&old_sch->dev);
+       /* Initialize new subchannel. */
+       spin_lock_irq(sch->lock);
+       cdev->private->schid = sch->schid;
+       cdev->ccwlock = sch->lock;
+       if (!sch_is_pseudo_sch(sch))
+               sch_set_cdev(sch, cdev);
+       spin_unlock_irq(sch->lock);
+       if (!sch_is_pseudo_sch(sch))
+               css_update_ssd_info(sch);
+       return 0;
+}
+
+static int ccw_device_move_to_orph(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct channel_subsystem *css = to_css(sch->dev.parent);
+
+       return ccw_device_move_to_sch(cdev, css->pseudo_subchannel);
 }
 
 static void io_subchannel_irq(struct subchannel *sch)
@@ -1199,9 +989,6 @@ void io_subchannel_init_config(struct subchannel *sch)
 {
        memset(&sch->config, 0, sizeof(sch->config));
        sch->config.csense = 1;
-       /* Use subchannel mp mode when there is more than 1 installed CHPID. */
-       if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0)
-               sch->config.mp = 1;
 }
 
 static void io_subchannel_init_fields(struct subchannel *sch)
@@ -1222,23 +1009,6 @@ static void io_subchannel_init_fields(struct subchannel *sch)
        io_subchannel_init_config(sch);
 }
 
-static void io_subchannel_do_unreg(struct work_struct *work)
-{
-       struct subchannel *sch;
-
-       sch = container_of(work, struct subchannel, work);
-       css_sch_device_unregister(sch);
-       put_device(&sch->dev);
-}
-
-/* Schedule unregister if we have no cdev. */
-static void io_subchannel_schedule_removal(struct subchannel *sch)
-{
-       get_device(&sch->dev);
-       INIT_WORK(&sch->work, io_subchannel_do_unreg);
-       queue_work(slow_path_wq, &sch->work);
-}
-
 /*
  * Note: We always return 0 so that we bind to the device even on error.
  * This is needed so that our remove function is called on unregister.
@@ -1247,8 +1017,6 @@ static int io_subchannel_probe(struct subchannel *sch)
 {
        struct ccw_device *cdev;
        int rc;
-       unsigned long flags;
-       struct ccw_dev_id dev_id;
 
        if (cio_is_console(sch->schid)) {
                rc = sysfs_create_group(&sch->dev.kobj,
@@ -1268,6 +1036,7 @@ static int io_subchannel_probe(struct subchannel *sch)
                cdev = sch_get_cdev(sch);
                cdev->dev.groups = ccwdev_attr_groups;
                device_initialize(&cdev->dev);
+               cdev->private->flags.initialized = 1;
                ccw_device_register(cdev);
                /*
                 * Check if the device is already online. If it is
@@ -1292,44 +1061,14 @@ static int io_subchannel_probe(struct subchannel *sch)
        sch->private = kzalloc(sizeof(struct io_subchannel_private),
                               GFP_KERNEL | GFP_DMA);
        if (!sch->private)
-               goto out_err;
-       /*
-        * First check if a fitting device may be found amongst the
-        * disconnected devices or in the orphanage.
-        */
-       dev_id.devno = sch->schib.pmcw.dev;
-       dev_id.ssid = sch->schid.ssid;
-       cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
-       if (!cdev)
-               cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
-                                                    &dev_id);
-       if (cdev) {
-               /*
-                * Schedule moving the device until when we have a registered
-                * subchannel to move to and succeed the probe. We can
-                * unregister later again, when the probe is through.
-                */
-               cdev->private->sch = sch;
-               PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_move_to_sch);
-               queue_work(slow_path_wq, &cdev->private->kick_work);
-               return 0;
-       }
-       cdev = io_subchannel_create_ccwdev(sch);
-       if (IS_ERR(cdev))
-               goto out_err;
-       rc = io_subchannel_recog(cdev, sch);
-       if (rc) {
-               spin_lock_irqsave(sch->lock, flags);
-               io_subchannel_recog_done(cdev);
-               spin_unlock_irqrestore(sch->lock, flags);
-       }
+               goto out_schedule;
+       css_schedule_eval(sch->schid);
        return 0;
-out_err:
-       kfree(sch->private);
-       sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
+
 out_schedule:
-       io_subchannel_schedule_removal(sch);
+       spin_lock_irq(sch->lock);
+       css_sched_sch_todo(sch, SCH_TODO_UNREG);
+       spin_unlock_irq(sch->lock);
        return 0;
 }
 
@@ -1337,32 +1076,23 @@ static int
 io_subchannel_remove (struct subchannel *sch)
 {
        struct ccw_device *cdev;
-       unsigned long flags;
 
        cdev = sch_get_cdev(sch);
        if (!cdev)
-               return 0;
+               goto out_free;
+       io_subchannel_quiesce(sch);
        /* Set ccw device to not operational and drop reference. */
-       spin_lock_irqsave(cdev->ccwlock, flags);
+       spin_lock_irq(cdev->ccwlock);
        sch_set_cdev(sch, NULL);
        cdev->private->state = DEV_STATE_NOT_OPER;
-       spin_unlock_irqrestore(cdev->ccwlock, flags);
+       spin_unlock_irq(cdev->ccwlock);
        ccw_device_unregister(cdev);
+out_free:
        kfree(sch->private);
        sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
        return 0;
 }
 
-static int io_subchannel_notify(struct subchannel *sch, int event)
-{
-       struct ccw_device *cdev;
-
-       cdev = sch_get_cdev(sch);
-       if (!cdev)
-               return 0;
-       return ccw_device_notify(cdev, event);
-}
-
 static void io_subchannel_verify(struct subchannel *sch)
 {
        struct ccw_device *cdev;
@@ -1372,36 +1102,6 @@ static void io_subchannel_verify(struct subchannel *sch)
                dev_fsm_event(cdev, DEV_EVENT_VERIFY);
 }
 
-static int check_for_io_on_path(struct subchannel *sch, int mask)
-{
-       if (cio_update_schib(sch))
-               return 0;
-       if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
-               return 1;
-       return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch,
-                                 struct ccw_device *cdev)
-{
-       if (cio_clear(sch)) {
-               /* Recheck device in case clear failed. */
-               sch->lpm = 0;
-               if (cdev->online)
-                       dev_fsm_event(cdev, DEV_EVENT_VERIFY);
-               else
-                       css_schedule_eval(sch->schid);
-               return;
-       }
-       cdev->private->state = DEV_STATE_CLEAR_VERIFY;
-       /* Request retry of internal operation. */
-       cdev->private->flags.intretry = 1;
-       /* Call handler. */
-       if (cdev->handler)
-               cdev->handler(cdev, cdev->private->intparm,
-                             ERR_PTR(-EIO));
-}
-
 static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
 {
        struct ccw_device *cdev;
@@ -1409,18 +1109,24 @@ static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
        cdev = sch_get_cdev(sch);
        if (!cdev)
                return;
-       if (check_for_io_on_path(sch, mask)) {
-               if (cdev->private->state == DEV_STATE_ONLINE)
-                       ccw_device_kill_io(cdev);
-               else {
-                       terminate_internal_io(sch, cdev);
-                       /* Re-start path verification. */
-                       dev_fsm_event(cdev, DEV_EVENT_VERIFY);
-               }
-       } else
-               /* trigger path verification. */
-               dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+       if (cio_update_schib(sch))
+               goto err;
+       /* Check for I/O on path. */
+       if (scsw_actl(&sch->schib.scsw) == 0 || sch->schib.pmcw.lpum != mask)
+               goto out;
+       if (cdev->private->state == DEV_STATE_ONLINE) {
+               ccw_device_kill_io(cdev);
+               goto out;
+       }
+       if (cio_clear(sch))
+               goto err;
+out:
+       /* Trigger path verification. */
+       dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+       return;
 
+err:
+       dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
 }
 
 static int io_subchannel_chp_event(struct subchannel *sch,
@@ -1457,46 +1163,41 @@ static int io_subchannel_chp_event(struct subchannel *sch,
        return 0;
 }
 
-static void
-io_subchannel_shutdown(struct subchannel *sch)
+static void io_subchannel_quiesce(struct subchannel *sch)
 {
        struct ccw_device *cdev;
        int ret;
 
+       spin_lock_irq(sch->lock);
        cdev = sch_get_cdev(sch);
-
        if (cio_is_console(sch->schid))
-               return;
+               goto out_unlock;
        if (!sch->schib.pmcw.ena)
-               /* Nothing to do. */
-               return;
+               goto out_unlock;
        ret = cio_disable_subchannel(sch);
        if (ret != -EBUSY)
-               /* Subchannel is disabled, we're done. */
-               return;
-       cdev->private->state = DEV_STATE_QUIESCE;
+               goto out_unlock;
        if (cdev->handler)
-               cdev->handler(cdev, cdev->private->intparm,
-                             ERR_PTR(-EIO));
-       ret = ccw_device_cancel_halt_clear(cdev);
-       if (ret == -EBUSY) {
-               ccw_device_set_timeout(cdev, HZ/10);
-               wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
+               cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO));
+       while (ret == -EBUSY) {
+               cdev->private->state = DEV_STATE_QUIESCE;
+               ret = ccw_device_cancel_halt_clear(cdev);
+               if (ret == -EBUSY) {
+                       ccw_device_set_timeout(cdev, HZ/10);
+                       spin_unlock_irq(sch->lock);
+                       wait_event(cdev->private->wait_q,
+                                  cdev->private->state != DEV_STATE_QUIESCE);
+                       spin_lock_irq(sch->lock);
+               }
+               ret = cio_disable_subchannel(sch);
        }
-       cio_disable_subchannel(sch);
+out_unlock:
+       spin_unlock_irq(sch->lock);
 }
 
-static int io_subchannel_get_status(struct subchannel *sch)
+static void io_subchannel_shutdown(struct subchannel *sch)
 {
-       struct schib schib;
-
-       if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
-               return CIO_GONE;
-       if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
-               return CIO_REVALIDATE;
-       if (!sch->lpm)
-               return CIO_NO_PATH;
-       return CIO_OPER;
+       io_subchannel_quiesce(sch);
 }
 
 static int device_is_disconnected(struct ccw_device *cdev)
@@ -1575,20 +1276,16 @@ static void ccw_device_schedule_recovery(void)
 static int purge_fn(struct device *dev, void *data)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
-       struct ccw_device_private *priv = cdev->private;
-       int unreg;
+       struct ccw_dev_id *id = &cdev->private->dev_id;
 
        spin_lock_irq(cdev->ccwlock);
-       unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) &&
-               (priv->state == DEV_STATE_OFFLINE);
+       if (is_blacklisted(id->ssid, id->devno) &&
+           (cdev->private->state == DEV_STATE_OFFLINE)) {
+               CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid,
+                             id->devno);
+               ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+       }
        spin_unlock_irq(cdev->ccwlock);
-       if (!unreg)
-               goto out;
-       CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid,
-                     priv->dev_id.devno);
-       ccw_device_schedule_sch_unregister(cdev);
-
-out:
        /* Abort loop in case of pending signal. */
        if (signal_pending(current))
                return -EINTR;
@@ -1630,91 +1327,169 @@ void ccw_device_set_notoper(struct ccw_device *cdev)
        cdev->private->state = DEV_STATE_NOT_OPER;
 }
 
-static int io_subchannel_sch_event(struct subchannel *sch, int slow)
+enum io_sch_action {
+       IO_SCH_UNREG,
+       IO_SCH_ORPH_UNREG,
+       IO_SCH_ATTACH,
+       IO_SCH_UNREG_ATTACH,
+       IO_SCH_ORPH_ATTACH,
+       IO_SCH_REPROBE,
+       IO_SCH_VERIFY,
+       IO_SCH_DISC,
+       IO_SCH_NOP,
+};
+
+static enum io_sch_action sch_get_action(struct subchannel *sch)
+{
+       struct ccw_device *cdev;
+
+       cdev = sch_get_cdev(sch);
+       if (cio_update_schib(sch)) {
+               /* Not operational. */
+               if (!cdev)
+                       return IO_SCH_UNREG;
+               if (!ccw_device_notify(cdev, CIO_GONE))
+                       return IO_SCH_UNREG;
+               return IO_SCH_ORPH_UNREG;
+       }
+       /* Operational. */
+       if (!cdev)
+               return IO_SCH_ATTACH;
+       if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
+               if (!ccw_device_notify(cdev, CIO_GONE))
+                       return IO_SCH_UNREG_ATTACH;
+               return IO_SCH_ORPH_ATTACH;
+       }
+       if ((sch->schib.pmcw.pam & sch->opm) == 0) {
+               if (!ccw_device_notify(cdev, CIO_NO_PATH))
+                       return IO_SCH_UNREG;
+               return IO_SCH_DISC;
+       }
+       if (device_is_disconnected(cdev))
+               return IO_SCH_REPROBE;
+       if (cdev->online)
+               return IO_SCH_VERIFY;
+       return IO_SCH_NOP;
+}
+
+/**
+ * io_subchannel_sch_event - process subchannel event
+ * @sch: subchannel
+ * @process: non-zero if function is called in process context
+ *
+ * An unspecified event occurred for this subchannel. Adjust data according
+ * to the current operational state of the subchannel and device. Return
+ * zero when the event has been handled sufficiently or -EAGAIN when this
+ * function should be called again in process context.
+ */
+static int io_subchannel_sch_event(struct subchannel *sch, int process)
 {
-       int event, ret, disc;
        unsigned long flags;
-       enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action;
        struct ccw_device *cdev;
+       struct ccw_dev_id dev_id;
+       enum io_sch_action action;
+       int rc = -EAGAIN;
 
        spin_lock_irqsave(sch->lock, flags);
+       if (!device_is_registered(&sch->dev))
+               goto out_unlock;
+       if (work_pending(&sch->todo_work))
+               goto out_unlock;
        cdev = sch_get_cdev(sch);
-       disc = device_is_disconnected(cdev);
-       if (disc && slow) {
-               /* Disconnected devices are evaluated directly only.*/
-               spin_unlock_irqrestore(sch->lock, flags);
-               return 0;
-       }
-       /* No interrupt after machine check - kill pending timers. */
-       if (cdev)
-               ccw_device_set_timeout(cdev, 0);
-       if (!disc && !slow) {
-               /* Non-disconnected devices are evaluated on the slow path. */
-               spin_unlock_irqrestore(sch->lock, flags);
-               return -EAGAIN;
+       if (cdev && work_pending(&cdev->private->todo_work))
+               goto out_unlock;
+       action = sch_get_action(sch);
+       CIO_MSG_EVENT(2, "event: sch 0.%x.%04x, process=%d, action=%d\n",
+                     sch->schid.ssid, sch->schid.sch_no, process,
+                     action);
+       /* Perform immediate actions while holding the lock. */
+       switch (action) {
+       case IO_SCH_REPROBE:
+               /* Trigger device recognition. */
+               ccw_device_trigger_reprobe(cdev);
+               rc = 0;
+               goto out_unlock;
+       case IO_SCH_VERIFY:
+               /* Trigger path verification. */
+               io_subchannel_verify(sch);
+               rc = 0;
+               goto out_unlock;
+       case IO_SCH_DISC:
+               ccw_device_set_disconnected(cdev);
+               rc = 0;
+               goto out_unlock;
+       case IO_SCH_ORPH_UNREG:
+       case IO_SCH_ORPH_ATTACH:
+               ccw_device_set_disconnected(cdev);
+               break;
+       case IO_SCH_UNREG_ATTACH:
+       case IO_SCH_UNREG:
+               if (cdev)
+                       ccw_device_set_notoper(cdev);
+               break;
+       case IO_SCH_NOP:
+               rc = 0;
+               goto out_unlock;
+       default:
+               break;
        }
-       event = io_subchannel_get_status(sch);
-       CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
-                     sch->schid.ssid, sch->schid.sch_no, event,
-                     disc ? "disconnected" : "normal",
-                     slow ? "slow" : "fast");
-       /* Analyze subchannel status. */
-       action = NONE;
-       switch (event) {
-       case CIO_NO_PATH:
-               if (disc) {
-                       /* Check if paths have become available. */
-                       action = REPROBE;
-                       break;
-               }
-               /* fall through */
-       case CIO_GONE:
-               /* Ask driver what to do with device. */
-               if (io_subchannel_notify(sch, event))
-                       action = DISC;
-               else
-                       action = UNREGISTER;
+       spin_unlock_irqrestore(sch->lock, flags);
+       /* All other actions require process context. */
+       if (!process)
+               goto out;
+       /* Handle attached ccw device. */
+       switch (action) {
+       case IO_SCH_ORPH_UNREG:
+       case IO_SCH_ORPH_ATTACH:
+               /* Move ccw device to orphanage. */
+               rc = ccw_device_move_to_orph(cdev);
+               if (rc)
+                       goto out;
                break;
-       case CIO_REVALIDATE:
-               /* Device will be removed, so no notify necessary. */
-               if (disc)
-                       /* Reprobe because immediate unregister might block. */
-                       action = REPROBE;
-               else
-                       action = UNREGISTER_PROBE;
+       case IO_SCH_UNREG_ATTACH:
+               /* Unregister ccw device. */
+               ccw_device_unregister(cdev);
                break;
-       case CIO_OPER:
-               if (disc)
-                       /* Get device operational again. */
-                       action = REPROBE;
+       default:
                break;
        }
-       /* Perform action. */
-       ret = 0;
+       /* Handle subchannel. */
        switch (action) {
-       case UNREGISTER:
-       case UNREGISTER_PROBE:
-               ccw_device_set_notoper(cdev);
-               /* Unregister device (will use subchannel lock). */
-               spin_unlock_irqrestore(sch->lock, flags);
+       case IO_SCH_ORPH_UNREG:
+       case IO_SCH_UNREG:
                css_sch_device_unregister(sch);
-               spin_lock_irqsave(sch->lock, flags);
                break;
-       case REPROBE:
+       case IO_SCH_ORPH_ATTACH:
+       case IO_SCH_UNREG_ATTACH:
+       case IO_SCH_ATTACH:
+               dev_id.ssid = sch->schid.ssid;
+               dev_id.devno = sch->schib.pmcw.dev;
+               cdev = get_ccwdev_by_dev_id(&dev_id);
+               if (!cdev) {
+                       sch_create_and_recog_new_device(sch);
+                       break;
+               }
+               rc = ccw_device_move_to_sch(cdev, sch);
+               if (rc) {
+                       /* Release reference from get_ccwdev_by_dev_id() */
+                       put_device(&cdev->dev);
+                       goto out;
+               }
+               spin_lock_irqsave(sch->lock, flags);
                ccw_device_trigger_reprobe(cdev);
-               break;
-       case DISC:
-               ccw_device_set_disconnected(cdev);
+               spin_unlock_irqrestore(sch->lock, flags);
+               /* Release reference from get_ccwdev_by_dev_id() */
+               put_device(&cdev->dev);
                break;
        default:
                break;
        }
-       spin_unlock_irqrestore(sch->lock, flags);
-       /* Probe if necessary. */
-       if (action == UNREGISTER_PROBE)
-               ret = css_probe_device(sch->schid);
+       return 0;
 
-       return ret;
+out_unlock:
+       spin_unlock_irqrestore(sch->lock, flags);
+out:
+       return rc;
 }
 
 #ifdef CONFIG_CCW_CONSOLE
@@ -1744,10 +1519,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
        sch->driver = &io_subchannel_driver;
        /* Initialize the ccw_device structure. */
        cdev->dev.parent= &sch->dev;
-       rc = io_subchannel_recog(cdev, sch);
-       if (rc)
-               return rc;
-
+       io_subchannel_recog(cdev, sch);
        /* Now wait for the async. recognition to come to an end. */
        spin_lock_irq(cdev->ccwlock);
        while (!dev_fsm_final_state(cdev))
@@ -1763,7 +1535,7 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
        rc = 0;
 out_unlock:
        spin_unlock_irq(cdev->ccwlock);
-       return 0;
+       return rc;
 }
 
 struct ccw_device *
@@ -1919,7 +1691,7 @@ static int ccw_device_pm_prepare(struct device *dev)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
 
-       if (work_pending(&cdev->private->kick_work))
+       if (work_pending(&cdev->private->todo_work))
                return -EAGAIN;
        /* Fail while device is being set online/offline. */
        if (atomic_read(&cdev->private->onoff))
@@ -2005,7 +1777,6 @@ static int ccw_device_pm_thaw(struct device *dev)
 static void __ccw_device_pm_restore(struct ccw_device *cdev)
 {
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
-       int ret;
 
        if (cio_is_console(sch->schid))
                goto out;
@@ -2015,22 +1786,10 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev)
         */
        spin_lock_irq(sch->lock);
        cdev->private->flags.resuming = 1;
-       ret = ccw_device_recognition(cdev);
+       ccw_device_recognition(cdev);
        spin_unlock_irq(sch->lock);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Couldn't start recognition for device "
-                             "0.%x.%04x (ret=%d)\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
-               spin_lock_irq(sch->lock);
-               cdev->private->state = DEV_STATE_DISCONNECTED;
-               spin_unlock_irq(sch->lock);
-               /* notify driver after the resume cb */
-               goto out;
-       }
        wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev) ||
                   cdev->private->state == DEV_STATE_DISCONNECTED);
-
 out:
        cdev->private->flags.resuming = 0;
 }
@@ -2040,7 +1799,7 @@ static int resume_handle_boxed(struct ccw_device *cdev)
        cdev->private->state = DEV_STATE_BOXED;
        if (ccw_device_notify(cdev, CIO_BOXED))
                return 0;
-       ccw_device_schedule_sch_unregister(cdev);
+       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
        return -ENODEV;
 }
 
@@ -2049,7 +1808,7 @@ static int resume_handle_disc(struct ccw_device *cdev)
        cdev->private->state = DEV_STATE_DISCONNECTED;
        if (ccw_device_notify(cdev, CIO_GONE))
                return 0;
-       ccw_device_schedule_sch_unregister(cdev);
+       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
        return -ENODEV;
 }
 
@@ -2094,9 +1853,7 @@ static int ccw_device_pm_restore(struct device *dev)
        /* check if the device type has changed */
        if (!ccw_device_test_sense_data(cdev)) {
                ccw_device_update_sense_data(cdev);
-               PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_do_unbind_bind);
-               queue_work(ccw_device_work, &cdev->private->kick_work);
+               ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
                ret = -ENODEV;
                goto out_unlock;
        }
@@ -2140,7 +1897,7 @@ out_disc_unlock:
        goto out_restore;
 
 out_unreg_unlock:
-       ccw_device_schedule_sch_unregister(cdev);
+       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL);
        ret = -ENODEV;
 out_unlock:
        spin_unlock_irq(sch->lock);
@@ -2205,6 +1962,77 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev)
        return sch->schid;
 }
 
+static void ccw_device_todo(struct work_struct *work)
+{
+       struct ccw_device_private *priv;
+       struct ccw_device *cdev;
+       struct subchannel *sch;
+       enum cdev_todo todo;
+
+       priv = container_of(work, struct ccw_device_private, todo_work);
+       cdev = priv->cdev;
+       sch = to_subchannel(cdev->dev.parent);
+       /* Find out todo. */
+       spin_lock_irq(cdev->ccwlock);
+       todo = priv->todo;
+       priv->todo = CDEV_TODO_NOTHING;
+       CIO_MSG_EVENT(4, "cdev_todo: cdev=0.%x.%04x todo=%d\n",
+                     priv->dev_id.ssid, priv->dev_id.devno, todo);
+       spin_unlock_irq(cdev->ccwlock);
+       /* Perform todo. */
+       switch (todo) {
+       case CDEV_TODO_ENABLE_CMF:
+               cmf_reenable(cdev);
+               break;
+       case CDEV_TODO_REBIND:
+               ccw_device_do_unbind_bind(cdev);
+               break;
+       case CDEV_TODO_REGISTER:
+               io_subchannel_register(cdev);
+               break;
+       case CDEV_TODO_UNREG_EVAL:
+               if (!sch_is_pseudo_sch(sch))
+                       css_schedule_eval(sch->schid);
+               /* fall-through */
+       case CDEV_TODO_UNREG:
+               if (sch_is_pseudo_sch(sch))
+                       ccw_device_unregister(cdev);
+               else
+                       ccw_device_call_sch_unregister(cdev);
+               break;
+       default:
+               break;
+       }
+       /* Release workqueue ref. */
+       put_device(&cdev->dev);
+}
+
+/**
+ * ccw_device_sched_todo - schedule ccw device operation
+ * @cdev: ccw device
+ * @todo: todo
+ *
+ * Schedule the operation identified by @todo to be performed on the slow path
+ * workqueue. Do nothing if another operation with higher priority is already
+ * scheduled. Needs to be called with ccwdev lock held.
+ */
+void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo)
+{
+       CIO_MSG_EVENT(4, "cdev_todo: sched cdev=0.%x.%04x todo=%d\n",
+                     cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
+                     todo);
+       if (cdev->private->todo >= todo)
+               return;
+       cdev->private->todo = todo;
+       /* Get workqueue ref. */
+       if (!get_device(&cdev->dev))
+               return;
+       if (!queue_work(slow_path_wq, &cdev->private->todo_work)) {
+               /* Already queued, release workqueue ref. */
+               put_device(&cdev->dev);
+       }
+}
+
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(ccw_device_set_online);
 EXPORT_SYMBOL(ccw_device_set_offline);
index 246c648..bcfe13e 100644 (file)
@@ -21,7 +21,6 @@ enum dev_state {
        DEV_STATE_DISBAND_PGID,
        DEV_STATE_BOXED,
        /* states to wait for i/o completion before doing something */
-       DEV_STATE_CLEAR_VERIFY,
        DEV_STATE_TIMEOUT_KILL,
        DEV_STATE_QUIESCE,
        /* special states for devices gone not operational */
@@ -29,6 +28,7 @@ enum dev_state {
        DEV_STATE_DISCONNECTED_SENSE_ID,
        DEV_STATE_CMFCHANGE,
        DEV_STATE_CMFUPDATE,
+       DEV_STATE_STEAL_LOCK,
        /* last element! */
        NR_DEV_STATES
 };
@@ -81,17 +81,16 @@ void io_subchannel_init_config(struct subchannel *sch);
 
 int ccw_device_cancel_halt_clear(struct ccw_device *);
 
-void ccw_device_do_unbind_bind(struct work_struct *);
-void ccw_device_move_to_orphanage(struct work_struct *);
 int ccw_device_is_orphan(struct ccw_device *);
 
-int ccw_device_recognition(struct ccw_device *);
+void ccw_device_recognition(struct ccw_device *);
 int ccw_device_online(struct ccw_device *);
 int ccw_device_offline(struct ccw_device *);
 void ccw_device_update_sense_data(struct ccw_device *);
 int ccw_device_test_sense_data(struct ccw_device *);
 void ccw_device_schedule_sch_unregister(struct ccw_device *);
 int ccw_purge_blacklisted(void);
+void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo);
 
 /* Function prototypes for device status and basic sense stuff. */
 void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
@@ -99,24 +98,28 @@ void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
 int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *);
 int ccw_device_do_sense(struct ccw_device *, struct irb *);
 
+/* Function prototype for internal request handling. */
+int lpm_adjust(int lpm, int mask);
+void ccw_request_start(struct ccw_device *);
+int ccw_request_cancel(struct ccw_device *cdev);
+void ccw_request_handler(struct ccw_device *cdev);
+void ccw_request_timeout(struct ccw_device *cdev);
+void ccw_request_notoper(struct ccw_device *cdev);
+
 /* Function prototypes for sense id stuff. */
 void ccw_device_sense_id_start(struct ccw_device *);
-void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event);
 void ccw_device_sense_id_done(struct ccw_device *, int);
 
 /* Function prototypes for path grouping stuff. */
-void ccw_device_sense_pgid_start(struct ccw_device *);
-void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event);
-void ccw_device_sense_pgid_done(struct ccw_device *, int);
-
 void ccw_device_verify_start(struct ccw_device *);
-void ccw_device_verify_irq(struct ccw_device *, enum dev_event);
 void ccw_device_verify_done(struct ccw_device *, int);
 
 void ccw_device_disband_start(struct ccw_device *);
-void ccw_device_disband_irq(struct ccw_device *, enum dev_event);
 void ccw_device_disband_done(struct ccw_device *, int);
 
+void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
+void ccw_device_stlck_done(struct ccw_device *, void *, int);
+
 int ccw_device_call_handler(struct ccw_device *);
 
 int ccw_device_stlck(struct ccw_device *);
index b9613d7..ae76065 100644 (file)
@@ -229,8 +229,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
 
        sch = to_subchannel(cdev->dev.parent);
 
-       ccw_device_set_timeout(cdev, 0);
-       cio_disable_subchannel(sch);
+       if (cio_disable_subchannel(sch))
+               state = DEV_STATE_NOT_OPER;
        /*
         * Now that we tried recognition, we have performed device selection
         * through ssch() and the path information is up to date.
@@ -263,22 +263,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
        }
        switch (state) {
        case DEV_STATE_NOT_OPER:
-               CIO_MSG_EVENT(2, "SenseID : unknown device %04x on "
-                             "subchannel 0.%x.%04x\n",
-                             cdev->private->dev_id.devno,
-                             sch->schid.ssid, sch->schid.sch_no);
                break;
        case DEV_STATE_OFFLINE:
                if (!cdev->online) {
                        ccw_device_update_sense_data(cdev);
-                       /* Issue device info message. */
-                       CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
-                                     "CU  Type/Mod = %04X/%02X, Dev Type/Mod "
-                                     "= %04X/%02X\n",
-                                     cdev->private->dev_id.ssid,
-                                     cdev->private->dev_id.devno,
-                                     cdev->id.cu_type, cdev->id.cu_model,
-                                     cdev->id.dev_type, cdev->id.dev_model);
                        break;
                }
                cdev->private->state = DEV_STATE_OFFLINE;
@@ -289,16 +277,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                        wake_up(&cdev->private->wait_q);
                } else {
                        ccw_device_update_sense_data(cdev);
-                       PREPARE_WORK(&cdev->private->kick_work,
-                                    ccw_device_do_unbind_bind);
-                       queue_work(ccw_device_work, &cdev->private->kick_work);
+                       ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
                }
                return;
        case DEV_STATE_BOXED:
-               CIO_MSG_EVENT(0, "SenseID : boxed device %04x on "
-                             " subchannel 0.%x.%04x\n",
-                             cdev->private->dev_id.devno,
-                             sch->schid.ssid, sch->schid.sch_no);
                if (cdev->id.cu_type != 0) { /* device was recognized before */
                        cdev->private->flags.recog_done = 1;
                        cdev->private->state = DEV_STATE_BOXED;
@@ -343,28 +325,16 @@ int ccw_device_notify(struct ccw_device *cdev, int event)
        return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
 }
 
-static void cmf_reenable_delayed(struct work_struct *work)
-{
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
-
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
-       cmf_reenable(cdev);
-}
-
 static void ccw_device_oper_notify(struct ccw_device *cdev)
 {
        if (ccw_device_notify(cdev, CIO_OPER)) {
                /* Reenable channel measurements, if needed. */
-               PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed);
-               queue_work(ccw_device_work, &cdev->private->kick_work);
+               ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
                return;
        }
        /* Driver doesn't want device back. */
        ccw_device_set_notoper(cdev);
-       PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unbind_bind);
-       queue_work(ccw_device_work, &cdev->private->kick_work);
+       ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
 }
 
 /*
@@ -392,14 +362,14 @@ ccw_device_done(struct ccw_device *cdev, int state)
                CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
                              cdev->private->dev_id.devno, sch->schid.sch_no);
                if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED))
-                       ccw_device_schedule_sch_unregister(cdev);
+                       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
                cdev->private->flags.donotify = 0;
                break;
        case DEV_STATE_NOT_OPER:
                CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n",
                              cdev->private->dev_id.devno, sch->schid.sch_no);
                if (!ccw_device_notify(cdev, CIO_GONE))
-                       ccw_device_schedule_sch_unregister(cdev);
+                       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
                else
                        ccw_device_set_disconnected(cdev);
                cdev->private->flags.donotify = 0;
@@ -409,7 +379,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
                              "%04x\n", cdev->private->dev_id.devno,
                              sch->schid.sch_no);
                if (!ccw_device_notify(cdev, CIO_NO_PATH))
-                       ccw_device_schedule_sch_unregister(cdev);
+                       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
                else
                        ccw_device_set_disconnected(cdev);
                cdev->private->flags.donotify = 0;
@@ -425,107 +395,12 @@ ccw_device_done(struct ccw_device *cdev, int state)
        wake_up(&cdev->private->wait_q);
 }
 
-static int cmp_pgid(struct pgid *p1, struct pgid *p2)
-{
-       char *c1;
-       char *c2;
-
-       c1 = (char *)p1;
-       c2 = (char *)p2;
-
-       return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
-}
-
-static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
-{
-       int i;
-       int last;
-
-       last = 0;
-       for (i = 0; i < 8; i++) {
-               if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
-                       /* No PGID yet */
-                       continue;
-               if (cdev->private->pgid[last].inf.ps.state1 ==
-                   SNID_STATE1_RESET) {
-                       /* First non-zero PGID */
-                       last = i;
-                       continue;
-               }
-               if (cmp_pgid(&cdev->private->pgid[i],
-                            &cdev->private->pgid[last]) == 0)
-                       /* Non-conflicting PGIDs */
-                       continue;
-
-               /* PGID mismatch, can't pathgroup. */
-               CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
-                             "0.%x.%04x, can't pathgroup\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno);
-               cdev->private->options.pgroup = 0;
-               return;
-       }
-       if (cdev->private->pgid[last].inf.ps.state1 ==
-           SNID_STATE1_RESET)
-               /* No previous pgid found */
-               memcpy(&cdev->private->pgid[0],
-                      &channel_subsystems[0]->global_pgid,
-                      sizeof(struct pgid));
-       else
-               /* Use existing pgid */
-               memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
-                      sizeof(struct pgid));
-}
-
-/*
- * Function called from device_pgid.c after sense path ground has completed.
- */
-void
-ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
-{
-       struct subchannel *sch;
-
-       sch = to_subchannel(cdev->dev.parent);
-       switch (err) {
-       case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
-               cdev->private->options.pgroup = 0;
-               break;
-       case 0: /* success */
-       case -EACCES: /* partial success, some paths not operational */
-               /* Check if all pgids are equal or 0. */
-               __ccw_device_get_common_pgid(cdev);
-               break;
-       case -ETIME:            /* Sense path group id stopped by timeout. */
-       case -EUSERS:           /* device is reserved for someone else. */
-               ccw_device_done(cdev, DEV_STATE_BOXED);
-               return;
-       default:
-               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               return;
-       }
-       /* Start Path Group verification. */
-       cdev->private->state = DEV_STATE_VERIFY;
-       cdev->private->flags.doverify = 0;
-       ccw_device_verify_start(cdev);
-}
-
 /*
  * Start device recognition.
  */
-int
-ccw_device_recognition(struct ccw_device *cdev)
+void ccw_device_recognition(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-       ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
-       if (ret != 0)
-               /* Couldn't enable the subchannel for i/o. Sick device. */
-               return ret;
-
-       /* After 60s the device recognition is considered to have failed. */
-       ccw_device_set_timeout(cdev, 60*HZ);
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
 
        /*
         * We used to start here with a sense pgid to find out whether a device
@@ -537,32 +412,33 @@ ccw_device_recognition(struct ccw_device *cdev)
         */
        cdev->private->flags.recog_done = 0;
        cdev->private->state = DEV_STATE_SENSE_ID;
+       if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) {
+               ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+               return;
+       }
        ccw_device_sense_id_start(cdev);
-       return 0;
 }
 
 /*
- * Handle timeout in device recognition.
+ * Handle events for states that use the ccw request infrastructure.
  */
-static void
-ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
 {
-       int ret;
-
-       ret = ccw_device_cancel_halt_clear(cdev);
-       switch (ret) {
-       case 0:
-               ccw_device_recog_done(cdev, DEV_STATE_BOXED);
+       switch (e) {
+       case DEV_EVENT_NOTOPER:
+               ccw_request_notoper(cdev);
                break;
-       case -ENODEV:
-               ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+       case DEV_EVENT_INTERRUPT:
+               ccw_request_handler(cdev);
+               break;
+       case DEV_EVENT_TIMEOUT:
+               ccw_request_timeout(cdev);
                break;
        default:
-               ccw_device_set_timeout(cdev, 3*HZ);
+               break;
        }
 }
 
-
 void
 ccw_device_verify_done(struct ccw_device *cdev, int err)
 {
@@ -571,21 +447,18 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
        sch = to_subchannel(cdev->dev.parent);
        /* Update schib - pom may have changed. */
        if (cio_update_schib(sch)) {
-               cdev->private->flags.donotify = 0;
-               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               return;
+               err = -ENODEV;
+               goto callback;
        }
        /* Update lpm with verified path mask. */
        sch->lpm = sch->vpm;
        /* Repeat path verification? */
        if (cdev->private->flags.doverify) {
-               cdev->private->flags.doverify = 0;
                ccw_device_verify_start(cdev);
                return;
        }
+callback:
        switch (err) {
-       case -EOPNOTSUPP: /* path grouping not supported, just set online. */
-               cdev->private->options.pgroup = 0;
        case 0:
                ccw_device_done(cdev, DEV_STATE_ONLINE);
                /* Deliver fake irb to device driver, if needed. */
@@ -604,18 +477,20 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
                }
                break;
        case -ETIME:
+       case -EUSERS:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
                ccw_device_done(cdev, DEV_STATE_BOXED);
                break;
+       case -EACCES:
+               /* Reset oper notify indication after verify error. */
+               cdev->private->flags.donotify = 0;
+               ccw_device_done(cdev, DEV_STATE_DISCONNECTED);
+               break;
        default:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
-               if (cdev->online) {
-                       ccw_device_set_timeout(cdev, 0);
-                       dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
-               } else
-                       ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
 }
@@ -640,17 +515,9 @@ ccw_device_online(struct ccw_device *cdev)
                        dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
                return ret;
        }
-       /* Do we want to do path grouping? */
-       if (!cdev->private->options.pgroup) {
-               /* Start initial path verification. */
-               cdev->private->state = DEV_STATE_VERIFY;
-               cdev->private->flags.doverify = 0;
-               ccw_device_verify_start(cdev);
-               return 0;
-       }
-       /* Do a SensePGID first. */
-       cdev->private->state = DEV_STATE_SENSE_PGID;
-       ccw_device_sense_pgid_start(cdev);
+       /* Start initial path verification. */
+       cdev->private->state = DEV_STATE_VERIFY;
+       ccw_device_verify_start(cdev);
        return 0;
 }
 
@@ -666,7 +533,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err)
                break;
        default:
                cdev->private->flags.donotify = 0;
-               dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
                ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
@@ -703,7 +569,7 @@ ccw_device_offline(struct ccw_device *cdev)
        if (cdev->private->state != DEV_STATE_ONLINE)
                return -EINVAL;
        /* Are we doing path grouping? */
-       if (!cdev->private->options.pgroup) {
+       if (!cdev->private->flags.pgroup) {
                /* No, set state offline immediately. */
                ccw_device_done(cdev, DEV_STATE_OFFLINE);
                return 0;
@@ -714,36 +580,6 @@ ccw_device_offline(struct ccw_device *cdev)
        return 0;
 }
 
-/*
- * Handle timeout in device online/offline process.
- */
-static void
-ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       int ret;
-
-       ret = ccw_device_cancel_halt_clear(cdev);
-       switch (ret) {
-       case 0:
-               ccw_device_done(cdev, DEV_STATE_BOXED);
-               break;
-       case -ENODEV:
-               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               break;
-       default:
-               ccw_device_set_timeout(cdev, 3*HZ);
-       }
-}
-
-/*
- * Handle not oper event in device recognition.
- */
-static void
-ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
-}
-
 /*
  * Handle not operational event in non-special state.
  */
@@ -751,7 +587,7 @@ static void ccw_device_generic_notoper(struct ccw_device *cdev,
                                       enum dev_event dev_event)
 {
        if (!ccw_device_notify(cdev, CIO_GONE))
-               ccw_device_schedule_sch_unregister(cdev);
+               ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
        else
                ccw_device_set_disconnected(cdev);
 }
@@ -802,10 +638,26 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
        }
        /* Device is idle, we can do the path verification. */
        cdev->private->state = DEV_STATE_VERIFY;
-       cdev->private->flags.doverify = 0;
        ccw_device_verify_start(cdev);
 }
 
+/*
+ * Handle path verification event in boxed state.
+ */
+static void ccw_device_boxed_verify(struct ccw_device *cdev,
+                                   enum dev_event dev_event)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+       if (cdev->online) {
+               if (cio_enable_subchannel(sch, (u32) (addr_t) sch))
+                       ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+               else
+                       ccw_device_online_verify(cdev, dev_event);
+       } else
+               css_schedule_eval(sch->schid);
+}
+
 /*
  * Got an interrupt for a normal io (state online).
  */
@@ -904,12 +756,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
         */
        if (scsw_fctl(&irb->scsw) &
            (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
-               /* Retry Basic Sense if requested. */
-               if (cdev->private->flags.intretry) {
-                       cdev->private->flags.intretry = 0;
-                       ccw_device_do_sense(cdev, irb);
-                       return;
-               }
                cdev->private->flags.dosense = 0;
                memset(&cdev->private->irb, 0, sizeof(struct irb));
                ccw_device_accumulate_irb(cdev, irb);
@@ -932,21 +778,6 @@ call_handler:
                ccw_device_online_verify(cdev, 0);
 }
 
-static void
-ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       struct irb *irb;
-
-       irb = (struct irb *) __LC_IRB;
-       /* Accumulate status. We don't do basic sense. */
-       ccw_device_accumulate_irb(cdev, irb);
-       /* Remember to clear irb to avoid residuals. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-       /* Try to start delayed device verification. */
-       ccw_device_online_verify(cdev, 0);
-       /* Note: Don't call handler for cio initiated clear! */
-}
-
 static void
 ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
 {
@@ -1003,32 +834,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
        cdev->private->flags.doverify = 1;
 }
 
-static void
-ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       struct irb *irb;
-
-       switch (dev_event) {
-       case DEV_EVENT_INTERRUPT:
-               irb = (struct irb *) __LC_IRB;
-               /* Check for unsolicited interrupt. */
-               if ((scsw_stctl(&irb->scsw) ==
-                    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
-                   (!scsw_cc(&irb->scsw)))
-                       /* FIXME: we should restart stlck here, but this
-                        * is extremely unlikely ... */
-                       goto out_wakeup;
-
-               ccw_device_accumulate_irb(cdev, irb);
-               /* We don't care about basic sense etc. */
-               break;
-       default: /* timeout */
-               break;
-       }
-out_wakeup:
-       wake_up(&cdev->private->wait_q);
-}
-
 static void
 ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
 {
@@ -1038,10 +843,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
        if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0)
                /* Couldn't enable the subchannel for i/o. Sick device. */
                return;
-
-       /* After 60s the device recognition is considered to have failed. */
-       ccw_device_set_timeout(cdev, 60*HZ);
-
        cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID;
        ccw_device_sense_id_start(cdev);
 }
@@ -1072,22 +873,20 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
 
        /* We should also udate ssd info, but this has to wait. */
        /* Check if this is another device which appeared on the same sch. */
-       if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
-               PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_move_to_orphanage);
-               queue_work(slow_path_wq, &cdev->private->kick_work);
-       } else
+       if (sch->schib.pmcw.dev != cdev->private->dev_id.devno)
+               css_schedule_eval(sch->schid);
+       else
                ccw_device_start_id(cdev, 0);
 }
 
-static void
-ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_disabled_irq(struct ccw_device *cdev,
+                                   enum dev_event dev_event)
 {
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
        /*
-        * An interrupt in state offline means a previous disable was not
+        * An interrupt in a disabled state means a previous disable was not
         * successful - should not happen, but we try to disable again.
         */
        cio_disable_subchannel(sch);
@@ -1113,10 +912,7 @@ static void
 ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
 {
        ccw_device_set_timeout(cdev, 0);
-       if (dev_event == DEV_EVENT_NOTOPER)
-               cdev->private->state = DEV_STATE_NOT_OPER;
-       else
-               cdev->private->state = DEV_STATE_OFFLINE;
+       cdev->private->state = DEV_STATE_NOT_OPER;
        wake_up(&cdev->private->wait_q);
 }
 
@@ -1126,17 +922,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
        int ret;
 
        ret = ccw_device_cancel_halt_clear(cdev);
-       switch (ret) {
-       case 0:
-               cdev->private->state = DEV_STATE_OFFLINE;
-               wake_up(&cdev->private->wait_q);
-               break;
-       case -ENODEV:
+       if (ret == -EBUSY) {
+               ccw_device_set_timeout(cdev, HZ/10);
+       } else {
                cdev->private->state = DEV_STATE_NOT_OPER;
                wake_up(&cdev->private->wait_q);
-               break;
-       default:
-               ccw_device_set_timeout(cdev, HZ/10);
        }
 }
 
@@ -1149,51 +939,38 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
 {
 }
 
-/*
- * Bug operation action. 
- */
-static void
-ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device "
-                     "0.%x.%04x\n", cdev->private->state, dev_event,
-                     cdev->private->dev_id.ssid,
-                     cdev->private->dev_id.devno);
-       BUG();
-}
-
 /*
  * device statemachine
  */
 fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
        [DEV_STATE_NOT_OPER] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_nop,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_bug,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_disabled_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_SENSE_PGID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_sense_pgid_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_SENSE_ID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_recog_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_sense_id_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_recog_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_OFFLINE] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_offline_irq,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_disabled_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
                [DEV_EVENT_VERIFY]      = ccw_device_offline_verify,
        },
        [DEV_STATE_VERIFY] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_verify_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_delay_verify,
        },
        [DEV_STATE_ONLINE] = {
@@ -1209,24 +986,18 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_VERIFY]      = ccw_device_online_verify,
        },
        [DEV_STATE_DISBAND_PGID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_disband_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_BOXED] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_stlck_done,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_stlck_done,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
-       },
-       /* states to wait for i/o completion before doing something */
-       [DEV_STATE_CLEAR_VERIFY] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_clear_verify,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_nop,
                [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_boxed_verify,
        },
+       /* states to wait for i/o completion before doing something */
        [DEV_STATE_TIMEOUT_KILL] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
                [DEV_EVENT_INTERRUPT]   = ccw_device_killing_irq,
@@ -1243,13 +1014,13 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
        [DEV_STATE_DISCONNECTED] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_nop,
                [DEV_EVENT_INTERRUPT]   = ccw_device_start_id,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_bug,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
                [DEV_EVENT_VERIFY]      = ccw_device_start_id,
        },
        [DEV_STATE_DISCONNECTED_SENSE_ID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_recog_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_sense_id_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_recog_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_CMFCHANGE] = {
@@ -1264,6 +1035,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_TIMEOUT]     = ccw_device_update_cmfblock,
                [DEV_EVENT_VERIFY]      = ccw_device_update_cmfblock,
        },
+       [DEV_STATE_STEAL_LOCK] = {
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
+               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+       },
 };
 
 EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
index 1bdaa61..78a0b43 100644 (file)
@@ -1,40 +1,39 @@
 /*
- * drivers/s390/cio/device_id.c
+ *  CCW device SENSE ID I/O handling.
  *
- *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- *                      IBM Corporation
- *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
- *              Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- * Sense ID functions.
+ *    Copyright IBM Corp. 2002,2009
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *              Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *              Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
-
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
 #include <asm/ccwdev.h>
-#include <asm/delay.h>
+#include <asm/setup.h>
 #include <asm/cio.h>
-#include <asm/lowcore.h>
 #include <asm/diag.h>
 
 #include "cio.h"
 #include "cio_debug.h"
-#include "css.h"
 #include "device.h"
-#include "ioasm.h"
 #include "io_sch.h"
 
+#define SENSE_ID_RETRIES       256
+#define SENSE_ID_TIMEOUT       (10 * HZ)
+#define SENSE_ID_MIN_LEN       4
+#define SENSE_ID_BASIC_LEN     7
+
 /**
- * vm_vdev_to_cu_type - Convert vm virtual device into control unit type
- *                     for certain devices.
- * @class: virtual device class
- * @type: virtual device type
+ * diag210_to_senseid - convert diag 0x210 data to sense id information
+ * @senseid: sense id
+ * @diag: diag 0x210 data
  *
- * Returns control unit type if a match was made or %0xffff otherwise.
+ * Return 0 on success, non-zero otherwise.
  */
-static int vm_vdev_to_cu_type(int class, int type)
+static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
 {
        static struct {
                int class, type, cu_type;
@@ -71,253 +70,153 @@ static int vm_vdev_to_cu_type(int class, int type)
        };
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(vm_devices); i++)
-               if (class == vm_devices[i].class && type == vm_devices[i].type)
-                       return vm_devices[i].cu_type;
+       /* Special case for osa devices. */
+       if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) {
+               senseid->cu_type = 0x3088;
+               senseid->cu_model = 0x60;
+               senseid->reserved = 0xff;
+               return 0;
+       }
+       for (i = 0; i < ARRAY_SIZE(vm_devices); i++) {
+               if (diag->vrdcvcla == vm_devices[i].class &&
+                   diag->vrdcvtyp == vm_devices[i].type) {
+                       senseid->cu_type = vm_devices[i].cu_type;
+                       senseid->reserved = 0xff;
+                       return 0;
+               }
+       }
 
-       return 0xffff;
+       return -ENODEV;
 }
 
 /**
- * diag_get_dev_info - retrieve device information via DIAG X'210'
- * @devno: device number
- * @ps: pointer to sense ID data area
+ * diag_get_dev_info - retrieve device information via diag 0x210
+ * @cdev: ccw device
  *
  * Returns zero on success, non-zero otherwise.
  */
-static int diag_get_dev_info(u16 devno, struct senseid *ps)
+static int diag210_get_dev_info(struct ccw_device *cdev)
 {
+       struct ccw_dev_id *dev_id = &cdev->private->dev_id;
+       struct senseid *senseid = &cdev->private->senseid;
        struct diag210 diag_data;
-       int ccode;
-
-       CIO_TRACE_EVENT (4, "VMvdinf");
-
-       diag_data = (struct diag210) {
-               .vrdcdvno = devno,
-               .vrdclen = sizeof (diag_data),
-       };
-
-       ccode = diag210 (&diag_data);
-       if ((ccode == 0) || (ccode == 2)) {
-               ps->reserved = 0xff;
-
-               /* Special case for osa devices. */
-               if (diag_data.vrdcvcla == 0x02 && diag_data.vrdcvtyp == 0x20) {
-                       ps->cu_type = 0x3088;
-                       ps->cu_model = 0x60;
-                       return 0;
-               }
-               ps->cu_type = vm_vdev_to_cu_type(diag_data.vrdcvcla,
-                                               diag_data.vrdcvtyp);
-               if (ps->cu_type != 0xffff)
-                       return 0;
-       }
-
-       CIO_MSG_EVENT(0, "DIAG X'210' for device %04X returned (cc = %d):"
-                     "vdev class : %02X, vdev type : %04X \n ...  "
-                     "rdev class : %02X, rdev type : %04X, "
-                     "rdev model: %02X\n",
-                     devno, ccode,
-                     diag_data.vrdcvcla, diag_data.vrdcvtyp,
-                     diag_data.vrdcrccl, diag_data.vrdccrty,
-                     diag_data.vrdccrmd);
-
+       int rc;
+
+       if (dev_id->ssid != 0)
+               return -ENODEV;
+       memset(&diag_data, 0, sizeof(diag_data));
+       diag_data.vrdcdvno      = dev_id->devno;
+       diag_data.vrdclen       = sizeof(diag_data);
+       rc = diag210(&diag_data);
+       CIO_TRACE_EVENT(4, "diag210");
+       CIO_HEX_EVENT(4, &rc, sizeof(rc));
+       CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data));
+       if (rc != 0 && rc != 2)
+               goto err_failed;
+       if (diag210_to_senseid(senseid, &diag_data))
+               goto err_unknown;
+       return 0;
+
+err_unknown:
+       CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
+                     dev_id->ssid, dev_id->devno);
+       return -ENODEV;
+err_failed:
+       CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
+                     dev_id->ssid, dev_id->devno, rc);
        return -ENODEV;
 }
 
 /*
- * Start Sense ID helper function.
- * Try to obtain the 'control unit'/'device type' information
- *  associated with the subchannel.
+ * Initialize SENSE ID data.
  */
-static int
-__ccw_device_sense_id_start(struct ccw_device *cdev)
-{
-       struct subchannel *sch;
-       struct ccw1 *ccw;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-       /* Setup sense channel program. */
-       ccw = cdev->private->iccws;
-       ccw->cmd_code = CCW_CMD_SENSE_ID;
-       ccw->cda = (__u32) __pa (&cdev->private->senseid);
-       ccw->count = sizeof (struct senseid);
-       ccw->flags = CCW_FLAG_SLI;
-
-       /* Reset device status. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-
-       /* Try on every path. */
-       ret = -ENODEV;
-       while (cdev->private->imask != 0) {
-               cdev->private->senseid.cu_type = 0xFFFF;
-               if ((sch->opm & cdev->private->imask) != 0 &&
-                   cdev->private->iretry > 0) {
-                       cdev->private->iretry--;
-                       /* Reset internal retry indication. */
-                       cdev->private->flags.intretry = 0;
-                       ret = cio_start (sch, cdev->private->iccws,
-                                        cdev->private->imask);
-                       /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-                       if (ret != -EACCES)
-                               return ret;
-               }
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-       }
-       return ret;
-}
-
-void
-ccw_device_sense_id_start(struct ccw_device *cdev)
+static void snsid_init(struct ccw_device *cdev)
 {
-       int ret;
-
-       memset (&cdev->private->senseid, 0, sizeof (struct senseid));
-       cdev->private->imask = 0x80;
-       cdev->private->iretry = 5;
-       ret = __ccw_device_sense_id_start(cdev);
-       if (ret && ret != -EBUSY)
-               ccw_device_sense_id_done(cdev, ret);
+       cdev->private->flags.esid = 0;
+       memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
+       cdev->private->senseid.cu_type = 0xffff;
 }
 
 /*
- * Called from interrupt context to check if a valid answer
- * to Sense ID was received.
+ * Check for complete SENSE ID data.
  */
-static int
-ccw_device_check_sense_id(struct ccw_device *cdev)
+static int snsid_check(struct ccw_device *cdev, void *data)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-
-       sch = to_subchannel(cdev->dev.parent);
-       irb = &cdev->private->irb;
-
-       /* Check the error cases. */
-       if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
-               /* Retry Sense ID if requested. */
-               if (cdev->private->flags.intretry) {
-                       cdev->private->flags.intretry = 0;
-                       return -EAGAIN;
-               }
-               return -ETIME;
-       }
-       if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
-               /*
-                * if the device doesn't support the SenseID
-                *  command further retries wouldn't help ...
-                * NB: We don't check here for intervention required like we
-                *     did before, because tape devices with no tape inserted
-                *     may present this status *in conjunction with* the
-                *     sense id information. So, for intervention required,
-                *     we use the "whack it until it talks" strategy...
-                */
-               CIO_MSG_EVENT(0, "SenseID : device %04x on Subchannel "
-                             "0.%x.%04x reports cmd reject\n",
-                             cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no);
+       struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
+       int len = sizeof(struct senseid) - scsw->count;
+
+       /* Check for incomplete SENSE ID data. */
+       if (len < SENSE_ID_MIN_LEN)
+               goto out_restart;
+       if (cdev->private->senseid.cu_type == 0xffff)
+               goto out_restart;
+       /* Check for incompatible SENSE ID data. */
+       if (cdev->private->senseid.reserved != 0xff)
                return -EOPNOTSUPP;
-       }
-       if (irb->esw.esw0.erw.cons) {
-               CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, "
-                             "lpum %02X, cnt %02d, sns :"
-                             " %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno,
-                             irb->esw.esw0.sublog.lpum,
-                             irb->esw.esw0.erw.scnt,
-                             irb->ecw[0], irb->ecw[1],
-                             irb->ecw[2], irb->ecw[3],
-                             irb->ecw[4], irb->ecw[5],
-                             irb->ecw[6], irb->ecw[7]);
-               return -EAGAIN;
-       }
-       if (irb->scsw.cmd.cc == 3) {
-               u8 lpm;
+       /* Check for extended-identification information. */
+       if (len > SENSE_ID_BASIC_LEN)
+               cdev->private->flags.esid = 1;
+       return 0;
 
-               lpm = to_io_private(sch)->orb.cmd.lpm;
-               if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
-                       CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
-                                     "on subchannel 0.%x.%04x is "
-                                     "'not operational'\n", lpm,
-                                     cdev->private->dev_id.devno,
-                                     sch->schid.ssid, sch->schid.sch_no);
-               return -EACCES;
-       }
-
-       /* Did we get a proper answer ? */
-       if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
-           cdev->private->senseid.reserved == 0xFF) {
-               if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
-                       cdev->private->flags.esid = 1;
-               return 0; /* Success */
-       }
-
-       /* Hmm, whatever happened, try again. */
-       CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
-                     "subchannel 0.%x.%04x returns status %02X%02X\n",
-                     cdev->private->dev_id.devno, sch->schid.ssid,
-                     sch->schid.sch_no,
-                     irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
+out_restart:
+       snsid_init(cdev);
        return -EAGAIN;
 }
 
 /*
- * Got interrupt for Sense ID.
+ * Process SENSE ID request result.
  */
-void
-ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-       irb = (struct irb *) __LC_IRB;
-       /* Retry sense id, if needed. */
-       if (irb->scsw.cmd.stctl ==
-           (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) {
-                       ret = __ccw_device_sense_id_start(cdev);
-                       if (ret && ret != -EBUSY)
-                               ccw_device_sense_id_done(cdev, ret);
+       struct ccw_dev_id *id = &cdev->private->dev_id;
+       struct senseid *senseid = &cdev->private->senseid;
+       int vm = 0;
+
+       if (rc && MACHINE_IS_VM) {
+               /* Try diag 0x210 fallback on z/VM. */
+               snsid_init(cdev);
+               if (diag210_get_dev_info(cdev) == 0) {
+                       rc = 0;
+                       vm = 1;
                }
-               return;
        }
-       if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
-               return;
-       ret = ccw_device_check_sense_id(cdev);
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-       switch (ret) {
-       /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */
-       case 0:                 /* Sense id succeeded. */
-       case -ETIME:            /* Sense id stopped by timeout. */
-               ccw_device_sense_id_done(cdev, ret);
-               break;
-       case -EACCES:           /* channel is not operational. */
-               sch->lpm &= ~cdev->private->imask;
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-               /* fall through. */
-       case -EAGAIN:           /* try again. */
-               ret = __ccw_device_sense_id_start(cdev);
-               if (ret == 0 || ret == -EBUSY)
-                       break;
-               /* fall through. */
-       default:                /* Sense ID failed. Try asking VM. */
-               if (MACHINE_IS_VM)
-                       ret = diag_get_dev_info(cdev->private->dev_id.devno,
-                                               &cdev->private->senseid);
-               else
-                       /*
-                        * If we can't couldn't identify the device type we
-                        *  consider the device "not operational".
-                        */
-                       ret = -ENODEV;
+       CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
+                     "%04x/%02x%s\n", id->ssid, id->devno, rc,
+                     senseid->cu_type, senseid->cu_model, senseid->dev_type,
+                     senseid->dev_model, vm ? " (diag210)" : "");
+       ccw_device_sense_id_done(cdev, rc);
+}
 
-               ccw_device_sense_id_done(cdev, ret);
-               break;
-       }
+/**
+ * ccw_device_sense_id_start - perform SENSE ID
+ * @cdev: ccw device
+ *
+ * Execute a SENSE ID channel program on @cdev to update its sense id
+ * information. When finished, call ccw_device_sense_id_done with a
+ * return code specifying the result.
+ */
+void ccw_device_sense_id_start(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+       struct ccw1 *cp = cdev->private->iccws;
+
+       CIO_TRACE_EVENT(4, "snsid");
+       CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+       /* Data setup. */
+       snsid_init(cdev);
+       /* Channel program setup. */
+       cp->cmd_code    = CCW_CMD_SENSE_ID;
+       cp->cda         = (u32) (addr_t) &cdev->private->senseid;
+       cp->count       = sizeof(struct senseid);
+       cp->flags       = CCW_FLAG_SLI;
+       /* Request setup. */
+       memset(req, 0, sizeof(*req));
+       req->cp         = cp;
+       req->timeout    = SENSE_ID_TIMEOUT;
+       req->maxretries = SENSE_ID_RETRIES;
+       req->lpm        = sch->schib.pmcw.pam & sch->opm;
+       req->check      = snsid_check;
+       req->callback   = snsid_callback;
+       ccw_request_start(cdev);
 }
index 2d0efee..6da8454 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/list.h>
 #include <linux/device.h>
 #include <linux/delay.h>
+#include <linux/completion.h>
 
 #include <asm/ccwdev.h>
 #include <asm/idals.h>
@@ -46,6 +47,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags)
        cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0;
        cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0;
        cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0;
+       cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0;
        return 0;
 }
 
@@ -74,6 +76,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags)
        cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0;
        cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0;
        cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0;
+       cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0;
        return 0;
 }
 
@@ -90,8 +93,33 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags)
        cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0;
        cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0;
        cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0;
+       cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0;
 }
 
+/**
+ * ccw_device_is_pathgroup - determine if paths to this device are grouped
+ * @cdev: ccw device
+ *
+ * Return non-zero if there is a path group, zero otherwise.
+ */
+int ccw_device_is_pathgroup(struct ccw_device *cdev)
+{
+       return cdev->private->flags.pgroup;
+}
+EXPORT_SYMBOL(ccw_device_is_pathgroup);
+
+/**
+ * ccw_device_is_multipath - determine if device is operating in multipath mode
+ * @cdev: ccw device
+ *
+ * Return non-zero if device is operating in multipath mode, zero otherwise.
+ */
+int ccw_device_is_multipath(struct ccw_device *cdev)
+{
+       return cdev->private->flags.mpath;
+}
+EXPORT_SYMBOL(ccw_device_is_multipath);
+
 /**
  * ccw_device_clear() - terminate I/O request processing
  * @cdev: target ccw device
@@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
                return -EINVAL;
        if (cdev->private->state == DEV_STATE_NOT_OPER)
                return -ENODEV;
-       if (cdev->private->state == DEV_STATE_VERIFY ||
-           cdev->private->state == DEV_STATE_CLEAR_VERIFY) {
+       if (cdev->private->state == DEV_STATE_VERIFY) {
                /* Remember to fake irb when finished. */
                if (!cdev->private->flags.fake_irb) {
                        cdev->private->flags.fake_irb = 1;
@@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
        return sch->lpm;
 }
 
-/*
- * Try to break the lock on a boxed device.
- */
-int
-ccw_device_stlck(struct ccw_device *cdev)
-{
-       void *buf, *buf2;
-       unsigned long flags;
-       struct subchannel *sch;
-       int ret;
+struct stlck_data {
+       struct completion done;
+       int rc;
+};
 
-       if (!cdev)
-               return -ENODEV;
+void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
+{
+       struct stlck_data *sdata = data;
 
-       if (cdev->drv && !cdev->private->options.force)
-               return -EINVAL;
+       sdata->rc = rc;
+       complete(&sdata->done);
+}
 
-       sch = to_subchannel(cdev->dev.parent);
-       
-       CIO_TRACE_EVENT(2, "stl lock");
-       CIO_TRACE_EVENT(2, dev_name(&cdev->dev));
+/*
+ * Perform unconditional reserve + release.
+ */
+int ccw_device_stlck(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct stlck_data data;
+       u8 *buffer;
+       int rc;
 
-       buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-       buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
-       if (!buf2) {
-               kfree(buf);
-               return -ENOMEM;
+       /* Check if steal lock operation is valid for this device. */
+       if (cdev->drv) {
+               if (!cdev->private->options.force)
+                       return -EINVAL;
        }
-       spin_lock_irqsave(sch->lock, flags);
-       ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
-       if (ret)
-               goto out_unlock;
-       /*
-        * Setup ccw. We chain an unconditional reserve and a release so we
-        * only break the lock.
-        */
-       cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK;
-       cdev->private->iccws[0].cda = (__u32) __pa(buf);
-       cdev->private->iccws[0].count = 32;
-       cdev->private->iccws[0].flags = CCW_FLAG_CC;
-       cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE;
-       cdev->private->iccws[1].cda = (__u32) __pa(buf2);
-       cdev->private->iccws[1].count = 32;
-       cdev->private->iccws[1].flags = 0;
-       ret = cio_start(sch, cdev->private->iccws, 0);
-       if (ret) {
-               cio_disable_subchannel(sch); //FIXME: return code?
+       buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+       init_completion(&data.done);
+       data.rc = -EIO;
+       spin_lock_irq(sch->lock);
+       rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
+       if (rc)
                goto out_unlock;
+       /* Perform operation. */
+       cdev->private->state = DEV_STATE_STEAL_LOCK,
+       ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
+       spin_unlock_irq(sch->lock);
+       /* Wait for operation to finish. */
+       if (wait_for_completion_interruptible(&data.done)) {
+               /* Got a signal. */
+               spin_lock_irq(sch->lock);
+               ccw_request_cancel(cdev);
+               spin_unlock_irq(sch->lock);
+               wait_for_completion(&data.done);
        }
-       cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
-       spin_unlock_irqrestore(sch->lock, flags);
-       wait_event(cdev->private->wait_q,
-                  cdev->private->irb.scsw.cmd.actl == 0);
-       spin_lock_irqsave(sch->lock, flags);
-       cio_disable_subchannel(sch); //FIXME: return code?
-       if ((cdev->private->irb.scsw.cmd.dstat !=
-            (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
-           (cdev->private->irb.scsw.cmd.cstat != 0))
-               ret = -EIO;
-       /* Clear irb. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
+       rc = data.rc;
+       /* Check results. */
+       spin_lock_irq(sch->lock);
+       cio_disable_subchannel(sch);
+       cdev->private->state = DEV_STATE_BOXED;
 out_unlock:
-       kfree(buf);
-       kfree(buf2);
-       spin_unlock_irqrestore(sch->lock, flags);
-       return ret;
+       spin_unlock_irq(sch->lock);
+       kfree(buffer);
+
+       return rc;
 }
 
 void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
index fc5ca1d..aad188e 100644 (file)
 /*
- * drivers/s390/cio/device_pgid.c
+ *  CCW device PGID and path verification I/O handling.
  *
- *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- *                      IBM Corporation
- *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
- *              Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- * Path Group ID functions.
+ *    Copyright IBM Corp. 2002,2009
+ *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *              Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *              Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
-
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
 #include <asm/ccwdev.h>
 #include <asm/cio.h>
-#include <asm/delay.h>
-#include <asm/lowcore.h>
 
 #include "cio.h"
 #include "cio_debug.h"
-#include "css.h"
 #include "device.h"
-#include "ioasm.h"
 #include "io_sch.h"
 
+#define PGID_RETRIES   256
+#define PGID_TIMEOUT   (10 * HZ)
+
 /*
- * Helper function called from interrupt context to decide whether an
- * operation should be tried again.
+ * Process path verification data and report result.
  */
-static int __ccw_device_should_retry(union scsw *scsw)
+static void verify_done(struct ccw_device *cdev, int rc)
 {
-       /* CC is only valid if start function bit is set. */
-       if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1)
-               return 1;
-       /* No more activity. For sense and set PGID we stubbornly try again. */
-       if (!scsw->cmd.actl)
-               return 1;
-       return 0;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_dev_id *id = &cdev->private->dev_id;
+       int mpath = cdev->private->flags.mpath;
+       int pgroup = cdev->private->flags.pgroup;
+
+       if (rc)
+               goto out;
+       /* Ensure consistent multipathing state at device and channel. */
+       if (sch->config.mp != mpath) {
+               sch->config.mp = mpath;
+               rc = cio_commit_config(sch);
+       }
+out:
+       CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d "
+                        "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath,
+                        sch->vpm);
+       ccw_device_verify_done(cdev, rc);
 }
 
 /*
- * Start Sense Path Group ID helper function. Used in ccw_device_recog
- * and ccw_device_sense_pgid.
+ * Create channel program to perform a NOOP.
  */
-static int
-__ccw_device_sense_pgid_start(struct ccw_device *cdev)
+static void nop_build_cp(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
-       struct ccw1 *ccw;
-       int ret;
-       int i;
-
-       sch = to_subchannel(cdev->dev.parent);
-       /* Return if we already checked on all paths. */
-       if (cdev->private->imask == 0)
-               return (sch->lpm == 0) ? -ENODEV : -EACCES;
-       i = 8 - ffs(cdev->private->imask);
-
-       /* Setup sense path group id channel program. */
-       ccw = cdev->private->iccws;
-       ccw->cmd_code = CCW_CMD_SENSE_PGID;
-       ccw->count = sizeof (struct pgid);
-       ccw->flags = CCW_FLAG_SLI;
-
-       /* Reset device status. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-       /* Try on every path. */
-       ret = -ENODEV;
-       while (cdev->private->imask != 0) {
-               /* Try every path multiple times. */
-               ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
-               if (cdev->private->iretry > 0) {
-                       cdev->private->iretry--;
-                       /* Reset internal retry indication. */
-                       cdev->private->flags.intretry = 0;
-                       ret = cio_start (sch, cdev->private->iccws, 
-                                        cdev->private->imask);
-                       /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-                       if (ret != -EACCES)
-                               return ret;
-                       CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel "
-                                     "0.%x.%04x, lpm %02X, became 'not "
-                                     "operational'\n",
-                                     cdev->private->dev_id.devno,
-                                     sch->schid.ssid,
-                                     sch->schid.sch_no, cdev->private->imask);
-
-               }
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-               i++;
-       }
-
-       return ret;
+       struct ccw_request *req = &cdev->private->req;
+       struct ccw1 *cp = cdev->private->iccws;
+
+       cp->cmd_code    = CCW_CMD_NOOP;
+       cp->cda         = 0;
+       cp->count       = 0;
+       cp->flags       = CCW_FLAG_SLI;
+       req->cp         = cp;
 }
 
-void
-ccw_device_sense_pgid_start(struct ccw_device *cdev)
+/*
+ * Perform NOOP on a single path.
+ */
+static void nop_do(struct ccw_device *cdev)
 {
-       int ret;
-
-       /* Set a timeout of 60s */
-       ccw_device_set_timeout(cdev, 60*HZ);
-
-       cdev->private->state = DEV_STATE_SENSE_PGID;
-       cdev->private->imask = 0x80;
-       cdev->private->iretry = 5;
-       memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
-       ret = __ccw_device_sense_pgid_start(cdev);
-       if (ret && ret != -EBUSY)
-               ccw_device_sense_pgid_done(cdev, ret);
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+
+       /* Adjust lpm. */
+       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm);
+       if (!req->lpm)
+               goto out_nopath;
+       nop_build_cp(cdev);
+       ccw_request_start(cdev);
+       return;
+
+out_nopath:
+       verify_done(cdev, sch->vpm ? 0 : -EACCES);
 }
 
 /*
- * Called from interrupt context to check if a valid answer
- * to Sense Path Group ID was received.
+ * Adjust NOOP I/O status.
  */
-static int
-__ccw_device_check_sense_pgid(struct ccw_device *cdev)
+static enum io_status nop_filter(struct ccw_device *cdev, void *data,
+                                struct irb *irb, enum io_status status)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-       int i;
-
-       sch = to_subchannel(cdev->dev.parent);
-       irb = &cdev->private->irb;
-       if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
-               /* Retry Sense PGID if requested. */
-               if (cdev->private->flags.intretry) {
-                       cdev->private->flags.intretry = 0;
-                       return -EAGAIN;
-               }
-               return -ETIME;
-       }
-       if (irb->esw.esw0.erw.cons &&
-           (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) {
-               /*
-                * If the device doesn't support the Sense Path Group ID
-                *  command further retries wouldn't help ...
-                */
-               return -EOPNOTSUPP;
-       }
-       if (irb->esw.esw0.erw.cons) {
-               CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, "
-                             "lpum %02X, cnt %02d, sns : "
-                             "%02X%02X%02X%02X %02X%02X%02X%02X ...\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno,
-                             irb->esw.esw0.sublog.lpum,
-                             irb->esw.esw0.erw.scnt,
-                             irb->ecw[0], irb->ecw[1],
-                             irb->ecw[2], irb->ecw[3],
-                             irb->ecw[4], irb->ecw[5],
-                             irb->ecw[6], irb->ecw[7]);
-               return -EAGAIN;
-       }
-       if (irb->scsw.cmd.cc == 3) {
-               u8 lpm;
-
-               lpm = to_io_private(sch)->orb.cmd.lpm;
-               CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
-                             " lpm %02X, became 'not operational'\n",
-                             cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no, lpm);
-               return -EACCES;
-       }
-       i = 8 - ffs(cdev->private->imask);
-       if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
-               CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
-                             "is reserved by someone else\n",
-                             cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no);
-               return -EUSERS;
-       }
-       return 0;
+       /* Only subchannel status might indicate a path error. */
+       if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0)
+               return IO_DONE;
+       return status;
 }
 
 /*
- * Got interrupt for Sense Path Group ID.
+ * Process NOOP request result for a single path.
  */
-void
-ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void nop_callback(struct ccw_device *cdev, void *data, int rc)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-       int ret;
-
-       irb = (struct irb *) __LC_IRB;
-
-       if (irb->scsw.cmd.stctl ==
-           (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (__ccw_device_should_retry(&irb->scsw)) {
-                       ret = __ccw_device_sense_pgid_start(cdev);
-                       if (ret && ret != -EBUSY)
-                               ccw_device_sense_pgid_done(cdev, ret);
-               }
-               return;
-       }
-       if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
-               return;
-       sch = to_subchannel(cdev->dev.parent);
-       ret = __ccw_device_check_sense_pgid(cdev);
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-       switch (ret) {
-       /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
-       case -EOPNOTSUPP:       /* Sense Path Group ID not supported */
-               ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
-               break;
-       case -ETIME:            /* Sense path group id stopped by timeout. */
-               ccw_device_sense_pgid_done(cdev, -ETIME);
-               break;
-       case -EACCES:           /* channel is not operational. */
-               sch->lpm &= ~cdev->private->imask;
-               /* Fall through. */
-       case 0:                 /* Sense Path Group ID successful. */
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-               /* Fall through. */
-       case -EAGAIN:           /* Try again. */
-               ret = __ccw_device_sense_pgid_start(cdev);
-               if (ret != 0 && ret != -EBUSY)
-                       ccw_device_sense_pgid_done(cdev, ret);
-               break;
-       case -EUSERS:           /* device is reserved for someone else. */
-               ccw_device_sense_pgid_done(cdev, -EUSERS);
-               break;
-       }
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+
+       if (rc == 0)
+               sch->vpm |= req->lpm;
+       else if (rc != -EACCES)
+               goto err;
+       req->lpm >>= 1;
+       nop_do(cdev);
+       return;
+
+err:
+       verify_done(cdev, rc);
 }
 
 /*
- * Path Group ID helper function.
+ * Create channel program to perform SET PGID on a single path.
  */
-static int
-__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
+static void spid_build_cp(struct ccw_device *cdev, u8 fn)
 {
-       struct subchannel *sch;
-       struct ccw1 *ccw;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-
-       /* Setup sense path group id channel program. */
-       cdev->private->pgid[0].inf.fc = func;
-       ccw = cdev->private->iccws;
-       if (cdev->private->flags.pgid_single)
-               cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
-       else
-               cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
-       ccw->cmd_code = CCW_CMD_SET_PGID;
-       ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
-       ccw->count = sizeof (struct pgid);
-       ccw->flags = CCW_FLAG_SLI;
-
-       /* Reset device status. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-
-       /* Try multiple times. */
-       ret = -EACCES;
-       if (cdev->private->iretry > 0) {
-               cdev->private->iretry--;
-               /* Reset internal retry indication. */
-               cdev->private->flags.intretry = 0;
-               ret = cio_start (sch, cdev->private->iccws,
-                                cdev->private->imask);
-               /* We expect an interrupt in case of success or busy
-                * indication. */
-               if ((ret == 0) || (ret == -EBUSY))
-                       return ret;
-       }
-       /* PGID command failed on this path. */
-       CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel "
-                     "0.%x.%04x, lpm %02X, became 'not operational'\n",
-                     cdev->private->dev_id.devno, sch->schid.ssid,
-                     sch->schid.sch_no, cdev->private->imask);
-       return ret;
+       struct ccw_request *req = &cdev->private->req;
+       struct ccw1 *cp = cdev->private->iccws;
+       int i = 8 - ffs(req->lpm);
+       struct pgid *pgid = &cdev->private->pgid[i];
+
+       pgid->inf.fc    = fn;
+       cp->cmd_code    = CCW_CMD_SET_PGID;
+       cp->cda         = (u32) (addr_t) pgid;
+       cp->count       = sizeof(*pgid);
+       cp->flags       = CCW_FLAG_SLI;
+       req->cp         = cp;
 }
 
 /*
- * Helper function to send a nop ccw down a path.
+ * Perform establish/resign SET PGID on a single path.
  */
-static int __ccw_device_do_nop(struct ccw_device *cdev)
+static void spid_do(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
-       struct ccw1 *ccw;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-
-       /* Setup nop channel program. */
-       ccw = cdev->private->iccws;
-       ccw->cmd_code = CCW_CMD_NOOP;
-       ccw->cda = 0;
-       ccw->count = 0;
-       ccw->flags = CCW_FLAG_SLI;
-
-       /* Reset device status. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-
-       /* Try multiple times. */
-       ret = -EACCES;
-       if (cdev->private->iretry > 0) {
-               cdev->private->iretry--;
-               /* Reset internal retry indication. */
-               cdev->private->flags.intretry = 0;
-               ret = cio_start (sch, cdev->private->iccws,
-                                cdev->private->imask);
-               /* We expect an interrupt in case of success or busy
-                * indication. */
-               if ((ret == 0) || (ret == -EBUSY))
-                       return ret;
-       }
-       /* nop command failed on this path. */
-       CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel "
-                     "0.%x.%04x, lpm %02X, became 'not operational'\n",
-                     cdev->private->dev_id.devno, sch->schid.ssid,
-                     sch->schid.sch_no, cdev->private->imask);
-       return ret;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+       u8 fn;
+
+       /* Use next available path that is not already in correct state. */
+       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm);
+       if (!req->lpm)
+               goto out_nopath;
+       /* Channel program setup. */
+       if (req->lpm & sch->opm)
+               fn = SPID_FUNC_ESTABLISH;
+       else
+               fn = SPID_FUNC_RESIGN;
+       if (cdev->private->flags.mpath)
+               fn |= SPID_FUNC_MULTI_PATH;
+       spid_build_cp(cdev, fn);
+       ccw_request_start(cdev);
+       return;
+
+out_nopath:
+       verify_done(cdev, sch->vpm ? 0 : -EACCES);
 }
 
+static void verify_start(struct ccw_device *cdev);
 
 /*
- * Called from interrupt context to check if a valid answer
- * to Set Path Group ID was received.
+ * Process SET PGID request result for a single path.
  */
-static int
-__ccw_device_check_pgid(struct ccw_device *cdev)
+static void spid_callback(struct ccw_device *cdev, void *data, int rc)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-
-       sch = to_subchannel(cdev->dev.parent);
-       irb = &cdev->private->irb;
-       if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
-               /* Retry Set PGID if requested. */
-               if (cdev->private->flags.intretry) {
-                       cdev->private->flags.intretry = 0;
-                       return -EAGAIN;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+
+       switch (rc) {
+       case 0:
+               sch->vpm |= req->lpm & sch->opm;
+               break;
+       case -EACCES:
+               break;
+       case -EOPNOTSUPP:
+               if (cdev->private->flags.mpath) {
+                       /* Try without multipathing. */
+                       cdev->private->flags.mpath = 0;
+                       goto out_restart;
                }
-               return -ETIME;
+               /* Try without pathgrouping. */
+               cdev->private->flags.pgroup = 0;
+               goto out_restart;
+       default:
+               goto err;
        }
-       if (irb->esw.esw0.erw.cons) {
-               if (irb->ecw[0] & SNS0_CMD_REJECT)
-                       return -EOPNOTSUPP;
-               /* Hmm, whatever happened, try again. */
-               CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, "
-                             "cnt %02d, "
-                             "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno,
-                             irb->esw.esw0.erw.scnt,
-                             irb->ecw[0], irb->ecw[1],
-                             irb->ecw[2], irb->ecw[3],
-                             irb->ecw[4], irb->ecw[5],
-                             irb->ecw[6], irb->ecw[7]);
-               return -EAGAIN;
-       }
-       if (irb->scsw.cmd.cc == 3) {
-               CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x,"
-                             " lpm %02X, became 'not operational'\n",
-                             cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no, cdev->private->imask);
-               return -EACCES;
-       }
-       return 0;
+       req->lpm >>= 1;
+       spid_do(cdev);
+       return;
+
+out_restart:
+       verify_start(cdev);
+       return;
+err:
+       verify_done(cdev, rc);
+}
+
+static void spid_start(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+
+       /* Initialize request data. */
+       memset(req, 0, sizeof(*req));
+       req->timeout    = PGID_TIMEOUT;
+       req->maxretries = PGID_RETRIES;
+       req->lpm        = 0x80;
+       req->callback   = spid_callback;
+       spid_do(cdev);
+}
+
+static int pgid_cmp(struct pgid *p1, struct pgid *p2)
+{
+       return memcmp((char *) p1 + 1, (char *) p2 + 1,
+                     sizeof(struct pgid) - 1);
 }
 
 /*
- * Called from interrupt context to check the path status after a nop has
- * been send.
+ * Determine pathgroup state from PGID data.
  */
-static int __ccw_device_check_nop(struct ccw_device *cdev)
+static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
+                        int *mismatch, int *reserved, int *reset)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-
-       sch = to_subchannel(cdev->dev.parent);
-       irb = &cdev->private->irb;
-       if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
-               /* Retry NOP if requested. */
-               if (cdev->private->flags.intretry) {
-                       cdev->private->flags.intretry = 0;
-                       return -EAGAIN;
+       struct pgid *pgid = &cdev->private->pgid[0];
+       struct pgid *first = NULL;
+       int lpm;
+       int i;
+
+       *mismatch = 0;
+       *reserved = 0;
+       *reset = 0;
+       for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) {
+               if ((cdev->private->pgid_valid_mask & lpm) == 0)
+                       continue;
+               if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
+                       *reserved = 1;
+               if (pgid->inf.ps.state1 == SNID_STATE1_RESET) {
+                       /* A PGID was reset. */
+                       *reset = 1;
+                       continue;
                }
-               return -ETIME;
-       }
-       if (irb->scsw.cmd.cc == 3) {
-               CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x,"
-                             " lpm %02X, became 'not operational'\n",
-                             cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no, cdev->private->imask);
-               return -EACCES;
+               if (!first) {
+                       first = pgid;
+                       continue;
+               }
+               if (pgid_cmp(pgid, first) != 0)
+                       *mismatch = 1;
        }
-       return 0;
+       if (!first)
+               first = &channel_subsystems[0]->global_pgid;
+       *p = first;
 }
 
-static void
-__ccw_device_verify_start(struct ccw_device *cdev)
+static u8 pgid_to_vpm(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
-       __u8 func;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-       /* Repeat for all paths. */
-       for (; cdev->private->imask; cdev->private->imask >>= 1,
-                                    cdev->private->iretry = 5) {
-               if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
-                       /* Path not available, try next. */
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct pgid *pgid;
+       int i;
+       int lpm;
+       u8 vpm = 0;
+
+       /* Set VPM bits for paths which are already in the target state. */
+       for (i = 0; i < 8; i++) {
+               lpm = 0x80 >> i;
+               if ((cdev->private->pgid_valid_mask & lpm) == 0)
                        continue;
-               if (cdev->private->options.pgroup) {
-                       if (sch->opm & cdev->private->imask)
-                               func = SPID_FUNC_ESTABLISH;
-                       else
-                               func = SPID_FUNC_RESIGN;
-                       ret = __ccw_device_do_pgid(cdev, func);
-               } else
-                       ret = __ccw_device_do_nop(cdev);
-               /* We expect an interrupt in case of success or busy
-                * indication. */
-               if (ret == 0 || ret == -EBUSY)
-                       return;
-               /* Permanent path failure, try next. */
+               pgid = &cdev->private->pgid[i];
+               if (sch->opm & lpm) {
+                       if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED)
+                               continue;
+               } else {
+                       if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED)
+                               continue;
+               }
+               if (cdev->private->flags.mpath) {
+                       if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH)
+                               continue;
+               } else {
+                       if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
+                               continue;
+               }
+               vpm |= lpm;
        }
-       /* Done with all paths. */
-       ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
+
+       return vpm;
 }
-               
-/*
- * Got interrupt for Set Path Group ID.
- */
-void
-ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
+
+static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-       int ret;
+       int i;
 
-       irb = (struct irb *) __LC_IRB;
+       for (i = 0; i < 8; i++)
+               memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid));
+}
 
-       if (irb->scsw.cmd.stctl ==
-           (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (__ccw_device_should_retry(&irb->scsw))
-                       __ccw_device_verify_start(cdev);
-               return;
+/*
+ * Process SENSE PGID data and report result.
+ */
+static void snid_done(struct ccw_device *cdev, int rc)
+{
+       struct ccw_dev_id *id = &cdev->private->dev_id;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct pgid *pgid;
+       int mismatch = 0;
+       int reserved = 0;
+       int reset = 0;
+
+       if (rc)
+               goto out;
+       pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset);
+       if (reserved)
+               rc = -EUSERS;
+       else if (mismatch)
+               rc = -EOPNOTSUPP;
+       else {
+               sch->vpm = pgid_to_vpm(cdev);
+               pgid_fill(cdev, pgid);
        }
-       if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
-               return;
-       sch = to_subchannel(cdev->dev.parent);
-       if (cdev->private->options.pgroup)
-               ret = __ccw_device_check_pgid(cdev);
-       else
-               ret = __ccw_device_check_nop(cdev);
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-
-       switch (ret) {
-       /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
+out:
+       CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
+                     "mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc,
+                     cdev->private->pgid_valid_mask, sch->vpm, mismatch,
+                     reserved, reset);
+       switch (rc) {
        case 0:
-               /* Path verification ccw finished successfully, update lpm. */
-               sch->vpm |= sch->opm & cdev->private->imask;
-               /* Go on with next path. */
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-               __ccw_device_verify_start(cdev);
+               /* Anything left to do? */
+               if (sch->vpm == sch->schib.pmcw.pam) {
+                       verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
+                       return;
+               }
+               /* Perform path-grouping. */
+               spid_start(cdev);
                break;
        case -EOPNOTSUPP:
-               /*
-                * One of those strange devices which claim to be able
-                * to do multipathing but not for Set Path Group ID.
-                */
-               if (cdev->private->flags.pgid_single)
-                       cdev->private->options.pgroup = 0;
-               else
-                       cdev->private->flags.pgid_single = 1;
-               /* Retry */
-               sch->vpm = 0;
-               cdev->private->imask = 0x80;
-               cdev->private->iretry = 5;
-               /* fall through. */
-       case -EAGAIN:           /* Try again. */
-               __ccw_device_verify_start(cdev);
-               break;
-       case -ETIME:            /* Set path group id stopped by timeout. */
-               ccw_device_verify_done(cdev, -ETIME);
-               break;
-       case -EACCES:           /* channel is not operational. */
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-               __ccw_device_verify_start(cdev);
+               /* Path-grouping not supported. */
+               cdev->private->flags.pgroup = 0;
+               cdev->private->flags.mpath = 0;
+               verify_start(cdev);
                break;
+       default:
+               verify_done(cdev, rc);
        }
 }
 
-void
-ccw_device_verify_start(struct ccw_device *cdev)
+/*
+ * Create channel program to perform a SENSE PGID on a single path.
+ */
+static void snid_build_cp(struct ccw_device *cdev)
+{
+       struct ccw_request *req = &cdev->private->req;
+       struct ccw1 *cp = cdev->private->iccws;
+       int i = 8 - ffs(req->lpm);
+
+       /* Channel program setup. */
+       cp->cmd_code    = CCW_CMD_SENSE_PGID;
+       cp->cda         = (u32) (addr_t) &cdev->private->pgid[i];
+       cp->count       = sizeof(struct pgid);
+       cp->flags       = CCW_FLAG_SLI;
+       req->cp         = cp;
+}
+
+/*
+ * Perform SENSE PGID on a single path.
+ */
+static void snid_do(struct ccw_device *cdev)
 {
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+
+       /* Adjust lpm if paths are not set in pam. */
+       req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam);
+       if (!req->lpm)
+               goto out_nopath;
+       snid_build_cp(cdev);
+       ccw_request_start(cdev);
+       return;
+
+out_nopath:
+       snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES);
+}
 
-       cdev->private->flags.pgid_single = 0;
-       cdev->private->imask = 0x80;
-       cdev->private->iretry = 5;
+/*
+ * Process SENSE PGID request result for single path.
+ */
+static void snid_callback(struct ccw_device *cdev, void *data, int rc)
+{
+       struct ccw_request *req = &cdev->private->req;
+
+       if (rc == 0)
+               cdev->private->pgid_valid_mask |= req->lpm;
+       else if (rc != -EACCES)
+               goto err;
+       req->lpm >>= 1;
+       snid_do(cdev);
+       return;
+
+err:
+       snid_done(cdev, rc);
+}
 
-       /* Start with empty vpm. */
-       sch->vpm = 0;
+/*
+ * Perform path verification.
+ */
+static void verify_start(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+       struct ccw_dev_id *devid = &cdev->private->dev_id;
 
-       /* Get current pam. */
-       if (cio_update_schib(sch)) {
-               ccw_device_verify_done(cdev, -ENODEV);
-               return;
+       sch->vpm = 0;
+       /* Initialize request data. */
+       memset(req, 0, sizeof(*req));
+       req->timeout    = PGID_TIMEOUT;
+       req->maxretries = PGID_RETRIES;
+       req->lpm        = 0x80;
+       if (cdev->private->flags.pgroup) {
+               CIO_TRACE_EVENT(4, "snid");
+               CIO_HEX_EVENT(4, devid, sizeof(*devid));
+               req->callback   = snid_callback;
+               snid_do(cdev);
+       } else {
+               CIO_TRACE_EVENT(4, "nop");
+               CIO_HEX_EVENT(4, devid, sizeof(*devid));
+               req->filter     = nop_filter;
+               req->callback   = nop_callback;
+               nop_do(cdev);
        }
-       /* After 60s path verification is considered to have failed. */
-       ccw_device_set_timeout(cdev, 60*HZ);
-       __ccw_device_verify_start(cdev);
 }
 
-static void
-__ccw_device_disband_start(struct ccw_device *cdev)
+/**
+ * ccw_device_verify_start - perform path verification
+ * @cdev: ccw device
+ *
+ * Perform an I/O on each available channel path to @cdev to determine which
+ * paths are operational. The resulting path mask is stored in sch->vpm.
+ * If device options specify pathgrouping, establish a pathgroup for the
+ * operational paths. When finished, call ccw_device_verify_done with a
+ * return code specifying the result.
+ */
+void ccw_device_verify_start(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-       while (cdev->private->imask != 0) {
-               if (sch->lpm & cdev->private->imask) {
-                       ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND);
-                       if (ret == 0)
-                               return;
-               }
-               cdev->private->iretry = 5;
-               cdev->private->imask >>= 1;
-       }
-       ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
+       CIO_TRACE_EVENT(4, "vrfy");
+       CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+       /* Initialize PGID data. */
+       memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
+       cdev->private->pgid_valid_mask = 0;
+       /*
+        * Initialize pathgroup and multipath state with target values.
+        * They may change in the course of path verification.
+        */
+       cdev->private->flags.pgroup = cdev->private->options.pgroup;
+       cdev->private->flags.mpath = cdev->private->options.mpath;
+       cdev->private->flags.doverify = 0;
+       verify_start(cdev);
 }
 
 /*
- * Got interrupt for Unset Path Group ID.
+ * Process disband SET PGID request result.
  */
-void
-ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void disband_callback(struct ccw_device *cdev, void *data, int rc)
 {
-       struct subchannel *sch;
-       struct irb *irb;
-       int ret;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_dev_id *id = &cdev->private->dev_id;
+
+       if (rc)
+               goto out;
+       /* Ensure consistent multipathing state at device and channel. */
+       cdev->private->flags.mpath = 0;
+       if (sch->config.mp) {
+               sch->config.mp = 0;
+               rc = cio_commit_config(sch);
+       }
+out:
+       CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno,
+                     rc);
+       ccw_device_disband_done(cdev, rc);
+}
 
-       irb = (struct irb *) __LC_IRB;
+/**
+ * ccw_device_disband_start - disband pathgroup
+ * @cdev: ccw device
+ *
+ * Execute a SET PGID channel program on @cdev to disband a previously
+ * established pathgroup. When finished, call ccw_device_disband_done with
+ * a return code specifying the result.
+ */
+void ccw_device_disband_start(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+       u8 fn;
+
+       CIO_TRACE_EVENT(4, "disb");
+       CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+       /* Request setup. */
+       memset(req, 0, sizeof(*req));
+       req->timeout    = PGID_TIMEOUT;
+       req->maxretries = PGID_RETRIES;
+       req->lpm        = sch->schib.pmcw.pam & sch->opm;
+       req->callback   = disband_callback;
+       fn = SPID_FUNC_DISBAND;
+       if (cdev->private->flags.mpath)
+               fn |= SPID_FUNC_MULTI_PATH;
+       spid_build_cp(cdev, fn);
+       ccw_request_start(cdev);
+}
 
-       if (irb->scsw.cmd.stctl ==
-           (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (__ccw_device_should_retry(&irb->scsw))
-                       __ccw_device_disband_start(cdev);
-               return;
-       }
-       if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
-               return;
-       sch = to_subchannel(cdev->dev.parent);
-       ret = __ccw_device_check_pgid(cdev);
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-       switch (ret) {
-       /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
-       case 0:                 /* disband successful. */
-               ccw_device_disband_done(cdev, ret);
-               break;
-       case -EOPNOTSUPP:
-               /*
-                * One of those strange devices which claim to be able
-                * to do multipathing but not for Unset Path Group ID.
-                */
-               cdev->private->flags.pgid_single = 1;
-               /* fall through. */
-       case -EAGAIN:           /* Try again. */
-               __ccw_device_disband_start(cdev);
-               break;
-       case -ETIME:            /* Set path group id stopped by timeout. */
-               ccw_device_disband_done(cdev, -ETIME);
-               break;
-       case -EACCES:           /* channel is not operational. */
-               cdev->private->imask >>= 1;
-               cdev->private->iretry = 5;
-               __ccw_device_disband_start(cdev);
-               break;
-       }
+static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
+{
+       struct ccw_request *req = &cdev->private->req;
+       struct ccw1 *cp = cdev->private->iccws;
+
+       cp[0].cmd_code = CCW_CMD_STLCK;
+       cp[0].cda = (u32) (addr_t) buf1;
+       cp[0].count = 32;
+       cp[0].flags = CCW_FLAG_CC;
+       cp[1].cmd_code = CCW_CMD_RELEASE;
+       cp[1].cda = (u32) (addr_t) buf2;
+       cp[1].count = 32;
+       cp[1].flags = 0;
+       req->cp = cp;
 }
 
-void
-ccw_device_disband_start(struct ccw_device *cdev)
+static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
 {
-       /* After 60s disbanding is considered to have failed. */
-       ccw_device_set_timeout(cdev, 60*HZ);
+       ccw_device_stlck_done(cdev, data, rc);
+}
 
-       cdev->private->flags.pgid_single = 0;
-       cdev->private->iretry = 5;
-       cdev->private->imask = 0x80;
-       __ccw_device_disband_start(cdev);
+/**
+ * ccw_device_stlck_start - perform unconditional release
+ * @cdev: ccw device
+ * @data: data pointer to be passed to ccw_device_stlck_done
+ * @buf1: data pointer used in channel program
+ * @buf2: data pointer used in channel program
+ *
+ * Execute a channel program on @cdev to release an existing PGID reservation.
+ * When finished, call ccw_device_stlck_done with a return code specifying the
+ * result.
+ */
+void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
+                           void *buf2)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct ccw_request *req = &cdev->private->req;
+
+       CIO_TRACE_EVENT(4, "stlck");
+       CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
+       /* Request setup. */
+       memset(req, 0, sizeof(*req));
+       req->timeout    = PGID_TIMEOUT;
+       req->maxretries = PGID_RETRIES;
+       req->lpm        = sch->schib.pmcw.pam & sch->opm;
+       req->data       = data;
+       req->callback   = stlck_callback;
+       stlck_build_cp(cdev, buf1, buf2);
+       ccw_request_start(cdev);
 }
+
index 5814dbe..66d8066 100644 (file)
@@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
        sense_ccw->count = SENSE_MAX_COUNT;
        sense_ccw->flags = CCW_FLAG_SLI;
 
-       /* Reset internal retry indication. */
-       cdev->private->flags.intretry = 0;
-
        rc = cio_start(sch, sense_ccw, 0xff);
        if (rc == -ENODEV || rc == -EACCES)
                dev_fsm_event(cdev, DEV_EVENT_VERIFY);
index 0b8f381..d72ae4c 100644 (file)
@@ -1,7 +1,10 @@
 #ifndef S390_IO_SCH_H
 #define S390_IO_SCH_H
 
+#include <linux/types.h>
 #include <asm/schid.h>
+#include <asm/ccwdev.h>
+#include "css.h"
 
 /*
  * command-mode operation request block
@@ -67,6 +70,52 @@ struct io_subchannel_private {
 
 #define MAX_CIWS 8
 
+/*
+ * Possible status values for a CCW request's I/O.
+ */
+enum io_status {
+       IO_DONE,
+       IO_RUNNING,
+       IO_STATUS_ERROR,
+       IO_PATH_ERROR,
+       IO_REJECTED,
+       IO_KILLED
+};
+
+/**
+ * ccw_request - Internal CCW request.
+ * @cp: channel program to start
+ * @timeout: maximum allowable time in jiffies between start I/O and interrupt
+ * @maxretries: number of retries per I/O operation and path
+ * @lpm: mask of paths to use
+ * @check: optional callback that determines if results are final
+ * @filter: optional callback to adjust request status based on IRB data
+ * @callback: final callback
+ * @data: user-defined pointer passed to all callbacks
+ * @mask: current path mask
+ * @retries: current number of retries
+ * @drc: delayed return code
+ * @cancel: non-zero if request was cancelled
+ * @done: non-zero if request was finished
+ */
+struct ccw_request {
+       struct ccw1 *cp;
+       unsigned long timeout;
+       u16 maxretries;
+       u8 lpm;
+       int (*check)(struct ccw_device *, void *);
+       enum io_status (*filter)(struct ccw_device *, void *, struct irb *,
+                                enum io_status);
+       void (*callback)(struct ccw_device *, void *, int);
+       void *data;
+       /* These fields are used internally. */
+       u16 mask;
+       u16 retries;
+       int drc;
+       int cancel:1;
+       int done:1;
+} __attribute__((packed));
+
 /*
  * sense-id response buffer layout
  */
@@ -82,32 +131,43 @@ struct senseid {
        struct ciw ciw[MAX_CIWS];       /* variable # of CIWs */
 }  __attribute__ ((packed, aligned(4)));
 
+enum cdev_todo {
+       CDEV_TODO_NOTHING,
+       CDEV_TODO_ENABLE_CMF,
+       CDEV_TODO_REBIND,
+       CDEV_TODO_REGISTER,
+       CDEV_TODO_UNREG,
+       CDEV_TODO_UNREG_EVAL,
+};
+
 struct ccw_device_private {
        struct ccw_device *cdev;
        struct subchannel *sch;
        int state;              /* device state */
        atomic_t onoff;
-       unsigned long registered;
        struct ccw_dev_id dev_id;       /* device id */
        struct subchannel_id schid;     /* subchannel number */
-       u8 imask;               /* lpm mask for SNID/SID/SPGID */
-       int iretry;             /* retry counter SNID/SID/SPGID */
+       struct ccw_request req;         /* internal I/O request */
+       int iretry;
+       u8 pgid_valid_mask;             /* mask of valid PGIDs */
        struct {
                unsigned int fast:1;    /* post with "channel end" */
                unsigned int repall:1;  /* report every interrupt status */
                unsigned int pgroup:1;  /* do path grouping */
                unsigned int force:1;   /* allow forced online */
+               unsigned int mpath:1;   /* do multipathing */
        } __attribute__ ((packed)) options;
        struct {
-               unsigned int pgid_single:1; /* use single path for Set PGID */
                unsigned int esid:1;        /* Ext. SenseID supported by HW */
                unsigned int dosense:1;     /* delayed SENSE required */
                unsigned int doverify:1;    /* delayed path verification */
                unsigned int donotify:1;    /* call notify function */
                unsigned int recog_done:1;  /* dev. recog. complete */
                unsigned int fake_irb:1;    /* deliver faked irb */
-               unsigned int intretry:1;    /* retry internal operation */
                unsigned int resuming:1;    /* recognition while resume */
+               unsigned int pgroup:1;      /* pathgroup is set up */
+               unsigned int mpath:1;       /* multipathing is set up */
+               unsigned int initialized:1; /* set if initial reference held */
        } __attribute__((packed)) flags;
        unsigned long intparm;  /* user interruption parameter */
        struct qdio_irq *qdio_data;
@@ -115,7 +175,8 @@ struct ccw_device_private {
        struct senseid senseid; /* SenseID info */
        struct pgid pgid[8];    /* path group IDs per chpid*/
        struct ccw1 iccws[2];   /* ccws for SNID/SID/SPGID commands */
-       struct work_struct kick_work;
+       struct work_struct todo_work;
+       enum cdev_todo todo;
        wait_queue_head_t wait_q;
        struct timer_list timer;
        void *cmb;                      /* measurement information */
index 1294876..20836ef 100644 (file)
@@ -102,6 +102,7 @@ static atomic_t ap_poll_requests = ATOMIC_INIT(0);
 static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
 static struct task_struct *ap_poll_kthread = NULL;
 static DEFINE_MUTEX(ap_poll_thread_mutex);
+static DEFINE_SPINLOCK(ap_poll_timer_lock);
 static void *ap_interrupt_indicator;
 static struct hrtimer ap_poll_timer;
 /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
@@ -282,6 +283,7 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
  * @psmid: The program supplied message identifier
  * @msg: The message text
  * @length: The message length
+ * @special: Special Bit
  *
  * Returns AP queue status structure.
  * Condition code 1 on NQAP can't happen because the L bit is 1.
@@ -289,7 +291,8 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
  * because a segment boundary was reached. The NQAP is repeated.
  */
 static inline struct ap_queue_status
-__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
+__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length,
+         unsigned int special)
 {
        typedef struct { char _[length]; } msgblock;
        register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
@@ -299,6 +302,9 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
        register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
        register unsigned long reg5 asm ("5") = (unsigned int) psmid;
 
+       if (special == 1)
+               reg0 |= 0x400000UL;
+
        asm volatile (
                "0: .long 0xb2ad0042\n"         /* DQAP */
                "   brc   2,0b"
@@ -312,13 +318,15 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
 {
        struct ap_queue_status status;
 
-       status = __ap_send(qid, psmid, msg, length);
+       status = __ap_send(qid, psmid, msg, length, 0);
        switch (status.response_code) {
        case AP_RESPONSE_NORMAL:
                return 0;
        case AP_RESPONSE_Q_FULL:
        case AP_RESPONSE_RESET_IN_PROGRESS:
                return -EBUSY;
+       case AP_RESPONSE_REQ_FAC_NOT_INST:
+               return -EINVAL;
        default:        /* Device is gone. */
                return -ENODEV;
        }
@@ -1008,7 +1016,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev)
        }
 
        status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
-                          msg, sizeof(msg));
+                          msg, sizeof(msg), 0);
        if (status.response_code != AP_RESPONSE_NORMAL) {
                rc = -ENODEV;
                goto out_free;
@@ -1163,16 +1171,19 @@ ap_config_timeout(unsigned long ptr)
 static inline void ap_schedule_poll_timer(void)
 {
        ktime_t hr_time;
+
+       spin_lock_bh(&ap_poll_timer_lock);
        if (ap_using_interrupts() || ap_suspend_flag)
-               return;
+               goto out;
        if (hrtimer_is_queued(&ap_poll_timer))
-               return;
+               goto out;
        if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) {
                hr_time = ktime_set(0, poll_timeout);
                hrtimer_forward_now(&ap_poll_timer, hr_time);
                hrtimer_restart(&ap_poll_timer);
        }
-       return;
+out:
+       spin_unlock_bh(&ap_poll_timer_lock);
 }
 
 /**
@@ -1243,7 +1254,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
        /* Start the next request on the queue. */
        ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
        status = __ap_send(ap_dev->qid, ap_msg->psmid,
-                          ap_msg->message, ap_msg->length);
+                          ap_msg->message, ap_msg->length, ap_msg->special);
        switch (status.response_code) {
        case AP_RESPONSE_NORMAL:
                atomic_inc(&ap_poll_requests);
@@ -1261,6 +1272,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
                *flags |= 2;
                break;
        case AP_RESPONSE_MESSAGE_TOO_BIG:
+       case AP_RESPONSE_REQ_FAC_NOT_INST:
                return -EINVAL;
        default:
                return -ENODEV;
@@ -1302,7 +1314,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
        if (list_empty(&ap_dev->requestq) &&
            ap_dev->queue_count < ap_dev->queue_depth) {
                status = __ap_send(ap_dev->qid, ap_msg->psmid,
-                                  ap_msg->message, ap_msg->length);
+                                  ap_msg->message, ap_msg->length,
+                                  ap_msg->special);
                switch (status.response_code) {
                case AP_RESPONSE_NORMAL:
                        list_add_tail(&ap_msg->list, &ap_dev->pendingq);
@@ -1317,6 +1330,7 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
                        ap_dev->requestq_count++;
                        ap_dev->total_request_count++;
                        return -EBUSY;
+               case AP_RESPONSE_REQ_FAC_NOT_INST:
                case AP_RESPONSE_MESSAGE_TOO_BIG:
                        ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
                        return -EINVAL;
@@ -1658,6 +1672,7 @@ int __init ap_module_init(void)
         */
        if (MACHINE_IS_VM)
                poll_timeout = 1500000;
+       spin_lock_init(&ap_poll_timer_lock);
        hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
        ap_poll_timer.function = ap_poll_timeout;
 
index a353622..4785d07 100644 (file)
@@ -87,6 +87,7 @@ struct ap_queue_status {
 #define AP_RESPONSE_INDEX_TOO_BIG      0x11
 #define AP_RESPONSE_NO_FIRST_PART      0x13
 #define AP_RESPONSE_MESSAGE_TOO_BIG    0x15
+#define AP_RESPONSE_REQ_FAC_NOT_INST   0x16
 
 /*
  * Known device types
@@ -96,8 +97,8 @@ struct ap_queue_status {
 #define AP_DEVICE_TYPE_PCIXCC  5
 #define AP_DEVICE_TYPE_CEX2A   6
 #define AP_DEVICE_TYPE_CEX2C   7
-#define AP_DEVICE_TYPE_CEX2A2  8
-#define AP_DEVICE_TYPE_CEX2C2  9
+#define AP_DEVICE_TYPE_CEX3A   8
+#define AP_DEVICE_TYPE_CEX3C   9
 
 /*
  * AP reset flag states
@@ -161,12 +162,25 @@ struct ap_message {
        size_t length;                  /* Message length. */
 
        void *private;                  /* ap driver private pointer. */
+       unsigned int special:1;         /* Used for special commands. */
 };
 
 #define AP_DEVICE(dt)                                  \
        .dev_type=(dt),                                 \
        .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
 
+/**
+ * ap_init_message() - Initialize ap_message.
+ * Initialize a message before using. Otherwise this might result in
+ * unexpected behaviour.
+ */
+static inline void ap_init_message(struct ap_message *ap_msg)
+{
+       ap_msg->psmid = 0;
+       ap_msg->length = 0;
+       ap_msg->special = 0;
+}
+
 /*
  * Note: don't use ap_send/ap_recv after using ap_queue_message
  * for the first time. Otherwise the ap message queue will get
index 65b6a96..0d4d18b 100644 (file)
@@ -299,9 +299,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf,
  */
 static int zcrypt_open(struct inode *inode, struct file *filp)
 {
-       lock_kernel();
        atomic_inc(&zcrypt_open_count);
-       unlock_kernel();
        return 0;
 }
 
@@ -1009,6 +1007,10 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
                       zcrypt_count_type(ZCRYPT_CEX2C));
        len += sprintf(resp_buff + len, "CEX2A count: %d\n",
                       zcrypt_count_type(ZCRYPT_CEX2A));
+       len += sprintf(resp_buff + len, "CEX3C count: %d\n",
+                      zcrypt_count_type(ZCRYPT_CEX3C));
+       len += sprintf(resp_buff + len, "CEX3A count: %d\n",
+                      zcrypt_count_type(ZCRYPT_CEX3A));
        len += sprintf(resp_buff + len, "requestq count: %d\n",
                       zcrypt_requestq_count());
        len += sprintf(resp_buff + len, "pendingq count: %d\n",
@@ -1017,7 +1019,7 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
                       atomic_read(&zcrypt_open_count));
        zcrypt_status_mask(workarea);
        len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
-                       "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A",
+                       "4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A",
                        resp_buff+len, workarea, AP_DEVICES);
        zcrypt_qdepth_mask(workarea);
        len += sprinthx("Waiting work element counts",
@@ -1095,8 +1097,9 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer,
                 * '0' for no device, '1' for PCICA, '2' for PCICC,
                 * '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3,
                 * '5' for CEX2C and '6' for CEX2A'
+                * '7' for CEX3C and '8' for CEX3A
                 */
-               if (*ptr >= '0' && *ptr <= '6')
+               if (*ptr >= '0' && *ptr <= '8')
                        j++;
                else if (*ptr == 'd' || *ptr == 'D')
                        zcrypt_disable_card(j++);
index 1d1ec74..8e7ffbf 100644 (file)
@@ -71,6 +71,8 @@ struct ica_z90_status {
 #define ZCRYPT_PCIXCC_MCL3     4
 #define ZCRYPT_CEX2C           5
 #define ZCRYPT_CEX2A           6
+#define ZCRYPT_CEX3C           7
+#define ZCRYPT_CEX3A           8
 
 /**
  * Large random numbers are pulled in 4096 byte chunks from the crypto cards
index 326ea08..c6fb0aa 100644 (file)
 
 #define CEX2A_MIN_MOD_SIZE       1     /*    8 bits    */
 #define CEX2A_MAX_MOD_SIZE     256     /* 2048 bits    */
+#define CEX3A_MIN_MOD_SIZE     CEX2A_MIN_MOD_SIZE
+#define CEX3A_MAX_MOD_SIZE     CEX2A_MAX_MOD_SIZE
 
 #define CEX2A_SPEED_RATING     970
+#define CEX3A_SPEED_RATING     900 /* Fixme: Needs finetuning */
 
 #define CEX2A_MAX_MESSAGE_SIZE 0x390   /* sizeof(struct type50_crb2_msg)    */
 #define CEX2A_MAX_RESPONSE_SIZE 0x110  /* max outputdatalength + type80_hdr */
 
+#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE
+#define CEX3A_MAX_RESPONSE_SIZE        CEX2A_MAX_RESPONSE_SIZE
+
 #define CEX2A_CLEANUP_TIME     (15*HZ)
+#define CEX3A_CLEANUP_TIME     CEX2A_CLEANUP_TIME
 
 static struct ap_device_id zcrypt_cex2a_ids[] = {
        { AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
-       { AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) },
+       { AP_DEVICE(AP_DEVICE_TYPE_CEX3A) },
        { /* end of list */ },
 };
 
@@ -298,6 +305,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
        struct completion work;
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -335,6 +343,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
        struct completion work;
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -373,31 +382,45 @@ static struct zcrypt_ops zcrypt_cex2a_ops = {
  */
 static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
 {
-       struct zcrypt_device *zdev;
-       int rc;
-
-       zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
-       if (!zdev)
-               return -ENOMEM;
-       zdev->ap_dev = ap_dev;
-       zdev->ops = &zcrypt_cex2a_ops;
-       zdev->online = 1;
-       zdev->user_space_type = ZCRYPT_CEX2A;
-       zdev->type_string = "CEX2A";
-       zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
-       zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
-       zdev->short_crt = 1;
-       zdev->speed_rating = CEX2A_SPEED_RATING;
-       ap_dev->reply = &zdev->reply;
-       ap_dev->private = zdev;
-       rc = zcrypt_device_register(zdev);
-       if (rc)
-               goto out_free;
-       return 0;
-
-out_free:
-       ap_dev->private = NULL;
-       zcrypt_device_free(zdev);
+       struct zcrypt_device *zdev = NULL;
+       int rc = 0;
+
+       switch (ap_dev->device_type) {
+       case AP_DEVICE_TYPE_CEX2A:
+               zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
+               if (!zdev)
+                       return -ENOMEM;
+               zdev->user_space_type = ZCRYPT_CEX2A;
+               zdev->type_string = "CEX2A";
+               zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
+               zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
+               zdev->short_crt = 1;
+               zdev->speed_rating = CEX2A_SPEED_RATING;
+               break;
+       case AP_DEVICE_TYPE_CEX3A:
+               zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE);
+               if (!zdev)
+                       return -ENOMEM;
+               zdev->user_space_type = ZCRYPT_CEX3A;
+               zdev->type_string = "CEX3A";
+               zdev->min_mod_size = CEX3A_MIN_MOD_SIZE;
+               zdev->max_mod_size = CEX3A_MAX_MOD_SIZE;
+               zdev->short_crt = 1;
+               zdev->speed_rating = CEX3A_SPEED_RATING;
+               break;
+       }
+       if (zdev != NULL) {
+               zdev->ap_dev = ap_dev;
+               zdev->ops = &zcrypt_cex2a_ops;
+               zdev->online = 1;
+               ap_dev->reply = &zdev->reply;
+               ap_dev->private = zdev;
+               rc = zcrypt_device_register(zdev);
+       }
+       if (rc) {
+               ap_dev->private = NULL;
+               zcrypt_device_free(zdev);
+       }
        return rc;
 }
 
index 17ba81b..e78df36 100644 (file)
@@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
        struct completion work;
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
        struct completion work;
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
index f4b0c47..a23726a 100644 (file)
@@ -483,6 +483,7 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev,
        struct completion work;
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -521,6 +522,7 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev,
        struct completion work;
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
index 5677b40..79c1205 100644 (file)
 #define PCIXCC_MIN_MOD_SIZE     16     /*  128 bits    */
 #define PCIXCC_MIN_MOD_SIZE_OLD         64     /*  512 bits    */
 #define PCIXCC_MAX_MOD_SIZE    256     /* 2048 bits    */
+#define CEX3C_MIN_MOD_SIZE     PCIXCC_MIN_MOD_SIZE
+#define CEX3C_MAX_MOD_SIZE     PCIXCC_MAX_MOD_SIZE
 
-#define PCIXCC_MCL2_SPEED_RATING       7870    /* FIXME: needs finetuning */
+#define PCIXCC_MCL2_SPEED_RATING       7870
 #define PCIXCC_MCL3_SPEED_RATING       7870
-#define CEX2C_SPEED_RATING             8540
+#define CEX2C_SPEED_RATING             7000
+#define CEX3C_SPEED_RATING             6500    /* FIXME: needs finetuning */
 
 #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c  /* max size type6 v2 crt message */
 #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply     */
@@ -72,7 +75,7 @@ struct response_type {
 static struct ap_device_id zcrypt_pcixcc_ids[] = {
        { AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) },
        { AP_DEVICE(AP_DEVICE_TYPE_CEX2C) },
-       { AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) },
+       { AP_DEVICE(AP_DEVICE_TYPE_CEX3C) },
        { /* end of list */ },
 };
 
@@ -326,6 +329,11 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
        function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
        memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
 
+       if (memcmp(function_code, "US", 2) == 0)
+               ap_msg->special = 1;
+       else
+               ap_msg->special = 0;
+
        /* copy data block */
        if (xcRB->request_data_length &&
            copy_from_user(req_data, xcRB->request_data_address,
@@ -688,6 +696,7 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
        };
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -727,6 +736,7 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
        };
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -766,6 +776,7 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev,
        };
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -805,6 +816,7 @@ static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev,
        };
        int rc;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -972,6 +984,7 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev)
        } __attribute__((packed)) *reply;
        int rc, i;
 
+       ap_init_message(&ap_msg);
        ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
        if (!ap_msg.message)
                return -ENOMEM;
@@ -1016,14 +1029,15 @@ out_free:
 static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
 {
        struct zcrypt_device *zdev;
-       int rc;
+       int rc = 0;
 
        zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE);
        if (!zdev)
                return -ENOMEM;
        zdev->ap_dev = ap_dev;
        zdev->online = 1;
-       if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) {
+       switch (ap_dev->device_type) {
+       case AP_DEVICE_TYPE_PCIXCC:
                rc = zcrypt_pcixcc_mcl(ap_dev);
                if (rc < 0) {
                        zcrypt_device_free(zdev);
@@ -1041,13 +1055,25 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
                        zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
                        zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
                }
-       } else {
+               break;
+       case AP_DEVICE_TYPE_CEX2C:
                zdev->user_space_type = ZCRYPT_CEX2C;
                zdev->type_string = "CEX2C";
                zdev->speed_rating = CEX2C_SPEED_RATING;
                zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
                zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
+               break;
+       case AP_DEVICE_TYPE_CEX3C:
+               zdev->user_space_type = ZCRYPT_CEX3C;
+               zdev->type_string = "CEX3C";
+               zdev->speed_rating = CEX3C_SPEED_RATING;
+               zdev->min_mod_size = CEX3C_MIN_MOD_SIZE;
+               zdev->max_mod_size = CEX3C_MAX_MOD_SIZE;
+               break;
+       default:
+               goto out_free;
        }
+
        rc = zcrypt_pcixcc_rng_supported(ap_dev);
        if (rc < 0) {
                zcrypt_device_free(zdev);
index 52c0343..1ba5115 100644 (file)
@@ -528,7 +528,7 @@ extern unsigned char ULP_ENABLE[];
                (PDU_ENCAPSULATION(buffer) + 0x17)
 #define QETH_ULP_ENABLE_RESP_LINK_TYPE(buffer) \
                (PDU_ENCAPSULATION(buffer) + 0x2b)
-/* Layer 2 defintions */
+/* Layer 2 definitions */
 #define QETH_PROT_LAYER2 0x08
 #define QETH_PROT_TCPIP  0x03
 #define QETH_PROT_OSN2   0x0a
index 2889e5f..9d0c941 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/miscdevice.h>
 #include <linux/seq_file.h>
 #include "zfcp_ext.h"
+#include "zfcp_fc.h"
 
 #define ZFCP_BUS_ID_SIZE       20
 
@@ -80,48 +81,40 @@ int zfcp_reqlist_isempty(struct zfcp_adapter *adapter)
 
 static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
 {
-       struct ccw_device *ccwdev;
+       struct ccw_device *cdev;
        struct zfcp_adapter *adapter;
        struct zfcp_port *port;
        struct zfcp_unit *unit;
 
-       ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
-       if (!ccwdev)
+       cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
+       if (!cdev)
                return;
 
-       if (ccw_device_set_online(ccwdev))
-               goto out_ccwdev;
+       if (ccw_device_set_online(cdev))
+               goto out_ccw_device;
 
-       mutex_lock(&zfcp_data.config_mutex);
-       adapter = dev_get_drvdata(&ccwdev->dev);
+       adapter = zfcp_ccw_adapter_by_cdev(cdev);
        if (!adapter)
-               goto out_unlock;
-       zfcp_adapter_get(adapter);
+               goto out_ccw_device;
 
        port = zfcp_get_port_by_wwpn(adapter, wwpn);
        if (!port)
                goto out_port;
 
-       zfcp_port_get(port);
        unit = zfcp_unit_enqueue(port, lun);
        if (IS_ERR(unit))
                goto out_unit;
-       mutex_unlock(&zfcp_data.config_mutex);
 
        zfcp_erp_unit_reopen(unit, 0, "auidc_1", NULL);
        zfcp_erp_wait(adapter);
        flush_work(&unit->scsi_work);
 
-       mutex_lock(&zfcp_data.config_mutex);
-       zfcp_unit_put(unit);
 out_unit:
-       zfcp_port_put(port);
+       put_device(&port->sysfs_device);
 out_port:
-       zfcp_adapter_put(adapter);
-out_unlock:
-       mutex_unlock(&zfcp_data.config_mutex);
-out_ccwdev:
-       put_device(&ccwdev->dev);
+       zfcp_ccw_adapter_put(adapter);
+out_ccw_device:
+       put_device(&cdev->dev);
        return;
 }
 
@@ -167,7 +160,7 @@ static int __init zfcp_module_init(void)
        int retval = -ENOMEM;
 
        zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn",
-                                       sizeof(struct ct_iu_gpn_ft_req));
+                                       sizeof(struct zfcp_fc_gpn_ft_req));
        if (!zfcp_data.gpn_ft_cache)
                goto out;
 
@@ -182,12 +175,14 @@ static int __init zfcp_module_init(void)
                goto out_sr_cache;
 
        zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid",
-                                       sizeof(struct zfcp_gid_pn_data));
+                                       sizeof(struct zfcp_fc_gid_pn));
        if (!zfcp_data.gid_pn_cache)
                goto out_gid_cache;
 
-       mutex_init(&zfcp_data.config_mutex);
-       rwlock_init(&zfcp_data.config_lock);
+       zfcp_data.adisc_cache = zfcp_cache_hw_align("zfcp_adisc",
+                                       sizeof(struct zfcp_fc_els_adisc));
+       if (!zfcp_data.adisc_cache)
+               goto out_adisc_cache;
 
        zfcp_data.scsi_transport_template =
                fc_attach_transport(&zfcp_transport_functions);
@@ -200,7 +195,7 @@ static int __init zfcp_module_init(void)
                goto out_misc;
        }
 
-       retval = zfcp_ccw_register();
+       retval = ccw_driver_register(&zfcp_ccw_driver);
        if (retval) {
                pr_err("The zfcp device driver could not register with "
                       "the common I/O layer\n");
@@ -216,6 +211,8 @@ out_ccw_register:
 out_misc:
        fc_release_transport(zfcp_data.scsi_transport_template);
 out_transport:
+       kmem_cache_destroy(zfcp_data.adisc_cache);
+out_adisc_cache:
        kmem_cache_destroy(zfcp_data.gid_pn_cache);
 out_gid_cache:
        kmem_cache_destroy(zfcp_data.sr_buffer_cache);
@@ -229,6 +226,20 @@ out:
 
 module_init(zfcp_module_init);
 
+static void __exit zfcp_module_exit(void)
+{
+       ccw_driver_unregister(&zfcp_ccw_driver);
+       misc_deregister(&zfcp_cfdc_misc);
+       fc_release_transport(zfcp_data.scsi_transport_template);
+       kmem_cache_destroy(zfcp_data.adisc_cache);
+       kmem_cache_destroy(zfcp_data.gid_pn_cache);
+       kmem_cache_destroy(zfcp_data.sr_buffer_cache);
+       kmem_cache_destroy(zfcp_data.qtcb_cache);
+       kmem_cache_destroy(zfcp_data.gpn_ft_cache);
+}
+
+module_exit(zfcp_module_exit);
+
 /**
  * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN
  * @port: pointer to port to search for unit
@@ -238,12 +249,18 @@ module_init(zfcp_module_init);
  */
 struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
 {
+       unsigned long flags;
        struct zfcp_unit *unit;
 
-       list_for_each_entry(unit, &port->unit_list_head, list)
-               if ((unit->fcp_lun == fcp_lun) &&
-                   !(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_REMOVE))
-                   return unit;
+       read_lock_irqsave(&port->unit_list_lock, flags);
+       list_for_each_entry(unit, &port->unit_list, list)
+               if (unit->fcp_lun == fcp_lun) {
+                       if (!get_device(&unit->sysfs_device))
+                               unit = NULL;
+                       read_unlock_irqrestore(&port->unit_list_lock, flags);
+                       return unit;
+               }
+       read_unlock_irqrestore(&port->unit_list_lock, flags);
        return NULL;
 }
 
@@ -257,18 +274,35 @@ struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *port, u64 fcp_lun)
 struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter,
                                        u64 wwpn)
 {
+       unsigned long flags;
        struct zfcp_port *port;
 
-       list_for_each_entry(port, &adapter->port_list_head, list)
-               if ((port->wwpn == wwpn) &&
-                   !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE))
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list)
+               if (port->wwpn == wwpn) {
+                       if (!get_device(&port->sysfs_device))
+                               port = NULL;
+                       read_unlock_irqrestore(&adapter->port_list_lock, flags);
                        return port;
+               }
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
        return NULL;
 }
 
-static void zfcp_sysfs_unit_release(struct device *dev)
+/**
+ * zfcp_unit_release - dequeue unit
+ * @dev: pointer to device
+ *
+ * waits until all work is done on unit and removes it then from the unit->list
+ * of the associated port.
+ */
+static void zfcp_unit_release(struct device *dev)
 {
-       kfree(container_of(dev, struct zfcp_unit, sysfs_device));
+       struct zfcp_unit *unit = container_of(dev, struct zfcp_unit,
+                                             sysfs_device);
+
+       put_device(&unit->port->sysfs_device);
+       kfree(unit);
 }
 
 /**
@@ -276,43 +310,40 @@ static void zfcp_sysfs_unit_release(struct device *dev)
  * @port: pointer to port where unit is added
  * @fcp_lun: FCP LUN of unit to be enqueued
  * Returns: pointer to enqueued unit on success, ERR_PTR on error
- * Locks: config_mutex must be held to serialize changes to the unit list
  *
  * Sets up some unit internal structures and creates sysfs entry.
  */
 struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
 {
        struct zfcp_unit *unit;
+       int retval = -ENOMEM;
 
-       read_lock_irq(&zfcp_data.config_lock);
-       if (zfcp_get_unit_by_lun(port, fcp_lun)) {
-               read_unlock_irq(&zfcp_data.config_lock);
-               return ERR_PTR(-EINVAL);
+       get_device(&port->sysfs_device);
+
+       unit = zfcp_get_unit_by_lun(port, fcp_lun);
+       if (unit) {
+               put_device(&unit->sysfs_device);
+               retval = -EEXIST;
+               goto err_out;
        }
-       read_unlock_irq(&zfcp_data.config_lock);
 
        unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
        if (!unit)
-               return ERR_PTR(-ENOMEM);
-
-       atomic_set(&unit->refcount, 0);
-       init_waitqueue_head(&unit->remove_wq);
-       INIT_WORK(&unit->scsi_work, zfcp_scsi_scan);
+               goto err_out;
 
        unit->port = port;
        unit->fcp_lun = fcp_lun;
+       unit->sysfs_device.parent = &port->sysfs_device;
+       unit->sysfs_device.release = zfcp_unit_release;
 
        if (dev_set_name(&unit->sysfs_device, "0x%016llx",
                         (unsigned long long) fcp_lun)) {
                kfree(unit);
-               return ERR_PTR(-ENOMEM);
+               goto err_out;
        }
-       unit->sysfs_device.parent = &port->sysfs_device;
-       unit->sysfs_device.release = zfcp_sysfs_unit_release;
-       dev_set_drvdata(&unit->sysfs_device, unit);
+       retval = -EINVAL;
 
-       /* mark unit unusable as long as sysfs registration is not complete */
-       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+       INIT_WORK(&unit->scsi_work, zfcp_scsi_scan);
 
        spin_lock_init(&unit->latencies.lock);
        unit->latencies.write.channel.min = 0xFFFFFFFF;
@@ -324,50 +355,30 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
 
        if (device_register(&unit->sysfs_device)) {
                put_device(&unit->sysfs_device);
-               return ERR_PTR(-EINVAL);
+               goto err_out;
        }
 
        if (sysfs_create_group(&unit->sysfs_device.kobj,
-                              &zfcp_sysfs_unit_attrs)) {
-               device_unregister(&unit->sysfs_device);
-               return ERR_PTR(-EINVAL);
-       }
+                              &zfcp_sysfs_unit_attrs))
+               goto err_out_put;
 
-       zfcp_unit_get(unit);
+       write_lock_irq(&port->unit_list_lock);
+       list_add_tail(&unit->list, &port->unit_list);
+       write_unlock_irq(&port->unit_list_lock);
 
-       write_lock_irq(&zfcp_data.config_lock);
-       list_add_tail(&unit->list, &port->unit_list_head);
-       atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
        atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status);
 
-       write_unlock_irq(&zfcp_data.config_lock);
-
-       zfcp_port_get(port);
-
        return unit;
-}
 
-/**
- * zfcp_unit_dequeue - dequeue unit
- * @unit: pointer to zfcp_unit
- *
- * waits until all work is done on unit and removes it then from the unit->list
- * of the associated port.
- */
-void zfcp_unit_dequeue(struct zfcp_unit *unit)
-{
-       wait_event(unit->remove_wq, atomic_read(&unit->refcount) == 0);
-       write_lock_irq(&zfcp_data.config_lock);
-       list_del(&unit->list);
-       write_unlock_irq(&zfcp_data.config_lock);
-       zfcp_port_put(unit->port);
-       sysfs_remove_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs);
+err_out_put:
        device_unregister(&unit->sysfs_device);
+err_out:
+       put_device(&port->sysfs_device);
+       return ERR_PTR(retval);
 }
 
 static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
 {
-       /* must only be called with zfcp_data.config_mutex taken */
        adapter->pool.erp_req =
                mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req));
        if (!adapter->pool.erp_req)
@@ -405,9 +416,9 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
        if (!adapter->pool.status_read_data)
                return -ENOMEM;
 
-       adapter->pool.gid_pn_data =
+       adapter->pool.gid_pn =
                mempool_create_slab_pool(1, zfcp_data.gid_pn_cache);
-       if (!adapter->pool.gid_pn_data)
+       if (!adapter->pool.gid_pn)
                return -ENOMEM;
 
        return 0;
@@ -415,7 +426,6 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
 
 static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
 {
-       /* zfcp_data.config_mutex must be held */
        if (adapter->pool.erp_req)
                mempool_destroy(adapter->pool.erp_req);
        if (adapter->pool.scsi_req)
@@ -428,8 +438,8 @@ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
                mempool_destroy(adapter->pool.status_read_req);
        if (adapter->pool.status_read_data)
                mempool_destroy(adapter->pool.status_read_data);
-       if (adapter->pool.gid_pn_data)
-               mempool_destroy(adapter->pool.gid_pn_data);
+       if (adapter->pool.gid_pn)
+               mempool_destroy(adapter->pool.gid_pn);
 }
 
 /**
@@ -497,53 +507,56 @@ static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter)
  * zfcp_adapter_enqueue - enqueue a new adapter to the list
  * @ccw_device: pointer to the struct cc_device
  *
- * Returns:    0             if a new adapter was successfully enqueued
- *             -ENOMEM       if alloc failed
+ * Returns:    struct zfcp_adapter*
  * Enqueues an adapter at the end of the adapter list in the driver data.
  * All adapter internal structures are set up.
  * Proc-fs entries are also created.
- * locks: config_mutex must be held to serialize changes to the adapter list
  */
-int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
+struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device)
 {
        struct zfcp_adapter *adapter;
 
-       /*
-        * Note: It is safe to release the list_lock, as any list changes
-        * are protected by the config_mutex, which must be held to get here
-        */
+       if (!get_device(&ccw_device->dev))
+               return ERR_PTR(-ENODEV);
 
        adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL);
-       if (!adapter)
-               return -ENOMEM;
+       if (!adapter) {
+               put_device(&ccw_device->dev);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       kref_init(&adapter->ref);
 
        ccw_device->handler = NULL;
        adapter->ccw_device = ccw_device;
-       atomic_set(&adapter->refcount, 0);
+
+       INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
+       INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
 
        if (zfcp_qdio_setup(adapter))
-               goto qdio_failed;
+               goto failed;
 
        if (zfcp_allocate_low_mem_buffers(adapter))
-               goto low_mem_buffers_failed;
+               goto failed;
 
        if (zfcp_reqlist_alloc(adapter))
-               goto low_mem_buffers_failed;
+               goto failed;
 
        if (zfcp_dbf_adapter_register(adapter))
-               goto debug_register_failed;
+               goto failed;
 
        if (zfcp_setup_adapter_work_queue(adapter))
-               goto work_queue_failed;
+               goto failed;
 
        if (zfcp_fc_gs_setup(adapter))
-               goto generic_services_failed;
+               goto failed;
+
+       rwlock_init(&adapter->port_list_lock);
+       INIT_LIST_HEAD(&adapter->port_list);
 
-       init_waitqueue_head(&adapter->remove_wq);
        init_waitqueue_head(&adapter->erp_ready_wq);
        init_waitqueue_head(&adapter->erp_done_wqh);
 
-       INIT_LIST_HEAD(&adapter->port_list_head);
        INIT_LIST_HEAD(&adapter->erp_ready_head);
        INIT_LIST_HEAD(&adapter->erp_running_head);
 
@@ -553,83 +566,85 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
        rwlock_init(&adapter->abort_lock);
 
        if (zfcp_erp_thread_setup(adapter))
-               goto erp_thread_failed;
-
-       INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
-       INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later);
+               goto failed;
 
        adapter->service_level.seq_print = zfcp_print_sl;
 
-       /* mark adapter unusable as long as sysfs registration is not complete */
-       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
-
        dev_set_drvdata(&ccw_device->dev, adapter);
 
        if (sysfs_create_group(&ccw_device->dev.kobj,
                               &zfcp_sysfs_adapter_attrs))
-               goto sysfs_failed;
-
-       atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
+               goto failed;
 
        if (!zfcp_adapter_scsi_register(adapter))
-               return 0;
+               return adapter;
 
-sysfs_failed:
-       zfcp_erp_thread_kill(adapter);
-erp_thread_failed:
-       zfcp_fc_gs_destroy(adapter);
-generic_services_failed:
+failed:
+       zfcp_adapter_unregister(adapter);
+       return ERR_PTR(-ENOMEM);
+}
+
+void zfcp_adapter_unregister(struct zfcp_adapter *adapter)
+{
+       struct ccw_device *cdev = adapter->ccw_device;
+
+       cancel_work_sync(&adapter->scan_work);
+       cancel_work_sync(&adapter->stat_work);
        zfcp_destroy_adapter_work_queue(adapter);
-work_queue_failed:
+
+       zfcp_fc_wka_ports_force_offline(adapter->gs);
+       zfcp_adapter_scsi_unregister(adapter);
+       sysfs_remove_group(&cdev->dev.kobj, &zfcp_sysfs_adapter_attrs);
+
+       zfcp_erp_thread_kill(adapter);
        zfcp_dbf_adapter_unregister(adapter->dbf);
-debug_register_failed:
-       dev_set_drvdata(&ccw_device->dev, NULL);
-       kfree(adapter->req_list);
-low_mem_buffers_failed:
-       zfcp_free_low_mem_buffers(adapter);
-qdio_failed:
        zfcp_qdio_destroy(adapter->qdio);
-       kfree(adapter);
-       return -ENOMEM;
+
+       zfcp_ccw_adapter_put(adapter); /* final put to release */
 }
 
 /**
- * zfcp_adapter_dequeue - remove the adapter from the resource list
- * @adapter: pointer to struct zfcp_adapter which should be removed
+ * zfcp_adapter_release - remove the adapter from the resource list
+ * @ref: pointer to struct kref
  * locks:      adapter list write lock is assumed to be held by caller
  */
-void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
+void zfcp_adapter_release(struct kref *ref)
 {
-       int retval = 0;
-       unsigned long flags;
+       struct zfcp_adapter *adapter = container_of(ref, struct zfcp_adapter,
+                                                   ref);
+       struct ccw_device *cdev = adapter->ccw_device;
 
-       cancel_work_sync(&adapter->stat_work);
-       zfcp_fc_wka_ports_force_offline(adapter->gs);
-       sysfs_remove_group(&adapter->ccw_device->dev.kobj,
-                          &zfcp_sysfs_adapter_attrs);
        dev_set_drvdata(&adapter->ccw_device->dev, NULL);
-       /* sanity check: no pending FSF requests */
-       spin_lock_irqsave(&adapter->req_list_lock, flags);
-       retval = zfcp_reqlist_isempty(adapter);
-       spin_unlock_irqrestore(&adapter->req_list_lock, flags);
-       if (!retval)
-               return;
-
        zfcp_fc_gs_destroy(adapter);
-       zfcp_erp_thread_kill(adapter);
-       zfcp_destroy_adapter_work_queue(adapter);
-       zfcp_dbf_adapter_unregister(adapter->dbf);
        zfcp_free_low_mem_buffers(adapter);
-       zfcp_qdio_destroy(adapter->qdio);
        kfree(adapter->req_list);
        kfree(adapter->fc_stats);
        kfree(adapter->stats_reset_data);
        kfree(adapter);
+       put_device(&cdev->dev);
 }
 
-static void zfcp_sysfs_port_release(struct device *dev)
+/**
+ * zfcp_device_unregister - remove port, unit from system
+ * @dev: reference to device which is to be removed
+ * @grp: related reference to attribute group
+ *
+ * Helper function to unregister port, unit from system
+ */
+void zfcp_device_unregister(struct device *dev,
+                           const struct attribute_group *grp)
 {
-       kfree(container_of(dev, struct zfcp_port, sysfs_device));
+       sysfs_remove_group(&dev->kobj, grp);
+       device_unregister(dev);
+}
+
+static void zfcp_port_release(struct device *dev)
+{
+       struct zfcp_port *port = container_of(dev, struct zfcp_port,
+                                             sysfs_device);
+
+       zfcp_ccw_adapter_put(port->adapter);
+       kfree(port);
 }
 
 /**
@@ -639,7 +654,6 @@ static void zfcp_sysfs_port_release(struct device *dev)
  * @status: initial status for the port
  * @d_id: destination id of the remote port to be enqueued
  * Returns: pointer to enqueued port on success, ERR_PTR on error
- * Locks: config_mutex must be held to serialize changes to the port list
  *
  * All port internal structures are set up and the sysfs entry is generated.
  * d_id is used to enqueue ports with a well known address like the Directory
@@ -649,20 +663,24 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
                                     u32 status, u32 d_id)
 {
        struct zfcp_port *port;
+       int retval = -ENOMEM;
 
-       read_lock_irq(&zfcp_data.config_lock);
-       if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
-               read_unlock_irq(&zfcp_data.config_lock);
-               return ERR_PTR(-EINVAL);
+       kref_get(&adapter->ref);
+
+       port = zfcp_get_port_by_wwpn(adapter, wwpn);
+       if (port) {
+               put_device(&port->sysfs_device);
+               retval = -EEXIST;
+               goto err_out;
        }
-       read_unlock_irq(&zfcp_data.config_lock);
 
        port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
        if (!port)
-               return ERR_PTR(-ENOMEM);
+               goto err_out;
+
+       rwlock_init(&port->unit_list_lock);
+       INIT_LIST_HEAD(&port->unit_list);
 
-       init_waitqueue_head(&port->remove_wq);
-       INIT_LIST_HEAD(&port->unit_list_head);
        INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup);
        INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
        INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);
@@ -671,58 +689,38 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
        port->d_id = d_id;
        port->wwpn = wwpn;
        port->rport_task = RPORT_NONE;
-
-       /* mark port unusable as long as sysfs registration is not complete */
-       atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
-       atomic_set(&port->refcount, 0);
+       port->sysfs_device.parent = &adapter->ccw_device->dev;
+       port->sysfs_device.release = zfcp_port_release;
 
        if (dev_set_name(&port->sysfs_device, "0x%016llx",
                         (unsigned long long)wwpn)) {
                kfree(port);
-               return ERR_PTR(-ENOMEM);
+               goto err_out;
        }
-       port->sysfs_device.parent = &adapter->ccw_device->dev;
-       port->sysfs_device.release = zfcp_sysfs_port_release;
-       dev_set_drvdata(&port->sysfs_device, port);
+       retval = -EINVAL;
 
        if (device_register(&port->sysfs_device)) {
                put_device(&port->sysfs_device);
-               return ERR_PTR(-EINVAL);
+               goto err_out;
        }
 
        if (sysfs_create_group(&port->sysfs_device.kobj,
-                              &zfcp_sysfs_port_attrs)) {
-               device_unregister(&port->sysfs_device);
-               return ERR_PTR(-EINVAL);
-       }
+                              &zfcp_sysfs_port_attrs))
+               goto err_out_put;
 
-       zfcp_port_get(port);
+       write_lock_irq(&adapter->port_list_lock);
+       list_add_tail(&port->list, &adapter->port_list);
+       write_unlock_irq(&adapter->port_list_lock);
 
-       write_lock_irq(&zfcp_data.config_lock);
-       list_add_tail(&port->list, &adapter->port_list_head);
-       atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
-       atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status);
+       atomic_set_mask(status | ZFCP_STATUS_COMMON_RUNNING, &port->status);
 
-       write_unlock_irq(&zfcp_data.config_lock);
-
-       zfcp_adapter_get(adapter);
        return port;
-}
 
-/**
- * zfcp_port_dequeue - dequeues a port from the port list of the adapter
- * @port: pointer to struct zfcp_port which should be removed
- */
-void zfcp_port_dequeue(struct zfcp_port *port)
-{
-       write_lock_irq(&zfcp_data.config_lock);
-       list_del(&port->list);
-       write_unlock_irq(&zfcp_data.config_lock);
-       wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
-       cancel_work_sync(&port->rport_work); /* usually not necessary */
-       zfcp_adapter_put(port->adapter);
-       sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs);
+err_out_put:
        device_unregister(&port->sysfs_device);
+err_out:
+       zfcp_ccw_adapter_put(adapter);
+       return ERR_PTR(retval);
 }
 
 /**
index e083394..c22cb72 100644 (file)
 
 #define ZFCP_MODEL_PRIV 0x4
 
-static int zfcp_ccw_suspend(struct ccw_device *cdev)
+static DEFINE_SPINLOCK(zfcp_ccw_adapter_ref_lock);
 
+struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *cdev)
 {
-       struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
-
-       if (!adapter)
-               return 0;
-
-       mutex_lock(&zfcp_data.config_mutex);
+       struct zfcp_adapter *adapter;
+       unsigned long flags;
 
-       zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL);
-       zfcp_erp_wait(adapter);
+       spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags);
+       adapter = dev_get_drvdata(&cdev->dev);
+       if (adapter)
+               kref_get(&adapter->ref);
+       spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags);
+       return adapter;
+}
 
-       mutex_unlock(&zfcp_data.config_mutex);
+void zfcp_ccw_adapter_put(struct zfcp_adapter *adapter)
+{
+       unsigned long flags;
 
-       return 0;
+       spin_lock_irqsave(&zfcp_ccw_adapter_ref_lock, flags);
+       kref_put(&adapter->ref, zfcp_adapter_release);
+       spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags);
 }
 
 static int zfcp_ccw_activate(struct ccw_device *cdev)
 
 {
-       struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
 
        if (!adapter)
                return 0;
@@ -46,6 +52,8 @@ static int zfcp_ccw_activate(struct ccw_device *cdev)
        zfcp_erp_wait(adapter);
        flush_work(&adapter->scan_work);
 
+       zfcp_ccw_adapter_put(adapter);
+
        return 0;
 }
 
@@ -67,28 +75,28 @@ int zfcp_ccw_priv_sch(struct zfcp_adapter *adapter)
 
 /**
  * zfcp_ccw_probe - probe function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
  *
  * This function gets called by the common i/o layer for each FCP
  * device found on the current system. This is only a stub to make cio
  * work: To only allocate adapter resources for devices actually used,
  * the allocation is deferred to the first call to ccw_set_online.
  */
-static int zfcp_ccw_probe(struct ccw_device *ccw_device)
+static int zfcp_ccw_probe(struct ccw_device *cdev)
 {
        return 0;
 }
 
 /**
  * zfcp_ccw_remove - remove function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
  *
  * This function gets called by the common i/o layer and removes an adapter
  * from the system. Task of this function is to get rid of all units and
  * ports that belong to this adapter. And in addition all resources of this
  * adapter will be freed too.
  */
-static void zfcp_ccw_remove(struct ccw_device *ccw_device)
+static void zfcp_ccw_remove(struct ccw_device *cdev)
 {
        struct zfcp_adapter *adapter;
        struct zfcp_port *port, *p;
@@ -96,49 +104,37 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device)
        LIST_HEAD(unit_remove_lh);
        LIST_HEAD(port_remove_lh);
 
-       ccw_device_set_offline(ccw_device);
+       ccw_device_set_offline(cdev);
 
-       mutex_lock(&zfcp_data.config_mutex);
-       adapter = dev_get_drvdata(&ccw_device->dev);
+       adapter = zfcp_ccw_adapter_by_cdev(cdev);
        if (!adapter)
-               goto out;
-       mutex_unlock(&zfcp_data.config_mutex);
+               return;
 
-       cancel_work_sync(&adapter->scan_work);
-
-       mutex_lock(&zfcp_data.config_mutex);
-
-       /* this also removes the scsi devices, so call it first */
-       zfcp_adapter_scsi_unregister(adapter);
-
-       write_lock_irq(&zfcp_data.config_lock);
-       list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
-               list_for_each_entry_safe(unit, u, &port->unit_list_head, list) {
+       write_lock_irq(&adapter->port_list_lock);
+       list_for_each_entry_safe(port, p, &adapter->port_list, list) {
+               write_lock(&port->unit_list_lock);
+               list_for_each_entry_safe(unit, u, &port->unit_list, list)
                        list_move(&unit->list, &unit_remove_lh);
-                       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
-                                       &unit->status);
-               }
+               write_unlock(&port->unit_list_lock);
                list_move(&port->list, &port_remove_lh);
-               atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
        }
-       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
-       write_unlock_irq(&zfcp_data.config_lock);
+       write_unlock_irq(&adapter->port_list_lock);
+       zfcp_ccw_adapter_put(adapter); /* put from zfcp_ccw_adapter_by_cdev */
 
-       list_for_each_entry_safe(port, p, &port_remove_lh, list) {
-               list_for_each_entry_safe(unit, u, &unit_remove_lh, list)
-                       zfcp_unit_dequeue(unit);
-               zfcp_port_dequeue(port);
-       }
-       wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
-       zfcp_adapter_dequeue(adapter);
+       list_for_each_entry_safe(unit, u, &unit_remove_lh, list)
+               zfcp_device_unregister(&unit->sysfs_device,
+                                      &zfcp_sysfs_unit_attrs);
 
-out:
-       mutex_unlock(&zfcp_data.config_mutex);
+       list_for_each_entry_safe(port, p, &port_remove_lh, list)
+               zfcp_device_unregister(&port->sysfs_device,
+                                      &zfcp_sysfs_port_attrs);
+
+       zfcp_adapter_unregister(adapter);
 }
 
 /**
  * zfcp_ccw_set_online - set_online function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
  *
  * This function gets called by the common i/o layer and sets an
  * adapter into state online.  The first call will allocate all
@@ -149,23 +145,20 @@ out:
  * the SCSI stack, that the QDIO queues will be set up and that the
  * adapter will be opened.
  */
-static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_online(struct ccw_device *cdev)
 {
-       struct zfcp_adapter *adapter;
-       int ret = 0;
-
-       mutex_lock(&zfcp_data.config_mutex);
-       adapter = dev_get_drvdata(&ccw_device->dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
 
        if (!adapter) {
-               ret = zfcp_adapter_enqueue(ccw_device);
-               if (ret) {
-                       dev_err(&ccw_device->dev,
+               adapter = zfcp_adapter_enqueue(cdev);
+
+               if (IS_ERR(adapter)) {
+                       dev_err(&cdev->dev,
                                "Setting up data structures for the "
                                "FCP adapter failed\n");
-                       goto out;
+                       return PTR_ERR(adapter);
                }
-               adapter = dev_get_drvdata(&ccw_device->dev);
+               kref_get(&adapter->ref);
        }
 
        /* initialize request counter */
@@ -177,58 +170,61 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
        zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
                                "ccsonl2", NULL);
        zfcp_erp_wait(adapter);
-out:
-       mutex_unlock(&zfcp_data.config_mutex);
-       if (!ret)
-               flush_work(&adapter->scan_work);
-       return ret;
+
+       flush_work(&adapter->scan_work);
+
+       zfcp_ccw_adapter_put(adapter);
+       return 0;
 }
 
 /**
  * zfcp_ccw_set_offline - set_offline function of zfcp driver
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
  *
  * This function gets called by the common i/o layer and sets an adapter
  * into state offline.
  */
-static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
+static int zfcp_ccw_set_offline(struct ccw_device *cdev)
 {
-       struct zfcp_adapter *adapter;
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+
+       if (!adapter)
+               return 0;
 
-       mutex_lock(&zfcp_data.config_mutex);
-       adapter = dev_get_drvdata(&ccw_device->dev);
        zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL);
        zfcp_erp_wait(adapter);
-       mutex_unlock(&zfcp_data.config_mutex);
+
+       zfcp_ccw_adapter_put(adapter);
        return 0;
 }
 
 /**
  * zfcp_ccw_notify - ccw notify function
- * @ccw_device: pointer to belonging ccw device
+ * @cdev: pointer to belonging ccw device
  * @event: indicates if adapter was detached or attached
  *
  * This function gets called by the common i/o layer if an adapter has gone
  * or reappeared.
  */
-static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
+static int zfcp_ccw_notify(struct ccw_device *cdev, int event)
 {
-       struct zfcp_adapter *adapter = dev_get_drvdata(&ccw_device->dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+
+       if (!adapter)
+               return 1;
 
        switch (event) {
        case CIO_GONE:
-               dev_warn(&adapter->ccw_device->dev,
-                        "The FCP device has been detached\n");
+               dev_warn(&cdev->dev, "The FCP device has been detached\n");
                zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1", NULL);
                break;
        case CIO_NO_PATH:
-               dev_warn(&adapter->ccw_device->dev,
+               dev_warn(&cdev->dev,
                         "The CHPID for the FCP device is offline\n");
                zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2", NULL);
                break;
        case CIO_OPER:
-               dev_info(&adapter->ccw_device->dev,
-                        "The FCP device is operational again\n");
+               dev_info(&cdev->dev, "The FCP device is operational again\n");
                zfcp_erp_modify_adapter_status(adapter, "ccnoti3", NULL,
                                               ZFCP_STATUS_COMMON_RUNNING,
                                               ZFCP_SET);
@@ -236,11 +232,13 @@ 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 FCP device "
-                        "did not respond within the specified time\n");
+               dev_warn(&cdev->dev, "The FCP device did not respond within "
+                                    "the specified time\n");
                zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti5", NULL);
                break;
        }
+
+       zfcp_ccw_adapter_put(adapter);
        return 1;
 }
 
@@ -250,18 +248,16 @@ static int zfcp_ccw_notify(struct ccw_device *ccw_device, int event)
  */
 static void zfcp_ccw_shutdown(struct ccw_device *cdev)
 {
-       struct zfcp_adapter *adapter;
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
 
-       mutex_lock(&zfcp_data.config_mutex);
-       adapter = dev_get_drvdata(&cdev->dev);
        if (!adapter)
-               goto out;
+               return;
 
        zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL);
        zfcp_erp_wait(adapter);
        zfcp_erp_thread_kill(adapter);
-out:
-       mutex_unlock(&zfcp_data.config_mutex);
+
+       zfcp_ccw_adapter_put(adapter);
 }
 
 struct ccw_driver zfcp_ccw_driver = {
@@ -274,18 +270,7 @@ struct ccw_driver zfcp_ccw_driver = {
        .set_offline = zfcp_ccw_set_offline,
        .notify      = zfcp_ccw_notify,
        .shutdown    = zfcp_ccw_shutdown,
-       .freeze      = zfcp_ccw_suspend,
+       .freeze      = zfcp_ccw_set_offline,
        .thaw        = zfcp_ccw_activate,
        .restore     = zfcp_ccw_activate,
 };
-
-/**
- * zfcp_ccw_register - ccw register function
- *
- * Registers the driver at the common i/o layer. This function will be called
- * at module load time/system start.
- */
-int __init zfcp_ccw_register(void)
-{
-       return ccw_driver_register(&zfcp_ccw_driver);
-}
index ef681df..f932400 100644 (file)
@@ -86,22 +86,17 @@ static int zfcp_cfdc_copy_to_user(void __user  *user_buffer,
 static struct zfcp_adapter *zfcp_cfdc_get_adapter(u32 devno)
 {
        char busid[9];
-       struct ccw_device *ccwdev;
-       struct zfcp_adapter *adapter = NULL;
+       struct ccw_device *cdev;
+       struct zfcp_adapter *adapter;
 
        snprintf(busid, sizeof(busid), "0.0.%04x", devno);
-       ccwdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
-       if (!ccwdev)
-               goto out;
-
-       adapter = dev_get_drvdata(&ccwdev->dev);
-       if (!adapter)
-               goto out_put;
-
-       zfcp_adapter_get(adapter);
-out_put:
-       put_device(&ccwdev->dev);
-out:
+       cdev = get_ccwdev_by_busid(&zfcp_ccw_driver, busid);
+       if (!cdev)
+               return NULL;
+
+       adapter = zfcp_ccw_adapter_by_cdev(cdev);
+
+       put_device(&cdev->dev);
        return adapter;
 }
 
@@ -212,7 +207,6 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
                retval = -ENXIO;
                goto free_buffer;
        }
-       zfcp_adapter_get(adapter);
 
        retval = zfcp_cfdc_sg_setup(data->command, fsf_cfdc->sg,
                                    data_user->control_file);
@@ -245,7 +239,7 @@ static long zfcp_cfdc_dev_ioctl(struct file *file, unsigned int command,
  free_sg:
        zfcp_sg_free_table(fsf_cfdc->sg, ZFCP_CFDC_PAGES);
  adapter_put:
-       zfcp_adapter_put(adapter);
+       zfcp_ccw_adapter_put(adapter);
  free_buffer:
        kfree(data);
  no_mem_sense:
index 215b707..8445095 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/debug.h>
 #include "zfcp_dbf.h"
 #include "zfcp_ext.h"
+#include "zfcp_fc.h"
 
 static u32 dbfsize = 4;
 
@@ -177,8 +178,7 @@ void _zfcp_dbf_hba_fsf_response(const char *tag2, int level,
 
        case FSF_QTCB_SEND_ELS:
                send_els = (struct zfcp_send_els *)fsf_req->data;
-               response->u.els.d_id = qtcb->bottom.support.d_id;
-               response->u.els.ls_code = send_els->ls_code >> 24;
+               response->u.els.d_id = ntoh24(qtcb->bottom.support.d_id);
                break;
 
        case FSF_QTCB_ABORT_FCP_CMND:
@@ -348,7 +348,6 @@ static void zfcp_dbf_hba_view_response(char **p,
 
        case FSF_QTCB_SEND_ELS:
                zfcp_dbf_out(p, "d_id", "0x%06x", r->u.els.d_id);
-               zfcp_dbf_out(p, "ls_code", "0x%02x", r->u.els.ls_code);
                break;
 
        case FSF_QTCB_ABORT_FCP_CMND:
@@ -677,14 +676,14 @@ void zfcp_dbf_rec_action(char *id2, struct zfcp_erp_action *erp_action)
 /**
  * zfcp_dbf_san_ct_request - trace event for issued CT request
  * @fsf_req: request containing issued CT data
+ * @d_id: destination id where ct request is sent to
  */
-void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req, u32 d_id)
 {
-       struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
-       struct zfcp_wka_port *wka_port = ct->wka_port;
-       struct zfcp_adapter *adapter = wka_port->adapter;
+       struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
        struct zfcp_dbf *dbf = adapter->dbf;
-       struct ct_hdr *hdr = sg_virt(ct->req);
+       struct fc_ct_hdr *hdr = sg_virt(ct->req);
        struct zfcp_dbf_san_record *r = &dbf->san_buf;
        struct zfcp_dbf_san_record_ct_request *oct = &r->u.ct_req;
        int level = 3;
@@ -695,19 +694,18 @@ void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
        strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE);
        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;
-       oct->cmd_req_code = hdr->cmd_rsp_code;
-       oct->revision = hdr->revision;
-       oct->gs_type = hdr->gs_type;
-       oct->gs_subtype = hdr->gs_subtype;
-       oct->options = hdr->options;
-       oct->max_res_size = hdr->max_res_size;
-       oct->len = min((int)ct->req->length - (int)sizeof(struct ct_hdr),
+       oct->d_id = d_id;
+       oct->cmd_req_code = hdr->ct_cmd;
+       oct->revision = hdr->ct_rev;
+       oct->gs_type = hdr->ct_fs_type;
+       oct->gs_subtype = hdr->ct_fs_subtype;
+       oct->options = hdr->ct_options;
+       oct->max_res_size = hdr->ct_mr_size;
+       oct->len = min((int)ct->req->length - (int)sizeof(struct fc_ct_hdr),
                       ZFCP_DBF_SAN_MAX_PAYLOAD);
        debug_event(dbf->san, level, r, sizeof(*r));
        zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level,
-                        (void *)hdr + sizeof(struct ct_hdr), oct->len);
+                        (void *)hdr + sizeof(struct fc_ct_hdr), oct->len);
        spin_unlock_irqrestore(&dbf->san_lock, flags);
 }
 
@@ -717,10 +715,9 @@ void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
  */
 void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req)
 {
-       struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
-       struct zfcp_wka_port *wka_port = ct->wka_port;
-       struct zfcp_adapter *adapter = wka_port->adapter;
-       struct ct_hdr *hdr = sg_virt(ct->resp);
+       struct zfcp_fsf_ct_els *ct = (struct zfcp_fsf_ct_els *)fsf_req->data;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
+       struct fc_ct_hdr *hdr = sg_virt(ct->resp);
        struct zfcp_dbf *dbf = adapter->dbf;
        struct zfcp_dbf_san_record *r = &dbf->san_buf;
        struct zfcp_dbf_san_record_ct_response *rct = &r->u.ct_resp;
@@ -732,25 +729,23 @@ void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req)
        strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE);
        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);
-       rct->cmd_rsp_code = hdr->cmd_rsp_code;
-       rct->revision = hdr->revision;
-       rct->reason_code = hdr->reason_code;
-       rct->expl = hdr->reason_code_expl;
-       rct->vendor_unique = hdr->vendor_unique;
-       rct->max_res_size = hdr->max_res_size;
-       rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
+       rct->cmd_rsp_code = hdr->ct_cmd;
+       rct->revision = hdr->ct_rev;
+       rct->reason_code = hdr->ct_reason;
+       rct->expl = hdr->ct_explan;
+       rct->vendor_unique = hdr->ct_vendor;
+       rct->max_res_size = hdr->ct_mr_size;
+       rct->len = min((int)ct->resp->length - (int)sizeof(struct fc_ct_hdr),
                       ZFCP_DBF_SAN_MAX_PAYLOAD);
        debug_event(dbf->san, level, r, sizeof(*r));
        zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level,
-                        (void *)hdr + sizeof(struct ct_hdr), rct->len);
+                        (void *)hdr + sizeof(struct fc_ct_hdr), rct->len);
        spin_unlock_irqrestore(&dbf->san_lock, flags);
 }
 
 static void zfcp_dbf_san_els(const char *tag, int level,
-                            struct zfcp_fsf_req *fsf_req, u32 s_id, u32 d_id,
-                            u8 ls_code, void *buffer, int buflen)
+                            struct zfcp_fsf_req *fsf_req, u32 d_id,
+                            void *buffer, int buflen)
 {
        struct zfcp_adapter *adapter = fsf_req->adapter;
        struct zfcp_dbf *dbf = adapter->dbf;
@@ -762,9 +757,7 @@ static void zfcp_dbf_san_els(const char *tag, int level,
        strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE);
        rec->fsf_reqid = fsf_req->req_id;
        rec->fsf_seqno = fsf_req->seq_no;
-       rec->s_id = s_id;
-       rec->d_id = d_id;
-       rec->u.els.ls_code = ls_code;
+       rec->u.els.d_id = d_id;
        debug_event(dbf->san, level, rec, sizeof(*rec));
        zfcp_dbf_hexdump(dbf->san, rec, sizeof(*rec), level,
                         buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD));
@@ -777,12 +770,11 @@ static void zfcp_dbf_san_els(const char *tag, int level,
  */
 void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req)
 {
-       struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data;
+       struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data;
+       u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id);
 
-       zfcp_dbf_san_els("oels", 2, fsf_req,
-                              fc_host_port_id(els->adapter->scsi_host),
-                              els->d_id, *(u8 *) sg_virt(els->req),
-                              sg_virt(els->req), els->req->length);
+       zfcp_dbf_san_els("oels", 2, fsf_req, d_id,
+                        sg_virt(els->req), els->req->length);
 }
 
 /**
@@ -791,12 +783,11 @@ void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req)
  */
 void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req)
 {
-       struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data;
+       struct zfcp_fsf_ct_els *els = (struct zfcp_fsf_ct_els *)fsf_req->data;
+       u32 d_id = ntoh24(fsf_req->qtcb->bottom.support.d_id);
 
-       zfcp_dbf_san_els("rels", 2, fsf_req, els->d_id,
-                              fc_host_port_id(els->adapter->scsi_host),
-                              *(u8 *)sg_virt(els->req), sg_virt(els->resp),
-                              els->resp->length);
+       zfcp_dbf_san_els("rels", 2, fsf_req, d_id,
+                              sg_virt(els->resp), els->resp->length);
 }
 
 /**
@@ -805,16 +796,13 @@ void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req)
  */
 void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *fsf_req)
 {
-       struct zfcp_adapter *adapter = fsf_req->adapter;
        struct fsf_status_read_buffer *buf =
                        (struct fsf_status_read_buffer *)fsf_req->data;
        int length = (int)buf->length -
                     (int)((void *)&buf->payload - (void *)buf);
 
-       zfcp_dbf_san_els("iels", 1, fsf_req, buf->d_id,
-                              fc_host_port_id(adapter->scsi_host),
-                              buf->payload.data[0], (void *)buf->payload.data,
-                              length);
+       zfcp_dbf_san_els("iels", 1, fsf_req, ntoh24(buf->d_id),
+                              (void *)buf->payload.data, length);
 }
 
 static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
@@ -829,11 +817,10 @@ static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
        zfcp_dbf_tag(&p, "tag", r->tag);
        zfcp_dbf_out(&p, "fsf_reqid", "0x%0Lx", r->fsf_reqid);
        zfcp_dbf_out(&p, "fsf_seqno", "0x%08x", r->fsf_seqno);
-       zfcp_dbf_out(&p, "s_id", "0x%06x", r->s_id);
-       zfcp_dbf_out(&p, "d_id", "0x%06x", r->d_id);
 
        if (strncmp(r->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) {
                struct zfcp_dbf_san_record_ct_request *ct = &r->u.ct_req;
+               zfcp_dbf_out(&p, "d_id", "0x%06x", ct->d_id);
                zfcp_dbf_out(&p, "cmd_req_code", "0x%04x", ct->cmd_req_code);
                zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision);
                zfcp_dbf_out(&p, "gs_type", "0x%02x", ct->gs_type);
@@ -852,7 +839,7 @@ static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
                   strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
                   strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
                struct zfcp_dbf_san_record_els *els = &r->u.els;
-               zfcp_dbf_out(&p, "ls_code", "0x%02x", els->ls_code);
+               zfcp_dbf_out(&p, "d_id", "0x%06x", els->d_id);
        }
        return p - out_buf;
 }
@@ -870,8 +857,9 @@ void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
        struct zfcp_dbf_scsi_record *rec = &dbf->scsi_buf;
        struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec;
        unsigned long flags;
-       struct fcp_rsp_iu *fcp_rsp;
-       char *fcp_rsp_info = NULL, *fcp_sns_info = NULL;
+       struct fcp_resp_with_ext *fcp_rsp;
+       struct fcp_resp_rsp_info *fcp_rsp_info = NULL;
+       char *fcp_sns_info = NULL;
        int offset = 0, buflen = 0;
 
        spin_lock_irqsave(&dbf->scsi_lock, flags);
@@ -895,20 +883,22 @@ void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
                                rec->scsi_allowed = scsi_cmnd->allowed;
                        }
                        if (fsf_req != NULL) {
-                               fcp_rsp = (struct fcp_rsp_iu *)
-                                   &(fsf_req->qtcb->bottom.io.fcp_rsp);
-                               fcp_rsp_info = (unsigned char *) &fcp_rsp[1];
-                               fcp_sns_info =
-                                   zfcp_get_fcp_sns_info_ptr(fcp_rsp);
-
-                               rec->rsp_validity = fcp_rsp->validity.value;
-                               rec->rsp_scsi_status = fcp_rsp->scsi_status;
-                               rec->rsp_resid = fcp_rsp->fcp_resid;
-                               if (fcp_rsp->validity.bits.fcp_rsp_len_valid)
-                                       rec->rsp_code = *(fcp_rsp_info + 3);
-                               if (fcp_rsp->validity.bits.fcp_sns_len_valid) {
-                                       buflen = min((int)fcp_rsp->fcp_sns_len,
-                                                    ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO);
+                               fcp_rsp = (struct fcp_resp_with_ext *)
+                                       &(fsf_req->qtcb->bottom.io.fcp_rsp);
+                               fcp_rsp_info = (struct fcp_resp_rsp_info *)
+                                       &fcp_rsp[1];
+                               fcp_sns_info = (char *) &fcp_rsp[1];
+                               if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL)
+                                       fcp_sns_info += fcp_rsp->ext.fr_sns_len;
+
+                               rec->rsp_validity = fcp_rsp->resp.fr_flags;
+                               rec->rsp_scsi_status = fcp_rsp->resp.fr_status;
+                               rec->rsp_resid = fcp_rsp->ext.fr_resid;
+                               if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL)
+                                       rec->rsp_code = fcp_rsp_info->rsp_code;
+                               if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) {
+                                       buflen = min(fcp_rsp->ext.fr_sns_len,
+                                          (u32)ZFCP_DBF_SCSI_MAX_FCP_SNS_INFO);
                                        rec->sns_info_len = buflen;
                                        memcpy(rec->sns_info, fcp_sns_info,
                                               min(buflen,
@@ -1067,6 +1057,8 @@ err_out:
  */
 void zfcp_dbf_adapter_unregister(struct zfcp_dbf *dbf)
 {
+       if (!dbf)
+               return;
        debug_unregister(dbf->scsi);
        debug_unregister(dbf->san);
        debug_unregister(dbf->hba);
index 6b1461e..8b7fd9a 100644 (file)
@@ -22,6 +22,7 @@
 #ifndef ZFCP_DBF_H
 #define ZFCP_DBF_H
 
+#include <scsi/fc/fc_fcp.h>
 #include "zfcp_ext.h"
 #include "zfcp_fsf.h"
 #include "zfcp_def.h"
@@ -122,7 +123,6 @@ struct zfcp_dbf_hba_record_response {
                } unit;
                struct {
                        u32 d_id;
-                       u8 ls_code;
                } els;
        } u;
 } __attribute__ ((packed));
@@ -166,6 +166,7 @@ struct zfcp_dbf_san_record_ct_request {
        u8 options;
        u16 max_res_size;
        u32 len;
+       u32 d_id;
 } __attribute__ ((packed));
 
 struct zfcp_dbf_san_record_ct_response {
@@ -179,16 +180,13 @@ struct zfcp_dbf_san_record_ct_response {
 } __attribute__ ((packed));
 
 struct zfcp_dbf_san_record_els {
-       u8 ls_code;
-       u32 len;
+       u32 d_id;
 } __attribute__ ((packed));
 
 struct zfcp_dbf_san_record {
        u8 tag[ZFCP_DBF_TAG_SIZE];
        u64 fsf_reqid;
        u32 fsf_seqno;
-       u32 s_id;
-       u32 d_id;
        union {
                struct zfcp_dbf_san_record_ct_request ct_req;
                struct zfcp_dbf_san_record_ct_response ct_resp;
@@ -343,7 +341,7 @@ static inline
 void zfcp_dbf_scsi_devreset(const char *tag, u8 flag, struct zfcp_unit *unit,
                            struct scsi_cmnd *scsi_cmnd)
 {
-       zfcp_dbf_scsi(flag == FCP_TARGET_RESET ? "trst" : "lrst", tag, 1,
+       zfcp_dbf_scsi(flag == FCP_TMF_TGT_RESET ? "trst" : "lrst", tag, 1,
                            unit->port->adapter->dbf, scsi_cmnd, NULL, 0);
 }
 
index 7da2fad..e1b5b88 100644 (file)
 /* timeout value for "default timer" for fsf requests */
 #define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ)
 
-/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
-
-/* task attribute values in FCP-2 FCP_CMND IU */
-#define SIMPLE_Q       0
-#define HEAD_OF_Q      1
-#define ORDERED_Q      2
-#define ACA_Q          4
-#define UNTAGGED       5
-
-/* task management flags in FCP-2 FCP_CMND IU */
-#define FCP_CLEAR_ACA          0x40
-#define FCP_TARGET_RESET       0x20
-#define FCP_LOGICAL_UNIT_RESET 0x10
-#define FCP_CLEAR_TASK_SET     0x04
-#define FCP_ABORT_TASK_SET     0x02
-
-#define FCP_CDB_LENGTH         16
-
-#define ZFCP_DID_MASK           0x00FFFFFF
-
-/* FCP(-2) FCP_CMND IU */
-struct fcp_cmnd_iu {
-       u64 fcp_lun;       /* FCP logical unit number */
-       u8  crn;                   /* command reference number */
-       u8  reserved0:5;           /* reserved */
-       u8  task_attribute:3;      /* task attribute */
-       u8  task_management_flags; /* task management flags */
-       u8  add_fcp_cdb_length:6;  /* additional FCP_CDB length */
-       u8  rddata:1;              /* read data */
-       u8  wddata:1;              /* write data */
-       u8  fcp_cdb[FCP_CDB_LENGTH];
-} __attribute__((packed));
-
-/* FCP(-2) FCP_RSP IU */
-struct fcp_rsp_iu {
-       u8  reserved0[10];
-       union {
-               struct {
-                       u8 reserved1:3;
-                       u8 fcp_conf_req:1;
-                       u8 fcp_resid_under:1;
-                       u8 fcp_resid_over:1;
-                       u8 fcp_sns_len_valid:1;
-                       u8 fcp_rsp_len_valid:1;
-               } bits;
-               u8 value;
-       } validity;
-       u8  scsi_status;
-       u32 fcp_resid;
-       u32 fcp_sns_len;
-       u32 fcp_rsp_len;
-} __attribute__((packed));
-
-
-#define RSP_CODE_GOOD           0
-#define RSP_CODE_LENGTH_MISMATCH 1
-#define RSP_CODE_FIELD_INVALID  2
-#define RSP_CODE_RO_MISMATCH    3
-#define RSP_CODE_TASKMAN_UNSUPP         4
-#define RSP_CODE_TASKMAN_FAILED         5
-
-/* see fc-fs */
-#define LS_RSCN  0x61
-#define LS_LOGO  0x05
-#define LS_PLOGI 0x03
-
-struct fcp_rscn_head {
-        u8  command;
-        u8  page_length; /* always 0x04 */
-        u16 payload_len;
-} __attribute__((packed));
-
-struct fcp_rscn_element {
-        u8  reserved:2;
-        u8  event_qual:4;
-        u8  addr_format:2;
-        u32 nport_did:24;
-} __attribute__((packed));
-
-/* see fc-ph */
-struct fcp_logo {
-        u32 command;
-        u32 nport_did;
-       u64 nport_wwpn;
-} __attribute__((packed));
-
-/*
- * FC-FS stuff
- */
-#define R_A_TOV                                10 /* seconds */
-
-#define ZFCP_LS_RLS                    0x0f
-#define ZFCP_LS_ADISC                  0x52
-#define ZFCP_LS_RPS                    0x56
-#define ZFCP_LS_RSCN                   0x61
-#define ZFCP_LS_RNID                   0x78
-
-struct zfcp_ls_adisc {
-       u8              code;
-       u8              field[3];
-       u32             hard_nport_id;
-       u64             wwpn;
-       u64             wwnn;
-       u32             nport_id;
-} __attribute__ ((packed));
-
-/*
- * FC-GS-2 stuff
- */
-#define ZFCP_CT_REVISION               0x01
-#define ZFCP_CT_DIRECTORY_SERVICE      0xFC
-#define ZFCP_CT_NAME_SERVER            0x02
-#define ZFCP_CT_SYNCHRONOUS            0x00
-#define ZFCP_CT_SCSI_FCP               0x08
-#define ZFCP_CT_UNABLE_TO_PERFORM_CMD  0x09
-#define ZFCP_CT_GID_PN                 0x0121
-#define ZFCP_CT_GPN_FT                 0x0172
-#define ZFCP_CT_ACCEPT                 0x8002
-#define ZFCP_CT_REJECT                 0x8001
-
-/*
- * FC-GS-4 stuff
- */
-#define ZFCP_CT_TIMEOUT                        (3 * R_A_TOV)
-
 /*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/
 
 /*
@@ -205,7 +80,6 @@ struct zfcp_ls_adisc {
 #define ZFCP_COMMON_FLAGS                      0xfff00000
 
 /* common status bits */
-#define ZFCP_STATUS_COMMON_REMOVE              0x80000000
 #define ZFCP_STATUS_COMMON_RUNNING             0x40000000
 #define ZFCP_STATUS_COMMON_ERP_FAILED          0x20000000
 #define ZFCP_STATUS_COMMON_UNBLOCKED           0x10000000
@@ -222,21 +96,10 @@ struct zfcp_ls_adisc {
 #define ZFCP_STATUS_ADAPTER_ERP_PENDING                0x00000100
 #define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED     0x00000200
 
-/* FC-PH/FC-GS well-known address identifiers for generic services */
-#define ZFCP_DID_WKA                           0xFFFFF0
-
 /* remote port status */
 #define ZFCP_STATUS_PORT_PHYS_OPEN             0x00000001
 #define ZFCP_STATUS_PORT_LINK_TEST             0x00000002
 
-/* well known address (WKA) port status*/
-enum zfcp_wka_status {
-       ZFCP_WKA_PORT_OFFLINE,
-       ZFCP_WKA_PORT_CLOSING,
-       ZFCP_WKA_PORT_OPENING,
-       ZFCP_WKA_PORT_ONLINE,
-};
-
 /* logical unit status */
 #define ZFCP_STATUS_UNIT_SHARED                        0x00000004
 #define ZFCP_STATUS_UNIT_READONLY              0x00000008
@@ -247,10 +110,7 @@ enum zfcp_wka_status {
 #define ZFCP_STATUS_FSFREQ_CLEANUP             0x00000010
 #define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED      0x00000040
 #define ZFCP_STATUS_FSFREQ_ABORTNOTNEEDED       0x00000080
-#define ZFCP_STATUS_FSFREQ_ABORTED              0x00000100
 #define ZFCP_STATUS_FSFREQ_TMFUNCFAILED         0x00000200
-#define ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP        0x00000400
-#define ZFCP_STATUS_FSFREQ_RETRY                0x00000800
 #define ZFCP_STATUS_FSFREQ_DISMISSED            0x00001000
 
 /************************* STRUCTURE DEFINITIONS *****************************/
@@ -265,125 +125,10 @@ struct zfcp_adapter_mempool {
        mempool_t *scsi_abort;
        mempool_t *status_read_req;
        mempool_t *status_read_data;
-       mempool_t *gid_pn_data;
+       mempool_t *gid_pn;
        mempool_t *qtcb_pool;
 };
 
-/*
- * header for CT_IU
- */
-struct ct_hdr {
-       u8 revision;            // 0x01
-       u8 in_id[3];            // 0x00
-       u8 gs_type;             // 0xFC Directory Service
-       u8 gs_subtype;          // 0x02 Name Server
-       u8 options;             // 0x00 single bidirectional exchange
-       u8 reserved0;
-       u16 cmd_rsp_code;       // 0x0121 GID_PN, or 0x0100 GA_NXT
-       u16 max_res_size;       // <= (4096 - 16) / 4
-       u8 reserved1;
-       u8 reason_code;
-       u8 reason_code_expl;
-       u8 vendor_unique;
-} __attribute__ ((packed));
-
-/* nameserver request CT_IU -- for requests where
- * a port name is required */
-struct ct_iu_gid_pn_req {
-       struct ct_hdr header;
-       u64 wwpn;
-} __attribute__ ((packed));
-
-/* FS_ACC IU and data unit for GID_PN nameserver request */
-struct ct_iu_gid_pn_resp {
-       struct ct_hdr header;
-       u32 d_id;
-} __attribute__ ((packed));
-
-struct ct_iu_gpn_ft_req {
-       struct ct_hdr header;
-       u8 flags;
-       u8 domain_id_scope;
-       u8 area_id_scope;
-       u8 fc4_type;
-} __attribute__ ((packed));
-
-
-/**
- * struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct
- * @wka_port: port where the request is sent to
- * @req: scatter-gather list for request
- * @resp: scatter-gather list for response
- * @handler: handler function (called for response to the request)
- * @handler_data: data passed to handler function
- * @completion: completion for synchronization purposes
- * @status: used to pass error status to calling function
- */
-struct zfcp_send_ct {
-       struct zfcp_wka_port *wka_port;
-       struct scatterlist *req;
-       struct scatterlist *resp;
-       void (*handler)(unsigned long);
-       unsigned long handler_data;
-       struct completion *completion;
-       int status;
-};
-
-/* used for name server requests in error recovery */
-struct zfcp_gid_pn_data {
-       struct zfcp_send_ct ct;
-       struct scatterlist req;
-       struct scatterlist resp;
-       struct ct_iu_gid_pn_req ct_iu_req;
-       struct ct_iu_gid_pn_resp ct_iu_resp;
-        struct zfcp_port *port;
-};
-
-/**
- * struct zfcp_send_els - used to pass parameters to function zfcp_fsf_send_els
- * @adapter: adapter where request is sent from
- * @port: port where ELS is destinated (port reference count has to be increased)
- * @d_id: destiniation id of port where request is sent to
- * @req: scatter-gather list for request
- * @resp: scatter-gather list for response
- * @handler: handler function (called for response to the request)
- * @handler_data: data passed to handler function
- * @completion: completion for synchronization purposes
- * @ls_code: hex code of ELS command
- * @status: used to pass error status to calling function
- */
-struct zfcp_send_els {
-       struct zfcp_adapter *adapter;
-       struct zfcp_port *port;
-       u32 d_id;
-       struct scatterlist *req;
-       struct scatterlist *resp;
-       void (*handler)(unsigned long);
-       unsigned long handler_data;
-       struct completion *completion;
-       int ls_code;
-       int status;
-};
-
-struct zfcp_wka_port {
-       struct zfcp_adapter     *adapter;
-       wait_queue_head_t       completion_wq;
-       enum zfcp_wka_status    status;
-       atomic_t                refcount;
-       u32                     d_id;
-       u32                     handle;
-       struct mutex            mutex;
-       struct delayed_work     work;
-};
-
-struct zfcp_wka_ports {
-       struct zfcp_wka_port ms;        /* management service */
-       struct zfcp_wka_port ts;        /* time service */
-       struct zfcp_wka_port ds;        /* directory service */
-       struct zfcp_wka_port as;        /* alias service */
-       struct zfcp_wka_port ks;        /* key distribution service */
-};
-
 struct zfcp_qdio_queue {
        struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q];
        u8                 first;       /* index of next free bfr in queue */
@@ -446,9 +191,7 @@ struct zfcp_qdio {
 };
 
 struct zfcp_adapter {
-       atomic_t                refcount;          /* reference count */
-       wait_queue_head_t       remove_wq;         /* can be used to wait for
-                                                     refcount drop to zero */
+       struct kref             ref;
        u64                     peer_wwnn;         /* P2P peer WWNN */
        u64                     peer_wwpn;         /* P2P peer WWPN */
        u32                     peer_d_id;         /* P2P peer D_ID */
@@ -461,7 +204,8 @@ struct zfcp_adapter {
         u32                    hardware_version;  /* of FCP channel */
        u16                     timer_ticks;       /* time int for a tick */
        struct Scsi_Host        *scsi_host;        /* Pointer to mid-layer */
-       struct list_head        port_list_head;    /* remote port list */
+       struct list_head        port_list;         /* remote port list */
+       rwlock_t                port_list_lock;    /* port list lock */
        unsigned long           req_no;            /* unique FSF req number */
        struct list_head        *req_list;         /* list of pending reqs */
        spinlock_t              req_list_lock;     /* request list lock */
@@ -485,7 +229,7 @@ struct zfcp_adapter {
        u32                     erp_low_mem_count; /* nr of erp actions waiting
                                                      for memory */
        struct task_struct      *erp_thread;
-       struct zfcp_wka_ports   *gs;               /* generic services */
+       struct zfcp_fc_wka_ports *gs;              /* generic services */
        struct zfcp_dbf         *dbf;              /* debug traces */
        struct zfcp_adapter_mempool     pool;      /* Adapter memory pools */
        struct fc_host_statistics *fc_stats;
@@ -500,11 +244,9 @@ struct zfcp_port {
        struct device          sysfs_device;   /* sysfs device */
        struct fc_rport        *rport;         /* rport of fc transport class */
        struct list_head       list;           /* list of remote ports */
-       atomic_t               refcount;       /* reference count */
-       wait_queue_head_t      remove_wq;      /* can be used to wait for
-                                                 refcount drop to zero */
        struct zfcp_adapter    *adapter;       /* adapter used to access port */
-       struct list_head       unit_list_head; /* head of logical unit list */
+       struct list_head        unit_list;      /* head of logical unit list */
+       rwlock_t                unit_list_lock; /* unit list lock */
        atomic_t               status;         /* status of this remote port */
        u64                    wwnn;           /* WWNN if known */
        u64                    wwpn;           /* WWPN */
@@ -523,9 +265,6 @@ struct zfcp_port {
 struct zfcp_unit {
        struct device          sysfs_device;   /* sysfs device */
        struct list_head       list;           /* list of logical units */
-       atomic_t               refcount;       /* reference count */
-       wait_queue_head_t      remove_wq;      /* can be used to wait for
-                                                 refcount drop to zero */
        struct zfcp_port       *port;          /* remote port of unit */
        atomic_t               status;         /* status of this logical unit */
        u64                    fcp_lun;        /* own FCP_LUN */
@@ -601,14 +340,11 @@ struct zfcp_fsf_req {
 struct zfcp_data {
        struct scsi_host_template scsi_host_template;
        struct scsi_transport_template *scsi_transport_template;
-       rwlock_t                config_lock;        /* serialises changes
-                                                      to adapter/port/unit
-                                                      lists */
-       struct mutex            config_mutex;
        struct kmem_cache       *gpn_ft_cache;
        struct kmem_cache       *qtcb_cache;
        struct kmem_cache       *sr_buffer_cache;
        struct kmem_cache       *gid_pn_cache;
+       struct kmem_cache       *adisc_cache;
 };
 
 /********************** ZFCP SPECIFIC DEFINES ********************************/
@@ -657,47 +393,4 @@ zfcp_reqlist_find_safe(struct zfcp_adapter *adapter, struct zfcp_fsf_req *req)
        return NULL;
 }
 
-/*
- *  functions needed for reference/usage counting
- */
-
-static inline void
-zfcp_unit_get(struct zfcp_unit *unit)
-{
-       atomic_inc(&unit->refcount);
-}
-
-static inline void
-zfcp_unit_put(struct zfcp_unit *unit)
-{
-       if (atomic_dec_return(&unit->refcount) == 0)
-               wake_up(&unit->remove_wq);
-}
-
-static inline void
-zfcp_port_get(struct zfcp_port *port)
-{
-       atomic_inc(&port->refcount);
-}
-
-static inline void
-zfcp_port_put(struct zfcp_port *port)
-{
-       if (atomic_dec_return(&port->refcount) == 0)
-               wake_up(&port->remove_wq);
-}
-
-static inline void
-zfcp_adapter_get(struct zfcp_adapter *adapter)
-{
-       atomic_inc(&adapter->refcount);
-}
-
-static inline void
-zfcp_adapter_put(struct zfcp_adapter *adapter)
-{
-       if (atomic_dec_return(&adapter->refcount) == 0)
-               wake_up(&adapter->remove_wq);
-}
-
 #endif /* ZFCP_DEF_H */
index f73e218..b51a11a 100644 (file)
@@ -99,9 +99,12 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
 
        if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
                zfcp_erp_action_dismiss(&port->erp_action);
-       else
-               list_for_each_entry(unit, &port->unit_list_head, list)
-                   zfcp_erp_action_dismiss_unit(unit);
+       else {
+               read_lock(&port->unit_list_lock);
+               list_for_each_entry(unit, &port->unit_list, list)
+                       zfcp_erp_action_dismiss_unit(unit);
+               read_unlock(&port->unit_list_lock);
+       }
 }
 
 static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
@@ -110,9 +113,12 @@ static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
 
        if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
                zfcp_erp_action_dismiss(&adapter->erp_action);
-       else
-               list_for_each_entry(port, &adapter->port_list_head, list)
+       else {
+               read_lock(&adapter->port_list_lock);
+               list_for_each_entry(port, &adapter->port_list, list)
                    zfcp_erp_action_dismiss_port(port);
+               read_unlock(&adapter->port_list_lock);
+       }
 }
 
 static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
@@ -168,7 +174,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
 
        switch (need) {
        case ZFCP_ERP_ACTION_REOPEN_UNIT:
-               zfcp_unit_get(unit);
+               if (!get_device(&unit->sysfs_device))
+                       return NULL;
                atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status);
                erp_action = &unit->erp_action;
                if (!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING))
@@ -177,7 +184,8 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
 
        case ZFCP_ERP_ACTION_REOPEN_PORT:
        case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
-               zfcp_port_get(port);
+               if (!get_device(&port->sysfs_device))
+                       return NULL;
                zfcp_erp_action_dismiss_port(port);
                atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
                erp_action = &port->erp_action;
@@ -186,7 +194,7 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need,
                break;
 
        case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
-               zfcp_adapter_get(adapter);
+               kref_get(&adapter->ref);
                zfcp_erp_action_dismiss_adapter(adapter);
                atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
                erp_action = &adapter->erp_action;
@@ -264,11 +272,16 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear,
 {
        unsigned long flags;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       write_lock(&adapter->erp_lock);
-       _zfcp_erp_adapter_reopen(adapter, clear, id, ref);
-       write_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       zfcp_erp_adapter_block(adapter, clear);
+       zfcp_scsi_schedule_rports_block(adapter);
+
+       write_lock_irqsave(&adapter->erp_lock, flags);
+       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+               zfcp_erp_adapter_failed(adapter, "erareo1", NULL);
+       else
+               zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter,
+                                       NULL, NULL, id, ref);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
 }
 
 /**
@@ -345,11 +358,9 @@ void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id,
        unsigned long flags;
        struct zfcp_adapter *adapter = port->adapter;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       write_lock(&adapter->erp_lock);
+       write_lock_irqsave(&adapter->erp_lock, flags);
        _zfcp_erp_port_forced_reopen(port, clear, id, ref);
-       write_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
 }
 
 static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
@@ -377,15 +388,13 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id,
  */
 int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id, void *ref)
 {
-       unsigned long flags;
        int retval;
+       unsigned long flags;
        struct zfcp_adapter *adapter = port->adapter;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       write_lock(&adapter->erp_lock);
+       write_lock_irqsave(&adapter->erp_lock, flags);
        retval = _zfcp_erp_port_reopen(port, clear, id, ref);
-       write_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
 
        return retval;
 }
@@ -424,11 +433,9 @@ void zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear, char *id,
        struct zfcp_port *port = unit->port;
        struct zfcp_adapter *adapter = port->adapter;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       write_lock(&adapter->erp_lock);
+       write_lock_irqsave(&adapter->erp_lock, flags);
        _zfcp_erp_unit_reopen(unit, clear, id, ref);
-       write_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
 }
 
 static int status_change_set(unsigned long mask, atomic_t *status)
@@ -540,8 +547,10 @@ static void _zfcp_erp_port_reopen_all(struct zfcp_adapter *adapter,
 {
        struct zfcp_port *port;
 
-       list_for_each_entry(port, &adapter->port_list_head, list)
+       read_lock(&adapter->port_list_lock);
+       list_for_each_entry(port, &adapter->port_list, list)
                _zfcp_erp_port_reopen(port, clear, id, ref);
+       read_unlock(&adapter->port_list_lock);
 }
 
 static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear,
@@ -549,8 +558,10 @@ static void _zfcp_erp_unit_reopen_all(struct zfcp_port *port, int clear,
 {
        struct zfcp_unit *unit;
 
-       list_for_each_entry(unit, &port->unit_list_head, list)
+       read_lock(&port->unit_list_lock);
+       list_for_each_entry(unit, &port->unit_list, list)
                _zfcp_erp_unit_reopen(unit, clear, id, ref);
+       read_unlock(&port->unit_list_lock);
 }
 
 static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act)
@@ -590,16 +601,14 @@ static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
 {
        unsigned long flags;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       read_lock(&adapter->erp_lock);
+       read_lock_irqsave(&adapter->erp_lock, flags);
        if (list_empty(&adapter->erp_ready_head) &&
            list_empty(&adapter->erp_running_head)) {
                        atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_PENDING,
                                          &adapter->status);
                        wake_up(&adapter->erp_done_wqh);
        }
-       read_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       read_unlock_irqrestore(&adapter->erp_lock, flags);
 }
 
 static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act)
@@ -1170,28 +1179,28 @@ static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result)
        switch (act->action) {
        case ZFCP_ERP_ACTION_REOPEN_UNIT:
                if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) {
-                       zfcp_unit_get(unit);
+                       get_device(&unit->sysfs_device);
                        if (scsi_queue_work(unit->port->adapter->scsi_host,
                                            &unit->scsi_work) <= 0)
-                               zfcp_unit_put(unit);
+                               put_device(&unit->sysfs_device);
                }
-               zfcp_unit_put(unit);
+               put_device(&unit->sysfs_device);
                break;
 
        case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED:
        case ZFCP_ERP_ACTION_REOPEN_PORT:
                if (result == ZFCP_ERP_SUCCEEDED)
                        zfcp_scsi_schedule_rport_register(port);
-               zfcp_port_put(port);
+               put_device(&port->sysfs_device);
                break;
 
        case ZFCP_ERP_ACTION_REOPEN_ADAPTER:
                if (result == ZFCP_ERP_SUCCEEDED) {
                        register_service_level(&adapter->service_level);
-                       schedule_work(&adapter->scan_work);
+                       queue_work(adapter->work_queue, &adapter->scan_work);
                } else
                        unregister_service_level(&adapter->service_level);
-               zfcp_adapter_put(adapter);
+               kref_put(&adapter->ref, zfcp_adapter_release);
                break;
        }
 }
@@ -1214,12 +1223,12 @@ static int zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
 static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
 {
        int retval;
-       struct zfcp_adapter *adapter = erp_action->adapter;
        unsigned long flags;
+       struct zfcp_adapter *adapter = erp_action->adapter;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       write_lock(&adapter->erp_lock);
+       kref_get(&adapter->ref);
 
+       write_lock_irqsave(&adapter->erp_lock, flags);
        zfcp_erp_strategy_check_fsfreq(erp_action);
 
        if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED) {
@@ -1231,11 +1240,9 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
        zfcp_erp_action_to_running(erp_action);
 
        /* no lock to allow for blocking operations */
-       write_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
        retval = zfcp_erp_strategy_do_action(erp_action);
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       write_lock(&adapter->erp_lock);
+       write_lock_irqsave(&adapter->erp_lock, flags);
 
        if (erp_action->status & ZFCP_STATUS_ERP_DISMISSED)
                retval = ZFCP_ERP_CONTINUES;
@@ -1273,12 +1280,12 @@ static int zfcp_erp_strategy(struct zfcp_erp_action *erp_action)
                zfcp_erp_strategy_followup_failed(erp_action);
 
  unlock:
-       write_unlock(&adapter->erp_lock);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       write_unlock_irqrestore(&adapter->erp_lock, flags);
 
        if (retval != ZFCP_ERP_CONTINUES)
                zfcp_erp_action_cleanup(erp_action, retval);
 
+       kref_put(&adapter->ref, zfcp_adapter_release);
        return retval;
 }
 
@@ -1415,6 +1422,7 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id,
                                    void *ref, u32 mask, int set_or_clear)
 {
        struct zfcp_port *port;
+       unsigned long flags;
        u32 common_mask = mask & ZFCP_COMMON_FLAGS;
 
        if (set_or_clear == ZFCP_SET) {
@@ -1429,10 +1437,13 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id,
                        atomic_set(&adapter->erp_counter, 0);
        }
 
-       if (common_mask)
-               list_for_each_entry(port, &adapter->port_list_head, list)
+       if (common_mask) {
+               read_lock_irqsave(&adapter->port_list_lock, flags);
+               list_for_each_entry(port, &adapter->port_list, list)
                        zfcp_erp_modify_port_status(port, id, ref, common_mask,
                                                    set_or_clear);
+               read_unlock_irqrestore(&adapter->port_list_lock, flags);
+       }
 }
 
 /**
@@ -1449,6 +1460,7 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref,
                                 u32 mask, int set_or_clear)
 {
        struct zfcp_unit *unit;
+       unsigned long flags;
        u32 common_mask = mask & ZFCP_COMMON_FLAGS;
 
        if (set_or_clear == ZFCP_SET) {
@@ -1463,10 +1475,13 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref,
                        atomic_set(&port->erp_counter, 0);
        }
 
-       if (common_mask)
-               list_for_each_entry(unit, &port->unit_list_head, list)
+       if (common_mask) {
+               read_lock_irqsave(&port->unit_list_lock, flags);
+               list_for_each_entry(unit, &port->unit_list, list)
                        zfcp_erp_modify_unit_status(unit, id, ref, common_mask,
                                                    set_or_clear);
+               read_unlock_irqrestore(&port->unit_list_lock, flags);
+       }
 }
 
 /**
@@ -1502,12 +1517,8 @@ void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, char *id, void *ref,
  */
 void zfcp_erp_port_boxed(struct zfcp_port *port, char *id, void *ref)
 {
-       unsigned long flags;
-
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
        zfcp_erp_modify_port_status(port, id, ref,
                                    ZFCP_STATUS_COMMON_ACCESS_BOXED, ZFCP_SET);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
        zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED, id, ref);
 }
 
@@ -1535,13 +1546,9 @@ void zfcp_erp_unit_boxed(struct zfcp_unit *unit, char *id, void *ref)
  */
 void zfcp_erp_port_access_denied(struct zfcp_port *port, char *id, void *ref)
 {
-       unsigned long flags;
-
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
        zfcp_erp_modify_port_status(port, id, ref,
                                    ZFCP_STATUS_COMMON_ERP_FAILED |
                                    ZFCP_STATUS_COMMON_ACCESS_DENIED, ZFCP_SET);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 }
 
 /**
@@ -1574,12 +1581,15 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id,
                                         void *ref)
 {
        struct zfcp_unit *unit;
+       unsigned long flags;
        int status = atomic_read(&port->status);
 
        if (!(status & (ZFCP_STATUS_COMMON_ACCESS_DENIED |
                        ZFCP_STATUS_COMMON_ACCESS_BOXED))) {
-               list_for_each_entry(unit, &port->unit_list_head, list)
+               read_lock_irqsave(&port->unit_list_lock, flags);
+               list_for_each_entry(unit, &port->unit_list, list)
                                    zfcp_erp_unit_access_changed(unit, id, ref);
+               read_unlock_irqrestore(&port->unit_list_lock, flags);
                return;
        }
 
@@ -1595,14 +1605,14 @@ static void zfcp_erp_port_access_changed(struct zfcp_port *port, char *id,
 void zfcp_erp_adapter_access_changed(struct zfcp_adapter *adapter, char *id,
                                     void *ref)
 {
-       struct zfcp_port *port;
        unsigned long flags;
+       struct zfcp_port *port;
 
        if (adapter->connection_features & FSF_FEATURE_NPIV_MODE)
                return;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       list_for_each_entry(port, &adapter->port_list_head, list)
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list)
                zfcp_erp_port_access_changed(port, id, ref);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
 }
index b3f28de..03dec83 100644 (file)
@@ -9,26 +9,31 @@
 #ifndef ZFCP_EXT_H
 #define ZFCP_EXT_H
 
+#include <linux/types.h>
+#include <scsi/fc/fc_els.h>
 #include "zfcp_def.h"
+#include "zfcp_fc.h"
 
 /* zfcp_aux.c */
 extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, u64);
 extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, u64);
-extern int zfcp_adapter_enqueue(struct ccw_device *);
-extern void zfcp_adapter_dequeue(struct zfcp_adapter *);
+extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *);
 extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, u64, u32,
                                           u32);
-extern void zfcp_port_dequeue(struct zfcp_port *);
 extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, u64);
-extern void zfcp_unit_dequeue(struct zfcp_unit *);
 extern int zfcp_reqlist_isempty(struct zfcp_adapter *);
 extern void zfcp_sg_free_table(struct scatterlist *, int);
 extern int zfcp_sg_setup_table(struct scatterlist *, int);
+extern void zfcp_device_unregister(struct device *,
+                                  const struct attribute_group *);
+extern void zfcp_adapter_release(struct kref *);
+extern void zfcp_adapter_unregister(struct zfcp_adapter *);
 
 /* zfcp_ccw.c */
-extern int zfcp_ccw_register(void);
 extern int zfcp_ccw_priv_sch(struct zfcp_adapter *);
 extern struct ccw_driver zfcp_ccw_driver;
+extern struct zfcp_adapter *zfcp_ccw_adapter_by_cdev(struct ccw_device *);
+extern void zfcp_ccw_adapter_put(struct zfcp_adapter *);
 
 /* zfcp_cfdc.c */
 extern struct miscdevice zfcp_cfdc_misc;
@@ -51,7 +56,7 @@ extern void _zfcp_dbf_hba_fsf_unsol(const char *, int level, struct zfcp_dbf *,
                                          struct fsf_status_read_buffer *);
 extern void zfcp_dbf_hba_qdio(struct zfcp_dbf *, unsigned int, int, int);
 extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
-extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *, u32);
 extern void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *);
 extern void zfcp_dbf_san_els_request(struct zfcp_fsf_req *);
 extern void zfcp_dbf_san_els_response(struct zfcp_fsf_req *);
@@ -92,24 +97,22 @@ extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, char *,
 extern void zfcp_erp_timeout_handler(unsigned long);
 
 /* zfcp_fc.c */
-extern int zfcp_fc_scan_ports(struct zfcp_adapter *);
-extern void _zfcp_fc_scan_ports_later(struct work_struct *);
+extern void zfcp_fc_scan_ports(struct work_struct *);
 extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
 extern void zfcp_fc_port_did_lookup(struct work_struct *);
 extern void zfcp_fc_trigger_did_lookup(struct zfcp_port *);
-extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
+extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fc_els_flogi *);
 extern void zfcp_fc_test_link(struct zfcp_port *);
 extern void zfcp_fc_link_test_work(struct work_struct *);
-extern void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *);
+extern void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *);
 extern int zfcp_fc_gs_setup(struct zfcp_adapter *);
 extern void zfcp_fc_gs_destroy(struct zfcp_adapter *);
-extern int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *);
-extern int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *);
+extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *);
 
 /* zfcp_fsf.c */
 extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
-extern int zfcp_fsf_open_wka_port(struct zfcp_wka_port *);
-extern int zfcp_fsf_close_wka_port(struct zfcp_wka_port *);
+extern int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *);
+extern int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *);
 extern int zfcp_fsf_close_port(struct zfcp_erp_action *);
 extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
 extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
@@ -125,8 +128,10 @@ extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
 extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
 extern int zfcp_fsf_status_read(struct zfcp_qdio *);
 extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
-extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *);
-extern int zfcp_fsf_send_els(struct zfcp_send_els *);
+extern int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *, struct zfcp_fsf_ct_els *,
+                           mempool_t *);
+extern int zfcp_fsf_send_els(struct zfcp_adapter *, u32,
+                            struct zfcp_fsf_ct_els *);
 extern int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *,
                                          struct scsi_cmnd *);
 extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
@@ -153,7 +158,6 @@ extern void zfcp_qdio_close(struct zfcp_qdio *);
 extern struct zfcp_data zfcp_data;
 extern int zfcp_adapter_scsi_register(struct zfcp_adapter *);
 extern void zfcp_adapter_scsi_unregister(struct zfcp_adapter *);
-extern char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *);
 extern struct fc_function_template zfcp_transport_functions;
 extern void zfcp_scsi_rport_work(struct work_struct *);
 extern void zfcp_scsi_schedule_rport_register(struct zfcp_port *);
index df23bce..ac5e3b7 100644 (file)
@@ -9,73 +9,38 @@
 #define KMSG_COMPONENT "zfcp"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
+#include <linux/types.h>
+#include <scsi/fc/fc_els.h>
+#include <scsi/libfc.h>
 #include "zfcp_ext.h"
+#include "zfcp_fc.h"
 
-enum rscn_address_format {
-       RSCN_PORT_ADDRESS       = 0x0,
-       RSCN_AREA_ADDRESS       = 0x1,
-       RSCN_DOMAIN_ADDRESS     = 0x2,
-       RSCN_FABRIC_ADDRESS     = 0x3,
+static u32 zfcp_fc_rscn_range_mask[] = {
+       [ELS_ADDR_FMT_PORT]             = 0xFFFFFF,
+       [ELS_ADDR_FMT_AREA]             = 0xFFFF00,
+       [ELS_ADDR_FMT_DOM]              = 0xFF0000,
+       [ELS_ADDR_FMT_FAB]              = 0x000000,
 };
 
-static u32 rscn_range_mask[] = {
-       [RSCN_PORT_ADDRESS]             = 0xFFFFFF,
-       [RSCN_AREA_ADDRESS]             = 0xFFFF00,
-       [RSCN_DOMAIN_ADDRESS]           = 0xFF0000,
-       [RSCN_FABRIC_ADDRESS]           = 0x000000,
-};
-
-struct gpn_ft_resp_acc {
-       u8 control;
-       u8 port_id[3];
-       u8 reserved[4];
-       u64 wwpn;
-} __attribute__ ((packed));
-
-#define ZFCP_CT_SIZE_ONE_PAGE  (PAGE_SIZE - sizeof(struct ct_hdr))
-#define ZFCP_GPN_FT_ENTRIES    (ZFCP_CT_SIZE_ONE_PAGE \
-                                       / sizeof(struct gpn_ft_resp_acc))
-#define ZFCP_GPN_FT_BUFFERS 4
-#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \
-                               - sizeof(struct ct_hdr))
-#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
-
-struct ct_iu_gpn_ft_resp {
-       struct ct_hdr header;
-       struct gpn_ft_resp_acc accept[ZFCP_GPN_FT_ENTRIES];
-} __attribute__ ((packed));
-
-struct zfcp_gpn_ft {
-       struct zfcp_send_ct ct;
-       struct scatterlist sg_req;
-       struct scatterlist sg_resp[ZFCP_GPN_FT_BUFFERS];
-};
-
-struct zfcp_fc_ns_handler_data {
-       struct completion done;
-       void (*handler)(unsigned long);
-       unsigned long handler_data;
-};
-
-static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port)
+static int zfcp_fc_wka_port_get(struct zfcp_fc_wka_port *wka_port)
 {
        if (mutex_lock_interruptible(&wka_port->mutex))
                return -ERESTARTSYS;
 
-       if (wka_port->status == ZFCP_WKA_PORT_OFFLINE ||
-           wka_port->status == ZFCP_WKA_PORT_CLOSING) {
-               wka_port->status = ZFCP_WKA_PORT_OPENING;
+       if (wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE ||
+           wka_port->status == ZFCP_FC_WKA_PORT_CLOSING) {
+               wka_port->status = ZFCP_FC_WKA_PORT_OPENING;
                if (zfcp_fsf_open_wka_port(wka_port))
-                       wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+                       wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
        }
 
        mutex_unlock(&wka_port->mutex);
 
        wait_event(wka_port->completion_wq,
-                  wka_port->status == ZFCP_WKA_PORT_ONLINE ||
-                  wka_port->status == ZFCP_WKA_PORT_OFFLINE);
+                  wka_port->status == ZFCP_FC_WKA_PORT_ONLINE ||
+                  wka_port->status == ZFCP_FC_WKA_PORT_OFFLINE);
 
-       if (wka_port->status == ZFCP_WKA_PORT_ONLINE) {
+       if (wka_port->status == ZFCP_FC_WKA_PORT_ONLINE) {
                atomic_inc(&wka_port->refcount);
                return 0;
        }
@@ -85,24 +50,24 @@ static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port)
 static void zfcp_fc_wka_port_offline(struct work_struct *work)
 {
        struct delayed_work *dw = to_delayed_work(work);
-       struct zfcp_wka_port *wka_port =
-                       container_of(dw, struct zfcp_wka_port, work);
+       struct zfcp_fc_wka_port *wka_port =
+                       container_of(dw, struct zfcp_fc_wka_port, work);
 
        mutex_lock(&wka_port->mutex);
        if ((atomic_read(&wka_port->refcount) != 0) ||
-           (wka_port->status != ZFCP_WKA_PORT_ONLINE))
+           (wka_port->status != ZFCP_FC_WKA_PORT_ONLINE))
                goto out;
 
-       wka_port->status = ZFCP_WKA_PORT_CLOSING;
+       wka_port->status = ZFCP_FC_WKA_PORT_CLOSING;
        if (zfcp_fsf_close_wka_port(wka_port)) {
-               wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+               wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
                wake_up(&wka_port->completion_wq);
        }
 out:
        mutex_unlock(&wka_port->mutex);
 }
 
-static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port)
+static void zfcp_fc_wka_port_put(struct zfcp_fc_wka_port *wka_port)
 {
        if (atomic_dec_return(&wka_port->refcount) != 0)
                return;
@@ -110,7 +75,7 @@ static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port)
        schedule_delayed_work(&wka_port->work, HZ / 100);
 }
 
-static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id,
+static void zfcp_fc_wka_port_init(struct zfcp_fc_wka_port *wka_port, u32 d_id,
                                  struct zfcp_adapter *adapter)
 {
        init_waitqueue_head(&wka_port->completion_wq);
@@ -118,107 +83,107 @@ static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id,
        wka_port->adapter = adapter;
        wka_port->d_id = d_id;
 
-       wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+       wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
        atomic_set(&wka_port->refcount, 0);
        mutex_init(&wka_port->mutex);
        INIT_DELAYED_WORK(&wka_port->work, zfcp_fc_wka_port_offline);
 }
 
-static void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka)
+static void zfcp_fc_wka_port_force_offline(struct zfcp_fc_wka_port *wka)
 {
        cancel_delayed_work_sync(&wka->work);
        mutex_lock(&wka->mutex);
-       wka->status = ZFCP_WKA_PORT_OFFLINE;
+       wka->status = ZFCP_FC_WKA_PORT_OFFLINE;
        mutex_unlock(&wka->mutex);
 }
 
-void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *gs)
+void zfcp_fc_wka_ports_force_offline(struct zfcp_fc_wka_ports *gs)
 {
+       if (!gs)
+               return;
        zfcp_fc_wka_port_force_offline(&gs->ms);
        zfcp_fc_wka_port_force_offline(&gs->ts);
        zfcp_fc_wka_port_force_offline(&gs->ds);
        zfcp_fc_wka_port_force_offline(&gs->as);
-       zfcp_fc_wka_port_force_offline(&gs->ks);
 }
 
 static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
-                                  struct fcp_rscn_element *elem)
+                                  struct fc_els_rscn_page *page)
 {
        unsigned long flags;
+       struct zfcp_adapter *adapter = fsf_req->adapter;
        struct zfcp_port *port;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
-               if ((port->d_id & range) == (elem->nport_did & range))
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list) {
+               if ((port->d_id & range) == (ntoh24(page->rscn_fid) & range))
                        zfcp_fc_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);
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
 }
 
 static void zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req)
 {
        struct fsf_status_read_buffer *status_buffer = (void *)fsf_req->data;
-       struct fcp_rscn_head *fcp_rscn_head;
-       struct fcp_rscn_element *fcp_rscn_element;
+       struct fc_els_rscn *head;
+       struct fc_els_rscn_page *page;
        u16 i;
        u16 no_entries;
-       u32 range_mask;
+       unsigned int afmt;
 
-       fcp_rscn_head = (struct fcp_rscn_head *) status_buffer->payload.data;
-       fcp_rscn_element = (struct fcp_rscn_element *) fcp_rscn_head;
+       head = (struct fc_els_rscn *) status_buffer->payload.data;
+       page = (struct fc_els_rscn_page *) head;
 
        /* see FC-FS */
-       no_entries = fcp_rscn_head->payload_len /
-                       sizeof(struct fcp_rscn_element);
+       no_entries = head->rscn_plen / sizeof(struct fc_els_rscn_page);
 
        for (i = 1; i < no_entries; i++) {
                /* skip head and start with 1st element */
-               fcp_rscn_element++;
-               range_mask = rscn_range_mask[fcp_rscn_element->addr_format];
-               _zfcp_fc_incoming_rscn(fsf_req, range_mask, fcp_rscn_element);
+               page++;
+               afmt = page->rscn_page_flags & ELS_RSCN_ADDR_FMT_MASK;
+               _zfcp_fc_incoming_rscn(fsf_req, zfcp_fc_rscn_range_mask[afmt],
+                                      page);
        }
-       schedule_work(&fsf_req->adapter->scan_work);
+       queue_work(fsf_req->adapter->work_queue, &fsf_req->adapter->scan_work);
 }
 
 static void zfcp_fc_incoming_wwpn(struct zfcp_fsf_req *req, u64 wwpn)
 {
+       unsigned long flags;
        struct zfcp_adapter *adapter = req->adapter;
        struct zfcp_port *port;
-       unsigned long flags;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       list_for_each_entry(port, &adapter->port_list_head, list)
-               if (port->wwpn == wwpn)
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list)
+               if (port->wwpn == wwpn) {
+                       zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req);
                        break;
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
-
-       if (port && (port->wwpn == wwpn))
-               zfcp_erp_port_forced_reopen(port, 0, "fciwwp1", req);
+               }
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
 }
 
 static void zfcp_fc_incoming_plogi(struct zfcp_fsf_req *req)
 {
-       struct fsf_status_read_buffer *status_buffer =
-               (struct fsf_status_read_buffer *)req->data;
-       struct fsf_plogi *els_plogi =
-               (struct fsf_plogi *) status_buffer->payload.data;
+       struct fsf_status_read_buffer *status_buffer;
+       struct fc_els_flogi *plogi;
 
-       zfcp_fc_incoming_wwpn(req, els_plogi->serv_param.wwpn);
+       status_buffer = (struct fsf_status_read_buffer *) req->data;
+       plogi = (struct fc_els_flogi *) status_buffer->payload.data;
+       zfcp_fc_incoming_wwpn(req, plogi->fl_wwpn);
 }
 
 static void zfcp_fc_incoming_logo(struct zfcp_fsf_req *req)
 {
        struct fsf_status_read_buffer *status_buffer =
                (struct fsf_status_read_buffer *)req->data;
-       struct fcp_logo *els_logo =
-               (struct fcp_logo *) status_buffer->payload.data;
+       struct fc_els_logo *logo =
+               (struct fc_els_logo *) status_buffer->payload.data;
 
-       zfcp_fc_incoming_wwpn(req, els_logo->nport_wwpn);
+       zfcp_fc_incoming_wwpn(req, logo->fl_n_port_wwn);
 }
 
 /**
@@ -232,79 +197,72 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
        unsigned int els_type = status_buffer->payload.data[0];
 
        zfcp_dbf_san_incoming_els(fsf_req);
-       if (els_type == LS_PLOGI)
+       if (els_type == ELS_PLOGI)
                zfcp_fc_incoming_plogi(fsf_req);
-       else if (els_type == LS_LOGO)
+       else if (els_type == ELS_LOGO)
                zfcp_fc_incoming_logo(fsf_req);
-       else if (els_type == LS_RSCN)
+       else if (els_type == ELS_RSCN)
                zfcp_fc_incoming_rscn(fsf_req);
 }
 
-static void zfcp_fc_ns_handler(unsigned long data)
+static void zfcp_fc_ns_gid_pn_eval(void *data)
 {
-       struct zfcp_fc_ns_handler_data *compl_rec =
-                       (struct zfcp_fc_ns_handler_data *) data;
-
-       if (compl_rec->handler)
-               compl_rec->handler(compl_rec->handler_data);
-
-       complete(&compl_rec->done);
-}
-
-static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
-{
-       struct zfcp_gid_pn_data *gid_pn = (struct zfcp_gid_pn_data *) data;
-       struct zfcp_send_ct *ct = &gid_pn->ct;
-       struct ct_iu_gid_pn_req *ct_iu_req = sg_virt(ct->req);
-       struct ct_iu_gid_pn_resp *ct_iu_resp = sg_virt(ct->resp);
+       struct zfcp_fc_gid_pn *gid_pn = data;
+       struct zfcp_fsf_ct_els *ct = &gid_pn->ct;
+       struct zfcp_fc_gid_pn_req *gid_pn_req = sg_virt(ct->req);
+       struct zfcp_fc_gid_pn_resp *gid_pn_resp = sg_virt(ct->resp);
        struct zfcp_port *port = gid_pn->port;
 
        if (ct->status)
                return;
-       if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT)
+       if (gid_pn_resp->ct_hdr.ct_cmd != FC_FS_ACC)
                return;
 
        /* paranoia */
-       if (ct_iu_req->wwpn != port->wwpn)
+       if (gid_pn_req->gid_pn.fn_wwpn != port->wwpn)
                return;
        /* looks like a valid d_id */
-       port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
+       port->d_id = ntoh24(gid_pn_resp->gid_pn.fp_fid);
+}
+
+static void zfcp_fc_complete(void *data)
+{
+       complete(data);
 }
 
 static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port,
-                                    struct zfcp_gid_pn_data *gid_pn)
+                                    struct zfcp_fc_gid_pn *gid_pn)
 {
        struct zfcp_adapter *adapter = port->adapter;
-       struct zfcp_fc_ns_handler_data compl_rec;
+       DECLARE_COMPLETION_ONSTACK(completion);
        int ret;
 
        /* setup parameters for send generic command */
        gid_pn->port = port;
-       gid_pn->ct.wka_port = &adapter->gs->ds;
-       gid_pn->ct.handler = zfcp_fc_ns_handler;
-       gid_pn->ct.handler_data = (unsigned long) &compl_rec;
-       gid_pn->ct.req = &gid_pn->req;
-       gid_pn->ct.resp = &gid_pn->resp;
-       sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
-                   sizeof(struct ct_iu_gid_pn_req));
-       sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
-                   sizeof(struct ct_iu_gid_pn_resp));
+       gid_pn->ct.handler = zfcp_fc_complete;
+       gid_pn->ct.handler_data = &completion;
+       gid_pn->ct.req = &gid_pn->sg_req;
+       gid_pn->ct.resp = &gid_pn->sg_resp;
+       sg_init_one(&gid_pn->sg_req, &gid_pn->gid_pn_req,
+                   sizeof(struct zfcp_fc_gid_pn_req));
+       sg_init_one(&gid_pn->sg_resp, &gid_pn->gid_pn_resp,
+                   sizeof(struct zfcp_fc_gid_pn_resp));
 
        /* setup nameserver request */
-       gid_pn->ct_iu_req.header.revision = ZFCP_CT_REVISION;
-       gid_pn->ct_iu_req.header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
-       gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
-       gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
-       gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
-       gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
-       gid_pn->ct_iu_req.wwpn = port->wwpn;
-
-       init_completion(&compl_rec.done);
-       compl_rec.handler = zfcp_fc_ns_gid_pn_eval;
-       compl_rec.handler_data = (unsigned long) gid_pn;
-       ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.gid_pn_req);
-       if (!ret)
-               wait_for_completion(&compl_rec.done);
+       gid_pn->gid_pn_req.ct_hdr.ct_rev = FC_CT_REV;
+       gid_pn->gid_pn_req.ct_hdr.ct_fs_type = FC_FST_DIR;
+       gid_pn->gid_pn_req.ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+       gid_pn->gid_pn_req.ct_hdr.ct_options = 0;
+       gid_pn->gid_pn_req.ct_hdr.ct_cmd = FC_NS_GID_PN;
+       gid_pn->gid_pn_req.ct_hdr.ct_mr_size = ZFCP_FC_CT_SIZE_PAGE / 4;
+       gid_pn->gid_pn_req.gid_pn.fn_wwpn = port->wwpn;
+
+       ret = zfcp_fsf_send_ct(&adapter->gs->ds, &gid_pn->ct,
+                              adapter->pool.gid_pn_req);
+       if (!ret) {
+               wait_for_completion(&completion);
+               zfcp_fc_ns_gid_pn_eval(gid_pn);
+       }
        return ret;
 }
 
@@ -316,10 +274,10 @@ static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port,
 static int zfcp_fc_ns_gid_pn(struct zfcp_port *port)
 {
        int ret;
-       struct zfcp_gid_pn_data *gid_pn;
+       struct zfcp_fc_gid_pn *gid_pn;
        struct zfcp_adapter *adapter = port->adapter;
 
-       gid_pn = mempool_alloc(adapter->pool.gid_pn_data, GFP_ATOMIC);
+       gid_pn = mempool_alloc(adapter->pool.gid_pn, GFP_ATOMIC);
        if (!gid_pn)
                return -ENOMEM;
 
@@ -333,7 +291,7 @@ static int zfcp_fc_ns_gid_pn(struct zfcp_port *port)
 
        zfcp_fc_wka_port_put(&adapter->gs->ds);
 out:
-       mempool_free(gid_pn, adapter->pool.gid_pn_data);
+       mempool_free(gid_pn, adapter->pool.gid_pn);
        return ret;
 }
 
@@ -357,7 +315,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work)
 
        zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL);
 out:
-       zfcp_port_put(port);
+       put_device(&port->sysfs_device);
 }
 
 /**
@@ -366,9 +324,9 @@ out:
  */
 void zfcp_fc_trigger_did_lookup(struct zfcp_port *port)
 {
-       zfcp_port_get(port);
+       get_device(&port->sysfs_device);
        if (!queue_work(port->adapter->work_queue, &port->gid_pn_work))
-               zfcp_port_put(port);
+               put_device(&port->sysfs_device);
 }
 
 /**
@@ -378,33 +336,36 @@ void zfcp_fc_trigger_did_lookup(struct zfcp_port *port)
  *
  * Evaluate PLOGI playload and copy important fields into zfcp_port structure
  */
-void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fsf_plogi *plogi)
-{
-       port->maxframe_size = plogi->serv_param.common_serv_param[7] |
-               ((plogi->serv_param.common_serv_param[6] & 0x0F) << 8);
-       if (plogi->serv_param.class1_serv_param[0] & 0x80)
+void zfcp_fc_plogi_evaluate(struct zfcp_port *port, struct fc_els_flogi *plogi)
+{
+       if (plogi->fl_wwpn != port->wwpn) {
+               port->d_id = 0;
+               dev_warn(&port->adapter->ccw_device->dev,
+                        "A port opened with WWPN 0x%016Lx returned data that "
+                        "identifies it as WWPN 0x%016Lx\n",
+                        (unsigned long long) port->wwpn,
+                        (unsigned long long) plogi->fl_wwpn);
+               return;
+       }
+
+       port->wwnn = plogi->fl_wwnn;
+       port->maxframe_size = plogi->fl_csp.sp_bb_data;
+
+       if (plogi->fl_cssp[0].cp_class & FC_CPC_VALID)
                port->supported_classes |= FC_COS_CLASS1;
-       if (plogi->serv_param.class2_serv_param[0] & 0x80)
+       if (plogi->fl_cssp[1].cp_class & FC_CPC_VALID)
                port->supported_classes |= FC_COS_CLASS2;
-       if (plogi->serv_param.class3_serv_param[0] & 0x80)
+       if (plogi->fl_cssp[2].cp_class & FC_CPC_VALID)
                port->supported_classes |= FC_COS_CLASS3;
-       if (plogi->serv_param.class4_serv_param[0] & 0x80)
+       if (plogi->fl_cssp[3].cp_class & FC_CPC_VALID)
                port->supported_classes |= FC_COS_CLASS4;
 }
 
-struct zfcp_els_adisc {
-       struct zfcp_send_els els;
-       struct scatterlist req;
-       struct scatterlist resp;
-       struct zfcp_ls_adisc ls_adisc;
-       struct zfcp_ls_adisc ls_adisc_acc;
-};
-
-static void zfcp_fc_adisc_handler(unsigned long data)
+static void zfcp_fc_adisc_handler(void *data)
 {
-       struct zfcp_els_adisc *adisc = (struct zfcp_els_adisc *) data;
+       struct zfcp_fc_els_adisc *adisc = data;
        struct zfcp_port *port = adisc->els.port;
-       struct zfcp_ls_adisc *ls_adisc = &adisc->ls_adisc_acc;
+       struct fc_els_adisc *adisc_resp = &adisc->adisc_resp;
 
        if (adisc->els.status) {
                /* request rejected or timed out */
@@ -414,9 +375,9 @@ static void zfcp_fc_adisc_handler(unsigned long data)
        }
 
        if (!port->wwnn)
-               port->wwnn = ls_adisc->wwnn;
+               port->wwnn = adisc_resp->adisc_wwnn;
 
-       if ((port->wwpn != ls_adisc->wwpn) ||
+       if ((port->wwpn != adisc_resp->adisc_wwpn) ||
            !(atomic_read(&port->status) & ZFCP_STATUS_COMMON_OPEN)) {
                zfcp_erp_port_reopen(port, ZFCP_STATUS_COMMON_ERP_FAILED,
                                     "fcadh_2", NULL);
@@ -427,40 +388,44 @@ static void zfcp_fc_adisc_handler(unsigned long data)
        zfcp_scsi_schedule_rport_register(port);
  out:
        atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
-       zfcp_port_put(port);
-       kfree(adisc);
+       put_device(&port->sysfs_device);
+       kmem_cache_free(zfcp_data.adisc_cache, adisc);
 }
 
 static int zfcp_fc_adisc(struct zfcp_port *port)
 {
-       struct zfcp_els_adisc *adisc;
+       struct zfcp_fc_els_adisc *adisc;
        struct zfcp_adapter *adapter = port->adapter;
+       int ret;
 
-       adisc = kzalloc(sizeof(struct zfcp_els_adisc), GFP_ATOMIC);
+       adisc = kmem_cache_alloc(zfcp_data.adisc_cache, GFP_ATOMIC);
        if (!adisc)
                return -ENOMEM;
 
+       adisc->els.port = port;
        adisc->els.req = &adisc->req;
        adisc->els.resp = &adisc->resp;
-       sg_init_one(adisc->els.req, &adisc->ls_adisc,
-                   sizeof(struct zfcp_ls_adisc));
-       sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
-                   sizeof(struct zfcp_ls_adisc));
+       sg_init_one(adisc->els.req, &adisc->adisc_req,
+                   sizeof(struct fc_els_adisc));
+       sg_init_one(adisc->els.resp, &adisc->adisc_resp,
+                   sizeof(struct fc_els_adisc));
 
-       adisc->els.adapter = adapter;
-       adisc->els.port = port;
-       adisc->els.d_id = port->d_id;
        adisc->els.handler = zfcp_fc_adisc_handler;
-       adisc->els.handler_data = (unsigned long) adisc;
-       adisc->els.ls_code = adisc->ls_adisc.code = ZFCP_LS_ADISC;
+       adisc->els.handler_data = adisc;
 
        /* acc. to FC-FS, hard_nport_id in ADISC should not be set for ports
           without FC-AL-2 capability, so we don't set it */
-       adisc->ls_adisc.wwpn = fc_host_port_name(adapter->scsi_host);
-       adisc->ls_adisc.wwnn = fc_host_node_name(adapter->scsi_host);
-       adisc->ls_adisc.nport_id = fc_host_port_id(adapter->scsi_host);
+       adisc->adisc_req.adisc_wwpn = fc_host_port_name(adapter->scsi_host);
+       adisc->adisc_req.adisc_wwnn = fc_host_node_name(adapter->scsi_host);
+       adisc->adisc_req.adisc_cmd = ELS_ADISC;
+       hton24(adisc->adisc_req.adisc_port_id,
+              fc_host_port_id(adapter->scsi_host));
+
+       ret = zfcp_fsf_send_els(adapter, port->d_id, &adisc->els);
+       if (ret)
+               kmem_cache_free(zfcp_data.adisc_cache, adisc);
 
-       return zfcp_fsf_send_els(&adisc->els);
+       return ret;
 }
 
 void zfcp_fc_link_test_work(struct work_struct *work)
@@ -469,7 +434,7 @@ void zfcp_fc_link_test_work(struct work_struct *work)
                container_of(work, struct zfcp_port, test_link_work);
        int retval;
 
-       zfcp_port_get(port);
+       get_device(&port->sysfs_device);
        port->rport_task = RPORT_DEL;
        zfcp_scsi_rport_work(&port->rport_work);
 
@@ -488,7 +453,7 @@ void zfcp_fc_link_test_work(struct work_struct *work)
        zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
 
 out:
-       zfcp_port_put(port);
+       put_device(&port->sysfs_device);
 }
 
 /**
@@ -501,12 +466,12 @@ out:
  */
 void zfcp_fc_test_link(struct zfcp_port *port)
 {
-       zfcp_port_get(port);
+       get_device(&port->sysfs_device);
        if (!queue_work(port->adapter->work_queue, &port->test_link_work))
-               zfcp_port_put(port);
+               put_device(&port->sysfs_device);
 }
 
-static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
+static void zfcp_free_sg_env(struct zfcp_fc_gpn_ft *gpn_ft, int buf_num)
 {
        struct scatterlist *sg = &gpn_ft->sg_req;
 
@@ -516,10 +481,10 @@ static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
        kfree(gpn_ft);
 }
 
-static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
+static struct zfcp_fc_gpn_ft *zfcp_alloc_sg_env(int buf_num)
 {
-       struct zfcp_gpn_ft *gpn_ft;
-       struct ct_iu_gpn_ft_req *req;
+       struct zfcp_fc_gpn_ft *gpn_ft;
+       struct zfcp_fc_gpn_ft_req *req;
 
        gpn_ft = kzalloc(sizeof(*gpn_ft), GFP_KERNEL);
        if (!gpn_ft)
@@ -542,159 +507,152 @@ out:
 }
 
 
-static int zfcp_fc_send_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
+static int zfcp_fc_send_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
                               struct zfcp_adapter *adapter, int max_bytes)
 {
-       struct zfcp_send_ct *ct = &gpn_ft->ct;
-       struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
-       struct zfcp_fc_ns_handler_data compl_rec;
+       struct zfcp_fsf_ct_els *ct = &gpn_ft->ct;
+       struct zfcp_fc_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
+       DECLARE_COMPLETION_ONSTACK(completion);
        int ret;
 
        /* prepare CT IU for GPN_FT */
-       req->header.revision = ZFCP_CT_REVISION;
-       req->header.gs_type = ZFCP_CT_DIRECTORY_SERVICE;
-       req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
-       req->header.options = ZFCP_CT_SYNCHRONOUS;
-       req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
-       req->header.max_res_size = max_bytes / 4;
-       req->flags = 0;
-       req->domain_id_scope = 0;
-       req->area_id_scope = 0;
-       req->fc4_type = ZFCP_CT_SCSI_FCP;
+       req->ct_hdr.ct_rev = FC_CT_REV;
+       req->ct_hdr.ct_fs_type = FC_FST_DIR;
+       req->ct_hdr.ct_fs_subtype = FC_NS_SUBTYPE;
+       req->ct_hdr.ct_options = 0;
+       req->ct_hdr.ct_cmd = FC_NS_GPN_FT;
+       req->ct_hdr.ct_mr_size = max_bytes / 4;
+       req->gpn_ft.fn_domain_id_scope = 0;
+       req->gpn_ft.fn_area_id_scope = 0;
+       req->gpn_ft.fn_fc4_type = FC_TYPE_FCP;
 
        /* prepare zfcp_send_ct */
-       ct->wka_port = &adapter->gs->ds;
-       ct->handler = zfcp_fc_ns_handler;
-       ct->handler_data = (unsigned long)&compl_rec;
+       ct->handler = zfcp_fc_complete;
+       ct->handler_data = &completion;
        ct->req = &gpn_ft->sg_req;
        ct->resp = gpn_ft->sg_resp;
 
-       init_completion(&compl_rec.done);
-       compl_rec.handler = NULL;
-       ret = zfcp_fsf_send_ct(ct, NULL);
+       ret = zfcp_fsf_send_ct(&adapter->gs->ds, ct, NULL);
        if (!ret)
-               wait_for_completion(&compl_rec.done);
+               wait_for_completion(&completion);
        return ret;
 }
 
-static void zfcp_fc_validate_port(struct zfcp_port *port)
+static void zfcp_fc_validate_port(struct zfcp_port *port, struct list_head *lh)
 {
-       struct zfcp_adapter *adapter = port->adapter;
-
        if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_NOESC))
                return;
 
        atomic_clear_mask(ZFCP_STATUS_COMMON_NOESC, &port->status);
 
        if ((port->supported_classes != 0) ||
-           !list_empty(&port->unit_list_head)) {
-               zfcp_port_put(port);
+           !list_empty(&port->unit_list))
                return;
-       }
-       zfcp_erp_port_shutdown(port, 0, "fcpval1", NULL);
-       zfcp_erp_wait(adapter);
-       zfcp_port_put(port);
-       zfcp_port_dequeue(port);
+
+       list_move_tail(&port->list, lh);
 }
 
-static int zfcp_fc_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
+static int zfcp_fc_eval_gpn_ft(struct zfcp_fc_gpn_ft *gpn_ft,
+                              struct zfcp_adapter *adapter, int max_entries)
 {
-       struct zfcp_send_ct *ct = &gpn_ft->ct;
+       struct zfcp_fsf_ct_els *ct = &gpn_ft->ct;
        struct scatterlist *sg = gpn_ft->sg_resp;
-       struct ct_hdr *hdr = sg_virt(sg);
-       struct gpn_ft_resp_acc *acc = sg_virt(sg);
-       struct zfcp_adapter *adapter = ct->wka_port->adapter;
+       struct fc_ct_hdr *hdr = sg_virt(sg);
+       struct fc_gpn_ft_resp *acc = sg_virt(sg);
        struct zfcp_port *port, *tmp;
+       unsigned long flags;
+       LIST_HEAD(remove_lh);
        u32 d_id;
        int ret = 0, x, last = 0;
 
        if (ct->status)
                return -EIO;
 
-       if (hdr->cmd_rsp_code != ZFCP_CT_ACCEPT) {
-               if (hdr->reason_code == ZFCP_CT_UNABLE_TO_PERFORM_CMD)
+       if (hdr->ct_cmd != FC_FS_ACC) {
+               if (hdr->ct_reason == FC_BA_RJT_UNABLE)
                        return -EAGAIN; /* might be a temporary condition */
                return -EIO;
        }
 
-       if (hdr->max_res_size) {
+       if (hdr->ct_mr_size) {
                dev_warn(&adapter->ccw_device->dev,
                         "The name server reported %d words residual data\n",
-                        hdr->max_res_size);
+                        hdr->ct_mr_size);
                return -E2BIG;
        }
 
-       mutex_lock(&zfcp_data.config_mutex);
-
        /* first entry is the header */
        for (x = 1; x < max_entries && !last; x++) {
-               if (x % (ZFCP_GPN_FT_ENTRIES + 1))
+               if (x % (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
                        acc++;
                else
                        acc = sg_virt(++sg);
 
-               last = acc->control & 0x80;
-               d_id = acc->port_id[0] << 16 | acc->port_id[1] << 8 |
-                      acc->port_id[2];
+               last = acc->fp_flags & FC_NS_FID_LAST;
+               d_id = ntoh24(acc->fp_fid);
 
                /* don't attach ports with a well known address */
-               if ((d_id & ZFCP_DID_WKA) == ZFCP_DID_WKA)
+               if (d_id >= FC_FID_WELL_KNOWN_BASE)
                        continue;
                /* skip the adapter's port and known remote ports */
-               if (acc->wwpn == fc_host_port_name(adapter->scsi_host))
-                       continue;
-               port = zfcp_get_port_by_wwpn(adapter, acc->wwpn);
-               if (port)
+               if (acc->fp_wwpn == fc_host_port_name(adapter->scsi_host))
                        continue;
 
-               port = zfcp_port_enqueue(adapter, acc->wwpn,
+               port = zfcp_port_enqueue(adapter, acc->fp_wwpn,
                                         ZFCP_STATUS_COMMON_NOESC, d_id);
-               if (IS_ERR(port))
-                       ret = PTR_ERR(port);
-               else
+               if (!IS_ERR(port))
                        zfcp_erp_port_reopen(port, 0, "fcegpf1", NULL);
+               else if (PTR_ERR(port) != -EEXIST)
+                       ret = PTR_ERR(port);
        }
 
        zfcp_erp_wait(adapter);
-       list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
-               zfcp_fc_validate_port(port);
-       mutex_unlock(&zfcp_data.config_mutex);
+       write_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry_safe(port, tmp, &adapter->port_list, list)
+               zfcp_fc_validate_port(port, &remove_lh);
+       write_unlock_irqrestore(&adapter->port_list_lock, flags);
+
+       list_for_each_entry_safe(port, tmp, &remove_lh, list) {
+               zfcp_erp_port_shutdown(port, 0, "fcegpf2", NULL);
+               zfcp_device_unregister(&port->sysfs_device,
+                                      &zfcp_sysfs_port_attrs);
+       }
+
        return ret;
 }
 
 /**
  * zfcp_fc_scan_ports - scan remote ports and attach new ports
- * @adapter: pointer to struct zfcp_adapter
+ * @work: reference to scheduled work
  */
-int zfcp_fc_scan_ports(struct zfcp_adapter *adapter)
+void zfcp_fc_scan_ports(struct work_struct *work)
 {
+       struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter,
+                                                   scan_work);
        int ret, i;
-       struct zfcp_gpn_ft *gpn_ft;
+       struct zfcp_fc_gpn_ft *gpn_ft;
        int chain, max_entries, buf_num, max_bytes;
 
        chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
-       buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1;
-       max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
-       max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
+       buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1;
+       max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE;
+       max_bytes = chain ? ZFCP_FC_GPN_FT_MAX_SIZE : ZFCP_FC_CT_SIZE_PAGE;
 
        if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT &&
            fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV)
-               return 0;
+               return;
 
-       ret = zfcp_fc_wka_port_get(&adapter->gs->ds);
-       if (ret)
-               return ret;
+       if (zfcp_fc_wka_port_get(&adapter->gs->ds))
+               return;
 
        gpn_ft = zfcp_alloc_sg_env(buf_num);
-       if (!gpn_ft) {
-               ret = -ENOMEM;
+       if (!gpn_ft)
                goto out;
-       }
 
        for (i = 0; i < 3; i++) {
                ret = zfcp_fc_send_gpn_ft(gpn_ft, adapter, max_bytes);
                if (!ret) {
-                       ret = zfcp_fc_eval_gpn_ft(gpn_ft, max_entries);
+                       ret = zfcp_fc_eval_gpn_ft(gpn_ft, adapter, max_entries);
                        if (ret == -EAGAIN)
                                ssleep(1);
                        else
@@ -704,174 +662,116 @@ int zfcp_fc_scan_ports(struct zfcp_adapter *adapter)
        zfcp_free_sg_env(gpn_ft, buf_num);
 out:
        zfcp_fc_wka_port_put(&adapter->gs->ds);
-       return ret;
-}
-
-
-void _zfcp_fc_scan_ports_later(struct work_struct *work)
-{
-       zfcp_fc_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
 }
 
-struct zfcp_els_fc_job {
-       struct zfcp_send_els els;
-       struct fc_bsg_job *job;
-};
-
-static void zfcp_fc_generic_els_handler(unsigned long data)
+static void zfcp_fc_ct_els_job_handler(void *data)
 {
-       struct zfcp_els_fc_job *els_fc_job = (struct zfcp_els_fc_job *) data;
-       struct fc_bsg_job *job = els_fc_job->job;
-       struct fc_bsg_reply *reply = job->reply;
-
-       if (els_fc_job->els.status) {
-               /* request rejected or timed out */
-               reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_REJECT;
-               goto out;
-       }
-
-       reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
-       reply->reply_payload_rcv_len = job->reply_payload.payload_len;
+       struct fc_bsg_job *job = data;
+       struct zfcp_fsf_ct_els *zfcp_ct_els = job->dd_data;
+       int status = zfcp_ct_els->status;
+       int reply_status;
 
-out:
-       job->state_flags = FC_RQST_STATE_DONE;
+       reply_status = status ? FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK;
+       job->reply->reply_data.ctels_reply.status = reply_status;
+       job->reply->reply_payload_rcv_len = job->reply_payload.payload_len;
        job->job_done(job);
-       kfree(els_fc_job);
 }
 
-int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *job)
+static int zfcp_fc_exec_els_job(struct fc_bsg_job *job,
+                               struct zfcp_adapter *adapter)
 {
-       struct zfcp_els_fc_job *els_fc_job;
+       struct zfcp_fsf_ct_els *els = job->dd_data;
        struct fc_rport *rport = job->rport;
-       struct Scsi_Host *shost;
-       struct zfcp_adapter *adapter;
        struct zfcp_port *port;
-       u8 *port_did;
-
-       shost = rport ? rport_to_shost(rport) : job->shost;
-       adapter = (struct zfcp_adapter *)shost->hostdata[0];
-
-       if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
-               return -EINVAL;
-
-       els_fc_job = kzalloc(sizeof(struct zfcp_els_fc_job), GFP_KERNEL);
-       if (!els_fc_job)
-               return -ENOMEM;
+       u32 d_id;
 
-       els_fc_job->els.adapter = adapter;
        if (rport) {
-               read_lock_irq(&zfcp_data.config_lock);
                port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
-               if (port)
-                       els_fc_job->els.d_id = port->d_id;
-               read_unlock_irq(&zfcp_data.config_lock);
-               if (!port) {
-                       kfree(els_fc_job);
+               if (!port)
                        return -EINVAL;
-               }
-       } else {
-               port_did = job->request->rqst_data.h_els.port_id;
-               els_fc_job->els.d_id = (port_did[0] << 16) +
-                                       (port_did[1] << 8) + port_did[2];
-       }
-
-       els_fc_job->els.req = job->request_payload.sg_list;
-       els_fc_job->els.resp = job->reply_payload.sg_list;
-       els_fc_job->els.handler = zfcp_fc_generic_els_handler;
-       els_fc_job->els.handler_data = (unsigned long) els_fc_job;
-       els_fc_job->job = job;
-
-       return zfcp_fsf_send_els(&els_fc_job->els);
-}
-
-struct zfcp_ct_fc_job {
-       struct zfcp_send_ct ct;
-       struct fc_bsg_job *job;
-};
-
-static void zfcp_fc_generic_ct_handler(unsigned long data)
-{
-       struct zfcp_ct_fc_job *ct_fc_job = (struct zfcp_ct_fc_job *) data;
-       struct fc_bsg_job *job = ct_fc_job->job;
-
-       job->reply->reply_data.ctels_reply.status = ct_fc_job->ct.status ?
-                               FC_CTELS_STATUS_REJECT : FC_CTELS_STATUS_OK;
-       job->reply->reply_payload_rcv_len = job->reply_payload.payload_len;
-       job->state_flags = FC_RQST_STATE_DONE;
-       job->job_done(job);
 
-       zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port);
+               d_id = port->d_id;
+               put_device(&port->sysfs_device);
+       } else
+               d_id = ntoh24(job->request->rqst_data.h_els.port_id);
 
-       kfree(ct_fc_job);
+       return zfcp_fsf_send_els(adapter, d_id, els);
 }
 
-int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *job)
+static int zfcp_fc_exec_ct_job(struct fc_bsg_job *job,
+                              struct zfcp_adapter *adapter)
 {
        int ret;
        u8 gs_type;
-       struct fc_rport *rport = job->rport;
-       struct Scsi_Host *shost;
-       struct zfcp_adapter *adapter;
-       struct zfcp_ct_fc_job *ct_fc_job;
+       struct zfcp_fsf_ct_els *ct = job->dd_data;
+       struct zfcp_fc_wka_port *wka_port;
        u32 preamble_word1;
 
-       shost = rport ? rport_to_shost(rport) : job->shost;
-
-       adapter = (struct zfcp_adapter *)shost->hostdata[0];
-       if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
-               return -EINVAL;
-
-       ct_fc_job = kzalloc(sizeof(struct zfcp_ct_fc_job), GFP_KERNEL);
-       if (!ct_fc_job)
-               return -ENOMEM;
-
        preamble_word1 = job->request->rqst_data.r_ct.preamble_word1;
        gs_type = (preamble_word1 & 0xff000000) >> 24;
 
        switch (gs_type) {
        case FC_FST_ALIAS:
-               ct_fc_job->ct.wka_port = &adapter->gs->as;
+               wka_port = &adapter->gs->as;
                break;
        case FC_FST_MGMT:
-               ct_fc_job->ct.wka_port = &adapter->gs->ms;
+               wka_port = &adapter->gs->ms;
                break;
        case FC_FST_TIME:
-               ct_fc_job->ct.wka_port = &adapter->gs->ts;
+               wka_port = &adapter->gs->ts;
                break;
        case FC_FST_DIR:
-               ct_fc_job->ct.wka_port = &adapter->gs->ds;
+               wka_port = &adapter->gs->ds;
                break;
        default:
-               kfree(ct_fc_job);
                return -EINVAL; /* no such service */
        }
 
-       ret = zfcp_fc_wka_port_get(ct_fc_job->ct.wka_port);
-       if (ret) {
-               kfree(ct_fc_job);
+       ret = zfcp_fc_wka_port_get(wka_port);
+       if (ret)
                return ret;
-       }
 
-       ct_fc_job->ct.req = job->request_payload.sg_list;
-       ct_fc_job->ct.resp = job->reply_payload.sg_list;
-       ct_fc_job->ct.handler = zfcp_fc_generic_ct_handler;
-       ct_fc_job->ct.handler_data = (unsigned long) ct_fc_job;
-       ct_fc_job->ct.completion = NULL;
-       ct_fc_job->job = job;
+       ret = zfcp_fsf_send_ct(wka_port, ct, NULL);
+       if (ret)
+               zfcp_fc_wka_port_put(wka_port);
 
-       ret = zfcp_fsf_send_ct(&ct_fc_job->ct, NULL);
-       if (ret) {
-               kfree(ct_fc_job);
-               zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port);
-       }
        return ret;
 }
 
+int zfcp_fc_exec_bsg_job(struct fc_bsg_job *job)
+{
+       struct Scsi_Host *shost;
+       struct zfcp_adapter *adapter;
+       struct zfcp_fsf_ct_els *ct_els = job->dd_data;
+
+       shost = job->rport ? rport_to_shost(job->rport) : job->shost;
+       adapter = (struct zfcp_adapter *)shost->hostdata[0];
+
+       if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_OPEN))
+               return -EINVAL;
+
+       ct_els->req = job->request_payload.sg_list;
+       ct_els->resp = job->reply_payload.sg_list;
+       ct_els->handler = zfcp_fc_ct_els_job_handler;
+       ct_els->handler_data = job;
+
+       switch (job->request->msgcode) {
+       case FC_BSG_RPT_ELS:
+       case FC_BSG_HST_ELS_NOLOGIN:
+               return zfcp_fc_exec_els_job(job, adapter);
+       case FC_BSG_RPT_CT:
+       case FC_BSG_HST_CT:
+               return zfcp_fc_exec_ct_job(job, adapter);
+       default:
+               return -EINVAL;
+       }
+}
+
 int zfcp_fc_gs_setup(struct zfcp_adapter *adapter)
 {
-       struct zfcp_wka_ports *wka_ports;
+       struct zfcp_fc_wka_ports *wka_ports;
 
-       wka_ports = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL);
+       wka_ports = kzalloc(sizeof(struct zfcp_fc_wka_ports), GFP_KERNEL);
        if (!wka_ports)
                return -ENOMEM;
 
@@ -880,7 +780,6 @@ int zfcp_fc_gs_setup(struct zfcp_adapter *adapter)
        zfcp_fc_wka_port_init(&wka_ports->ts, FC_FID_TIME_SERV, adapter);
        zfcp_fc_wka_port_init(&wka_ports->ds, FC_FID_DIR_SERV, adapter);
        zfcp_fc_wka_port_init(&wka_ports->as, FC_FID_ALIASES, adapter);
-       zfcp_fc_wka_port_init(&wka_ports->ks, FC_FID_SEC_KEY, adapter);
 
        return 0;
 }
diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h
new file mode 100644 (file)
index 0000000..cb2a366
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * zfcp device driver
+ *
+ * Fibre Channel related definitions and inline functions for the zfcp
+ * device driver
+ *
+ * Copyright IBM Corporation 2009
+ */
+
+#ifndef ZFCP_FC_H
+#define ZFCP_FC_H
+
+#include <scsi/fc/fc_els.h>
+#include <scsi/fc/fc_fcp.h>
+#include <scsi/fc/fc_ns.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_tcq.h>
+#include "zfcp_fsf.h"
+
+#define ZFCP_FC_CT_SIZE_PAGE     (PAGE_SIZE - sizeof(struct fc_ct_hdr))
+#define ZFCP_FC_GPN_FT_ENT_PAGE          (ZFCP_FC_CT_SIZE_PAGE \
+                                       / sizeof(struct fc_gpn_ft_resp))
+#define ZFCP_FC_GPN_FT_NUM_BUFS          4 /* memory pages */
+
+#define ZFCP_FC_GPN_FT_MAX_SIZE          (ZFCP_FC_GPN_FT_NUM_BUFS * PAGE_SIZE \
+                                       - sizeof(struct fc_ct_hdr))
+#define ZFCP_FC_GPN_FT_MAX_ENT   (ZFCP_FC_GPN_FT_NUM_BUFS * \
+                                       (ZFCP_FC_GPN_FT_ENT_PAGE + 1))
+
+/**
+ * struct zfcp_fc_gid_pn_req - container for ct header plus gid_pn request
+ * @ct_hdr: FC GS common transport header
+ * @gid_pn: GID_PN request
+ */
+struct zfcp_fc_gid_pn_req {
+       struct fc_ct_hdr        ct_hdr;
+       struct fc_ns_gid_pn     gid_pn;
+} __packed;
+
+/**
+ * struct zfcp_fc_gid_pn_resp - container for ct header plus gid_pn response
+ * @ct_hdr: FC GS common transport header
+ * @gid_pn: GID_PN response
+ */
+struct zfcp_fc_gid_pn_resp {
+       struct fc_ct_hdr        ct_hdr;
+       struct fc_gid_pn_resp   gid_pn;
+} __packed;
+
+/**
+ * struct zfcp_fc_gid_pn - everything required in zfcp for gid_pn request
+ * @ct: data passed to zfcp_fsf for issuing fsf request
+ * @sg_req: scatterlist entry for request data
+ * @sg_resp: scatterlist entry for response data
+ * @gid_pn_req: GID_PN request data
+ * @gid_pn_resp: GID_PN response data
+ */
+struct zfcp_fc_gid_pn {
+       struct zfcp_fsf_ct_els ct;
+       struct scatterlist sg_req;
+       struct scatterlist sg_resp;
+       struct zfcp_fc_gid_pn_req gid_pn_req;
+       struct zfcp_fc_gid_pn_resp gid_pn_resp;
+       struct zfcp_port *port;
+};
+
+/**
+ * struct zfcp_fc_gpn_ft - container for ct header plus gpn_ft request
+ * @ct_hdr: FC GS common transport header
+ * @gpn_ft: GPN_FT request
+ */
+struct zfcp_fc_gpn_ft_req {
+       struct fc_ct_hdr        ct_hdr;
+       struct fc_ns_gid_ft     gpn_ft;
+} __packed;
+
+/**
+ * struct zfcp_fc_gpn_ft_resp - container for ct header plus gpn_ft response
+ * @ct_hdr: FC GS common transport header
+ * @gpn_ft: Array of gpn_ft response data to fill one memory page
+ */
+struct zfcp_fc_gpn_ft_resp {
+       struct fc_ct_hdr        ct_hdr;
+       struct fc_gpn_ft_resp   gpn_ft[ZFCP_FC_GPN_FT_ENT_PAGE];
+} __packed;
+
+/**
+ * struct zfcp_fc_gpn_ft - zfcp data for gpn_ft request
+ * @ct: data passed to zfcp_fsf for issuing fsf request
+ * @sg_req: scatter list entry for gpn_ft request
+ * @sg_resp: scatter list entries for gpn_ft responses (per memory page)
+ */
+struct zfcp_fc_gpn_ft {
+       struct zfcp_fsf_ct_els ct;
+       struct scatterlist sg_req;
+       struct scatterlist sg_resp[ZFCP_FC_GPN_FT_NUM_BUFS];
+};
+
+/**
+ * struct zfcp_fc_els_adisc - everything required in zfcp for issuing ELS ADISC
+ * @els: data required for issuing els fsf command
+ * @req: scatterlist entry for ELS ADISC request
+ * @resp: scatterlist entry for ELS ADISC response
+ * @adisc_req: ELS ADISC request data
+ * @adisc_resp: ELS ADISC response data
+ */
+struct zfcp_fc_els_adisc {
+       struct zfcp_fsf_ct_els els;
+       struct scatterlist req;
+       struct scatterlist resp;
+       struct fc_els_adisc adisc_req;
+       struct fc_els_adisc adisc_resp;
+};
+
+/**
+ * enum zfcp_fc_wka_status - FC WKA port status in zfcp
+ * @ZFCP_FC_WKA_PORT_OFFLINE: Port is closed and not in use
+ * @ZFCP_FC_WKA_PORT_CLOSING: The FSF "close port" request is pending
+ * @ZFCP_FC_WKA_PORT_OPENING: The FSF "open port" request is pending
+ * @ZFCP_FC_WKA_PORT_ONLINE: The port is open and the port handle is valid
+ */
+enum zfcp_fc_wka_status {
+       ZFCP_FC_WKA_PORT_OFFLINE,
+       ZFCP_FC_WKA_PORT_CLOSING,
+       ZFCP_FC_WKA_PORT_OPENING,
+       ZFCP_FC_WKA_PORT_ONLINE,
+};
+
+/**
+ * struct zfcp_fc_wka_port - representation of well-known-address (WKA) FC port
+ * @adapter: Pointer to adapter structure this WKA port belongs to
+ * @completion_wq: Wait for completion of open/close command
+ * @status: Current status of WKA port
+ * @refcount: Reference count to keep port open as long as it is in use
+ * @d_id: FC destination id or well-known-address
+ * @handle: FSF handle for the open WKA port
+ * @mutex: Mutex used during opening/closing state changes
+ * @work: For delaying the closing of the WKA port
+ */
+struct zfcp_fc_wka_port {
+       struct zfcp_adapter     *adapter;
+       wait_queue_head_t       completion_wq;
+       enum zfcp_fc_wka_status status;
+       atomic_t                refcount;
+       u32                     d_id;
+       u32                     handle;
+       struct mutex            mutex;
+       struct delayed_work     work;
+};
+
+/**
+ * struct zfcp_fc_wka_ports - Data structures for FC generic services
+ * @ms: FC Management service
+ * @ts: FC time service
+ * @ds: FC directory service
+ * @as: FC alias service
+ */
+struct zfcp_fc_wka_ports {
+       struct zfcp_fc_wka_port ms;
+       struct zfcp_fc_wka_port ts;
+       struct zfcp_fc_wka_port ds;
+       struct zfcp_fc_wka_port as;
+};
+
+/**
+ * zfcp_fc_scsi_to_fcp - setup FCP command with data from scsi_cmnd
+ * @fcp: fcp_cmnd to setup
+ * @scsi: scsi_cmnd where to get LUN, task attributes/flags and CDB
+ */
+static inline
+void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi)
+{
+       char tag[2];
+
+       int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun);
+
+       if (scsi_populate_tag_msg(scsi, tag)) {
+               switch (tag[0]) {
+               case MSG_ORDERED_TAG:
+                       fcp->fc_pri_ta |= FCP_PTA_ORDERED;
+                       break;
+               case MSG_SIMPLE_TAG:
+                       fcp->fc_pri_ta |= FCP_PTA_SIMPLE;
+                       break;
+               };
+       } else
+               fcp->fc_pri_ta = FCP_PTA_SIMPLE;
+
+       if (scsi->sc_data_direction == DMA_FROM_DEVICE)
+               fcp->fc_flags |= FCP_CFL_RDDATA;
+       if (scsi->sc_data_direction == DMA_TO_DEVICE)
+               fcp->fc_flags |= FCP_CFL_WRDATA;
+
+       memcpy(fcp->fc_cdb, scsi->cmnd, scsi->cmd_len);
+
+       fcp->fc_dl = scsi_bufflen(scsi);
+}
+
+/**
+ * zfcp_fc_fcp_tm - setup FCP command as task management command
+ * @fcp: fcp_cmnd to setup
+ * @dev: scsi_device where to send the task management command
+ * @tm: task management flags to setup tm command
+ */
+static inline
+void zfcp_fc_fcp_tm(struct fcp_cmnd *fcp, struct scsi_device *dev, u8 tm_flags)
+{
+       int_to_scsilun(dev->lun, (struct scsi_lun *) &fcp->fc_lun);
+       fcp->fc_tm_flags |= tm_flags;
+}
+
+/**
+ * zfcp_fc_evap_fcp_rsp - evaluate FCP RSP IU and update scsi_cmnd accordingly
+ * @fcp_rsp: FCP RSP IU to evaluate
+ * @scsi: SCSI command where to update status and sense buffer
+ */
+static inline
+void zfcp_fc_eval_fcp_rsp(struct fcp_resp_with_ext *fcp_rsp,
+                         struct scsi_cmnd *scsi)
+{
+       struct fcp_resp_rsp_info *rsp_info;
+       char *sense;
+       u32 sense_len, resid;
+       u8 rsp_flags;
+
+       set_msg_byte(scsi, COMMAND_COMPLETE);
+       scsi->result |= fcp_rsp->resp.fr_status;
+
+       rsp_flags = fcp_rsp->resp.fr_flags;
+
+       if (unlikely(rsp_flags & FCP_RSP_LEN_VAL)) {
+               rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1];
+               if (rsp_info->rsp_code == FCP_TMF_CMPL)
+                       set_host_byte(scsi, DID_OK);
+               else {
+                       set_host_byte(scsi, DID_ERROR);
+                       return;
+               }
+       }
+
+       if (unlikely(rsp_flags & FCP_SNS_LEN_VAL)) {
+               sense = (char *) &fcp_rsp[1];
+               if (rsp_flags & FCP_RSP_LEN_VAL)
+                       sense += fcp_rsp->ext.fr_sns_len;
+               sense_len = min(fcp_rsp->ext.fr_sns_len,
+                               (u32) SCSI_SENSE_BUFFERSIZE);
+               memcpy(scsi->sense_buffer, sense, sense_len);
+       }
+
+       if (unlikely(rsp_flags & FCP_RESID_UNDER)) {
+               resid = fcp_rsp->ext.fr_resid;
+               scsi_set_resid(scsi, resid);
+               if (scsi_bufflen(scsi) - resid < scsi->underflow &&
+                    !(rsp_flags & FCP_SNS_LEN_VAL) &&
+                    fcp_rsp->resp.fr_status == SAM_STAT_GOOD)
+                       set_host_byte(scsi, DID_ERROR);
+       }
+}
+
+#endif
index 4e41baa..482dcd9 100644 (file)
@@ -10,7 +10,9 @@
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
 #include <linux/blktrace_api.h>
+#include <scsi/fc/fc_els.h>
 #include "zfcp_ext.h"
+#include "zfcp_fc.h"
 #include "zfcp_dbf.h"
 
 static void zfcp_fsf_request_timeout_handler(unsigned long data)
@@ -122,36 +124,32 @@ void zfcp_fsf_req_free(struct zfcp_fsf_req *req)
 
 static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req)
 {
+       unsigned long flags;
        struct fsf_status_read_buffer *sr_buf = req->data;
        struct zfcp_adapter *adapter = req->adapter;
        struct zfcp_port *port;
-       int d_id = sr_buf->d_id & ZFCP_DID_MASK;
-       unsigned long flags;
+       int d_id = ntoh24(sr_buf->d_id);
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       list_for_each_entry(port, &adapter->port_list_head, list)
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list)
                if (port->d_id == d_id) {
-                       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
                        zfcp_erp_port_reopen(port, 0, "fssrpc1", req);
-                       return;
+                       break;
                }
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
 }
 
 static void zfcp_fsf_link_down_info_eval(struct zfcp_fsf_req *req, char *id,
                                         struct fsf_link_down_info *link_down)
 {
        struct zfcp_adapter *adapter = req->adapter;
-       unsigned long flags;
 
        if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED)
                return;
 
        atomic_set_mask(ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED, &adapter->status);
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
        zfcp_scsi_schedule_rports_block(adapter);
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 
        if (!link_down)
                goto out;
@@ -291,7 +289,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
                        zfcp_erp_adapter_access_changed(adapter, "fssrh_3",
                                                        req);
                if (sr_buf->status_subtype & FSF_STATUS_READ_SUB_INCOMING_ELS)
-                       schedule_work(&adapter->scan_work);
+                       queue_work(adapter->work_queue, &adapter->scan_work);
                break;
        case FSF_STATUS_READ_CFDC_UPDATED:
                zfcp_erp_adapter_access_changed(adapter, "fssrh_4", req);
@@ -317,7 +315,6 @@ static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req)
        case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
                return;
        case FSF_SQ_COMMAND_ABORTED:
-               req->status |= ZFCP_STATUS_FSFREQ_ABORTED;
                break;
        case FSF_SQ_NO_RECOM:
                dev_err(&req->adapter->ccw_device->dev,
@@ -358,8 +355,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
        zfcp_dbf_hba_fsf_response(req);
 
        if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                       ZFCP_STATUS_FSFREQ_RETRY; /* only for SCSI cmnds. */
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                return;
        }
 
@@ -377,7 +373,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
        case FSF_PROT_ERROR_STATE:
        case FSF_PROT_SEQ_NUMB_ERROR:
                zfcp_erp_adapter_reopen(adapter, 0, "fspse_2", req);
-               req->status |= ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_PROT_UNSUPP_QTCB_TYPE:
                dev_err(&adapter->ccw_device->dev,
@@ -480,20 +476,27 @@ void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
 
 static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
 {
-       struct fsf_qtcb_bottom_config *bottom;
+       struct fsf_qtcb_bottom_config *bottom = &req->qtcb->bottom.config;
        struct zfcp_adapter *adapter = req->adapter;
        struct Scsi_Host *shost = adapter->scsi_host;
+       struct fc_els_flogi *nsp, *plogi;
 
-       bottom = &req->qtcb->bottom.config;
+       /* adjust pointers for missing command code */
+       nsp = (struct fc_els_flogi *) ((u8 *)&bottom->nport_serv_param
+                                       - sizeof(u32));
+       plogi = (struct fc_els_flogi *) ((u8 *)&bottom->plogi_payload
+                                       - sizeof(u32));
 
        if (req->data)
                memcpy(req->data, bottom, sizeof(*bottom));
 
-       fc_host_node_name(shost) = bottom->nport_serv_param.wwnn;
-       fc_host_port_name(shost) = bottom->nport_serv_param.wwpn;
-       fc_host_port_id(shost) = bottom->s_id & ZFCP_DID_MASK;
+       fc_host_port_name(shost) = nsp->fl_wwpn;
+       fc_host_node_name(shost) = nsp->fl_wwnn;
+       fc_host_port_id(shost) = ntoh24(bottom->s_id);
        fc_host_speed(shost) = bottom->fc_link_speed;
        fc_host_supported_classes(shost) = FC_COS_CLASS2 | FC_COS_CLASS3;
+       fc_host_supported_fc4s(shost)[2] = 1; /* FCP */
+       fc_host_active_fc4s(shost)[2] = 1; /* FCP */
 
        adapter->hydra_version = bottom->adapter_type;
        adapter->timer_ticks = bottom->timer_interval;
@@ -503,9 +506,9 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
 
        switch (bottom->fc_topology) {
        case FSF_TOPO_P2P:
-               adapter->peer_d_id = bottom->peer_d_id & ZFCP_DID_MASK;
-               adapter->peer_wwpn = bottom->plogi_payload.wwpn;
-               adapter->peer_wwnn = bottom->plogi_payload.wwnn;
+               adapter->peer_d_id = ntoh24(bottom->peer_d_id);
+               adapter->peer_wwpn = plogi->fl_wwpn;
+               adapter->peer_wwnn = plogi->fl_wwnn;
                fc_host_port_type(shost) = FC_PORTTYPE_PTP;
                break;
        case FSF_TOPO_FABRIC:
@@ -881,13 +884,11 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
                break;
        case FSF_PORT_BOXED:
                zfcp_erp_port_boxed(unit->port, "fsafch3", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_LUN_BOXED:
                zfcp_erp_unit_boxed(unit, "fsafch4", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                 break;
        case FSF_ADAPTER_STATUS_AVAILABLE:
                switch (fsq->word[0]) {
@@ -958,10 +959,10 @@ out:
 static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
 {
        struct zfcp_adapter *adapter = req->adapter;
-       struct zfcp_send_ct *send_ct = req->data;
+       struct zfcp_fsf_ct_els *ct = req->data;
        struct fsf_qtcb_header *header = &req->qtcb->header;
 
-       send_ct->status = -EINVAL;
+       ct->status = -EINVAL;
 
        if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
                goto skip_fsfstatus;
@@ -969,7 +970,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
        switch (header->fsf_status) {
         case FSF_GOOD:
                zfcp_dbf_san_ct_response(req);
-               send_ct->status = 0;
+               ct->status = 0;
                break;
         case FSF_SERVICE_CLASS_NOT_SUPPORTED:
                zfcp_fsf_class_not_supp(req);
@@ -985,8 +986,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
        case FSF_ACCESS_DENIED:
                break;
         case FSF_PORT_BOXED:
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_PORT_HANDLE_NOT_VALID:
                zfcp_erp_adapter_reopen(adapter, 0, "fsscth1", req);
@@ -1001,8 +1001,8 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
        }
 
 skip_fsfstatus:
-       if (send_ct->handler)
-               send_ct->handler(send_ct->handler_data);
+       if (ct->handler)
+               ct->handler(ct->handler_data);
 }
 
 static void zfcp_fsf_setup_ct_els_unchained(struct qdio_buffer_element *sbale,
@@ -1071,15 +1071,17 @@ static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req,
                                 int max_sbals)
 {
        int ret;
+       unsigned int fcp_chan_timeout;
 
        ret = zfcp_fsf_setup_ct_els_sbals(req, sg_req, sg_resp, max_sbals);
        if (ret)
                return ret;
 
        /* common settings for ct/gs and els requests */
+       fcp_chan_timeout = 2 * FC_DEF_R_A_TOV / 1000;
        req->qtcb->bottom.support.service_class = FSF_CLASS_3;
-       req->qtcb->bottom.support.timeout = 2 * R_A_TOV;
-       zfcp_fsf_start_timer(req, (2 * R_A_TOV + 10) * HZ);
+       req->qtcb->bottom.support.timeout = fcp_chan_timeout;
+       zfcp_fsf_start_timer(req, (fcp_chan_timeout + 10) * HZ);
 
        return 0;
 }
@@ -1089,9 +1091,9 @@ static int zfcp_fsf_setup_ct_els(struct zfcp_fsf_req *req,
  * @ct: pointer to struct zfcp_send_ct with data for request
  * @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req
  */
-int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool)
+int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port,
+                    struct zfcp_fsf_ct_els *ct, mempool_t *pool)
 {
-       struct zfcp_wka_port *wka_port = ct->wka_port;
        struct zfcp_qdio *qdio = wka_port->adapter->qdio;
        struct zfcp_fsf_req *req;
        int ret = -EIO;
@@ -1117,7 +1119,7 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool)
        req->qtcb->header.port_handle = wka_port->handle;
        req->data = ct;
 
-       zfcp_dbf_san_ct_request(req);
+       zfcp_dbf_san_ct_request(req, wka_port->d_id);
 
        ret = zfcp_fsf_req_send(req);
        if (ret)
@@ -1134,7 +1136,7 @@ out:
 
 static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
 {
-       struct zfcp_send_els *send_els = req->data;
+       struct zfcp_fsf_ct_els *send_els = req->data;
        struct zfcp_port *port = send_els->port;
        struct fsf_qtcb_header *header = &req->qtcb->header;
 
@@ -1154,9 +1156,6 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
        case FSF_ADAPTER_STATUS_AVAILABLE:
                switch (header->fsf_status_qual.word[0]){
                case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
-                       if (port && (send_els->ls_code != ZFCP_LS_ADISC))
-                               zfcp_fc_test_link(port);
-                       /*fall through */
                case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
                case FSF_SQ_RETRY_IF_POSSIBLE:
                        req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1188,10 +1187,11 @@ skip_fsfstatus:
  * zfcp_fsf_send_els - initiate an ELS command (FC-FS)
  * @els: pointer to struct zfcp_send_els with data for the command
  */
-int zfcp_fsf_send_els(struct zfcp_send_els *els)
+int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id,
+                     struct zfcp_fsf_ct_els *els)
 {
        struct zfcp_fsf_req *req;
-       struct zfcp_qdio *qdio = els->adapter->qdio;
+       struct zfcp_qdio *qdio = adapter->qdio;
        int ret = -EIO;
 
        spin_lock_bh(&qdio->req_q_lock);
@@ -1211,7 +1211,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
        if (ret)
                goto failed_send;
 
-       req->qtcb->bottom.support.d_id = els->d_id;
+       hton24(req->qtcb->bottom.support.d_id, d_id);
        req->handler = zfcp_fsf_send_els_handler;
        req->data = els;
 
@@ -1422,7 +1422,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
 {
        struct zfcp_port *port = req->data;
        struct fsf_qtcb_header *header = &req->qtcb->header;
-       struct fsf_plogi *plogi;
+       struct fc_els_flogi *plogi;
 
        if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
                goto out;
@@ -1472,23 +1472,10 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
                 * another GID_PN straight after a port has been opened.
                 * Alternately, an ADISC/PDISC ELS should suffice, as well.
                 */
-               plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
+               plogi = (struct fc_els_flogi *) req->qtcb->bottom.support.els;
                if (req->qtcb->bottom.support.els1_length >=
-                   FSF_PLOGI_MIN_LEN) {
-                       if (plogi->serv_param.wwpn != port->wwpn) {
-                               port->d_id = 0;
-                               dev_warn(&port->adapter->ccw_device->dev,
-                                        "A port opened with WWPN 0x%016Lx "
-                                        "returned data that identifies it as "
-                                        "WWPN 0x%016Lx\n",
-                                        (unsigned long long) port->wwpn,
-                                        (unsigned long long)
-                                         plogi->serv_param.wwpn);
-                       } else {
-                               port->wwnn = plogi->serv_param.wwnn;
+                   FSF_PLOGI_MIN_LEN)
                                zfcp_fc_plogi_evaluate(port, plogi);
-                       }
-               }
                break;
        case FSF_UNKNOWN_OP_SUBTYPE:
                req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1496,7 +1483,7 @@ static void zfcp_fsf_open_port_handler(struct zfcp_fsf_req *req)
        }
 
 out:
-       zfcp_port_put(port);
+       put_device(&port->sysfs_device);
 }
 
 /**
@@ -1530,18 +1517,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 = port->d_id;
+       hton24(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);
+       get_device(&port->sysfs_device);
 
        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);
+               put_device(&port->sysfs_device);
        }
 out:
        spin_unlock_bh(&qdio->req_q_lock);
@@ -1618,11 +1605,11 @@ out:
 
 static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
 {
-       struct zfcp_wka_port *wka_port = req->data;
+       struct zfcp_fc_wka_port *wka_port = req->data;
        struct fsf_qtcb_header *header = &req->qtcb->header;
 
        if (req->status & ZFCP_STATUS_FSFREQ_ERROR) {
-               wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+               wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
                goto out;
        }
 
@@ -1635,13 +1622,13 @@ static void zfcp_fsf_open_wka_port_handler(struct zfcp_fsf_req *req)
                req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                /* fall through */
        case FSF_ACCESS_DENIED:
-               wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+               wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
                break;
        case FSF_GOOD:
                wka_port->handle = header->port_handle;
                /* fall through */
        case FSF_PORT_ALREADY_OPEN:
-               wka_port->status = ZFCP_WKA_PORT_ONLINE;
+               wka_port->status = ZFCP_FC_WKA_PORT_ONLINE;
        }
 out:
        wake_up(&wka_port->completion_wq);
@@ -1649,10 +1636,10 @@ out:
 
 /**
  * zfcp_fsf_open_wka_port - create and send open wka-port request
- * @wka_port: pointer to struct zfcp_wka_port
+ * @wka_port: pointer to struct zfcp_fc_wka_port
  * Returns: 0 on success, error otherwise
  */
-int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
+int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port)
 {
        struct qdio_buffer_element *sbale;
        struct zfcp_qdio *qdio = wka_port->adapter->qdio;
@@ -1677,7 +1664,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
        req->handler = zfcp_fsf_open_wka_port_handler;
-       req->qtcb->bottom.support.d_id = wka_port->d_id;
+       hton24(req->qtcb->bottom.support.d_id, wka_port->d_id);
        req->data = wka_port;
 
        zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
@@ -1691,23 +1678,23 @@ out:
 
 static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
 {
-       struct zfcp_wka_port *wka_port = req->data;
+       struct zfcp_fc_wka_port *wka_port = req->data;
 
        if (req->qtcb->header.fsf_status == FSF_PORT_HANDLE_NOT_VALID) {
                req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                zfcp_erp_adapter_reopen(wka_port->adapter, 0, "fscwph1", req);
        }
 
-       wka_port->status = ZFCP_WKA_PORT_OFFLINE;
+       wka_port->status = ZFCP_FC_WKA_PORT_OFFLINE;
        wake_up(&wka_port->completion_wq);
 }
 
 /**
  * zfcp_fsf_close_wka_port - create and send close wka port request
- * @erp_action: pointer to struct zfcp_erp_action
+ * @wka_port: WKA port to open
  * Returns: 0 on success, error otherwise
  */
-int zfcp_fsf_close_wka_port(struct zfcp_wka_port *wka_port)
+int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port)
 {
        struct qdio_buffer_element *sbale;
        struct zfcp_qdio *qdio = wka_port->adapter->qdio;
@@ -1765,13 +1752,13 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
                /* can't use generic zfcp_erp_modify_port_status because
                 * ZFCP_STATUS_COMMON_OPEN must not be reset for the port */
                atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
-               list_for_each_entry(unit, &port->unit_list_head, list)
+               read_lock(&port->unit_list_lock);
+               list_for_each_entry(unit, &port->unit_list, list)
                        atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
                                          &unit->status);
+               read_unlock(&port->unit_list_lock);
                zfcp_erp_port_boxed(port, "fscpph2", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
-
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_ADAPTER_STATUS_AVAILABLE:
                switch (header->fsf_status_qual.word[0]) {
@@ -1787,9 +1774,11 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
                 * ZFCP_STATUS_COMMON_OPEN must not be reset for the port
                 */
                atomic_clear_mask(ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
-               list_for_each_entry(unit, &port->unit_list_head, list)
+               read_lock(&port->unit_list_lock);
+               list_for_each_entry(unit, &port->unit_list, list)
                        atomic_clear_mask(ZFCP_STATUS_COMMON_OPEN,
                                          &unit->status);
+               read_unlock(&port->unit_list_lock);
                break;
        }
 }
@@ -1873,8 +1862,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
                break;
        case FSF_PORT_BOXED:
                zfcp_erp_port_boxed(unit->port, "fsouh_2", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_LUN_SHARING_VIOLATION:
                if (header->fsf_status_qual.word[0])
@@ -2036,8 +2024,7 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
                break;
        case FSF_PORT_BOXED:
                zfcp_erp_port_boxed(unit->port, "fscuh_3", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_ADAPTER_STATUS_AVAILABLE:
                switch (req->qtcb->header.fsf_status_qual.word[0]) {
@@ -2109,72 +2096,57 @@ static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat)
        lat_rec->max = max(lat_rec->max, lat);
 }
 
-static void zfcp_fsf_req_latency(struct zfcp_fsf_req *req)
+static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi)
 {
-       struct fsf_qual_latency_info *lat_inf;
-       struct latency_cont *lat;
+       struct fsf_qual_latency_info *lat_in;
+       struct latency_cont *lat = NULL;
        struct zfcp_unit *unit = req->unit;
+       struct zfcp_blk_drv_data blktrc;
+       int ticks = req->adapter->timer_ticks;
 
-       lat_inf = &req->qtcb->prefix.prot_status_qual.latency_info;
-
-       switch (req->qtcb->bottom.io.data_direction) {
-       case FSF_DATADIR_READ:
-               lat = &unit->latencies.read;
-               break;
-       case FSF_DATADIR_WRITE:
-               lat = &unit->latencies.write;
-               break;
-       case FSF_DATADIR_CMND:
-               lat = &unit->latencies.cmd;
-               break;
-       default:
-               return;
-       }
+       lat_in = &req->qtcb->prefix.prot_status_qual.latency_info;
 
-       spin_lock(&unit->latencies.lock);
-       zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat);
-       zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat);
-       lat->counter++;
-       spin_unlock(&unit->latencies.lock);
-}
-
-#ifdef CONFIG_BLK_DEV_IO_TRACE
-static void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
-{
-       struct fsf_qual_latency_info *lat_inf;
-       struct scsi_cmnd *scsi_cmnd = (struct scsi_cmnd *)fsf_req->data;
-       struct request *req = scsi_cmnd->request;
-       struct zfcp_blk_drv_data trace;
-       int ticks = fsf_req->adapter->timer_ticks;
+       blktrc.flags = 0;
+       blktrc.magic = ZFCP_BLK_DRV_DATA_MAGIC;
+       if (req->status & ZFCP_STATUS_FSFREQ_ERROR)
+               blktrc.flags |= ZFCP_BLK_REQ_ERROR;
+       blktrc.inb_usage = req->queue_req.qdio_inb_usage;
+       blktrc.outb_usage = req->queue_req.qdio_outb_usage;
+
+       if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) {
+               blktrc.flags |= ZFCP_BLK_LAT_VALID;
+               blktrc.channel_lat = lat_in->channel_lat * ticks;
+               blktrc.fabric_lat = lat_in->fabric_lat * ticks;
+
+               switch (req->qtcb->bottom.io.data_direction) {
+               case FSF_DATADIR_READ:
+                       lat = &unit->latencies.read;
+                       break;
+               case FSF_DATADIR_WRITE:
+                       lat = &unit->latencies.write;
+                       break;
+               case FSF_DATADIR_CMND:
+                       lat = &unit->latencies.cmd;
+                       break;
+               }
 
-       trace.flags = 0;
-       trace.magic = ZFCP_BLK_DRV_DATA_MAGIC;
-       if (fsf_req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA) {
-               trace.flags |= ZFCP_BLK_LAT_VALID;
-               lat_inf = &fsf_req->qtcb->prefix.prot_status_qual.latency_info;
-               trace.channel_lat = lat_inf->channel_lat * ticks;
-               trace.fabric_lat = lat_inf->fabric_lat * ticks;
+               if (lat) {
+                       spin_lock(&unit->latencies.lock);
+                       zfcp_fsf_update_lat(&lat->channel, lat_in->channel_lat);
+                       zfcp_fsf_update_lat(&lat->fabric, lat_in->fabric_lat);
+                       lat->counter++;
+                       spin_unlock(&unit->latencies.lock);
+               }
        }
-       if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
-               trace.flags |= ZFCP_BLK_REQ_ERROR;
-       trace.inb_usage = fsf_req->queue_req.qdio_inb_usage;
-       trace.outb_usage = fsf_req->queue_req.qdio_outb_usage;
 
-       blk_add_driver_data(req->q, req, &trace, sizeof(trace));
-}
-#else
-static inline void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
-{
+       blk_add_driver_data(scsi->request->q, scsi->request, &blktrc,
+                           sizeof(blktrc));
 }
-#endif
 
 static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
 {
        struct scsi_cmnd *scpnt;
-       struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
-           &(req->qtcb->bottom.io.fcp_rsp);
-       u32 sns_len;
-       char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
+       struct fcp_resp_with_ext *fcp_rsp;
        unsigned long flags;
 
        read_lock_irqsave(&req->adapter->abort_lock, flags);
@@ -2185,50 +2157,16 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
                return;
        }
 
-       if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ABORTED)) {
-               set_host_byte(scpnt, DID_SOFT_ERROR);
-               goto skip_fsfstatus;
-       }
-
        if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) {
-               set_host_byte(scpnt, DID_ERROR);
+               set_host_byte(scpnt, DID_TRANSPORT_DISRUPTED);
                goto skip_fsfstatus;
        }
 
-       set_msg_byte(scpnt, COMMAND_COMPLETE);
-
-       scpnt->result |= fcp_rsp_iu->scsi_status;
+       fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp;
+       zfcp_fc_eval_fcp_rsp(fcp_rsp, scpnt);
 
-       if (req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)
-               zfcp_fsf_req_latency(req);
-
-       zfcp_fsf_trace_latency(req);
-
-       if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
-               if (fcp_rsp_info[3] == RSP_CODE_GOOD)
-                       set_host_byte(scpnt, DID_OK);
-               else {
-                       set_host_byte(scpnt, DID_ERROR);
-                       goto skip_fsfstatus;
-               }
-       }
-
-       if (unlikely(fcp_rsp_iu->validity.bits.fcp_sns_len_valid)) {
-               sns_len = FSF_FCP_RSP_SIZE - sizeof(struct fcp_rsp_iu) +
-                         fcp_rsp_iu->fcp_rsp_len;
-               sns_len = min(sns_len, (u32) SCSI_SENSE_BUFFERSIZE);
-               sns_len = min(sns_len, fcp_rsp_iu->fcp_sns_len);
-
-               memcpy(scpnt->sense_buffer,
-                      zfcp_get_fcp_sns_info_ptr(fcp_rsp_iu), sns_len);
-       }
+       zfcp_fsf_req_trace(req, scpnt);
 
-       if (unlikely(fcp_rsp_iu->validity.bits.fcp_resid_under)) {
-               scsi_set_resid(scpnt, fcp_rsp_iu->fcp_resid);
-               if (scsi_bufflen(scpnt) - scsi_get_resid(scpnt) <
-                   scpnt->underflow)
-                       set_host_byte(scpnt, DID_ERROR);
-       }
 skip_fsfstatus:
        if (scpnt->result != 0)
                zfcp_dbf_scsi_result("erro", 3, req->adapter->dbf, scpnt, req);
@@ -2250,11 +2188,13 @@ skip_fsfstatus:
 
 static void zfcp_fsf_send_fcp_ctm_handler(struct zfcp_fsf_req *req)
 {
-       struct fcp_rsp_iu *fcp_rsp_iu = (struct fcp_rsp_iu *)
-           &(req->qtcb->bottom.io.fcp_rsp);
-       char *fcp_rsp_info = (unsigned char *) &fcp_rsp_iu[1];
+       struct fcp_resp_with_ext *fcp_rsp;
+       struct fcp_resp_rsp_info *rsp_info;
+
+       fcp_rsp = (struct fcp_resp_with_ext *) &req->qtcb->bottom.io.fcp_rsp;
+       rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1];
 
-       if ((fcp_rsp_info[3] != RSP_CODE_GOOD) ||
+       if ((rsp_info->rsp_code != FCP_TMF_CMPL) ||
             (req->status & ZFCP_STATUS_FSFREQ_ERROR))
                req->status |= ZFCP_STATUS_FSFREQ_TMFUNCFAILED;
 }
@@ -2314,13 +2254,11 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
                break;
        case FSF_PORT_BOXED:
                zfcp_erp_port_boxed(unit->port, "fssfch5", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_LUN_BOXED:
                zfcp_erp_unit_boxed(unit, "fssfch6", req);
-               req->status |= ZFCP_STATUS_FSFREQ_ERROR |
-                              ZFCP_STATUS_FSFREQ_RETRY;
+               req->status |= ZFCP_STATUS_FSFREQ_ERROR;
                break;
        case FSF_ADAPTER_STATUS_AVAILABLE:
                if (header->fsf_status_qual.word[0] ==
@@ -2335,24 +2273,10 @@ skip_fsfstatus:
        else {
                zfcp_fsf_send_fcp_command_task_handler(req);
                req->unit = NULL;
-               zfcp_unit_put(unit);
+               put_device(&unit->sysfs_device);
        }
 }
 
-static void zfcp_set_fcp_dl(struct fcp_cmnd_iu *fcp_cmd, u32 fcp_dl)
-{
-       u32 *fcp_dl_ptr;
-
-       /*
-        * fcp_dl_addr = start address of fcp_cmnd structure +
-        * size of fixed part + size of dynamically sized add_dcp_cdb field
-        * SEE FCP-2 documentation
-        */
-       fcp_dl_ptr = (u32 *) ((unsigned char *) &fcp_cmd[1] +
-                       (fcp_cmd->add_fcp_cdb_length << 2));
-       *fcp_dl_ptr = fcp_dl;
-}
-
 /**
  * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command)
  * @unit: unit where command is sent to
@@ -2362,7 +2286,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
                                   struct scsi_cmnd *scsi_cmnd)
 {
        struct zfcp_fsf_req *req;
-       struct fcp_cmnd_iu *fcp_cmnd_iu;
+       struct fcp_cmnd *fcp_cmnd;
        unsigned int sbtype = SBAL_FLAGS0_TYPE_READ;
        int real_bytes, retval = -EIO;
        struct zfcp_adapter *adapter = unit->port->adapter;
@@ -2387,23 +2311,21 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
        }
 
        req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
-       zfcp_unit_get(unit);
+       get_device(&unit->sysfs_device);
        req->unit = unit;
        req->data = scsi_cmnd;
        req->handler = zfcp_fsf_send_fcp_command_handler;
        req->qtcb->header.lun_handle = unit->handle;
        req->qtcb->header.port_handle = unit->port->handle;
        req->qtcb->bottom.io.service_class = FSF_CLASS_3;
+       req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN;
 
        scsi_cmnd->host_scribble = (unsigned char *) req->req_id;
 
-       fcp_cmnd_iu = (struct fcp_cmnd_iu *) &(req->qtcb->bottom.io.fcp_cmnd);
-       fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
        /*
         * set depending on data direction:
         *      data direction bits in SBALE (SB Type)
         *      data direction bits in QTCB
-        *      data direction bits in FCP_CMND IU
         */
        switch (scsi_cmnd->sc_data_direction) {
        case DMA_NONE:
@@ -2411,32 +2333,17 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
                break;
        case DMA_FROM_DEVICE:
                req->qtcb->bottom.io.data_direction = FSF_DATADIR_READ;
-               fcp_cmnd_iu->rddata = 1;
                break;
        case DMA_TO_DEVICE:
                req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE;
                sbtype = SBAL_FLAGS0_TYPE_WRITE;
-               fcp_cmnd_iu->wddata = 1;
                break;
        case DMA_BIDIRECTIONAL:
                goto failed_scsi_cmnd;
        }
 
-       if (likely((scsi_cmnd->device->simple_tags) ||
-                  ((atomic_read(&unit->status) & ZFCP_STATUS_UNIT_READONLY) &&
-                   (atomic_read(&unit->status) & ZFCP_STATUS_UNIT_SHARED))))
-               fcp_cmnd_iu->task_attribute = SIMPLE_Q;
-       else
-               fcp_cmnd_iu->task_attribute = UNTAGGED;
-
-       if (unlikely(scsi_cmnd->cmd_len > FCP_CDB_LENGTH))
-               fcp_cmnd_iu->add_fcp_cdb_length =
-                       (scsi_cmnd->cmd_len - FCP_CDB_LENGTH) >> 2;
-
-       memcpy(fcp_cmnd_iu->fcp_cdb, scsi_cmnd->cmnd, scsi_cmnd->cmd_len);
-
-       req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
-               fcp_cmnd_iu->add_fcp_cdb_length + sizeof(u32);
+       fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
+       zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd);
 
        real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->queue_req, sbtype,
                                             scsi_sglist(scsi_cmnd),
@@ -2454,8 +2361,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
                goto failed_scsi_cmnd;
        }
 
-       zfcp_set_fcp_dl(fcp_cmnd_iu, real_bytes);
-
        retval = zfcp_fsf_req_send(req);
        if (unlikely(retval))
                goto failed_scsi_cmnd;
@@ -2463,7 +2368,7 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
        goto out;
 
 failed_scsi_cmnd:
-       zfcp_unit_put(unit);
+       put_device(&unit->sysfs_device);
        zfcp_fsf_req_free(req);
        scsi_cmnd->host_scribble = NULL;
 out:
@@ -2481,7 +2386,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
 {
        struct qdio_buffer_element *sbale;
        struct zfcp_fsf_req *req = NULL;
-       struct fcp_cmnd_iu *fcp_cmnd_iu;
+       struct fcp_cmnd *fcp_cmnd;
        struct zfcp_qdio *qdio = unit->port->adapter->qdio;
 
        if (unlikely(!(atomic_read(&unit->status) &
@@ -2507,16 +2412,14 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
        req->qtcb->header.port_handle = unit->port->handle;
        req->qtcb->bottom.io.data_direction = FSF_DATADIR_CMND;
        req->qtcb->bottom.io.service_class = FSF_CLASS_3;
-       req->qtcb->bottom.io.fcp_cmnd_length =  sizeof(struct fcp_cmnd_iu) +
-                                               sizeof(u32);
+       req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN;
 
        sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
        sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
        sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
 
-       fcp_cmnd_iu = (struct fcp_cmnd_iu *) &req->qtcb->bottom.io.fcp_cmnd;
-       fcp_cmnd_iu->fcp_lun = unit->fcp_lun;
-       fcp_cmnd_iu->task_management_flags = tm_flags;
+       fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
+       zfcp_fc_fcp_tm(fcp_cmnd, unit->device, tm_flags);
 
        zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT);
        if (!zfcp_fsf_req_send(req))
index dcc7c1d..b3de682 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/pfn.h>
 #include <linux/scatterlist.h>
+#include <scsi/libfc.h>
 
 #define FSF_QTCB_CURRENT_VERSION               0x00000001
 
@@ -228,7 +229,8 @@ struct fsf_status_read_buffer {
        u32 length;
        u32 res1;
        struct fsf_queue_designator queue_designator;
-       u32 d_id;
+       u8 res2;
+       u8 d_id[3];
        u32 class;
        u64 fcp_lun;
        u8  res3[24];
@@ -309,22 +311,7 @@ struct fsf_qtcb_header {
        u8  res4[16];
 } __attribute__ ((packed));
 
-struct fsf_nport_serv_param {
-       u8  common_serv_param[16];
-       u64 wwpn;
-       u64 wwnn;
-       u8  class1_serv_param[16];
-       u8  class2_serv_param[16];
-       u8  class3_serv_param[16];
-       u8  class4_serv_param[16];
-       u8  vendor_version_level[16];
-} __attribute__ ((packed));
-
 #define FSF_PLOGI_MIN_LEN      112
-struct fsf_plogi {
-       u32    code;
-       struct fsf_nport_serv_param serv_param;
-} __attribute__ ((packed));
 
 #define FSF_FCP_CMND_SIZE      288
 #define FSF_FCP_RSP_SIZE       128
@@ -342,8 +329,8 @@ struct fsf_qtcb_bottom_io {
 
 struct fsf_qtcb_bottom_support {
        u32 operation_subtype;
-       u8  res1[12];
-       u32 d_id;
+       u8  res1[13];
+       u8 d_id[3];
        u32 option;
        u64 fcp_lun;
        u64 res2;
@@ -372,18 +359,18 @@ struct fsf_qtcb_bottom_config {
        u32 fc_topology;
        u32 fc_link_speed;
        u32 adapter_type;
-       u32 peer_d_id;
+       u8 res0;
+       u8 peer_d_id[3];
        u8 res1[2];
        u16 timer_interval;
-       u8 res2[8];
-       u32 s_id;
-       struct fsf_nport_serv_param nport_serv_param;
-       u8 reserved_nport_serv_param[16];
+       u8 res2[9];
+       u8 s_id[3];
+       u8 nport_serv_param[128];
        u8 res3[8];
        u32 adapter_ports;
        u32 hardware_version;
        u8 serial_number[32];
-       struct fsf_nport_serv_param plogi_payload;
+       u8 plogi_payload[112];
        struct fsf_statistics_info stat_info;
        u8 res4[112];
 } __attribute__ ((packed));
@@ -450,4 +437,22 @@ struct zfcp_blk_drv_data {
        u64 fabric_lat;
 } __attribute__ ((packed));
 
+/**
+ * struct zfcp_fsf_ct_els - zfcp data for ct or els request
+ * @req: scatter-gather list for request
+ * @resp: scatter-gather list for response
+ * @handler: handler function (called for response to the request)
+ * @handler_data: data passed to handler function
+ * @port: Optional pointer to port for zfcp internal ELS (only test link ADISC)
+ * @status: used to pass error status to calling function
+ */
+struct zfcp_fsf_ct_els {
+       struct scatterlist *req;
+       struct scatterlist *resp;
+       void (*handler)(void *);
+       void *handler_data;
+       struct zfcp_port *port;
+       int status;
+};
+
 #endif                         /* FSF_H */
index 0e1a346..771cc53 100644 (file)
@@ -9,29 +9,33 @@
 #define KMSG_COMPONENT "zfcp"
 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
+#include <linux/types.h>
+#include <scsi/fc/fc_fcp.h>
 #include <asm/atomic.h>
 #include "zfcp_ext.h"
 #include "zfcp_dbf.h"
+#include "zfcp_fc.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)
+static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth,
+                                       int reason)
 {
-       char *fcp_sns_info_ptr;
-
-       fcp_sns_info_ptr = (unsigned char *) &fcp_rsp_iu[1];
-       if (fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)
-               fcp_sns_info_ptr += fcp_rsp_iu->fcp_rsp_len;
-
-       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);
+       switch (reason) {
+       case SCSI_QDEPTH_DEFAULT:
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+               break;
+       case SCSI_QDEPTH_QFULL:
+               scsi_track_queue_full(sdev, depth);
+               break;
+       case SCSI_QDEPTH_RAMP_UP:
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
        return sdev->queue_depth;
 }
 
@@ -39,7 +43,7 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt)
 {
        struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata;
        unit->device = NULL;
-       zfcp_unit_put(unit);
+       put_device(&unit->sysfs_device);
 }
 
 static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
@@ -99,12 +103,26 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
        }
 
        status = atomic_read(&unit->status);
-       if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) ||
-                    !(status & ZFCP_STATUS_COMMON_RUNNING))) {
+       if (unlikely(status & ZFCP_STATUS_COMMON_ERP_FAILED) &&
+                    !(atomic_read(&unit->port->status) &
+                      ZFCP_STATUS_COMMON_ERP_FAILED)) {
+               /* only unit access denied, but port is good
+                * not covered by FC transport, have to fail here */
                zfcp_scsi_command_fail(scpnt, DID_ERROR);
                return 0;
        }
 
+       if (unlikely(!(status & ZFCP_STATUS_COMMON_UNBLOCKED))) {
+               /* This could be either
+                * open unit pending: this is temporary, will result in
+                *      open unit or ERP_FAILED, so retry command
+                * call to rport_delete pending: mimic retry from
+                *      fc_remote_port_chkready until rport is BLOCKED
+                */
+               zfcp_scsi_command_fail(scpnt, DID_IMM_RETRY);
+               return 0;
+       }
+
        ret = zfcp_fsf_send_fcp_command_task(unit, scpnt);
        if (unlikely(ret == -EBUSY))
                return SCSI_MLQUEUE_DEVICE_BUSY;
@@ -115,49 +133,44 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
 }
 
 static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *adapter,
-                                         int channel, unsigned int id,
-                                         unsigned int lun)
+                                         unsigned int id, u64 lun)
 {
+       unsigned long flags;
        struct zfcp_port *port;
-       struct zfcp_unit *unit;
-       int scsi_lun;
+       struct zfcp_unit *unit = NULL;
 
-       list_for_each_entry(port, &adapter->port_list_head, list) {
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list) {
                if (!port->rport || (id != port->rport->scsi_target_id))
                        continue;
-               list_for_each_entry(unit, &port->unit_list_head, list) {
-                       scsi_lun = scsilun_to_int(
-                               (struct scsi_lun *)&unit->fcp_lun);
-                       if (lun == scsi_lun)
-                               return unit;
-               }
+               unit = zfcp_get_unit_by_lun(port, lun);
+               if (unit)
+                       break;
        }
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
 
-       return NULL;
+       return unit;
 }
 
 static int zfcp_scsi_slave_alloc(struct scsi_device *sdp)
 {
        struct zfcp_adapter *adapter;
        struct zfcp_unit *unit;
-       unsigned long flags;
-       int retval = -ENXIO;
+       u64 lun;
 
        adapter = (struct zfcp_adapter *) sdp->host->hostdata[0];
        if (!adapter)
                goto out;
 
-       read_lock_irqsave(&zfcp_data.config_lock, flags);
-       unit = zfcp_unit_lookup(adapter, sdp->channel, sdp->id, sdp->lun);
+       int_to_scsilun(sdp->lun, (struct scsi_lun *)&lun);
+       unit = zfcp_unit_lookup(adapter, sdp->id, lun);
        if (unit) {
                sdp->hostdata = unit;
                unit->device = sdp;
-               zfcp_unit_get(unit);
-               retval = 0;
+               return 0;
        }
-       read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 out:
-       return retval;
+       return -ENXIO;
 }
 
 static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
@@ -196,6 +209,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
                        break;
 
                zfcp_erp_wait(adapter);
+               fc_block_scsi_eh(scpnt);
                if (!(atomic_read(&adapter->status) &
                      ZFCP_STATUS_COMMON_RUNNING)) {
                        zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL,
@@ -235,6 +249,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
                        break;
 
                zfcp_erp_wait(adapter);
+               fc_block_scsi_eh(scpnt);
                if (!(atomic_read(&adapter->status) &
                      ZFCP_STATUS_COMMON_RUNNING)) {
                        zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt);
@@ -249,9 +264,6 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
        if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
                zfcp_dbf_scsi_devreset("fail", tm_flags, unit, scpnt);
                retval = FAILED;
-       } else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) {
-               zfcp_dbf_scsi_devreset("nsup", tm_flags, unit, scpnt);
-               retval = FAILED;
        } else
                zfcp_dbf_scsi_devreset("okay", tm_flags, unit, scpnt);
 
@@ -261,12 +273,12 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
 
 static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt)
 {
-       return zfcp_task_mgmt_function(scpnt, FCP_LOGICAL_UNIT_RESET);
+       return zfcp_task_mgmt_function(scpnt, FCP_TMF_LUN_RESET);
 }
 
 static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt)
 {
-       return zfcp_task_mgmt_function(scpnt, FCP_TARGET_RESET);
+       return zfcp_task_mgmt_function(scpnt, FCP_TMF_TGT_RESET);
 }
 
 static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
@@ -276,6 +288,7 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt)
 
        zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt);
        zfcp_erp_wait(adapter);
+       fc_block_scsi_eh(scpnt);
 
        return SUCCESS;
 }
@@ -303,7 +316,7 @@ int zfcp_adapter_scsi_register(struct zfcp_adapter *adapter)
        adapter->scsi_host->max_lun = 1;
        adapter->scsi_host->max_channel = 0;
        adapter->scsi_host->unique_id = dev_id.devno;
-       adapter->scsi_host->max_cmd_len = 255;
+       adapter->scsi_host->max_cmd_len = 16; /* in struct fcp_cmnd */
        adapter->scsi_host->transportt = zfcp_data.scsi_transport_template;
 
        adapter->scsi_host->hostdata[0] = (unsigned long) adapter;
@@ -325,12 +338,11 @@ void zfcp_adapter_scsi_unregister(struct zfcp_adapter *adapter)
        if (!shost)
                return;
 
-       read_lock_irq(&zfcp_data.config_lock);
-       list_for_each_entry(port, &adapter->port_list_head, list)
-               if (port->rport)
-                       port->rport = NULL;
+       read_lock_irq(&adapter->port_list_lock);
+       list_for_each_entry(port, &adapter->port_list, list)
+               port->rport = NULL;
+       read_unlock_irq(&adapter->port_list_lock);
 
-       read_unlock_irq(&zfcp_data.config_lock);
        fc_remove_host(shost);
        scsi_remove_host(shost);
        scsi_host_put(shost);
@@ -348,7 +360,7 @@ zfcp_init_fc_host_stats(struct zfcp_adapter *adapter)
                fc_stats = kmalloc(sizeof(*fc_stats), GFP_KERNEL);
                if (!fc_stats)
                        return NULL;
-               adapter->fc_stats = fc_stats; /* freed in adater_dequeue */
+               adapter->fc_stats = fc_stats; /* freed in adapter_release */
        }
        memset(adapter->fc_stats, 0, sizeof(*adapter->fc_stats));
        return adapter->fc_stats;
@@ -464,7 +476,7 @@ static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
                adapter->stats_reset = jiffies/HZ;
                kfree(adapter->stats_reset_data);
                adapter->stats_reset_data = data; /* finally freed in
-                                                    adapter_dequeue */
+                                                    adapter_release */
        }
 }
 
@@ -495,7 +507,7 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
  * @rport: The FC rport where to teminate I/O
  *
  * Abort all pending SCSI commands for a port by closing the
- * port. Using a reopen for avoids a conflict with a shutdown
+ * port. Using a reopen avoiding a conflict with a shutdown
  * overwriting a reopen.
  */
 static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
@@ -505,15 +517,11 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
        struct zfcp_adapter *adapter =
                (struct zfcp_adapter *)shost->hostdata[0];
 
-       write_lock_irq(&zfcp_data.config_lock);
        port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
-       if (port)
-               zfcp_port_get(port);
-       write_unlock_irq(&zfcp_data.config_lock);
 
        if (port) {
                zfcp_erp_port_reopen(port, 0, "sctrpi1", NULL);
-               zfcp_port_put(port);
+               put_device(&port->sysfs_device);
        }
 }
 
@@ -555,31 +563,34 @@ static void zfcp_scsi_rport_block(struct zfcp_port *port)
 
 void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
 {
-       zfcp_port_get(port);
+       get_device(&port->sysfs_device);
        port->rport_task = RPORT_ADD;
 
        if (!queue_work(port->adapter->work_queue, &port->rport_work))
-               zfcp_port_put(port);
+               put_device(&port->sysfs_device);
 }
 
 void zfcp_scsi_schedule_rport_block(struct zfcp_port *port)
 {
-       zfcp_port_get(port);
+       get_device(&port->sysfs_device);
        port->rport_task = RPORT_DEL;
 
        if (port->rport && queue_work(port->adapter->work_queue,
                                      &port->rport_work))
                return;
 
-       zfcp_port_put(port);
+       put_device(&port->sysfs_device);
 }
 
 void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter)
 {
+       unsigned long flags;
        struct zfcp_port *port;
 
-       list_for_each_entry(port, &adapter->port_list_head, list)
+       read_lock_irqsave(&adapter->port_list_lock, flags);
+       list_for_each_entry(port, &adapter->port_list, list)
                zfcp_scsi_schedule_rport_block(port);
+       read_unlock_irqrestore(&adapter->port_list_lock, flags);
 }
 
 void zfcp_scsi_rport_work(struct work_struct *work)
@@ -597,7 +608,7 @@ void zfcp_scsi_rport_work(struct work_struct *work)
                }
        }
 
-       zfcp_port_put(port);
+       put_device(&port->sysfs_device);
 }
 
 
@@ -615,21 +626,7 @@ void zfcp_scsi_scan(struct work_struct *work)
                                 scsilun_to_int((struct scsi_lun *)
                                                &unit->fcp_lun), 0);
 
-       zfcp_unit_put(unit);
-}
-
-static int zfcp_execute_fc_job(struct fc_bsg_job *job)
-{
-       switch (job->request->msgcode) {
-       case FC_BSG_RPT_ELS:
-       case FC_BSG_HST_ELS_NOLOGIN:
-               return zfcp_fc_execute_els_fc_job(job);
-       case FC_BSG_RPT_CT:
-       case FC_BSG_HST_CT:
-               return zfcp_fc_execute_ct_fc_job(job);
-       default:
-               return -EINVAL;
-       }
+       put_device(&unit->sysfs_device);
 }
 
 struct fc_function_template zfcp_transport_functions = {
@@ -643,6 +640,7 @@ struct fc_function_template zfcp_transport_functions = {
        .show_host_port_name = 1,
        .show_host_permanent_port_name = 1,
        .show_host_supported_classes = 1,
+       .show_host_supported_fc4s = 1,
        .show_host_supported_speeds = 1,
        .show_host_maxframe_size = 1,
        .show_host_serial_number = 1,
@@ -652,13 +650,15 @@ struct fc_function_template zfcp_transport_functions = {
        .get_host_port_state = zfcp_get_host_port_state,
        .terminate_rport_io = zfcp_scsi_terminate_rport_io,
        .show_host_port_state = 1,
-       .bsg_request = zfcp_execute_fc_job,
+       .show_host_active_fc4s = 1,
+       .bsg_request = zfcp_fc_exec_bsg_job,
        /* no functions registered for following dynamic attributes but
           directly set by LLDD */
        .show_host_port_type = 1,
        .show_host_speed = 1,
        .show_host_port_id = 1,
        .disable_target_scan = 1,
+       .dd_bsg_size = sizeof(struct zfcp_fsf_ct_els),
 };
 
 struct zfcp_data zfcp_data = {
index d310008..f539e00 100644 (file)
@@ -3,7 +3,7 @@
  *
  * sysfs attributes.
  *
- * Copyright IBM Corporation 2008
+ * Copyright IBM Corporation 2008, 2009
  */
 
 #define KMSG_COMPONENT "zfcp"
@@ -19,30 +19,44 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev,             \
                                                   struct device_attribute *at,\
                                                   char *buf)                  \
 {                                                                             \
-       struct _feat_def *_feat = dev_get_drvdata(dev);                        \
+       struct _feat_def *_feat = container_of(dev, struct _feat_def,          \
+                                              sysfs_device);                  \
                                                                               \
        return sprintf(buf, _format, _value);                                  \
 }                                                                             \
 static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO,                                   \
                     zfcp_sysfs_##_feat##_##_name##_show, NULL);
 
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, status, "0x%08x\n",
-                atomic_read(&adapter->status));
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwnn, "0x%016llx\n",
-                (unsigned long long) adapter->peer_wwnn);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_wwpn, "0x%016llx\n",
-                (unsigned long long) adapter->peer_wwpn);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, peer_d_id, "0x%06x\n",
-                adapter->peer_d_id);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, card_version, "0x%04x\n",
-                adapter->hydra_version);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, lic_version, "0x%08x\n",
-                adapter->fsf_lic_version);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, hardware_version, "0x%08x\n",
-                adapter->hardware_version);
-ZFCP_DEFINE_ATTR(zfcp_adapter, adapter, in_recovery, "%d\n",
-                (atomic_read(&adapter->status) &
-                 ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+#define ZFCP_DEFINE_A_ATTR(_name, _format, _value)                          \
+static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev,        \
+                                                struct device_attribute *at,\
+                                                char *buf)                  \
+{                                                                           \
+       struct ccw_device *cdev = to_ccwdev(dev);                            \
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);       \
+       int i;                                                               \
+                                                                            \
+       if (!adapter)                                                        \
+               return -ENODEV;                                              \
+                                                                            \
+       i = sprintf(buf, _format, _value);                                   \
+       zfcp_ccw_adapter_put(adapter);                                       \
+       return i;                                                            \
+}                                                                           \
+static ZFCP_DEV_ATTR(adapter, _name, S_IRUGO,                               \
+                    zfcp_sysfs_adapter_##_name##_show, NULL);
+
+ZFCP_DEFINE_A_ATTR(status, "0x%08x\n", atomic_read(&adapter->status));
+ZFCP_DEFINE_A_ATTR(peer_wwnn, "0x%016llx\n",
+                  (unsigned long long) adapter->peer_wwnn);
+ZFCP_DEFINE_A_ATTR(peer_wwpn, "0x%016llx\n",
+                  (unsigned long long) adapter->peer_wwpn);
+ZFCP_DEFINE_A_ATTR(peer_d_id, "0x%06x\n", adapter->peer_d_id);
+ZFCP_DEFINE_A_ATTR(card_version, "0x%04x\n", adapter->hydra_version);
+ZFCP_DEFINE_A_ATTR(lic_version, "0x%08x\n", adapter->fsf_lic_version);
+ZFCP_DEFINE_A_ATTR(hardware_version, "0x%08x\n", adapter->hardware_version);
+ZFCP_DEFINE_A_ATTR(in_recovery, "%d\n", (atomic_read(&adapter->status) &
+                                        ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
 
 ZFCP_DEFINE_ATTR(zfcp_port, port, status, "0x%08x\n",
                 atomic_read(&port->status));
@@ -73,7 +87,8 @@ static ssize_t zfcp_sysfs_##_feat##_failed_show(struct device *dev,          \
                                                struct device_attribute *attr, \
                                                char *buf)                     \
 {                                                                             \
-       struct _feat_def *_feat = dev_get_drvdata(dev);                        \
+       struct _feat_def *_feat = container_of(dev, struct _feat_def,          \
+                                              sysfs_device);                  \
                                                                               \
        if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_ERP_FAILED)       \
                return sprintf(buf, "1\n");                                    \
@@ -84,15 +99,13 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev,               \
                                                 struct device_attribute *attr,\
                                                 const char *buf, size_t count)\
 {                                                                             \
-       struct _feat_def *_feat = dev_get_drvdata(dev);                        \
+       struct _feat_def *_feat = container_of(dev, struct _feat_def,          \
+                                              sysfs_device);                  \
        unsigned long val;                                                     \
        int retval = 0;                                                        \
                                                                               \
-       mutex_lock(&zfcp_data.config_mutex);                                   \
-       if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) {         \
-               retval = -EBUSY;                                               \
-               goto out;                                                      \
-       }                                                                      \
+       if (!(_feat && get_device(&_feat->sysfs_device)))                      \
+               return -EBUSY;                                                 \
                                                                               \
        if (strict_strtoul(buf, 0, &val) || val != 0) {                        \
                retval = -EINVAL;                                              \
@@ -105,29 +118,82 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev,             \
                                  _reopen_id, NULL);                           \
        zfcp_erp_wait(_adapter);                                               \
 out:                                                                          \
-       mutex_unlock(&zfcp_data.config_mutex);                                 \
+       put_device(&_feat->sysfs_device);                                      \
        return retval ? retval : (ssize_t) count;                              \
 }                                                                             \
 static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO,                        \
                     zfcp_sysfs_##_feat##_failed_show,                         \
                     zfcp_sysfs_##_feat##_failed_store);
 
-ZFCP_SYSFS_FAILED(zfcp_adapter, adapter, adapter, "syafai1", "syafai2");
 ZFCP_SYSFS_FAILED(zfcp_port, port, port->adapter, "sypfai1", "sypfai2");
 ZFCP_SYSFS_FAILED(zfcp_unit, unit, unit->port->adapter, "syufai1", "syufai2");
 
+static ssize_t zfcp_sysfs_adapter_failed_show(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+       int i;
+
+       if (!adapter)
+               return -ENODEV;
+
+       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED)
+               i = sprintf(buf, "1\n");
+       else
+               i = sprintf(buf, "0\n");
+
+       zfcp_ccw_adapter_put(adapter);
+       return i;
+}
+
+static ssize_t zfcp_sysfs_adapter_failed_store(struct device *dev,
+                                              struct device_attribute *attr,
+                                              const char *buf, size_t count)
+{
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
+       unsigned long val;
+       int retval = 0;
+
+       if (!adapter)
+               return -ENODEV;
+
+       if (strict_strtoul(buf, 0, &val) || val != 0) {
+               retval = -EINVAL;
+               goto out;
+       }
+
+       zfcp_erp_modify_adapter_status(adapter, "syafai1", NULL,
+                                      ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
+       zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
+                               "syafai2", NULL);
+       zfcp_erp_wait(adapter);
+out:
+       zfcp_ccw_adapter_put(adapter);
+       return retval ? retval : (ssize_t) count;
+}
+static ZFCP_DEV_ATTR(adapter, failed, S_IWUSR | S_IRUGO,
+                    zfcp_sysfs_adapter_failed_show,
+                    zfcp_sysfs_adapter_failed_store);
+
 static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
                                            struct device_attribute *attr,
                                            const char *buf, size_t count)
 {
-       struct zfcp_adapter *adapter = dev_get_drvdata(dev);
-       int ret;
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
 
-       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
-               return -EBUSY;
+       if (!adapter)
+               return -ENODEV;
 
-       ret = zfcp_fc_scan_ports(adapter);
-       return ret ? ret : (ssize_t) count;
+       /* sync the user-space- with the kernel-invocation of scan_work */
+       queue_work(adapter->work_queue, &adapter->scan_work);
+       flush_work(&adapter->scan_work);
+       zfcp_ccw_adapter_put(adapter);
+
+       return (ssize_t) count;
 }
 static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
                     zfcp_sysfs_port_rescan_store);
@@ -136,44 +202,34 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
                                            struct device_attribute *attr,
                                            const char *buf, size_t count)
 {
-       struct zfcp_adapter *adapter = dev_get_drvdata(dev);
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
        struct zfcp_port *port;
        u64 wwpn;
-       int retval = 0;
-       LIST_HEAD(port_remove_lh);
+       int retval = -EINVAL;
 
-       mutex_lock(&zfcp_data.config_mutex);
-       if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
-               retval = -EBUSY;
-               goto out;
-       }
+       if (!adapter)
+               return -ENODEV;
 
-       if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn)) {
-               retval = -EINVAL;
+       if (strict_strtoull(buf, 0, (unsigned long long *) &wwpn))
                goto out;
-       }
 
-       write_lock_irq(&zfcp_data.config_lock);
        port = zfcp_get_port_by_wwpn(adapter, wwpn);
-       if (port && (atomic_read(&port->refcount) == 0)) {
-               zfcp_port_get(port);
-               atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status);
-               list_move(&port->list, &port_remove_lh);
-       } else
-               port = NULL;
-       write_unlock_irq(&zfcp_data.config_lock);
-
-       if (!port) {
-               retval = -ENXIO;
+       if (!port)
                goto out;
-       }
+       else
+               retval = 0;
+
+       write_lock_irq(&adapter->port_list_lock);
+       list_del(&port->list);
+       write_unlock_irq(&adapter->port_list_lock);
+
+       put_device(&port->sysfs_device);
 
        zfcp_erp_port_shutdown(port, 0, "syprs_1", NULL);
-       zfcp_erp_wait(adapter);
-       zfcp_port_put(port);
-       zfcp_port_dequeue(port);
+       zfcp_device_unregister(&port->sysfs_device, &zfcp_sysfs_port_attrs);
  out:
-       mutex_unlock(&zfcp_data.config_mutex);
+       zfcp_ccw_adapter_put(adapter);
        return retval ? retval : (ssize_t) count;
 }
 static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
@@ -202,16 +258,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
                                         struct device_attribute *attr,
                                         const char *buf, size_t count)
 {
-       struct zfcp_port *port = dev_get_drvdata(dev);
+       struct zfcp_port *port = container_of(dev, struct zfcp_port,
+                                             sysfs_device);
        struct zfcp_unit *unit;
        u64 fcp_lun;
        int retval = -EINVAL;
 
-       mutex_lock(&zfcp_data.config_mutex);
-       if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
-               retval = -EBUSY;
-               goto out;
-       }
+       if (!(port && get_device(&port->sysfs_device)))
+               return -EBUSY;
 
        if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
                goto out;
@@ -219,15 +273,14 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
        unit = zfcp_unit_enqueue(port, fcp_lun);
        if (IS_ERR(unit))
                goto out;
-
-       retval = 0;
+       else
+               retval = 0;
 
        zfcp_erp_unit_reopen(unit, 0, "syuas_1", NULL);
        zfcp_erp_wait(unit->port->adapter);
        flush_work(&unit->scsi_work);
-       zfcp_unit_put(unit);
 out:
-       mutex_unlock(&zfcp_data.config_mutex);
+       put_device(&port->sysfs_device);
        return retval ? retval : (ssize_t) count;
 }
 static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
@@ -236,54 +289,37 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
                                            struct device_attribute *attr,
                                            const char *buf, size_t count)
 {
-       struct zfcp_port *port = dev_get_drvdata(dev);
+       struct zfcp_port *port = container_of(dev, struct zfcp_port,
+                                             sysfs_device);
        struct zfcp_unit *unit;
        u64 fcp_lun;
-       int retval = 0;
-       LIST_HEAD(unit_remove_lh);
+       int retval = -EINVAL;
 
-       mutex_lock(&zfcp_data.config_mutex);
-       if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
-               retval = -EBUSY;
-               goto out;
-       }
+       if (!(port && get_device(&port->sysfs_device)))
+               return -EBUSY;
 
-       if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) {
-               retval = -EINVAL;
+       if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
                goto out;
-       }
 
-       write_lock_irq(&zfcp_data.config_lock);
        unit = zfcp_get_unit_by_lun(port, fcp_lun);
-       if (unit) {
-               write_unlock_irq(&zfcp_data.config_lock);
-               /* wait for possible timeout during SCSI probe */
-               flush_work(&unit->scsi_work);
-               write_lock_irq(&zfcp_data.config_lock);
-
-               if (atomic_read(&unit->refcount) == 0) {
-                       zfcp_unit_get(unit);
-                       atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE,
-                                       &unit->status);
-                       list_move(&unit->list, &unit_remove_lh);
-               } else {
-                       unit = NULL;
-               }
-       }
+       if (!unit)
+               goto out;
+       else
+               retval = 0;
 
-       write_unlock_irq(&zfcp_data.config_lock);
+       /* wait for possible timeout during SCSI probe */
+       flush_work(&unit->scsi_work);
 
-       if (!unit) {
-               retval = -ENXIO;
-               goto out;
-       }
+       write_lock_irq(&port->unit_list_lock);
+       list_del(&unit->list);
+       write_unlock_irq(&port->unit_list_lock);
+
+       put_device(&unit->sysfs_device);
 
        zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
-       zfcp_erp_wait(unit->port->adapter);
-       zfcp_unit_put(unit);
-       zfcp_unit_dequeue(unit);
+       zfcp_device_unregister(&unit->sysfs_device, &zfcp_sysfs_unit_attrs);
 out:
-       mutex_unlock(&zfcp_data.config_mutex);
+       put_device(&port->sysfs_device);
        return retval ? retval : (ssize_t) count;
 }
 static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
index 58e583b..aa2b60a 100644 (file)
 #define ENVCTRL_CPUTEMP_MON                    1    /* cpu temperature monitor */
 #define ENVCTRL_CPUVOLTAGE_MON         2    /* voltage monitor         */
 #define ENVCTRL_FANSTAT_MON            3    /* fan status monitor      */
-#define ENVCTRL_ETHERTEMP_MON          4    /* ethernet temperarture */
+#define ENVCTRL_ETHERTEMP_MON          4    /* ethernet temperature */
                                             /* monitor                     */
 #define ENVCTRL_VOLTAGESTAT_MON                5    /* voltage status monitor  */
 #define ENVCTRL_MTHRBDTEMP_MON         6    /* motherboard temperature */
-#define ENVCTRL_SCSITEMP_MON           7    /* scsi temperarture */
+#define ENVCTRL_SCSITEMP_MON           7    /* scsi temperature */
 #define ENVCTRL_GLOBALADDR_MON         8    /* global address */
 
 /* Child device type.
index 36c21b1..3bf7592 100644 (file)
@@ -186,8 +186,12 @@ static ssize_t twa_show_stats(struct device *dev,
 } /* End twa_show_stats() */
 
 /* This function will set a devices queue depth */
-static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+                                 int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (queue_depth > TW_Q_LENGTH-2)
                queue_depth = TW_Q_LENGTH-2;
        scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
@@ -732,7 +736,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int
                break;
        case TW_IOCTL_GET_COMPATIBILITY_INFO:
                tw_ioctl->driver_command.status = 0;
-               /* Copy compatiblity struct into ioctl data buffer */
+               /* Copy compatibility struct into ioctl data buffer */
                tw_compat_info = (TW_Compatibility_Info *)tw_ioctl->data_buffer;
                memcpy(tw_compat_info, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
                break;
diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c
new file mode 100644 (file)
index 0000000..4d314d7
--- /dev/null
@@ -0,0 +1,1924 @@
+/*
+   3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
+
+   Written By: Adam Radford <linuxraid@lsi.com>
+
+   Copyright (C) 2009 LSI 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; 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.
+
+   NO WARRANTY
+   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+   solely responsible for determining the appropriateness of using and
+   distributing the Program and assumes all risks associated with its
+   exercise of rights under this Agreement, including but not limited to
+   the risks and costs of program errors, damage to or loss of data,
+   programs or equipment, and unavailability or interruption of operations.
+
+   DISCLAIMER OF LIABILITY
+   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+   USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+   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
+
+   Controllers supported by this driver:
+
+   LSI 3ware 9750 6Gb/s SAS/SATA-RAID
+
+   Bugs/Comments/Suggestions should be mailed to:
+   linuxraid@lsi.com
+
+   For more information, goto:
+   http://www.lsi.com
+
+   History
+   -------
+   3.26.02.000 - Initial driver release.
+*/
+
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_cmnd.h>
+#include "3w-sas.h"
+
+/* Globals */
+#define TW_DRIVER_VERSION "3.26.02.000"
+static TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT];
+static unsigned int twl_device_extension_count;
+static int twl_major = -1;
+extern struct timezone sys_tz;
+
+/* Module parameters */
+MODULE_AUTHOR ("LSI");
+MODULE_DESCRIPTION ("LSI 3ware SAS/SATA-RAID Linux Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(TW_DRIVER_VERSION);
+
+static int use_msi;
+module_param(use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi, "Use Message Signaled Interrupts. Default: 0");
+
+/* Function prototypes */
+static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset);
+
+/* Functions */
+
+/* This function returns AENs through sysfs */
+static ssize_t twl_sysfs_aen_read(struct kobject *kobj,
+                                 struct bin_attribute *bin_attr,
+                                 char *outbuf, loff_t offset, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct Scsi_Host *shost = class_to_shost(dev);
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
+       unsigned long flags = 0;
+       ssize_t ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       spin_lock_irqsave(tw_dev->host->host_lock, flags);
+       ret = memory_read_from_buffer(outbuf, count, &offset, tw_dev->event_queue[0], sizeof(TW_Event) * TW_Q_LENGTH);
+       spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+       return ret;
+} /* End twl_sysfs_aen_read() */
+
+/* aen_read sysfs attribute initializer */
+static struct bin_attribute twl_sysfs_aen_read_attr = {
+       .attr = {
+               .name = "3ware_aen_read",
+               .mode = S_IRUSR,
+       }, 
+       .size = 0,
+       .read = twl_sysfs_aen_read
+};
+
+/* This function returns driver compatibility info through sysfs */
+static ssize_t twl_sysfs_compat_info(struct kobject *kobj,
+                                    struct bin_attribute *bin_attr,
+                                    char *outbuf, loff_t offset, size_t count)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct Scsi_Host *shost = class_to_shost(dev);
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)shost->hostdata;
+       unsigned long flags = 0;
+       ssize_t ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       spin_lock_irqsave(tw_dev->host->host_lock, flags);
+       ret = memory_read_from_buffer(outbuf, count, &offset, &tw_dev->tw_compat_info, sizeof(TW_Compatibility_Info));
+       spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+       return ret;
+} /* End twl_sysfs_compat_info() */
+
+/* compat_info sysfs attribute initializer */
+static struct bin_attribute twl_sysfs_compat_info_attr = {
+       .attr = {
+               .name = "3ware_compat_info",
+               .mode = S_IRUSR,
+       }, 
+       .size = 0,
+       .read = twl_sysfs_compat_info
+};
+
+/* Show some statistics about the card */
+static ssize_t twl_show_stats(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *host = class_to_shost(dev);
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+       unsigned long flags = 0;
+       ssize_t len;
+
+       spin_lock_irqsave(tw_dev->host->host_lock, flags);
+       len = snprintf(buf, PAGE_SIZE, "3w-sas Driver version: %s\n"
+                      "Current commands posted:   %4d\n"
+                      "Max commands posted:       %4d\n"
+                      "Last sgl length:           %4d\n"
+                      "Max sgl length:            %4d\n"
+                      "Last sector count:         %4d\n"
+                      "Max sector count:          %4d\n"
+                      "SCSI Host Resets:          %4d\n"
+                      "AEN's:                     %4d\n", 
+                      TW_DRIVER_VERSION,
+                      tw_dev->posted_request_count,
+                      tw_dev->max_posted_request_count,
+                      tw_dev->sgl_entries,
+                      tw_dev->max_sgl_entries,
+                      tw_dev->sector_count,
+                      tw_dev->max_sector_count,
+                      tw_dev->num_resets,
+                      tw_dev->aen_count);
+       spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+       return len;
+} /* End twl_show_stats() */
+
+/* This function will set a devices queue depth */
+static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+                                 int reason)
+{
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
+       if (queue_depth > TW_Q_LENGTH-2)
+               queue_depth = TW_Q_LENGTH-2;
+       scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
+       return queue_depth;
+} /* End twl_change_queue_depth() */
+
+/* stats sysfs attribute initializer */
+static struct device_attribute twl_host_stats_attr = {
+       .attr = {
+               .name =         "3ware_stats",
+               .mode =         S_IRUGO,
+       },
+       .show = twl_show_stats
+};
+
+/* Host attributes initializer */
+static struct device_attribute *twl_host_attrs[] = {
+       &twl_host_stats_attr,
+       NULL,
+};
+
+/* This function will look up an AEN severity string */
+static char *twl_aen_severity_lookup(unsigned char severity_code)
+{
+       char *retval = NULL;
+
+       if ((severity_code < (unsigned char) TW_AEN_SEVERITY_ERROR) ||
+           (severity_code > (unsigned char) TW_AEN_SEVERITY_DEBUG))
+               goto out;
+
+       retval = twl_aen_severity_table[severity_code];
+out:
+       return retval;
+} /* End twl_aen_severity_lookup() */
+
+/* This function will queue an event */
+static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
+{
+       u32 local_time;
+       struct timeval time;
+       TW_Event *event;
+       unsigned short aen;
+       char host[16];
+       char *error_str;
+
+       tw_dev->aen_count++;
+
+       /* Fill out event info */
+       event = tw_dev->event_queue[tw_dev->error_index];
+
+       host[0] = '\0';
+       if (tw_dev->host)
+               sprintf(host, " scsi%d:", tw_dev->host->host_no);
+
+       aen = le16_to_cpu(header->status_block.error);
+       memset(event, 0, sizeof(TW_Event));
+
+       event->severity = TW_SEV_OUT(header->status_block.severity__reserved);
+       do_gettimeofday(&time);
+       local_time = (u32)(time.tv_sec - (sys_tz.tz_minuteswest * 60));
+       event->time_stamp_sec = local_time;
+       event->aen_code = aen;
+       event->retrieved = TW_AEN_NOT_RETRIEVED;
+       event->sequence_id = tw_dev->error_sequence_id;
+       tw_dev->error_sequence_id++;
+
+       /* Check for embedded error string */
+       error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
+
+       header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+       event->parameter_len = strlen(header->err_specific_desc);
+       memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str));
+       if (event->severity != TW_AEN_SEVERITY_DEBUG)
+               printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
+                      host,
+                      twl_aen_severity_lookup(TW_SEV_OUT(header->status_block.severity__reserved)),
+                      TW_MESSAGE_SOURCE_CONTROLLER_EVENT, aen, error_str,
+                      header->err_specific_desc);
+       else
+               tw_dev->aen_count--;
+
+       tw_dev->error_index = (tw_dev->error_index + 1 ) % TW_Q_LENGTH;
+} /* End twl_aen_queue_event() */
+
+/* This function will attempt to post a command packet to the board */
+static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id)
+{
+       dma_addr_t command_que_value;
+
+       command_que_value = tw_dev->command_packet_phys[request_id];
+       command_que_value += TW_COMMAND_OFFSET;
+
+       /* First write upper 4 bytes */
+       writel((u32)((u64)command_que_value >> 32), TWL_HIBQPH_REG_ADDR(tw_dev));
+       /* Then the lower 4 bytes */
+       writel((u32)(command_que_value | TWL_PULL_MODE), TWL_HIBQPL_REG_ADDR(tw_dev));
+
+       tw_dev->state[request_id] = TW_S_POSTED;
+       tw_dev->posted_request_count++;
+       if (tw_dev->posted_request_count > tw_dev->max_posted_request_count)
+               tw_dev->max_posted_request_count = tw_dev->posted_request_count;
+
+       return 0;
+} /* End twl_post_command_packet() */
+
+/* This function will perform a pci-dma mapping for a scatter gather list */
+static int twl_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id)
+{
+       int use_sg;
+       struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+
+       use_sg = scsi_dma_map(cmd);
+       if (!use_sg)
+               return 0;
+       else if (use_sg < 0) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1, "Failed to map scatter gather list");
+               return 0;
+       }
+
+       cmd->SCp.phase = TW_PHASE_SGLIST;
+       cmd->SCp.have_data_in = use_sg;
+
+       return use_sg;
+} /* End twl_map_scsi_sg_data() */
+
+/* This function hands scsi cdb's to the firmware */
+static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg)
+{
+       TW_Command_Full *full_command_packet;
+       TW_Command_Apache *command_packet;
+       int i, sg_count;
+       struct scsi_cmnd *srb = NULL;
+       struct scatterlist *sglist = NULL, *sg;
+       int retval = 1;
+
+       if (tw_dev->srb[request_id]) {
+               srb = tw_dev->srb[request_id];
+               if (scsi_sglist(srb))
+                       sglist = scsi_sglist(srb);
+       }
+
+       /* Initialize command packet */
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       full_command_packet->header.header_desc.size_header = 128;
+       full_command_packet->header.status_block.error = 0;
+       full_command_packet->header.status_block.severity__reserved = 0;
+
+       command_packet = &full_command_packet->command.newcommand;
+       command_packet->status = 0;
+       command_packet->opcode__reserved = TW_OPRES_IN(0, TW_OP_EXECUTE_SCSI);
+
+       /* We forced 16 byte cdb use earlier */
+       if (!cdb)
+               memcpy(command_packet->cdb, srb->cmnd, TW_MAX_CDB_LEN);
+       else
+               memcpy(command_packet->cdb, cdb, TW_MAX_CDB_LEN);
+
+       if (srb) {
+               command_packet->unit = srb->device->id;
+               command_packet->request_id__lunl =
+                       cpu_to_le16(TW_REQ_LUN_IN(srb->device->lun, request_id));
+       } else {
+               command_packet->request_id__lunl =
+                       cpu_to_le16(TW_REQ_LUN_IN(0, request_id));
+               command_packet->unit = 0;
+       }
+
+       command_packet->sgl_offset = 16;
+
+       if (!sglistarg) {
+               /* Map sglist from scsi layer to cmd packet */
+               if (scsi_sg_count(srb)) {
+                       sg_count = twl_map_scsi_sg_data(tw_dev, request_id);
+                       if (sg_count == 0)
+                               goto out;
+
+                       scsi_for_each_sg(srb, sg, sg_count, i) {
+                               command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg));
+                               command_packet->sg_list[i].length = TW_CPU_TO_SGL(sg_dma_len(sg));
+                       }
+                       command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id])));
+               }
+       } else {
+               /* Internal cdb post */
+               for (i = 0; i < use_sg; i++) {
+                       command_packet->sg_list[i].address = TW_CPU_TO_SGL(sglistarg[i].address);
+                       command_packet->sg_list[i].length = TW_CPU_TO_SGL(sglistarg[i].length);
+               }
+               command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN(0, use_sg));
+       }
+
+       /* Update some stats */
+       if (srb) {
+               tw_dev->sector_count = scsi_bufflen(srb) / 512;
+               if (tw_dev->sector_count > tw_dev->max_sector_count)
+                       tw_dev->max_sector_count = tw_dev->sector_count;
+               tw_dev->sgl_entries = scsi_sg_count(srb);
+               if (tw_dev->sgl_entries > tw_dev->max_sgl_entries)
+                       tw_dev->max_sgl_entries = tw_dev->sgl_entries;
+       }
+
+       /* Now post the command to the board */
+       retval = twl_post_command_packet(tw_dev, request_id);
+
+out:
+       return retval;
+} /* End twl_scsiop_execute_scsi() */
+
+/* This function will read the aen queue from the isr */
+static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
+{
+       char cdb[TW_MAX_CDB_LEN];
+       TW_SG_Entry_ISO sglist[1];
+       TW_Command_Full *full_command_packet;
+       int retval = 1;
+
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+       /* Initialize cdb */
+       memset(&cdb, 0, TW_MAX_CDB_LEN);
+       cdb[0] = REQUEST_SENSE; /* opcode */
+       cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+       /* Initialize sglist */
+       memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
+       sglist[0].length = TW_SECTOR_SIZE;
+       sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+       /* Mark internal command */
+       tw_dev->srb[request_id] = NULL;
+
+       /* Now post the command packet */
+       if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2, "Post failed while reading AEN queue");
+               goto out;
+       }
+       retval = 0;
+out:
+       return retval;
+} /* End twl_aen_read_queue() */
+
+/* This function will sync firmware time with the host time */
+static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
+{
+       u32 schedulertime;
+       struct timeval utc;
+       TW_Command_Full *full_command_packet;
+       TW_Command *command_packet;
+       TW_Param_Apache *param;
+       u32 local_time;
+
+       /* Fill out the command packet */
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       memset(full_command_packet, 0, sizeof(TW_Command_Full));
+       command_packet = &full_command_packet->command.oldcommand;
+       command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_SET_PARAM);
+       command_packet->request_id = request_id;
+       command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
+       command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
+       command_packet->size = TW_COMMAND_SIZE;
+       command_packet->byte6_offset.parameter_count = cpu_to_le16(1);
+
+       /* Setup the param */
+       param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+       memset(param, 0, TW_SECTOR_SIZE);
+       param->table_id = cpu_to_le16(TW_TIMEKEEP_TABLE | 0x8000); /* Controller time keep table */
+       param->parameter_id = cpu_to_le16(0x3); /* SchedulerTime */
+       param->parameter_size_bytes = cpu_to_le16(4);
+
+       /* Convert system time in UTC to local time seconds since last 
+           Sunday 12:00AM */
+       do_gettimeofday(&utc);
+       local_time = (u32)(utc.tv_sec - (sys_tz.tz_minuteswest * 60));
+       schedulertime = local_time - (3 * 86400);
+       schedulertime = cpu_to_le32(schedulertime % 604800);
+
+       memcpy(param->data, &schedulertime, sizeof(u32));
+
+       /* Mark internal command */
+       tw_dev->srb[request_id] = NULL;
+
+       /* Now post the command */
+       twl_post_command_packet(tw_dev, request_id);
+} /* End twl_aen_sync_time() */
+
+/* This function will assign an available request id */
+static void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
+{
+       *request_id = tw_dev->free_queue[tw_dev->free_head];
+       tw_dev->free_head = (tw_dev->free_head + 1) % TW_Q_LENGTH;
+       tw_dev->state[*request_id] = TW_S_STARTED;
+} /* End twl_get_request_id() */
+
+/* This function will free a request id */
+static void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id)
+{
+       tw_dev->free_queue[tw_dev->free_tail] = request_id;
+       tw_dev->state[request_id] = TW_S_FINISHED;
+       tw_dev->free_tail = (tw_dev->free_tail + 1) % TW_Q_LENGTH;
+} /* End twl_free_request_id() */
+
+/* This function will complete an aen request from the isr */
+static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id)
+{
+       TW_Command_Full *full_command_packet;
+       TW_Command *command_packet;
+       TW_Command_Apache_Header *header;
+       unsigned short aen;
+       int retval = 1;
+
+       header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+       tw_dev->posted_request_count--;
+       aen = le16_to_cpu(header->status_block.error);
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       command_packet = &full_command_packet->command.oldcommand;
+
+       /* First check for internal completion of set param for time sync */
+       if (TW_OP_OUT(command_packet->opcode__sgloffset) == TW_OP_SET_PARAM) {
+               /* Keep reading the queue in case there are more aen's */
+               if (twl_aen_read_queue(tw_dev, request_id))
+                       goto out2;
+               else {
+                       retval = 0;
+                       goto out;
+               }
+       }
+
+       switch (aen) {
+       case TW_AEN_QUEUE_EMPTY:
+               /* Quit reading the queue if this is the last one */
+               break;
+       case TW_AEN_SYNC_TIME_WITH_HOST:
+               twl_aen_sync_time(tw_dev, request_id);
+               retval = 0;
+               goto out;
+       default:
+               twl_aen_queue_event(tw_dev, header);
+
+               /* If there are more aen's, keep reading the queue */
+               if (twl_aen_read_queue(tw_dev, request_id))
+                       goto out2;
+               else {
+                       retval = 0;
+                       goto out;
+               }
+       }
+       retval = 0;
+out2:
+       tw_dev->state[request_id] = TW_S_COMPLETED;
+       twl_free_request_id(tw_dev, request_id);
+       clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+out:
+       return retval;
+} /* End twl_aen_complete() */
+
+/* This function will poll for a response */
+static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
+{
+       unsigned long before;
+       dma_addr_t mfa;
+       u32 regh, regl;
+       u32 response;
+       int retval = 1;
+       int found = 0;
+
+       before = jiffies;
+
+       while (!found) {
+               if (sizeof(dma_addr_t) > 4) {
+                       regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
+                       regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+                       mfa = ((u64)regh << 32) | regl;
+               } else
+                       mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+
+               response = (u32)mfa;
+
+               if (TW_RESID_OUT(response) == request_id)
+                       found = 1;
+
+               if (time_after(jiffies, before + HZ * seconds))
+                       goto out;
+
+               msleep(50);
+       }
+       retval = 0;
+out: 
+       return retval;
+} /* End twl_poll_response() */
+
+/* This function will drain the aen queue */
+static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
+{
+       int request_id = 0;
+       char cdb[TW_MAX_CDB_LEN];
+       TW_SG_Entry_ISO sglist[1];
+       int finished = 0, count = 0;
+       TW_Command_Full *full_command_packet;
+       TW_Command_Apache_Header *header;
+       unsigned short aen;
+       int first_reset = 0, queue = 0, retval = 1;
+
+       if (no_check_reset)
+               first_reset = 0;
+       else
+               first_reset = 1;
+
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       memset(full_command_packet, 0, sizeof(TW_Command_Full));
+
+       /* Initialize cdb */
+       memset(&cdb, 0, TW_MAX_CDB_LEN);
+       cdb[0] = REQUEST_SENSE; /* opcode */
+       cdb[4] = TW_ALLOCATION_LENGTH; /* allocation length */
+
+       /* Initialize sglist */
+       memset(&sglist, 0, sizeof(TW_SG_Entry_ISO));
+       sglist[0].length = TW_SECTOR_SIZE;
+       sglist[0].address = tw_dev->generic_buffer_phys[request_id];
+
+       /* Mark internal command */
+       tw_dev->srb[request_id] = NULL;
+
+       do {
+               /* Send command to the board */
+               if (twl_scsiop_execute_scsi(tw_dev, request_id, cdb, 1, sglist)) {
+                       TW_PRINTK(tw_dev->host, TW_DRIVER, 0x3, "Error posting request sense");
+                       goto out;
+               }
+
+               /* Now poll for completion */
+               if (twl_poll_response(tw_dev, request_id, 30)) {
+                       TW_PRINTK(tw_dev->host, TW_DRIVER, 0x4, "No valid response while draining AEN queue");
+                       tw_dev->posted_request_count--;
+                       goto out;
+               }
+
+               tw_dev->posted_request_count--;
+               header = (TW_Command_Apache_Header *)tw_dev->generic_buffer_virt[request_id];
+               aen = le16_to_cpu(header->status_block.error);
+               queue = 0;
+               count++;
+
+               switch (aen) {
+               case TW_AEN_QUEUE_EMPTY:
+                       if (first_reset != 1)
+                               goto out;
+                       else
+                               finished = 1;
+                       break;
+               case TW_AEN_SOFT_RESET:
+                       if (first_reset == 0)
+                               first_reset = 1;
+                       else
+                               queue = 1;
+                       break;
+               case TW_AEN_SYNC_TIME_WITH_HOST:
+                       break;
+               default:
+                       queue = 1;
+               }
+
+               /* Now queue an event info */
+               if (queue)
+                       twl_aen_queue_event(tw_dev, header);
+       } while ((finished == 0) && (count < TW_MAX_AEN_DRAIN));
+
+       if (count == TW_MAX_AEN_DRAIN)
+               goto out;
+
+       retval = 0;
+out:
+       tw_dev->state[request_id] = TW_S_INITIAL;
+       return retval;
+} /* End twl_aen_drain_queue() */
+
+/* This function will allocate memory and check if it is correctly aligned */
+static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
+{
+       int i;
+       dma_addr_t dma_handle;
+       unsigned long *cpu_addr;
+       int retval = 1;
+
+       cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle);
+       if (!cpu_addr) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x5, "Memory allocation failed");
+               goto out;
+       }
+
+       memset(cpu_addr, 0, size*TW_Q_LENGTH);
+
+       for (i = 0; i < TW_Q_LENGTH; i++) {
+               switch(which) {
+               case 0:
+                       tw_dev->command_packet_phys[i] = dma_handle+(i*size);
+                       tw_dev->command_packet_virt[i] = (TW_Command_Full *)((unsigned char *)cpu_addr + (i*size));
+                       break;
+               case 1:
+                       tw_dev->generic_buffer_phys[i] = dma_handle+(i*size);
+                       tw_dev->generic_buffer_virt[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size));
+                       break;
+               case 2:
+                       tw_dev->sense_buffer_phys[i] = dma_handle+(i*size);
+                       tw_dev->sense_buffer_virt[i] = (TW_Command_Apache_Header *)((unsigned char *)cpu_addr + (i*size));
+                       break;
+               }
+       }
+       retval = 0;
+out:
+       return retval;
+} /* End twl_allocate_memory() */
+
+/* This function will load the request id and various sgls for ioctls */
+static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
+{
+       TW_Command *oldcommand;
+       TW_Command_Apache *newcommand;
+       TW_SG_Entry_ISO *sgl;
+       unsigned int pae = 0;
+
+       if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4))
+               pae = 1;
+
+       if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {
+               newcommand = &full_command_packet->command.newcommand;
+               newcommand->request_id__lunl =
+                       cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->request_id__lunl), request_id));
+               if (length) {
+                       newcommand->sg_list[0].address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+                       newcommand->sg_list[0].length = TW_CPU_TO_SGL(length);
+               }
+               newcommand->sgl_entries__lunh =
+                       cpu_to_le16(TW_REQ_LUN_IN(TW_LUN_OUT(newcommand->sgl_entries__lunh), length ? 1 : 0));
+       } else {
+               oldcommand = &full_command_packet->command.oldcommand;
+               oldcommand->request_id = request_id;
+
+               if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {
+                       /* Load the sg list */
+                       sgl = (TW_SG_Entry_ISO *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry_ISO)/4) + pae + (sizeof(dma_addr_t) > 4 ? 1 : 0));
+                       sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1);
+                       sgl->length = TW_CPU_TO_SGL(length);
+                       oldcommand->size += pae;
+                       oldcommand->size += sizeof(dma_addr_t) > 4 ? 1 : 0;
+               }
+       }
+} /* End twl_load_sgl() */
+
+/* This function handles ioctl for the character device
+   This interface is used by smartmontools open source software */
+static int twl_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       long timeout;
+       unsigned long *cpu_addr, data_buffer_length_adjusted = 0, flags = 0;
+       dma_addr_t dma_handle;
+       int request_id = 0;
+       TW_Ioctl_Driver_Command driver_command;
+       TW_Ioctl_Buf_Apache *tw_ioctl;
+       TW_Command_Full *full_command_packet;
+       TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)];
+       int retval = -EFAULT;
+       void __user *argp = (void __user *)arg;
+
+       /* Only let one of these through at a time */
+       if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
+               retval = -EINTR;
+               goto out;
+       }
+
+       /* First copy down the driver command */
+       if (copy_from_user(&driver_command, argp, sizeof(TW_Ioctl_Driver_Command)))
+               goto out2;
+
+       /* Check data buffer size */
+       if (driver_command.buffer_length > TW_MAX_SECTORS * 2048) {
+               retval = -EINVAL;
+               goto out2;
+       }
+
+       /* Hardware can only do multiple of 512 byte transfers */
+       data_buffer_length_adjusted = (driver_command.buffer_length + 511) & ~511;
+
+       /* Now allocate ioctl buf memory */
+       cpu_addr = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, &dma_handle, GFP_KERNEL);
+       if (!cpu_addr) {
+               retval = -ENOMEM;
+               goto out2;
+       }
+
+       tw_ioctl = (TW_Ioctl_Buf_Apache *)cpu_addr;
+
+       /* Now copy down the entire ioctl */
+       if (copy_from_user(tw_ioctl, argp, driver_command.buffer_length + sizeof(TW_Ioctl_Buf_Apache) - 1))
+               goto out3;
+
+       /* See which ioctl we are doing */
+       switch (cmd) {
+       case TW_IOCTL_FIRMWARE_PASS_THROUGH:
+               spin_lock_irqsave(tw_dev->host->host_lock, flags);
+               twl_get_request_id(tw_dev, &request_id);
+
+               /* Flag internal command */
+               tw_dev->srb[request_id] = NULL;
+
+               /* Flag chrdev ioctl */
+               tw_dev->chrdev_request_id = request_id;
+
+               full_command_packet = (TW_Command_Full *)&tw_ioctl->firmware_command;
+
+               /* Load request id and sglist for both command types */
+               twl_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted);
+
+               memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full));
+
+               /* Now post the command packet to the controller */
+               twl_post_command_packet(tw_dev, request_id);
+               spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+               timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ;
+
+               /* Now wait for command to complete */
+               timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout);
+
+               /* We timed out, and didn't get an interrupt */
+               if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) {
+                       /* Now we need to reset the board */
+                       printk(KERN_WARNING "3w-sas: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n",
+                              tw_dev->host->host_no, TW_DRIVER, 0x6,
+                              cmd);
+                       retval = -EIO;
+                       twl_reset_device_extension(tw_dev, 1);
+                       goto out3;
+               }
+
+               /* Now copy in the command packet response */
+               memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virt[request_id], sizeof(TW_Command_Full));
+               
+               /* Now complete the io */
+               spin_lock_irqsave(tw_dev->host->host_lock, flags);
+               tw_dev->posted_request_count--;
+               tw_dev->state[request_id] = TW_S_COMPLETED;
+               twl_free_request_id(tw_dev, request_id);
+               spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+               break;
+       default:
+               retval = -ENOTTY;
+               goto out3;
+       }
+
+       /* Now copy the entire response to userspace */
+       if (copy_to_user(argp, tw_ioctl, sizeof(TW_Ioctl_Buf_Apache) + driver_command.buffer_length - 1) == 0)
+               retval = 0;
+out3:
+       /* Now free ioctl buf memory */
+       dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_Ioctl_Buf_Apache) - 1, cpu_addr, dma_handle);
+out2:
+       mutex_unlock(&tw_dev->ioctl_lock);
+out:
+       return retval;
+} /* End twl_chrdev_ioctl() */
+
+/* This function handles open for the character device */
+static int twl_chrdev_open(struct inode *inode, struct file *file)
+{
+       unsigned int minor_number;
+       int retval = -ENODEV;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               retval = -EACCES;
+               goto out;
+       }
+
+       cycle_kernel_lock();
+       minor_number = iminor(inode);
+       if (minor_number >= twl_device_extension_count)
+               goto out;
+       retval = 0;
+out:
+       return retval;
+} /* End twl_chrdev_open() */
+
+/* File operations struct for character device */
+static const struct file_operations twl_fops = {
+       .owner          = THIS_MODULE,
+       .ioctl          = twl_chrdev_ioctl,
+       .open           = twl_chrdev_open,
+       .release        = NULL
+};
+
+/* This function passes sense data from firmware to scsi layer */
+static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host)
+{
+       TW_Command_Apache_Header *header;
+       TW_Command_Full *full_command_packet;
+       unsigned short error;
+       char *error_str;
+       int retval = 1;
+
+       header = tw_dev->sense_buffer_virt[i];
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+
+       /* Get embedded firmware error string */
+       error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]);
+
+       /* Don't print error for Logical unit not supported during rollcall */
+       error = le16_to_cpu(header->status_block.error);
+       if ((error != TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED) && (error != TW_ERROR_UNIT_OFFLINE) && (error != TW_ERROR_INVALID_FIELD_IN_CDB)) {
+               if (print_host)
+                       printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+                              tw_dev->host->host_no,
+                              TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                              header->status_block.error,
+                              error_str, 
+                              header->err_specific_desc);
+               else
+                       printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s:%s.\n",
+                              TW_MESSAGE_SOURCE_CONTROLLER_ERROR,
+                              header->status_block.error,
+                              error_str,
+                              header->err_specific_desc);
+       }
+
+       if (copy_sense) {
+               memcpy(tw_dev->srb[request_id]->sense_buffer, header->sense_data, TW_SENSE_DATA_LENGTH);
+               tw_dev->srb[request_id]->result = (full_command_packet->command.newcommand.status << 1);
+               goto out;
+       }
+out:
+       return retval;
+} /* End twl_fill_sense() */
+
+/* This function will free up device extension resources */
+static void twl_free_device_extension(TW_Device_Extension *tw_dev)
+{
+       if (tw_dev->command_packet_virt[0])
+               pci_free_consistent(tw_dev->tw_pci_dev,
+                                   sizeof(TW_Command_Full)*TW_Q_LENGTH,
+                                   tw_dev->command_packet_virt[0],
+                                   tw_dev->command_packet_phys[0]);
+
+       if (tw_dev->generic_buffer_virt[0])
+               pci_free_consistent(tw_dev->tw_pci_dev,
+                                   TW_SECTOR_SIZE*TW_Q_LENGTH,
+                                   tw_dev->generic_buffer_virt[0],
+                                   tw_dev->generic_buffer_phys[0]);
+
+       if (tw_dev->sense_buffer_virt[0])
+               pci_free_consistent(tw_dev->tw_pci_dev,
+                                   sizeof(TW_Command_Apache_Header)*
+                                   TW_Q_LENGTH,
+                                   tw_dev->sense_buffer_virt[0],
+                                   tw_dev->sense_buffer_phys[0]);
+
+       kfree(tw_dev->event_queue[0]);
+} /* End twl_free_device_extension() */
+
+/* This function will get parameter table entries from the firmware */
+static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
+{
+       TW_Command_Full *full_command_packet;
+       TW_Command *command_packet;
+       TW_Param_Apache *param;
+       void *retval = NULL;
+
+       /* Setup the command packet */
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       memset(full_command_packet, 0, sizeof(TW_Command_Full));
+       command_packet = &full_command_packet->command.oldcommand;
+
+       command_packet->opcode__sgloffset = TW_OPSGL_IN(2, TW_OP_GET_PARAM);
+       command_packet->size              = TW_COMMAND_SIZE;
+       command_packet->request_id        = request_id;
+       command_packet->byte6_offset.block_count = cpu_to_le16(1);
+
+       /* Now setup the param */
+       param = (TW_Param_Apache *)tw_dev->generic_buffer_virt[request_id];
+       memset(param, 0, TW_SECTOR_SIZE);
+       param->table_id = cpu_to_le16(table_id | 0x8000);
+       param->parameter_id = cpu_to_le16(parameter_id);
+       param->parameter_size_bytes = cpu_to_le16(parameter_size_bytes);
+
+       command_packet->byte8_offset.param.sgl[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]);
+       command_packet->byte8_offset.param.sgl[0].length = TW_CPU_TO_SGL(TW_SECTOR_SIZE);
+
+       /* Post the command packet to the board */
+       twl_post_command_packet(tw_dev, request_id);
+
+       /* Poll for completion */
+       if (twl_poll_response(tw_dev, request_id, 30))
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x7, "No valid response during get param")
+       else
+               retval = (void *)&(param->data[0]);
+
+       tw_dev->posted_request_count--;
+       tw_dev->state[request_id] = TW_S_INITIAL;
+
+       return retval;
+} /* End twl_get_param() */
+
+/* This function will send an initconnection command to controller */
+static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits,
+                             u32 set_features, unsigned short current_fw_srl, 
+                             unsigned short current_fw_arch_id, 
+                             unsigned short current_fw_branch, 
+                             unsigned short current_fw_build, 
+                             unsigned short *fw_on_ctlr_srl, 
+                             unsigned short *fw_on_ctlr_arch_id, 
+                             unsigned short *fw_on_ctlr_branch, 
+                             unsigned short *fw_on_ctlr_build, 
+                             u32 *init_connect_result)
+{
+       TW_Command_Full *full_command_packet;
+       TW_Initconnect *tw_initconnect;
+       int request_id = 0, retval = 1;
+
+       /* Initialize InitConnection command packet */
+       full_command_packet = tw_dev->command_packet_virt[request_id];
+       memset(full_command_packet, 0, sizeof(TW_Command_Full));
+       full_command_packet->header.header_desc.size_header = 128;
+       
+       tw_initconnect = (TW_Initconnect *)&full_command_packet->command.oldcommand;
+       tw_initconnect->opcode__reserved = TW_OPRES_IN(0, TW_OP_INIT_CONNECTION);
+       tw_initconnect->request_id = request_id;
+       tw_initconnect->message_credits = cpu_to_le16(message_credits);
+       tw_initconnect->features = set_features;
+
+       /* Turn on 64-bit sgl support if we need to */
+       tw_initconnect->features |= sizeof(dma_addr_t) > 4 ? 1 : 0;
+
+       tw_initconnect->features = cpu_to_le32(tw_initconnect->features);
+
+       if (set_features & TW_EXTENDED_INIT_CONNECT) {
+               tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE_EXTENDED;
+               tw_initconnect->fw_srl = cpu_to_le16(current_fw_srl);
+               tw_initconnect->fw_arch_id = cpu_to_le16(current_fw_arch_id);
+               tw_initconnect->fw_branch = cpu_to_le16(current_fw_branch);
+               tw_initconnect->fw_build = cpu_to_le16(current_fw_build);
+       } else 
+               tw_initconnect->size = TW_INIT_COMMAND_PACKET_SIZE;
+
+       /* Send command packet to the board */
+       twl_post_command_packet(tw_dev, request_id);
+
+       /* Poll for completion */
+       if (twl_poll_response(tw_dev, request_id, 30)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x8, "No valid response during init connection");
+       } else {
+               if (set_features & TW_EXTENDED_INIT_CONNECT) {
+                       *fw_on_ctlr_srl = le16_to_cpu(tw_initconnect->fw_srl);
+                       *fw_on_ctlr_arch_id = le16_to_cpu(tw_initconnect->fw_arch_id);
+                       *fw_on_ctlr_branch = le16_to_cpu(tw_initconnect->fw_branch);
+                       *fw_on_ctlr_build = le16_to_cpu(tw_initconnect->fw_build);
+                       *init_connect_result = le32_to_cpu(tw_initconnect->result);
+               }
+               retval = 0;
+       }
+
+       tw_dev->posted_request_count--;
+       tw_dev->state[request_id] = TW_S_INITIAL;
+
+       return retval;
+} /* End twl_initconnection() */
+
+/* This function will initialize the fields of a device extension */
+static int twl_initialize_device_extension(TW_Device_Extension *tw_dev)
+{
+       int i, retval = 1;
+
+       /* Initialize command packet buffers */
+       if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Full), 0)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x9, "Command packet memory allocation failed");
+               goto out;
+       }
+
+       /* Initialize generic buffer */
+       if (twl_allocate_memory(tw_dev, TW_SECTOR_SIZE, 1)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0xa, "Generic memory allocation failed");
+               goto out;
+       }
+
+       /* Allocate sense buffers */
+       if (twl_allocate_memory(tw_dev, sizeof(TW_Command_Apache_Header), 2)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0xb, "Sense buffer allocation failed");
+               goto out;
+       }
+
+       /* Allocate event info space */
+       tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL);
+       if (!tw_dev->event_queue[0]) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0xc, "Event info memory allocation failed");
+               goto out;
+       }
+
+       for (i = 0; i < TW_Q_LENGTH; i++) {
+               tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event)));
+               tw_dev->free_queue[i] = i;
+               tw_dev->state[i] = TW_S_INITIAL;
+       }
+
+       tw_dev->free_head = TW_Q_START;
+       tw_dev->free_tail = TW_Q_START;
+       tw_dev->error_sequence_id = 1;
+       tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+       mutex_init(&tw_dev->ioctl_lock);
+       init_waitqueue_head(&tw_dev->ioctl_wqueue);
+
+       retval = 0;
+out:
+       return retval;
+} /* End twl_initialize_device_extension() */
+
+/* This function will perform a pci-dma unmap */
+static void twl_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id)
+{
+       struct scsi_cmnd *cmd = tw_dev->srb[request_id];
+
+       if (cmd->SCp.phase == TW_PHASE_SGLIST)
+               scsi_dma_unmap(cmd);
+} /* End twl_unmap_scsi_data() */
+
+/* This function will handle attention interrupts */
+static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev)
+{
+       int retval = 1;
+       u32 request_id, doorbell;
+
+       /* Read doorbell status */
+       doorbell = readl(TWL_HOBDB_REG_ADDR(tw_dev));
+
+       /* Check for controller errors */
+       if (doorbell & TWL_DOORBELL_CONTROLLER_ERROR) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0xd, "Microcontroller Error: clearing");
+               goto out;
+       }
+
+       /* Check if we need to perform an AEN drain */
+       if (doorbell & TWL_DOORBELL_ATTENTION_INTERRUPT) {
+               if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {
+                       twl_get_request_id(tw_dev, &request_id);
+                       if (twl_aen_read_queue(tw_dev, request_id)) {
+                               tw_dev->state[request_id] = TW_S_COMPLETED;
+                               twl_free_request_id(tw_dev, request_id);
+                               clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);
+                       }
+               }
+       }
+
+       retval = 0;
+out:
+       /* Clear doorbell interrupt */
+       TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+       /* Make sure the clear was flushed by reading it back */
+       readl(TWL_HOBDBC_REG_ADDR(tw_dev));
+
+       return retval;
+} /* End twl_handle_attention_interrupt() */
+
+/* Interrupt service routine */
+static irqreturn_t twl_interrupt(int irq, void *dev_instance)
+{
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance;
+       int i, handled = 0, error = 0;
+       dma_addr_t mfa = 0;
+       u32 reg, regl, regh, response, request_id = 0;
+       struct scsi_cmnd *cmd;
+       TW_Command_Full *full_command_packet;
+
+       spin_lock(tw_dev->host->host_lock);
+
+       /* Read host interrupt status */
+       reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
+
+       /* Check if this is our interrupt, otherwise bail */
+       if (!(reg & TWL_HISTATUS_VALID_INTERRUPT))
+               goto twl_interrupt_bail;
+
+       handled = 1;
+
+       /* If we are resetting, bail */
+       if (test_bit(TW_IN_RESET, &tw_dev->flags))
+               goto twl_interrupt_bail;
+
+       /* Attention interrupt */
+       if (reg & TWL_HISTATUS_ATTENTION_INTERRUPT) {
+               if (twl_handle_attention_interrupt(tw_dev)) {
+                       TWL_MASK_INTERRUPTS(tw_dev);
+                       goto twl_interrupt_bail;
+               }
+       }
+
+       /* Response interrupt */
+       while (reg & TWL_HISTATUS_RESPONSE_INTERRUPT) {
+               if (sizeof(dma_addr_t) > 4) {
+                       regh = readl(TWL_HOBQPH_REG_ADDR(tw_dev));
+                       regl = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+                       mfa = ((u64)regh << 32) | regl;
+               } else
+                       mfa = readl(TWL_HOBQPL_REG_ADDR(tw_dev));
+
+               error = 0;
+               response = (u32)mfa;
+
+               /* Check for command packet error */
+               if (!TW_NOTMFA_OUT(response)) {
+                       for (i=0;i<TW_Q_LENGTH;i++) {
+                               if (tw_dev->sense_buffer_phys[i] == mfa) {
+                                       request_id = le16_to_cpu(tw_dev->sense_buffer_virt[i]->header_desc.request_id);
+                                       if (tw_dev->srb[request_id] != NULL)
+                                               error = twl_fill_sense(tw_dev, i, request_id, 1, 1);
+                                       else {
+                                               /* Skip ioctl error prints */
+                                               if (request_id != tw_dev->chrdev_request_id)
+                                                       error = twl_fill_sense(tw_dev, i, request_id, 0, 1);
+                                               else
+                                                       memcpy(tw_dev->command_packet_virt[request_id], tw_dev->sense_buffer_virt[i], sizeof(TW_Command_Apache_Header));
+                                       }
+
+                                       /* Now re-post the sense buffer */
+                                       writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
+                                       writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
+                                       break;
+                               }
+                       }
+               } else
+                       request_id = TW_RESID_OUT(response);
+
+               full_command_packet = tw_dev->command_packet_virt[request_id];
+
+               /* Check for correct state */
+               if (tw_dev->state[request_id] != TW_S_POSTED) {
+                       if (tw_dev->srb[request_id] != NULL) {
+                               TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Received a request id that wasn't posted");
+                               TWL_MASK_INTERRUPTS(tw_dev);
+                               goto twl_interrupt_bail;
+                       }
+               }
+
+               /* Check for internal command completion */
+               if (tw_dev->srb[request_id] == NULL) {
+                       if (request_id != tw_dev->chrdev_request_id) {
+                               if (twl_aen_complete(tw_dev, request_id))
+                                       TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "Error completing AEN during attention interrupt");
+                       } else {
+                               tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+                               wake_up(&tw_dev->ioctl_wqueue);
+                       }
+               } else {
+                       cmd = tw_dev->srb[request_id];
+
+                       if (!error)
+                               cmd->result = (DID_OK << 16);
+                       
+                       /* Report residual bytes for single sgl */
+                       if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) {
+                               if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id]))
+                                       scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length);
+                       }
+
+                       /* Now complete the io */
+                       tw_dev->state[request_id] = TW_S_COMPLETED;
+                       twl_free_request_id(tw_dev, request_id);
+                       tw_dev->posted_request_count--;
+                       tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+                       twl_unmap_scsi_data(tw_dev, request_id);
+               }
+
+               /* Check for another response interrupt */
+               reg = readl(TWL_HISTAT_REG_ADDR(tw_dev));
+       }
+
+twl_interrupt_bail:
+       spin_unlock(tw_dev->host->host_lock);
+       return IRQ_RETVAL(handled);
+} /* End twl_interrupt() */
+
+/* This function will poll for a register change */
+static int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds)
+{
+       unsigned long before;
+       int retval = 1;
+       u32 reg_value;
+
+       reg_value = readl(reg);
+       before = jiffies;
+
+        while ((reg_value & value) != result) {
+               reg_value = readl(reg);
+               if (time_after(jiffies, before + HZ * seconds))
+                       goto out;
+               msleep(50);
+       }
+       retval = 0;
+out:
+       return retval;
+} /* End twl_poll_register() */
+
+/* This function will reset a controller */
+static int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
+{
+       int retval = 1;
+       int i = 0;
+       u32 status = 0;
+       unsigned short fw_on_ctlr_srl = 0, fw_on_ctlr_arch_id = 0;
+       unsigned short fw_on_ctlr_branch = 0, fw_on_ctlr_build = 0;
+       u32 init_connect_result = 0;
+       int tries = 0;
+       int do_soft_reset = soft_reset;
+
+       while (tries < TW_MAX_RESET_TRIES) {
+               /* Do a soft reset if one is needed */
+               if (do_soft_reset) {
+                       TWL_SOFT_RESET(tw_dev);
+
+                       /* Make sure controller is in a good state */
+                       if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, 0x0, 30)) {
+                               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Controller never went non-ready during reset sequence");
+                               tries++;
+                               continue;
+                       }
+                       if (twl_poll_register(tw_dev, TWL_SCRPD3_REG_ADDR(tw_dev), TWL_CONTROLLER_READY, TWL_CONTROLLER_READY, 60)) {
+                               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x11, "Controller not ready during reset sequence");
+                               tries++;
+                               continue;
+                       }
+               }
+
+               /* Initconnect */
+               if (twl_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS,
+                                      TW_EXTENDED_INIT_CONNECT, TW_CURRENT_DRIVER_SRL,
+                                      TW_9750_ARCH_ID, TW_CURRENT_DRIVER_BRANCH,
+                                      TW_CURRENT_DRIVER_BUILD, &fw_on_ctlr_srl,
+                                      &fw_on_ctlr_arch_id, &fw_on_ctlr_branch,
+                                      &fw_on_ctlr_build, &init_connect_result)) {
+                       TW_PRINTK(tw_dev->host, TW_DRIVER, 0x12, "Initconnection failed while checking SRL");
+                       do_soft_reset = 1;
+                       tries++;
+                       continue;
+               }
+
+               /* Load sense buffers */
+               while (i < TW_Q_LENGTH) {
+                       writel((u32)((u64)tw_dev->sense_buffer_phys[i] >> 32), TWL_HOBQPH_REG_ADDR(tw_dev));
+                       writel((u32)tw_dev->sense_buffer_phys[i], TWL_HOBQPL_REG_ADDR(tw_dev));
+
+                       /* Check status for over-run after each write */
+                       status = readl(TWL_STATUS_REG_ADDR(tw_dev));
+                       if (!(status & TWL_STATUS_OVERRUN_SUBMIT))
+                           i++;
+               }
+
+               /* Now check status */
+               status = readl(TWL_STATUS_REG_ADDR(tw_dev));
+               if (status) {
+                       TW_PRINTK(tw_dev->host, TW_DRIVER, 0x13, "Bad controller status after loading sense buffers");
+                       do_soft_reset = 1;
+                       tries++;
+                       continue;
+               }
+
+               /* Drain the AEN queue */
+               if (twl_aen_drain_queue(tw_dev, soft_reset)) {
+                       TW_PRINTK(tw_dev->host, TW_DRIVER, 0x14, "AEN drain failed during reset sequence");
+                       do_soft_reset = 1;
+                       tries++;
+                       continue;
+               }
+
+               /* Load rest of compatibility struct */
+               strncpy(tw_dev->tw_compat_info.driver_version, TW_DRIVER_VERSION, strlen(TW_DRIVER_VERSION));
+               tw_dev->tw_compat_info.driver_srl_high = TW_CURRENT_DRIVER_SRL;
+               tw_dev->tw_compat_info.driver_branch_high = TW_CURRENT_DRIVER_BRANCH;
+               tw_dev->tw_compat_info.driver_build_high = TW_CURRENT_DRIVER_BUILD;
+               tw_dev->tw_compat_info.driver_srl_low = TW_BASE_FW_SRL;
+               tw_dev->tw_compat_info.driver_branch_low = TW_BASE_FW_BRANCH;
+               tw_dev->tw_compat_info.driver_build_low = TW_BASE_FW_BUILD;
+               tw_dev->tw_compat_info.fw_on_ctlr_srl = fw_on_ctlr_srl;
+               tw_dev->tw_compat_info.fw_on_ctlr_branch = fw_on_ctlr_branch;
+               tw_dev->tw_compat_info.fw_on_ctlr_build = fw_on_ctlr_build;
+
+               /* If we got here, controller is in a good state */
+               retval = 0;
+               goto out;
+       }
+out:
+       return retval;
+} /* End twl_reset_sequence() */
+
+/* This function will reset a device extension */
+static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset)
+{
+       int i = 0, retval = 1;
+       unsigned long flags = 0;
+
+       /* Block SCSI requests while we are resetting */
+       if (ioctl_reset)
+               scsi_block_requests(tw_dev->host);
+
+       set_bit(TW_IN_RESET, &tw_dev->flags);
+       TWL_MASK_INTERRUPTS(tw_dev);
+       TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+       spin_lock_irqsave(tw_dev->host->host_lock, flags);
+
+       /* Abort all requests that are in progress */
+       for (i = 0; i < TW_Q_LENGTH; i++) {
+               if ((tw_dev->state[i] != TW_S_FINISHED) &&
+                   (tw_dev->state[i] != TW_S_INITIAL) &&
+                   (tw_dev->state[i] != TW_S_COMPLETED)) {
+                       if (tw_dev->srb[i]) {
+                               tw_dev->srb[i]->result = (DID_RESET << 16);
+                               tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+                               twl_unmap_scsi_data(tw_dev, i);
+                       }
+               }
+       }
+
+       /* Reset queues and counts */
+       for (i = 0; i < TW_Q_LENGTH; i++) {
+               tw_dev->free_queue[i] = i;
+               tw_dev->state[i] = TW_S_INITIAL;
+       }
+       tw_dev->free_head = TW_Q_START;
+       tw_dev->free_tail = TW_Q_START;
+       tw_dev->posted_request_count = 0;
+
+       spin_unlock_irqrestore(tw_dev->host->host_lock, flags);
+
+       if (twl_reset_sequence(tw_dev, 1))
+               goto out;
+
+       TWL_UNMASK_INTERRUPTS(tw_dev);
+
+       clear_bit(TW_IN_RESET, &tw_dev->flags);
+       tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;
+
+       retval = 0;
+out:
+       if (ioctl_reset)
+               scsi_unblock_requests(tw_dev->host);
+       return retval;
+} /* End twl_reset_device_extension() */
+
+/* This funciton returns unit geometry in cylinders/heads/sectors */
+static int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
+{
+       int heads, sectors;
+       TW_Device_Extension *tw_dev;
+
+       tw_dev = (TW_Device_Extension *)sdev->host->hostdata;
+
+       if (capacity >= 0x200000) {
+               heads = 255;
+               sectors = 63;
+       } else {
+               heads = 64;
+               sectors = 32;
+       }
+
+       geom[0] = heads;
+       geom[1] = sectors;
+       geom[2] = sector_div(capacity, heads * sectors); /* cylinders */
+
+       return 0;
+} /* End twl_scsi_biosparam() */
+
+/* This is the new scsi eh reset function */
+static int twl_scsi_eh_reset(struct scsi_cmnd *SCpnt)
+{
+       TW_Device_Extension *tw_dev = NULL;
+       int retval = FAILED;
+
+       tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+       tw_dev->num_resets++;
+
+       sdev_printk(KERN_WARNING, SCpnt->device,
+               "WARNING: (0x%02X:0x%04X): Command (0x%x) timed out, resetting card.\n",
+               TW_DRIVER, 0x2c, SCpnt->cmnd[0]);
+
+       /* Make sure we are not issuing an ioctl or resetting from ioctl */
+       mutex_lock(&tw_dev->ioctl_lock);
+
+       /* Now reset the card and some of the device extension data */
+       if (twl_reset_device_extension(tw_dev, 0)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x15, "Controller reset failed during scsi host reset");
+               goto out;
+       }
+
+       retval = SUCCESS;
+out:
+       mutex_unlock(&tw_dev->ioctl_lock);
+       return retval;
+} /* End twl_scsi_eh_reset() */
+
+/* This is the main scsi queue function to handle scsi opcodes */
+static int twl_scsi_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+       int request_id, retval;
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)SCpnt->device->host->hostdata;
+
+       /* If we are resetting due to timed out ioctl, report as busy */
+       if (test_bit(TW_IN_RESET, &tw_dev->flags)) {
+               retval = SCSI_MLQUEUE_HOST_BUSY;
+               goto out;
+       }
+
+       /* Save done function into scsi_cmnd struct */
+       SCpnt->scsi_done = done;
+               
+       /* Get a free request id */
+       twl_get_request_id(tw_dev, &request_id);
+
+       /* Save the scsi command for use by the ISR */
+       tw_dev->srb[request_id] = SCpnt;
+
+       /* Initialize phase to zero */
+       SCpnt->SCp.phase = TW_PHASE_INITIAL;
+
+       retval = twl_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL);
+       if (retval) {
+               tw_dev->state[request_id] = TW_S_COMPLETED;
+               twl_free_request_id(tw_dev, request_id);
+               SCpnt->result = (DID_ERROR << 16);
+               done(SCpnt);
+               retval = 0;
+       }
+out:
+       return retval;
+} /* End twl_scsi_queue() */
+
+/* This function tells the controller to shut down */
+static void __twl_shutdown(TW_Device_Extension *tw_dev)
+{
+       /* Disable interrupts */
+       TWL_MASK_INTERRUPTS(tw_dev);
+
+       /* Free up the IRQ */
+       free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+       printk(KERN_WARNING "3w-sas: Shutting down host %d.\n", tw_dev->host->host_no);
+
+       /* Tell the card we are shutting down */
+       if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x16, "Connection shutdown failed");
+       } else {
+               printk(KERN_WARNING "3w-sas: Shutdown complete.\n");
+       }
+
+       /* Clear doorbell interrupt just before exit */
+       TWL_CLEAR_DB_INTERRUPT(tw_dev);
+} /* End __twl_shutdown() */
+
+/* Wrapper for __twl_shutdown */
+static void twl_shutdown(struct pci_dev *pdev)
+{
+       struct Scsi_Host *host = pci_get_drvdata(pdev);
+       TW_Device_Extension *tw_dev;
+
+       if (!host)
+               return;
+
+       tw_dev = (TW_Device_Extension *)host->hostdata;
+
+       if (tw_dev->online) 
+               __twl_shutdown(tw_dev);
+} /* End twl_shutdown() */
+
+/* This function configures unit settings when a unit is coming on-line */
+static int twl_slave_configure(struct scsi_device *sdev)
+{
+       /* Force 60 second timeout */
+       blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
+
+       return 0;
+} /* End twl_slave_configure() */
+
+/* scsi_host_template initializer */
+static struct scsi_host_template driver_template = {
+       .module                 = THIS_MODULE,
+       .name                   = "3w-sas",
+       .queuecommand           = twl_scsi_queue,
+       .eh_host_reset_handler  = twl_scsi_eh_reset,
+       .bios_param             = twl_scsi_biosparam,
+       .change_queue_depth     = twl_change_queue_depth,
+       .can_queue              = TW_Q_LENGTH-2,
+       .slave_configure        = twl_slave_configure,
+       .this_id                = -1,
+       .sg_tablesize           = TW_LIBERATOR_MAX_SGL_LENGTH,
+       .max_sectors            = TW_MAX_SECTORS,
+       .cmd_per_lun            = TW_MAX_CMDS_PER_LUN,
+       .use_clustering         = ENABLE_CLUSTERING,
+       .shost_attrs            = twl_host_attrs,
+       .emulated               = 1
+};
+
+/* This function will probe and initialize a card */
+static int __devinit twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
+{
+       struct Scsi_Host *host = NULL;
+       TW_Device_Extension *tw_dev;
+       int retval = -ENODEV;
+       int *ptr_phycount, phycount=0;
+
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               TW_PRINTK(host, TW_DRIVER, 0x17, "Failed to enable pci device");
+               goto out_disable_device;
+       }
+
+       pci_set_master(pdev);
+       pci_try_set_mwi(pdev);
+
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+           || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+                   || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+                       TW_PRINTK(host, TW_DRIVER, 0x18, "Failed to set dma mask");
+                       retval = -ENODEV;
+                       goto out_disable_device;
+               }
+
+       host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension));
+       if (!host) {
+               TW_PRINTK(host, TW_DRIVER, 0x19, "Failed to allocate memory for device extension");
+               retval = -ENOMEM;
+               goto out_disable_device;
+       }
+       tw_dev = shost_priv(host);
+
+       /* Save values to device extension */
+       tw_dev->host = host;
+       tw_dev->tw_pci_dev = pdev;
+
+       if (twl_initialize_device_extension(tw_dev)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Failed to initialize device extension");
+               goto out_free_device_extension;
+       }
+
+       /* Request IO regions */
+       retval = pci_request_regions(pdev, "3w-sas");
+       if (retval) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Failed to get mem region");
+               goto out_free_device_extension;
+       }
+
+       /* Save base address, use region 1 */
+       tw_dev->base_addr = pci_iomap(pdev, 1, 0);
+       if (!tw_dev->base_addr) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to ioremap");
+               goto out_release_mem_region;
+       }
+
+       /* Disable interrupts on the card */
+       TWL_MASK_INTERRUPTS(tw_dev);
+
+       /* Initialize the card */
+       if (twl_reset_sequence(tw_dev, 0)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Controller reset failed during probe");
+               goto out_iounmap;
+       }
+
+       /* Set host specific parameters */
+       host->max_id = TW_MAX_UNITS;
+       host->max_cmd_len = TW_MAX_CDB_LEN;
+       host->max_lun = TW_MAX_LUNS;
+       host->max_channel = 0;
+
+       /* Register the card with the kernel SCSI layer */
+       retval = scsi_add_host(host, &pdev->dev);
+       if (retval) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "scsi add host failed");
+               goto out_iounmap;
+       }
+
+       pci_set_drvdata(pdev, host);
+
+       printk(KERN_WARNING "3w-sas: scsi%d: Found an LSI 3ware %s Controller at 0x%llx, IRQ: %d.\n",
+              host->host_no,
+              (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
+                                    TW_PARAM_MODEL, TW_PARAM_MODEL_LENGTH),
+              (u64)pci_resource_start(pdev, 1), pdev->irq);
+
+       ptr_phycount = twl_get_param(tw_dev, 2, TW_PARAM_PHY_SUMMARY_TABLE,
+                                    TW_PARAM_PHYCOUNT, TW_PARAM_PHYCOUNT_LENGTH);
+       if (ptr_phycount)
+               phycount = le32_to_cpu(*(int *)ptr_phycount);
+
+       printk(KERN_WARNING "3w-sas: scsi%d: Firmware %s, BIOS %s, Phys: %d.\n",
+              host->host_no,
+              (char *)twl_get_param(tw_dev, 1, TW_VERSION_TABLE,
+                                    TW_PARAM_FWVER, TW_PARAM_FWVER_LENGTH),
+              (char *)twl_get_param(tw_dev, 2, TW_VERSION_TABLE,
+                                    TW_PARAM_BIOSVER, TW_PARAM_BIOSVER_LENGTH),
+              phycount);
+
+       /* Try to enable MSI */
+       if (use_msi && !pci_enable_msi(pdev))
+               set_bit(TW_USING_MSI, &tw_dev->flags);
+
+       /* Now setup the interrupt handler */
+       retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev);
+       if (retval) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1f, "Error requesting IRQ");
+               goto out_remove_host;
+       }
+
+       twl_device_extension_list[twl_device_extension_count] = tw_dev;
+       twl_device_extension_count++;
+
+       /* Re-enable interrupts on the card */
+       TWL_UNMASK_INTERRUPTS(tw_dev);
+       
+       /* Finally, scan the host */
+       scsi_scan_host(host);
+
+       /* Add sysfs binary files */
+       if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr))
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x20, "Failed to create sysfs binary file: 3ware_aen_read");
+       if (sysfs_create_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr))
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x21, "Failed to create sysfs binary file: 3ware_compat_info");
+
+       if (twl_major == -1) {
+               if ((twl_major = register_chrdev (0, "twl", &twl_fops)) < 0)
+                       TW_PRINTK(host, TW_DRIVER, 0x22, "Failed to register character device");
+       }
+       tw_dev->online = 1;
+       return 0;
+
+out_remove_host:
+       if (test_bit(TW_USING_MSI, &tw_dev->flags))
+               pci_disable_msi(pdev);
+       scsi_remove_host(host);
+out_iounmap:
+       iounmap(tw_dev->base_addr);
+out_release_mem_region:
+       pci_release_regions(pdev);
+out_free_device_extension:
+       twl_free_device_extension(tw_dev);
+       scsi_host_put(host);
+out_disable_device:
+       pci_disable_device(pdev);
+
+       return retval;
+} /* End twl_probe() */
+
+/* This function is called to remove a device */
+static void twl_remove(struct pci_dev *pdev)
+{
+       struct Scsi_Host *host = pci_get_drvdata(pdev);
+       TW_Device_Extension *tw_dev;
+
+       if (!host)
+               return;
+
+       tw_dev = (TW_Device_Extension *)host->hostdata;
+
+       if (!tw_dev->online)
+               return;
+
+       /* Remove sysfs binary files */
+       sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_aen_read_attr);
+       sysfs_remove_bin_file(&host->shost_dev.kobj, &twl_sysfs_compat_info_attr);
+
+       scsi_remove_host(tw_dev->host);
+
+       /* Unregister character device */
+       if (twl_major >= 0) {
+               unregister_chrdev(twl_major, "twl");
+               twl_major = -1;
+       }
+
+       /* Shutdown the card */
+       __twl_shutdown(tw_dev);
+
+       /* Disable MSI if enabled */
+       if (test_bit(TW_USING_MSI, &tw_dev->flags))
+               pci_disable_msi(pdev);
+
+       /* Free IO remapping */
+       iounmap(tw_dev->base_addr);
+
+       /* Free up the mem region */
+       pci_release_regions(pdev);
+
+       /* Free up device extension resources */
+       twl_free_device_extension(tw_dev);
+
+       scsi_host_put(tw_dev->host);
+       pci_disable_device(pdev);
+       twl_device_extension_count--;
+} /* End twl_remove() */
+
+#ifdef CONFIG_PM
+/* This function is called on PCI suspend */
+static int twl_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct Scsi_Host *host = pci_get_drvdata(pdev);
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+       printk(KERN_WARNING "3w-sas: Suspending host %d.\n", tw_dev->host->host_no);
+       /* Disable interrupts */
+       TWL_MASK_INTERRUPTS(tw_dev);
+
+       free_irq(tw_dev->tw_pci_dev->irq, tw_dev);
+
+       /* Tell the card we are shutting down */
+       if (twl_initconnection(tw_dev, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL)) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x23, "Connection shutdown failed during suspend");
+       } else {
+               printk(KERN_WARNING "3w-sas: Suspend complete.\n");
+       }
+
+       /* Clear doorbell interrupt */
+       TWL_CLEAR_DB_INTERRUPT(tw_dev);
+
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+       return 0;
+} /* End twl_suspend() */
+
+/* This function is called on PCI resume */
+static int twl_resume(struct pci_dev *pdev)
+{
+       int retval = 0;
+       struct Scsi_Host *host = pci_get_drvdata(pdev);
+       TW_Device_Extension *tw_dev = (TW_Device_Extension *)host->hostdata;
+
+       printk(KERN_WARNING "3w-sas: Resuming host %d.\n", tw_dev->host->host_no);
+       pci_set_power_state(pdev, PCI_D0);
+       pci_enable_wake(pdev, PCI_D0, 0);
+       pci_restore_state(pdev);
+
+       retval = pci_enable_device(pdev);
+       if (retval) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x24, "Enable device failed during resume");
+               return retval;
+       }
+
+       pci_set_master(pdev);
+       pci_try_set_mwi(pdev);
+
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+           || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
+               if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))
+                   || pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
+                       TW_PRINTK(host, TW_DRIVER, 0x25, "Failed to set dma mask during resume");
+                       retval = -ENODEV;
+                       goto out_disable_device;
+               }
+
+       /* Initialize the card */
+       if (twl_reset_sequence(tw_dev, 0)) {
+               retval = -ENODEV;
+               goto out_disable_device;
+       }
+
+       /* Now setup the interrupt handler */
+       retval = request_irq(pdev->irq, twl_interrupt, IRQF_SHARED, "3w-sas", tw_dev);
+       if (retval) {
+               TW_PRINTK(tw_dev->host, TW_DRIVER, 0x26, "Error requesting IRQ during resume");
+               retval = -ENODEV;
+               goto out_disable_device;
+       }
+
+       /* Now enable MSI if enabled */
+       if (test_bit(TW_USING_MSI, &tw_dev->flags))
+               pci_enable_msi(pdev);
+
+       /* Re-enable interrupts on the card */
+       TWL_UNMASK_INTERRUPTS(tw_dev);
+
+       printk(KERN_WARNING "3w-sas: Resume complete.\n");
+       return 0;
+
+out_disable_device:
+       scsi_remove_host(host);
+       pci_disable_device(pdev);
+
+       return retval;
+} /* End twl_resume() */
+#endif
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id twl_pci_tbl[] __devinitdata = {
+       { PCI_VDEVICE(3WARE, PCI_DEVICE_ID_3WARE_9750) },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, twl_pci_tbl);
+
+/* pci_driver initializer */
+static struct pci_driver twl_driver = {
+       .name           = "3w-sas",
+       .id_table       = twl_pci_tbl,
+       .probe          = twl_probe,
+       .remove         = twl_remove,
+#ifdef CONFIG_PM
+       .suspend        = twl_suspend,
+       .resume         = twl_resume,
+#endif
+       .shutdown       = twl_shutdown
+};
+
+/* This function is called on driver initialization */
+static int __init twl_init(void)
+{
+       printk(KERN_INFO "LSI 3ware SAS/SATA-RAID Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION);
+
+       return pci_register_driver(&twl_driver);
+} /* End twl_init() */
+
+/* This function is called on driver exit */
+static void __exit twl_exit(void)
+{
+       pci_unregister_driver(&twl_driver);
+} /* End twl_exit() */
+
+module_init(twl_init);
+module_exit(twl_exit);
+
diff --git a/drivers/scsi/3w-sas.h b/drivers/scsi/3w-sas.h
new file mode 100644 (file)
index 0000000..d474892
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+   3w-sas.h -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux.
+
+   Written By: Adam Radford <linuxraid@lsi.com>
+
+   Copyright (C) 2009 LSI 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; 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.
+
+   NO WARRANTY
+   THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+   LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+   solely responsible for determining the appropriateness of using and
+   distributing the Program and assumes all risks associated with its
+   exercise of rights under this Agreement, including but not limited to
+   the risks and costs of program errors, damage to or loss of data,
+   programs or equipment, and unavailability or interruption of operations.
+
+   DISCLAIMER OF LIABILITY
+   NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+   DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+   TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+   USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+   HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+   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
+
+   Bugs/Comments/Suggestions should be mailed to:
+   linuxraid@lsi.com
+
+   For more information, goto:
+   http://www.lsi.com
+*/
+
+#ifndef _3W_SAS_H
+#define _3W_SAS_H
+
+/* AEN severity table */
+static char *twl_aen_severity_table[] =
+{
+       "None", "ERROR", "WARNING", "INFO", "DEBUG", NULL
+};
+
+/* Liberator register offsets */
+#define TWL_STATUS                         0x0  /* Status */
+#define TWL_HIBDB                          0x20 /* Inbound doorbell */
+#define TWL_HISTAT                         0x30 /* Host interrupt status */
+#define TWL_HIMASK                         0x34 /* Host interrupt mask */
+#define TWL_HOBDB                         0x9C /* Outbound doorbell */
+#define TWL_HOBDBC                         0xA0 /* Outbound doorbell clear */
+#define TWL_SCRPD3                         0xBC /* Scratchpad */
+#define TWL_HIBQPL                         0xC0 /* Host inbound Q low */
+#define TWL_HIBQPH                         0xC4 /* Host inbound Q high */
+#define TWL_HOBQPL                         0xC8 /* Host outbound Q low */
+#define TWL_HOBQPH                         0xCC /* Host outbound Q high */
+#define TWL_HISTATUS_VALID_INTERRUPT      0xC
+#define TWL_HISTATUS_ATTENTION_INTERRUPT   0x4
+#define TWL_HISTATUS_RESPONSE_INTERRUPT           0x8
+#define TWL_STATUS_OVERRUN_SUBMIT         0x2000
+#define TWL_ISSUE_SOFT_RESET              0x100
+#define TWL_CONTROLLER_READY              0x2000
+#define TWL_DOORBELL_CONTROLLER_ERROR     0x200000
+#define TWL_DOORBELL_ATTENTION_INTERRUPT   0x40000
+#define TWL_PULL_MODE                     0x1
+
+/* Command packet opcodes used by the driver */
+#define TW_OP_INIT_CONNECTION 0x1
+#define TW_OP_GET_PARAM              0x12
+#define TW_OP_SET_PARAM              0x13
+#define TW_OP_EXECUTE_SCSI    0x10
+
+/* Asynchronous Event Notification (AEN) codes used by the driver */
+#define TW_AEN_QUEUE_EMPTY       0x0000
+#define TW_AEN_SOFT_RESET        0x0001
+#define TW_AEN_SYNC_TIME_WITH_HOST 0x031
+#define TW_AEN_SEVERITY_ERROR    0x1
+#define TW_AEN_SEVERITY_DEBUG    0x4
+#define TW_AEN_NOT_RETRIEVED 0x1
+
+/* Command state defines */
+#define TW_S_INITIAL   0x1  /* Initial state */
+#define TW_S_STARTED   0x2  /* Id in use */
+#define TW_S_POSTED    0x4  /* Posted to the controller */
+#define TW_S_COMPLETED 0x8  /* Completed by isr */
+#define TW_S_FINISHED  0x10 /* I/O completely done */
+
+/* Compatibility defines */
+#define TW_9750_ARCH_ID 10
+#define TW_CURRENT_DRIVER_SRL 40
+#define TW_CURRENT_DRIVER_BUILD 0
+#define TW_CURRENT_DRIVER_BRANCH 0
+
+/* Phase defines */
+#define TW_PHASE_INITIAL 0
+#define TW_PHASE_SGLIST  2
+
+/* Misc defines */
+#define TW_SECTOR_SIZE                        512
+#define TW_MAX_UNITS                         32
+#define TW_INIT_MESSAGE_CREDITS                      0x100
+#define TW_INIT_COMMAND_PACKET_SIZE          0x3
+#define TW_INIT_COMMAND_PACKET_SIZE_EXTENDED  0x6
+#define TW_EXTENDED_INIT_CONNECT             0x2
+#define TW_BASE_FW_SRL                       24
+#define TW_BASE_FW_BRANCH                    0
+#define TW_BASE_FW_BUILD                     1
+#define TW_Q_LENGTH                          256
+#define TW_Q_START                           0
+#define TW_MAX_SLOT                          32
+#define TW_MAX_RESET_TRIES                   2
+#define TW_MAX_CMDS_PER_LUN                  254
+#define TW_MAX_AEN_DRAIN                     255
+#define TW_IN_RESET                           2
+#define TW_USING_MSI                         3
+#define TW_IN_ATTENTION_LOOP                 4
+#define TW_MAX_SECTORS                        256
+#define TW_MAX_CDB_LEN                        16
+#define TW_IOCTL_CHRDEV_TIMEOUT               60 /* 60 seconds */
+#define TW_IOCTL_CHRDEV_FREE                  -1
+#define TW_COMMAND_OFFSET                     128 /* 128 bytes */
+#define TW_VERSION_TABLE                      0x0402
+#define TW_TIMEKEEP_TABLE                    0x040A
+#define TW_INFORMATION_TABLE                 0x0403
+#define TW_PARAM_FWVER                       3
+#define TW_PARAM_FWVER_LENGTH                16
+#define TW_PARAM_BIOSVER                     4
+#define TW_PARAM_BIOSVER_LENGTH                      16
+#define TW_PARAM_MODEL                       8
+#define TW_PARAM_MODEL_LENGTH                16
+#define TW_PARAM_PHY_SUMMARY_TABLE           1
+#define TW_PARAM_PHYCOUNT                    2
+#define TW_PARAM_PHYCOUNT_LENGTH             1
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH        0x108  // Used by smartmontools
+#define TW_ALLOCATION_LENGTH                 128
+#define TW_SENSE_DATA_LENGTH                 18
+#define TW_ERROR_LOGICAL_UNIT_NOT_SUPPORTED   0x10a
+#define TW_ERROR_INVALID_FIELD_IN_CDB        0x10d
+#define TW_ERROR_UNIT_OFFLINE                 0x128
+#define TW_MESSAGE_SOURCE_CONTROLLER_ERROR    3
+#define TW_MESSAGE_SOURCE_CONTROLLER_EVENT    4
+#define TW_DRIVER                            6
+#ifndef PCI_DEVICE_ID_3WARE_9750
+#define PCI_DEVICE_ID_3WARE_9750 0x1010
+#endif
+
+/* Bitmask macros to eliminate bitfields */
+
+/* opcode: 5, reserved: 3 */
+#define TW_OPRES_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_OP_OUT(x) (x & 0x1f)
+
+/* opcode: 5, sgloffset: 3 */
+#define TW_OPSGL_IN(x,y) ((x << 5) | (y & 0x1f))
+#define TW_SGL_OUT(x) ((x >> 5) & 0x7)
+
+/* severity: 3, reserved: 5 */
+#define TW_SEV_OUT(x) (x & 0x7)
+
+/* not_mfa: 1, reserved: 7, status: 8, request_id: 16 */
+#define TW_RESID_OUT(x) ((x >> 16) & 0xffff)
+#define TW_NOTMFA_OUT(x) (x & 0x1)
+
+/* request_id: 12, lun: 4 */
+#define TW_REQ_LUN_IN(lun, request_id) (((lun << 12) & 0xf000) | (request_id & 0xfff))
+#define TW_LUN_OUT(lun) ((lun >> 12) & 0xf)
+
+/* Register access macros */
+#define TWL_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_STATUS)
+#define TWL_HOBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPL)
+#define TWL_HOBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBQPH)
+#define TWL_HOBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDB)
+#define TWL_HOBDBC_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HOBDBC)
+#define TWL_HIMASK_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIMASK)
+#define TWL_HISTAT_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HISTAT)
+#define TWL_HIBQPH_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPH)
+#define TWL_HIBQPL_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBQPL)
+#define TWL_HIBDB_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_HIBDB)
+#define TWL_SCRPD3_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + TWL_SCRPD3)
+#define TWL_MASK_INTERRUPTS(x) (writel(~0, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_UNMASK_INTERRUPTS(x) (writel(~TWL_HISTATUS_VALID_INTERRUPT, TWL_HIMASK_REG_ADDR(tw_dev)))
+#define TWL_CLEAR_DB_INTERRUPT(x) (writel(~0, TWL_HOBDBC_REG_ADDR(tw_dev)))
+#define TWL_SOFT_RESET(x) (writel(TWL_ISSUE_SOFT_RESET, TWL_HIBDB_REG_ADDR(tw_dev)))
+
+/* Macros */
+#define TW_PRINTK(h,a,b,c) { \
+if (h) \
+printk(KERN_WARNING "3w-sas: scsi%d: ERROR: (0x%02X:0x%04X): %s.\n",h->host_no,a,b,c); \
+else \
+printk(KERN_WARNING "3w-sas: ERROR: (0x%02X:0x%04X): %s.\n",a,b,c); \
+}
+#define TW_MAX_LUNS 16
+#define TW_COMMAND_SIZE (sizeof(dma_addr_t) > 4 ? 6 : 4)
+#define TW_LIBERATOR_MAX_SGL_LENGTH (sizeof(dma_addr_t) > 4 ? 46 : 92)
+#define TW_LIBERATOR_MAX_SGL_LENGTH_OLD (sizeof(dma_addr_t) > 4 ? 47 : 94)
+#define TW_PADDING_LENGTH_LIBERATOR 136
+#define TW_PADDING_LENGTH_LIBERATOR_OLD 132
+#define TW_CPU_TO_SGL(x) (sizeof(dma_addr_t) > 4 ? cpu_to_le64(x) : cpu_to_le32(x))
+
+#pragma pack(1)
+
+/* SGL entry */
+typedef struct TAG_TW_SG_Entry_ISO {
+       dma_addr_t address;
+       dma_addr_t length;
+} TW_SG_Entry_ISO;
+
+/* Old Command Packet with ISO SGL */
+typedef struct TW_Command {
+       unsigned char opcode__sgloffset;
+       unsigned char size;
+       unsigned char request_id;
+       unsigned char unit__hostid;
+       /* Second DWORD */
+       unsigned char status;
+       unsigned char flags;
+       union {
+               unsigned short block_count;
+               unsigned short parameter_count;
+       } byte6_offset;
+       union {
+               struct {
+                       u32 lba;
+                       TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD];
+                       unsigned char padding[TW_PADDING_LENGTH_LIBERATOR_OLD];
+               } io;
+               struct {
+                       TW_SG_Entry_ISO sgl[TW_LIBERATOR_MAX_SGL_LENGTH_OLD];
+                       u32 padding;
+                       unsigned char padding2[TW_PADDING_LENGTH_LIBERATOR_OLD];
+               } param;
+       } byte8_offset;
+} TW_Command;
+
+/* New Command Packet with ISO SGL */
+typedef struct TAG_TW_Command_Apache {
+       unsigned char opcode__reserved;
+       unsigned char unit;
+       unsigned short request_id__lunl;
+       unsigned char status;
+       unsigned char sgl_offset;
+       unsigned short sgl_entries__lunh;
+       unsigned char cdb[16];
+       TW_SG_Entry_ISO sg_list[TW_LIBERATOR_MAX_SGL_LENGTH];
+       unsigned char padding[TW_PADDING_LENGTH_LIBERATOR];
+} TW_Command_Apache;
+
+/* New command packet header */
+typedef struct TAG_TW_Command_Apache_Header {
+       unsigned char sense_data[TW_SENSE_DATA_LENGTH];
+       struct {
+               char reserved[4];
+               unsigned short error;
+               unsigned char padding;
+               unsigned char severity__reserved;
+       } status_block;
+       unsigned char err_specific_desc[98];
+       struct {
+               unsigned char size_header;
+               unsigned short request_id;
+               unsigned char size_sense;
+       } header_desc;
+} TW_Command_Apache_Header;
+
+/* This struct is a union of the 2 command packets */
+typedef struct TAG_TW_Command_Full {
+       TW_Command_Apache_Header header;
+       union {
+               TW_Command oldcommand;
+               TW_Command_Apache newcommand;
+       } command;
+} TW_Command_Full;
+
+/* Initconnection structure */
+typedef struct TAG_TW_Initconnect {
+       unsigned char opcode__reserved;
+       unsigned char size;
+       unsigned char request_id;
+       unsigned char res2;
+       unsigned char status;
+       unsigned char flags;
+       unsigned short message_credits;
+       u32 features;
+       unsigned short fw_srl;
+       unsigned short fw_arch_id;
+       unsigned short fw_branch;
+       unsigned short fw_build;
+       u32 result;
+} TW_Initconnect;
+
+/* Event info structure */
+typedef struct TAG_TW_Event
+{
+       unsigned int sequence_id;
+       unsigned int time_stamp_sec;
+       unsigned short aen_code;
+       unsigned char severity;
+       unsigned char retrieved;
+       unsigned char repeat_count;
+       unsigned char parameter_len;
+       unsigned char parameter_data[98];
+} TW_Event;
+
+typedef struct TAG_TW_Ioctl_Driver_Command {
+       unsigned int control_code;
+       unsigned int status;
+       unsigned int unique_id;
+       unsigned int sequence_id;
+       unsigned int os_specific;
+       unsigned int buffer_length;
+} TW_Ioctl_Driver_Command;
+
+typedef struct TAG_TW_Ioctl_Apache {
+       TW_Ioctl_Driver_Command driver_command;
+        char padding[488];
+       TW_Command_Full firmware_command;
+       char data_buffer[1];
+} TW_Ioctl_Buf_Apache;
+
+/* GetParam descriptor */
+typedef struct {
+       unsigned short  table_id;
+       unsigned short  parameter_id;
+       unsigned short  parameter_size_bytes;
+       unsigned short  actual_parameter_size_bytes;
+       unsigned char   data[1];
+} TW_Param_Apache;
+
+/* Compatibility information structure */
+typedef struct TAG_TW_Compatibility_Info
+{
+       char driver_version[32];
+       unsigned short working_srl;
+       unsigned short working_branch;
+       unsigned short working_build;
+       unsigned short driver_srl_high;
+       unsigned short driver_branch_high;
+       unsigned short driver_build_high;
+       unsigned short driver_srl_low;
+       unsigned short driver_branch_low;
+       unsigned short driver_build_low;
+       unsigned short fw_on_ctlr_srl;
+       unsigned short fw_on_ctlr_branch;
+       unsigned short fw_on_ctlr_build;
+} TW_Compatibility_Info;
+
+#pragma pack()
+
+typedef struct TAG_TW_Device_Extension {
+       void                     __iomem *base_addr;
+       unsigned long           *generic_buffer_virt[TW_Q_LENGTH];
+       dma_addr_t              generic_buffer_phys[TW_Q_LENGTH];
+       TW_Command_Full         *command_packet_virt[TW_Q_LENGTH];
+       dma_addr_t              command_packet_phys[TW_Q_LENGTH];
+       TW_Command_Apache_Header *sense_buffer_virt[TW_Q_LENGTH];
+       dma_addr_t              sense_buffer_phys[TW_Q_LENGTH];
+       struct pci_dev          *tw_pci_dev;
+       struct scsi_cmnd        *srb[TW_Q_LENGTH];
+       unsigned char           free_queue[TW_Q_LENGTH];
+       unsigned char           free_head;
+       unsigned char           free_tail;
+       int                     state[TW_Q_LENGTH];
+       unsigned int            posted_request_count;
+       unsigned int            max_posted_request_count;
+       unsigned int            max_sgl_entries;
+       unsigned int            sgl_entries;
+       unsigned int            num_resets;
+       unsigned int            sector_count;
+       unsigned int            max_sector_count;
+       unsigned int            aen_count;
+       struct Scsi_Host        *host;
+       long                    flags;
+       TW_Event                *event_queue[TW_Q_LENGTH];
+       unsigned char           error_index;
+       unsigned int            error_sequence_id;
+       int                     chrdev_request_id;
+       wait_queue_head_t       ioctl_wqueue;
+       struct mutex            ioctl_lock;
+       TW_Compatibility_Info   tw_compat_info;
+       char                    online;
+} TW_Device_Extension;
+
+#endif /* _3W_SAS_H */
+
index faa0fcf..f65a1e9 100644 (file)
@@ -8,7 +8,7 @@
 
    Copyright (C) 1999-2009 3ware Inc.
 
-   Kernel compatiblity By:     Andre Hedrick <andre@suse.com>
+   Kernel compatibility By:    Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
    
    Further tiny build fixes and trivial hoovering    Alan Cox
@@ -521,8 +521,12 @@ static ssize_t tw_show_stats(struct device *dev, struct device_attribute *attr,
 } /* End tw_show_stats() */
 
 /* This function will set a devices queue depth */
-static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth)
+static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth,
+                                int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (queue_depth > TW_Q_LENGTH-2)
                queue_depth = TW_Q_LENGTH-2;
        scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
index f5a9add..9f4a911 100644 (file)
@@ -175,7 +175,7 @@ STATIC void NCR_700_chip_reset(struct Scsi_Host *host);
 STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt);
 STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt);
 STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt);
-static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth);
+static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason);
 static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth);
 
 STATIC struct device_attribute *NCR_700_dev_attrs[];
@@ -1491,7 +1491,7 @@ NCR_700_intr(int irq, void *dev_id)
        unsigned long flags;
        int handled = 0;
 
-       /* Use the host lock to serialise acess to the 53c700
+       /* Use the host lock to serialise access to the 53c700
         * hardware.  Note: In future, we may need to take the queue
         * lock to enter the done routines.  When that happens, we
         * need to ensure that for this driver, the host lock and the
@@ -2082,8 +2082,11 @@ NCR_700_slave_destroy(struct scsi_device *SDp)
 }
 
 static int
-NCR_700_change_queue_depth(struct scsi_device *SDp, int depth)
+NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (depth > NCR_700_MAX_TAGS)
                depth = NCR_700_MAX_TAGS;
 
index e11cca4..36900c7 100644 (file)
@@ -399,6 +399,17 @@ config SCSI_3W_9XXX
          Please read the comments at the top of
          <file:drivers/scsi/3w-9xxx.c>.
 
+config SCSI_3W_SAS
+       tristate "3ware 97xx SAS/SATA-RAID support"
+       depends on PCI && SCSI
+       help
+         This driver supports the LSI 3ware 9750 6Gb/s SAS/SATA-RAID cards.
+
+         <http://www.lsi.com>
+
+         Please read the comments at the top of
+         <file:drivers/scsi/3w-sas.c>.
+
 config SCSI_7000FASST
        tristate "7000FASST SCSI support"
        depends on ISA && SCSI && ISA_DMA_API
@@ -621,6 +632,14 @@ config SCSI_FLASHPOINT
          substantial, so users of MultiMaster Host Adapters may not
          wish to include it.
 
+config VMWARE_PVSCSI
+       tristate "VMware PVSCSI driver support"
+       depends on PCI && SCSI && X86
+       help
+         This driver supports VMware's para virtualized SCSI HBA.
+         To compile this driver as a module, choose M here: the
+         module will be called vmw_pvscsi.
+
 config LIBFC
        tristate "LibFC module"
        select SCSI_FC_ATTRS
@@ -644,7 +663,7 @@ config FCOE
 config FCOE_FNIC
        tristate "Cisco FNIC Driver"
        depends on PCI && X86
-       select LIBFC
+       select LIBFCOE
        help
          This is support for the Cisco PCI-Express FCoE HBA.
 
@@ -1818,6 +1837,14 @@ config SCSI_PMCRAID
        ---help---
          This driver supports the PMC SIERRA MaxRAID adapters.
 
+config SCSI_PM8001
+       tristate "PMC-Sierra SPC 8001 SAS/SATA Based Host Adapter driver"
+       depends on PCI && SCSI
+       select SCSI_SAS_LIBSAS
+       help
+         This driver supports PMC-Sierra PCIE SAS/SATA 8x6G SPC 8001 chip
+         based host adapters.
+
 config SCSI_SRP
        tristate "SCSI RDMA Protocol helper library"
        depends on SCSI && PCI
index 3ad61db..280d3c6 100644 (file)
@@ -70,6 +70,7 @@ obj-$(CONFIG_SCSI_AIC79XX)    += aic7xxx/
 obj-$(CONFIG_SCSI_AACRAID)     += aacraid/
 obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
 obj-$(CONFIG_SCSI_AIC94XX)     += aic94xx/
+obj-$(CONFIG_SCSI_PM8001)      += pm8001/
 obj-$(CONFIG_SCSI_IPS)         += ips.o
 obj-$(CONFIG_SCSI_FD_MCS)      += fd_mcs.o
 obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
@@ -113,6 +114,7 @@ obj-$(CONFIG_SCSI_MESH)             += mesh.o
 obj-$(CONFIG_SCSI_MAC53C94)    += mac53c94.o
 obj-$(CONFIG_BLK_DEV_3W_XXXX_RAID) += 3w-xxxx.o
 obj-$(CONFIG_SCSI_3W_9XXX)     += 3w-9xxx.o
+obj-$(CONFIG_SCSI_3W_SAS)      += 3w-sas.o
 obj-$(CONFIG_SCSI_PPA)         += ppa.o
 obj-$(CONFIG_SCSI_IMM)         += imm.o
 obj-$(CONFIG_JAZZ_ESP)         += esp_scsi.o   jazz_esp.o
@@ -133,6 +135,7 @@ obj-$(CONFIG_SCSI_CXGB3_ISCSI)      += libiscsi.o libiscsi_tcp.o cxgb3i/
 obj-$(CONFIG_SCSI_BNX2_ISCSI)  += libiscsi.o bnx2i/
 obj-$(CONFIG_BE2ISCSI)         += libiscsi.o be2iscsi/
 obj-$(CONFIG_SCSI_PMCRAID)     += pmcraid.o
+obj-$(CONFIG_VMWARE_PVSCSI)    += vmw_pvscsi.o
 
 obj-$(CONFIG_ARM)              += arm/
 
index cdbdec9..83986ed 100644 (file)
@@ -526,10 +526,10 @@ struct aac_driver_ident
 
 /*
  *     The adapter interface specs all queues to be located in the same
- *     physically contigous block. The host structure that defines the
+ *     physically contiguous block. The host structure that defines the
  *     commuication queues will assume they are each a separate physically
- *     contigous memory region that will support them all being one big
- *     contigous block.
+ *     contiguous memory region that will support them all being one big
+ *     contiguous block.
  *     There is a command and response queue for each level and direction of
  *     commuication. These regions are accessed by both the host and adapter.
  */
index d598eba..666d515 100644 (file)
@@ -226,7 +226,7 @@ static int aac_comm_init(struct aac_dev * dev)
        spin_lock_init(&dev->fib_lock);
 
        /*
-        *      Allocate the physically contigous space for the commuication
+        *      Allocate the physically contiguous space for the commuication
         *      queue headers. 
         */
 
index 9b97c3e..e9373a2 100644 (file)
@@ -472,8 +472,12 @@ static int aac_slave_configure(struct scsi_device *sdev)
  *     total capacity and the queue depth supported by the target device.
  */
 
-static int aac_change_queue_depth(struct scsi_device *sdev, int depth)
+static int aac_change_queue_depth(struct scsi_device *sdev, int depth,
+                                 int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (sdev->tagged_supported && (sdev->type == TYPE_DISK) &&
            (sdev_channel(sdev) == CONTAINER_CHANNEL)) {
                struct scsi_device * dev;
index b756041..22626ab 100644 (file)
@@ -7969,7 +7969,7 @@ static int advansys_reset(struct scsi_cmnd *scp)
                ASC_DBG(1, "before AscInitAsc1000Driver()\n");
                status = AscInitAsc1000Driver(asc_dvc);
 
-               /* Refer to ASC_IERR_* defintions for meaning of 'err_code'. */
+               /* Refer to ASC_IERR_* definitions for meaning of 'err_code'. */
                if (asc_dvc->err_code) {
                        scmd_printk(KERN_INFO, scp, "SCSI bus reset error: "
                                    "0x%x\n", asc_dvc->err_code);
index 58bc175..2fb78e3 100644 (file)
@@ -217,7 +217,7 @@ BEGIN_CRITICAL;
 scbdma_tohost_done:
        test    CCSCBCTL, CCARREN jz fill_qoutfifo_dmadone;
        /*
-        * An SCB has been succesfully uploaded to the host.
+        * An SCB has been successfully uploaded to the host.
         * If the SCB was uploaded for some reason other than
         * bad SCSI status (currently only for underruns), we
         * queue the SCB for normal completion.  Otherwise, we
@@ -1281,7 +1281,7 @@ END_CRITICAL;
  * Is it a disconnect message?  Set a flag in the SCB to remind us
  * and await the bus going free.  If this is an untagged transaction
  * store the SCB id for it in our untagged target table for lookup on
- * a reselction.
+ * a reselection.
  */
 mesgin_disconnect:
        /*
index 63b521d..4d419c1 100644 (file)
@@ -2487,7 +2487,7 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
                /*
                 * Although the driver does not care about the
                 * 'Selection in Progress' status bit, the busy
-                * LED does.  SELINGO is only cleared by a sucessfull
+                * LED does.  SELINGO is only cleared by a successfull
                 * selection, so we must manually clear it to insure
                 * the LED turns off just incase no future successful
                 * selections occur (e.g. no devices on the bus).
index 75b2331..1222a7a 100644 (file)
@@ -2335,7 +2335,7 @@ ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd)
                        /*
                         * The sequencer will never re-reference the
                         * in-core SCB.  To make sure we are notified
-                        * during reslection, set the MK_MESSAGE flag in
+                        * during reselection, set the MK_MESSAGE flag in
                         * the card's copy of the SCB.
                         */
                        ahd_outb(ahd, SCB_CONTROL,
index 1519639..5a4cfc9 100644 (file)
@@ -1693,7 +1693,7 @@ if ((ahc->flags & AHC_INITIATORROLE) != 0) {
  * Is it a disconnect message?  Set a flag in the SCB to remind us
  * and await the bus going free.  If this is an untagged transaction
  * store the SCB id for it in our untagged target table for lookup on
- * a reselction.
+ * a reselection.
  */
 mesgin_disconnect:
        /*
index 8dfb59d..45aa728 100644 (file)
@@ -1733,7 +1733,7 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
                /*
                 * Although the driver does not care about the
                 * 'Selection in Progress' status bit, the busy
-                * LED does.  SELINGO is only cleared by a sucessfull
+                * LED does.  SELINGO is only cleared by a successfull
                 * selection, so we must manually clear it to insure
                 * the LED turns off just incase no future successful
                 * selections occur (e.g. no devices on the bus).
index fd2b978..8cb05dc 100644 (file)
@@ -2290,7 +2290,7 @@ ahc_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag)
                 * In the non-paging case, the sequencer will
                 * never re-reference the in-core SCB.
                 * To make sure we are notified during
-                * reslection, set the MK_MESSAGE flag in
+                * reselection, set the MK_MESSAGE flag in
                 * the card's copy of the SCB.
                 */
                if ((ahc->flags & AHC_PAGESCBS) == 0) {
index a43e8cd..28aaf34 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Aic94xx SAS/SATA driver hardware registers defintions.
+ * Aic94xx SAS/SATA driver hardware registers definitions.
  *
  * Copyright (C) 2004 Adaptec, Inc.  All rights reserved.
  * Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
index 80aac01..47d5d19 100644 (file)
@@ -98,8 +98,11 @@ static void arcmsr_flush_hbb_cache(struct AdapterControlBlock *acb);
 static const char *arcmsr_info(struct Scsi_Host *);
 static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
 static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev,
-                                                               int queue_depth)
+                                         int queue_depth, int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
                queue_depth = ARCMSR_MAX_CMD_PERLUN;
        scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
index b36020d..a93a504 100644 (file)
 
 #include <linux/pci.h>
 #include <linux/if_vlan.h>
-
-#define FW_VER_LEN 32
+#include <linux/blk-iopoll.h>
+#define FW_VER_LEN     32
+#define MCC_Q_LEN      128
+#define MCC_CQ_LEN     256
 
 struct be_dma_mem {
        void *va;
@@ -74,18 +76,14 @@ static inline void queue_tail_inc(struct be_queue_info *q)
 
 struct be_eq_obj {
        struct be_queue_info q;
-       char desc[32];
-
-       /* Adaptive interrupt coalescing (AIC) info */
-       bool enable_aic;
-       u16 min_eqd;            /* in usecs */
-       u16 max_eqd;            /* in usecs */
-       u16 cur_eqd;            /* in usecs */
+       struct beiscsi_hba *phba;
+       struct be_queue_info *cq;
+       struct blk_iopoll       iopoll;
 };
 
 struct be_mcc_obj {
-       struct be_queue_info *q;
-       struct be_queue_info *cq;
+       struct be_queue_info q;
+       struct be_queue_info cq;
 };
 
 struct be_ctrl_info {
@@ -176,8 +174,4 @@ static inline void swap_dws(void *wrb, int len)
        } while (len);
 #endif /* __BIG_ENDIAN */
 }
-
-extern void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
-                             u16 num_popped);
-
 #endif /* BEISCSI_H */
index 08007b6..698a527 100644 (file)
 #include "be_mgmt.h"
 #include "be_main.h"
 
+static void be_mcc_notify(struct beiscsi_hba *phba)
+{
+       struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+       u32 val = 0;
+
+       val |= mccq->id & DB_MCCQ_RING_ID_MASK;
+       val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT;
+       iowrite32(val, phba->db_va + DB_MCCQ_OFFSET);
+}
+
 static inline bool be_mcc_compl_is_new(struct be_mcc_compl *compl)
 {
        if (compl->flags != 0) {
@@ -54,13 +64,56 @@ static int be_mcc_compl_process(struct be_ctrl_info *ctrl,
        return 0;
 }
 
+
 static inline bool is_link_state_evt(u32 trailer)
 {
        return (((trailer >> ASYNC_TRAILER_EVENT_CODE_SHIFT) &
-               ASYNC_TRAILER_EVENT_CODE_MASK) == ASYNC_EVENT_CODE_LINK_STATE);
+                 ASYNC_TRAILER_EVENT_CODE_MASK) ==
+                 ASYNC_EVENT_CODE_LINK_STATE);
+}
+
+static struct be_mcc_compl *be_mcc_compl_get(struct beiscsi_hba *phba)
+{
+       struct be_queue_info *mcc_cq = &phba->ctrl.mcc_obj.cq;
+       struct be_mcc_compl *compl = queue_tail_node(mcc_cq);
+
+       if (be_mcc_compl_is_new(compl)) {
+               queue_tail_inc(mcc_cq);
+               return compl;
+       }
+       return NULL;
+}
+
+static void be2iscsi_fail_session(struct iscsi_cls_session *cls_session)
+{
+       iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_CONN_FAILED);
+}
+
+static void beiscsi_async_link_state_process(struct beiscsi_hba *phba,
+               struct be_async_event_link_state *evt)
+{
+       switch (evt->port_link_status) {
+       case ASYNC_EVENT_LINK_DOWN:
+               SE_DEBUG(DBG_LVL_1, "Link Down on Physical Port %d \n",
+                                               evt->physical_port);
+               phba->state |= BE_ADAPTER_LINK_DOWN;
+               break;
+       case ASYNC_EVENT_LINK_UP:
+               phba->state = BE_ADAPTER_UP;
+               SE_DEBUG(DBG_LVL_1, "Link UP on Physical Port %d \n",
+                                               evt->physical_port);
+               iscsi_host_for_each_session(phba->shost,
+                                           be2iscsi_fail_session);
+               break;
+       default:
+               SE_DEBUG(DBG_LVL_1, "Unexpected Async Notification %d on"
+                                   "Physical Port %d \n",
+                                    evt->port_link_status,
+                                    evt->physical_port);
+       }
 }
 
-void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
+static void beiscsi_cq_notify(struct beiscsi_hba *phba, u16 qid, bool arm,
                       u16 num_popped)
 {
        u32 val = 0;
@@ -68,7 +121,66 @@ void beiscsi_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm,
        if (arm)
                val |= 1 << DB_CQ_REARM_SHIFT;
        val |= num_popped << DB_CQ_NUM_POPPED_SHIFT;
-       iowrite32(val, ctrl->db + DB_CQ_OFFSET);
+       iowrite32(val, phba->db_va + DB_CQ_OFFSET);
+}
+
+
+int beiscsi_process_mcc(struct beiscsi_hba *phba)
+{
+       struct be_mcc_compl *compl;
+       int num = 0, status = 0;
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+
+       spin_lock_bh(&phba->ctrl.mcc_cq_lock);
+       while ((compl = be_mcc_compl_get(phba))) {
+               if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
+                       /* Interpret flags as an async trailer */
+                       BUG_ON(!is_link_state_evt(compl->flags));
+
+                       /* Interpret compl as a async link evt */
+                       beiscsi_async_link_state_process(phba,
+                               (struct be_async_event_link_state *) compl);
+               } else if (compl->flags & CQE_FLAGS_COMPLETED_MASK) {
+                               status = be_mcc_compl_process(ctrl, compl);
+                               atomic_dec(&phba->ctrl.mcc_obj.q.used);
+               }
+               be_mcc_compl_use(compl);
+               num++;
+       }
+
+       if (num)
+               beiscsi_cq_notify(phba, phba->ctrl.mcc_obj.cq.id, true, num);
+
+       spin_unlock_bh(&phba->ctrl.mcc_cq_lock);
+       return status;
+}
+
+/* Wait till no more pending mcc requests are present */
+static int be_mcc_wait_compl(struct beiscsi_hba *phba)
+{
+#define mcc_timeout            120000 /* 5s timeout */
+       int i, status;
+       for (i = 0; i < mcc_timeout; i++) {
+               status = beiscsi_process_mcc(phba);
+               if (status)
+                       return status;
+
+               if (atomic_read(&phba->ctrl.mcc_obj.q.used) == 0)
+                       break;
+               udelay(100);
+       }
+       if (i == mcc_timeout) {
+               dev_err(&phba->pcidev->dev, "mccq poll timed out\n");
+               return -1;
+       }
+       return 0;
+}
+
+/* Notify MCC requests and wait for completion */
+int be_mcc_notify_wait(struct beiscsi_hba *phba)
+{
+       be_mcc_notify(phba);
+       return be_mcc_wait_compl(phba);
 }
 
 static int be_mbox_db_ready_wait(struct be_ctrl_info *ctrl)
@@ -142,6 +254,52 @@ int be_mbox_notify(struct be_ctrl_info *ctrl)
        return 0;
 }
 
+/*
+ * Insert the mailbox address into the doorbell in two steps
+ * Polls on the mbox doorbell till a command completion (or a timeout) occurs
+ */
+static int be_mbox_notify_wait(struct beiscsi_hba *phba)
+{
+       int status;
+       u32 val = 0;
+       void __iomem *db = phba->ctrl.db + MPU_MAILBOX_DB_OFFSET;
+       struct be_dma_mem *mbox_mem = &phba->ctrl.mbox_mem;
+       struct be_mcc_mailbox *mbox = mbox_mem->va;
+       struct be_mcc_compl *compl = &mbox->compl;
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+
+       val |= MPU_MAILBOX_DB_HI_MASK;
+       /* at bits 2 - 31 place mbox dma addr msb bits 34 - 63 */
+       val |= (upper_32_bits(mbox_mem->dma) >> 2) << 2;
+       iowrite32(val, db);
+
+       /* wait for ready to be set */
+       status = be_mbox_db_ready_wait(ctrl);
+       if (status != 0)
+               return status;
+
+       val = 0;
+       /* at bits 2 - 31 place mbox dma addr lsb bits 4 - 33 */
+       val |= (u32)(mbox_mem->dma >> 4) << 2;
+       iowrite32(val, db);
+
+       status = be_mbox_db_ready_wait(ctrl);
+       if (status != 0)
+               return status;
+
+       /* A cq entry has been made now */
+       if (be_mcc_compl_is_new(compl)) {
+               status = be_mcc_compl_process(ctrl, &mbox->compl);
+               be_mcc_compl_use(compl);
+               if (status)
+                       return status;
+       } else {
+               dev_err(&phba->pcidev->dev, "invalid mailbox completion\n");
+               return -1;
+       }
+       return 0;
+}
+
 void be_wrb_hdr_prepare(struct be_mcc_wrb *wrb, int payload_len,
                                bool embedded, u8 sge_cnt)
 {
@@ -203,6 +361,20 @@ struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem)
        return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb;
 }
 
+struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba)
+{
+       struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
+       struct be_mcc_wrb *wrb;
+
+       BUG_ON(atomic_read(&mccq->used) >= mccq->len);
+       wrb = queue_head_node(mccq);
+       queue_head_inc(mccq);
+       atomic_inc(&mccq->used);
+       memset(wrb, 0, sizeof(*wrb));
+       return wrb;
+}
+
+
 int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
                          struct be_queue_info *eq, int eq_delay)
 {
@@ -212,6 +384,7 @@ int beiscsi_cmd_eq_create(struct be_ctrl_info *ctrl,
        struct be_dma_mem *q_mem = &eq->dma_mem;
        int status;
 
+       SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_eq_create\n");
        spin_lock(&ctrl->mbox_lock);
        memset(wrb, 0, sizeof(*wrb));
 
@@ -249,6 +422,7 @@ int be_cmd_fw_initialize(struct be_ctrl_info *ctrl)
        int status;
        u8 *endian_check;
 
+       SE_DEBUG(DBG_LVL_8, "In be_cmd_fw_initialize\n");
        spin_lock(&ctrl->mbox_lock);
        memset(wrb, 0, sizeof(*wrb));
 
@@ -282,6 +456,7 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
        void *ctxt = &req->context;
        int status;
 
+       SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_cq_create \n");
        spin_lock(&ctrl->mbox_lock);
        memset(wrb, 0, sizeof(*wrb));
 
@@ -289,7 +464,6 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
 
        be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
                        OPCODE_COMMON_CQ_CREATE, sizeof(*req));
-
        if (!q_mem->va)
                SE_DEBUG(DBG_LVL_1, "uninitialized q_mem->va\n");
 
@@ -329,6 +503,53 @@ static u32 be_encoded_q_len(int q_len)
                len_encoded = 0;
        return len_encoded;
 }
+
+int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
+                       struct be_queue_info *mccq,
+                       struct be_queue_info *cq)
+{
+       struct be_mcc_wrb *wrb;
+       struct be_cmd_req_mcc_create *req;
+       struct be_dma_mem *q_mem = &mccq->dma_mem;
+       struct be_ctrl_info *ctrl;
+       void *ctxt;
+       int status;
+
+       spin_lock(&phba->ctrl.mbox_lock);
+       ctrl = &phba->ctrl;
+       wrb = wrb_from_mbox(&ctrl->mbox_mem);
+       req = embedded_payload(wrb);
+       ctxt = &req->context;
+
+       be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+
+       be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
+                       OPCODE_COMMON_MCC_CREATE, sizeof(*req));
+
+       req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size);
+
+       AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt,
+                     PCI_FUNC(phba->pcidev->devfn));
+       AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1);
+       AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt,
+               be_encoded_q_len(mccq->len));
+       AMAP_SET_BITS(struct amap_mcc_context, cq_id, ctxt, cq->id);
+
+       be_dws_cpu_to_le(ctxt, sizeof(req->context));
+
+       be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
+
+       status = be_mbox_notify_wait(phba);
+       if (!status) {
+               struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb);
+               mccq->id = le16_to_cpu(resp->id);
+               mccq->created = true;
+       }
+       spin_unlock(&phba->ctrl.mbox_lock);
+
+       return status;
+}
+
 int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
                          int queue_type)
 {
@@ -337,6 +558,7 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
        u8 subsys = 0, opcode = 0;
        int status;
 
+       SE_DEBUG(DBG_LVL_8, "In beiscsi_cmd_q_destroy \n");
        spin_lock(&ctrl->mbox_lock);
        memset(wrb, 0, sizeof(*wrb));
        be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
@@ -350,6 +572,10 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
                subsys = CMD_SUBSYSTEM_COMMON;
                opcode = OPCODE_COMMON_CQ_DESTROY;
                break;
+       case QTYPE_MCCQ:
+               subsys = CMD_SUBSYSTEM_COMMON;
+               opcode = OPCODE_COMMON_MCC_DESTROY;
+               break;
        case QTYPE_WRBQ:
                subsys = CMD_SUBSYSTEM_ISCSI;
                opcode = OPCODE_COMMON_ISCSI_WRBQ_DESTROY;
@@ -377,30 +603,6 @@ int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
        return status;
 }
 
-int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr)
-{
-       struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
-       struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
-       int status;
-
-       spin_lock(&ctrl->mbox_lock);
-       memset(wrb, 0, sizeof(*wrb));
-       be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
-       be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
-                          OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
-                          sizeof(*req));
-
-       status = be_mbox_notify(ctrl);
-       if (!status) {
-               struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
-
-               memcpy(mac_addr, resp->mac_address, ETH_ALEN);
-       }
-
-       spin_unlock(&ctrl->mbox_lock);
-       return status;
-}
-
 int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
                                    struct be_queue_info *cq,
                                    struct be_queue_info *dq, int length,
@@ -412,6 +614,7 @@ int be_cmd_create_default_pdu_queue(struct be_ctrl_info *ctrl,
        void *ctxt = &req->context;
        int status;
 
+       SE_DEBUG(DBG_LVL_8, "In be_cmd_create_default_pdu_queue\n");
        spin_lock(&ctrl->mbox_lock);
        memset(wrb, 0, sizeof(*wrb));
 
@@ -468,8 +671,10 @@ int be_cmd_wrbq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem,
        be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
 
        status = be_mbox_notify(ctrl);
-       if (!status)
+       if (!status) {
                wrbq->id = le16_to_cpu(resp->cid);
+               wrbq->created = true;
+       }
        spin_unlock(&ctrl->mbox_lock);
        return status;
 }
index c20d686..5de8acb 100644 (file)
@@ -47,6 +47,8 @@ struct be_mcc_wrb {
 
 #define CQE_FLAGS_VALID_MASK (1 << 31)
 #define CQE_FLAGS_ASYNC_MASK (1 << 30)
+#define CQE_FLAGS_COMPLETED_MASK       (1 << 28)
+#define CQE_FLAGS_CONSUMED_MASK        (1 << 27)
 
 /* Completion Status */
 #define MCC_STATUS_SUCCESS 0x0
@@ -173,7 +175,7 @@ struct be_cmd_req_hdr {
        u8 domain;              /* dword 0 */
        u32 timeout;            /* dword 1 */
        u32 request_length;     /* dword 2 */
-       u32 rsvd              /* dword 3 */
+       u32 rsvd0;              /* dword 3 */
 };
 
 struct be_cmd_resp_hdr {
@@ -382,7 +384,6 @@ struct be_cmd_req_modify_eq_delay {
 
 #define ETH_ALEN       6
 
-
 struct be_cmd_req_get_mac_addr {
        struct be_cmd_req_hdr hdr;
        u32 nic_port_count;
@@ -417,14 +418,21 @@ int beiscsi_cmd_cq_create(struct be_ctrl_info *ctrl,
 
 int beiscsi_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q,
                          int type);
+int beiscsi_cmd_mccq_create(struct beiscsi_hba *phba,
+                       struct be_queue_info *mccq,
+                       struct be_queue_info *cq);
+
 int be_poll_mcc(struct be_ctrl_info *ctrl);
-unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl);
-int be_cmd_get_mac_addr(struct be_ctrl_info *ctrl, u8 *mac_addr);
+unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
+                                     struct beiscsi_hba *phba);
+int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr);
 
 /*ISCSI Functuions */
 int be_cmd_fw_initialize(struct be_ctrl_info *ctrl);
 
 struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem);
+struct be_mcc_wrb *wrb_from_mccq(struct beiscsi_hba *phba);
+int be_mcc_notify_wait(struct beiscsi_hba *phba);
 
 int be_mbox_notify(struct be_ctrl_info *ctrl);
 
@@ -531,6 +539,23 @@ struct amap_sol_cqe {
        u8 valid;               /* dword 3 */
 } __packed;
 
+#define SOL_ICD_INDEX_MASK     0x0003FFC0
+struct amap_sol_cqe_ring {
+       u8 hw_sts[8];           /* dword 0 */
+       u8 i_sts[8];            /* dword 0 */
+       u8 i_resp[8];           /* dword 0 */
+       u8 i_flags[7];          /* dword 0 */
+       u8 s;                   /* dword 0 */
+       u8 i_exp_cmd_sn[32];    /* dword 1 */
+       u8 code[6];             /* dword 2 */
+       u8 icd_index[12];       /* dword 2 */
+       u8 rsvd[6];             /* dword 2 */
+       u8 i_cmd_wnd[8];        /* dword 2 */
+       u8 i_res_cnt[31];       /* dword 3 */
+       u8 valid;               /* dword 3 */
+} __packed;
+
+
 
 /**
  * Post WRB Queue Doorbell Register used by the host Storage
@@ -664,8 +689,8 @@ struct be_fw_cfg {
 #define        OPCODE_COMMON_TCP_UPLOAD        56
 #define OPCODE_COMMON_ISCSI_ERROR_RECOVERY_INVALIDATE_COMMANDS 1
 /* --- CMD_ISCSI_INVALIDATE_CONNECTION_TYPE --- */
-#define CMD_ISCSI_CONNECTION_INVALIDATE 1
-#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 2
+#define CMD_ISCSI_CONNECTION_INVALIDATE 0x8001
+#define CMD_ISCSI_CONNECTION_ISSUE_TCP_RST 0x8002
 #define OPCODE_ISCSI_INI_DRIVER_INVALIDATE_CONNECTION 42
 
 #define INI_WR_CMD                     1       /* Initiator write command */
index 2fd2544..d587b03 100644 (file)
@@ -67,11 +67,11 @@ struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
                cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
        }
 
-        cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
-                                          shost, cmds_max,
-                                          sizeof(*beiscsi_sess),
-                                          sizeof(*io_task),
-                                          initial_cmdsn, ISCSI_MAX_TARGET);
+       cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
+                                         shost, cmds_max,
+                                         sizeof(*beiscsi_sess),
+                                         sizeof(*io_task),
+                                         initial_cmdsn, ISCSI_MAX_TARGET);
        if (!cls_session)
                return NULL;
        sess = cls_session->dd_data;
@@ -297,7 +297,7 @@ int beiscsi_get_host_param(struct Scsi_Host *shost,
 
        switch (param) {
        case ISCSI_HOST_PARAM_HWADDRESS:
-               be_cmd_get_mac_addr(&phba->ctrl, phba->mac_address);
+               be_cmd_get_mac_addr(phba, phba->mac_address);
                len = sysfs_format_mac(buf, phba->mac_address, ETH_ALEN);
                break;
        default:
@@ -377,16 +377,12 @@ int beiscsi_conn_start(struct iscsi_cls_conn *cls_conn)
        struct beiscsi_conn *beiscsi_conn = conn->dd_data;
        struct beiscsi_endpoint *beiscsi_ep;
        struct beiscsi_offload_params params;
-       struct iscsi_session *session = conn->session;
-       struct Scsi_Host *shost = iscsi_session_to_shost(session->cls_session);
-       struct beiscsi_hba *phba = iscsi_host_priv(shost);
 
        memset(&params, 0, sizeof(struct beiscsi_offload_params));
        beiscsi_ep = beiscsi_conn->ep;
        if (!beiscsi_ep)
                SE_DEBUG(DBG_LVL_1, "In beiscsi_conn_start , no beiscsi_ep\n");
 
-       free_mgmt_sgl_handle(phba, beiscsi_conn->plogin_sgl_handle);
        beiscsi_conn->login_in_progress = 0;
        beiscsi_set_params_for_offld(beiscsi_conn, &params);
        beiscsi_offload_connection(beiscsi_conn, &params);
@@ -498,6 +494,13 @@ beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr,
                SE_DEBUG(DBG_LVL_1, "shost is NULL \n");
                return ERR_PTR(ret);
        }
+
+       if (phba->state) {
+               ret = -EBUSY;
+               SE_DEBUG(DBG_LVL_1, "The Adapet state is Not UP \n");
+               return ERR_PTR(ret);
+       }
+
        ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint));
        if (!ep) {
                ret = -ENOMEM;
index 4f1aca3..1a557fa 100644 (file)
@@ -39,7 +39,8 @@
 
 static unsigned int be_iopoll_budget = 10;
 static unsigned int be_max_phys_size = 64;
-static unsigned int enable_msix;
+static unsigned int enable_msix = 1;
+static unsigned int ring_mode;
 
 MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
 MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
@@ -58,6 +59,17 @@ static int beiscsi_slave_configure(struct scsi_device *sdev)
        return 0;
 }
 
+/*------------------- PCI Driver operations and data ----------------- */
+static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
+       { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
+       { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
+       { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
+       { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) },
+       { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID4) },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
+
 static struct scsi_host_template beiscsi_sht = {
        .module = THIS_MODULE,
        .name = "ServerEngines 10Gbe open-iscsi Initiator Driver",
@@ -76,16 +88,8 @@ static struct scsi_host_template beiscsi_sht = {
        .cmd_per_lun = BEISCSI_CMD_PER_LUN,
        .use_clustering = ENABLE_CLUSTERING,
 };
-static struct scsi_transport_template *beiscsi_scsi_transport;
 
-/*------------------- PCI Driver operations and data ----------------- */
-static DEFINE_PCI_DEVICE_TABLE(beiscsi_pci_id_table) = {
-       { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
-       { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
-       { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
-       { 0 }
-};
-MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
+static struct scsi_transport_template *beiscsi_scsi_transport;
 
 static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
 {
@@ -104,7 +108,6 @@ static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
        shost->max_cmd_len = BEISCSI_MAX_CMD_LEN;
        shost->max_lun = BEISCSI_NUM_MAX_LUN;
        shost->transportt = beiscsi_scsi_transport;
-
        phba = iscsi_host_priv(shost);
        memset(phba, 0, sizeof(*phba));
        phba->shost = shost;
@@ -181,6 +184,7 @@ static int beiscsi_enable_pci(struct pci_dev *pcidev)
                return ret;
        }
 
+       pci_set_master(pcidev);
        if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) {
                ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
                if (ret) {
@@ -203,7 +207,6 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
        status = beiscsi_map_pci_bars(phba, pdev);
        if (status)
                return status;
-
        mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
        mbox_mem_alloc->va = pci_alloc_consistent(pdev,
                                                  mbox_mem_alloc->size,
@@ -219,6 +222,9 @@ static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
        mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
        memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
        spin_lock_init(&ctrl->mbox_lock);
+       spin_lock_init(&phba->ctrl.mcc_lock);
+       spin_lock_init(&phba->ctrl.mcc_cq_lock);
+
        return status;
 }
 
@@ -267,6 +273,113 @@ static void hwi_ring_eq_db(struct beiscsi_hba *phba,
        iowrite32(val, phba->db_va + DB_EQ_OFFSET);
 }
 
+/**
+ * be_isr_mcc - The isr routine of the driver.
+ * @irq: Not used
+ * @dev_id: Pointer to host adapter structure
+ */
+static irqreturn_t be_isr_mcc(int irq, void *dev_id)
+{
+       struct beiscsi_hba *phba;
+       struct be_eq_entry *eqe = NULL;
+       struct be_queue_info *eq;
+       struct be_queue_info *mcc;
+       unsigned int num_eq_processed;
+       struct be_eq_obj *pbe_eq;
+       unsigned long flags;
+
+       pbe_eq = dev_id;
+       eq = &pbe_eq->q;
+       phba =  pbe_eq->phba;
+       mcc = &phba->ctrl.mcc_obj.cq;
+       eqe = queue_tail_node(eq);
+       if (!eqe)
+               SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
+
+       num_eq_processed = 0;
+
+       while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+                               & EQE_VALID_MASK) {
+               if (((eqe->dw[offsetof(struct amap_eq_entry,
+                    resource_id) / 32] &
+                    EQE_RESID_MASK) >> 16) == mcc->id) {
+                       spin_lock_irqsave(&phba->isr_lock, flags);
+                       phba->todo_mcc_cq = 1;
+                       spin_unlock_irqrestore(&phba->isr_lock, flags);
+               }
+               AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+               queue_tail_inc(eq);
+               eqe = queue_tail_node(eq);
+               num_eq_processed++;
+       }
+       if (phba->todo_mcc_cq)
+               queue_work(phba->wq, &phba->work_cqs);
+       if (num_eq_processed)
+               hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * be_isr_msix - The isr routine of the driver.
+ * @irq: Not used
+ * @dev_id: Pointer to host adapter structure
+ */
+static irqreturn_t be_isr_msix(int irq, void *dev_id)
+{
+       struct beiscsi_hba *phba;
+       struct be_eq_entry *eqe = NULL;
+       struct be_queue_info *eq;
+       struct be_queue_info *cq;
+       unsigned int num_eq_processed;
+       struct be_eq_obj *pbe_eq;
+       unsigned long flags;
+
+       pbe_eq = dev_id;
+       eq = &pbe_eq->q;
+       cq = pbe_eq->cq;
+       eqe = queue_tail_node(eq);
+       if (!eqe)
+               SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
+
+       phba = pbe_eq->phba;
+       num_eq_processed = 0;
+       if (blk_iopoll_enabled) {
+               while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+                                       & EQE_VALID_MASK) {
+                       if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
+                               blk_iopoll_sched(&pbe_eq->iopoll);
+
+                       AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+                       queue_tail_inc(eq);
+                       eqe = queue_tail_node(eq);
+                       num_eq_processed++;
+               }
+               if (num_eq_processed)
+                       hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
+
+               return IRQ_HANDLED;
+       } else {
+               while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+                                               & EQE_VALID_MASK) {
+                       spin_lock_irqsave(&phba->isr_lock, flags);
+                       phba->todo_cq = 1;
+                       spin_unlock_irqrestore(&phba->isr_lock, flags);
+                       AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+                       queue_tail_inc(eq);
+                       eqe = queue_tail_node(eq);
+                       num_eq_processed++;
+               }
+               if (phba->todo_cq)
+                       queue_work(phba->wq, &phba->work_cqs);
+
+               if (num_eq_processed)
+                       hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
+
+               return IRQ_HANDLED;
+       }
+}
+
 /**
  * be_isr - The isr routine of the driver.
  * @irq: Not used
@@ -280,48 +393,70 @@ static irqreturn_t be_isr(int irq, void *dev_id)
        struct be_eq_entry *eqe = NULL;
        struct be_queue_info *eq;
        struct be_queue_info *cq;
+       struct be_queue_info *mcc;
        unsigned long flags, index;
-       unsigned int num_eq_processed;
+       unsigned int num_mcceq_processed, num_ioeq_processed;
        struct be_ctrl_info *ctrl;
+       struct be_eq_obj *pbe_eq;
        int isr;
 
        phba = dev_id;
-       if (!enable_msix) {
-               ctrl = &phba->ctrl;;
-               isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
-                              (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
-               if (!isr)
-                       return IRQ_NONE;
-       }
+       ctrl = &phba->ctrl;;
+       isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
+                      (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
+       if (!isr)
+               return IRQ_NONE;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        phwi_context = phwi_ctrlr->phwi_ctxt;
-       eq = &phwi_context->be_eq.q;
-       cq = &phwi_context->be_cq;
+       pbe_eq = &phwi_context->be_eq[0];
+
+       eq = &phwi_context->be_eq[0].q;
+       mcc = &phba->ctrl.mcc_obj.cq;
        index = 0;
        eqe = queue_tail_node(eq);
        if (!eqe)
                SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
 
-       num_eq_processed = 0;
+       num_ioeq_processed = 0;
+       num_mcceq_processed = 0;
        if (blk_iopoll_enabled) {
                while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
                                        & EQE_VALID_MASK) {
-                       if (!blk_iopoll_sched_prep(&phba->iopoll))
-                               blk_iopoll_sched(&phba->iopoll);
-
+                       if (((eqe->dw[offsetof(struct amap_eq_entry,
+                            resource_id) / 32] &
+                            EQE_RESID_MASK) >> 16) == mcc->id) {
+                               spin_lock_irqsave(&phba->isr_lock, flags);
+                               phba->todo_mcc_cq = 1;
+                               spin_unlock_irqrestore(&phba->isr_lock, flags);
+                               num_mcceq_processed++;
+                       } else {
+                               if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
+                                       blk_iopoll_sched(&pbe_eq->iopoll);
+                               num_ioeq_processed++;
+                       }
                        AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
                        queue_tail_inc(eq);
                        eqe = queue_tail_node(eq);
-                       num_eq_processed++;
-                       SE_DEBUG(DBG_LVL_8, "Valid EQE\n");
                }
-               if (num_eq_processed) {
-                       hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 0, 1);
+               if (num_ioeq_processed || num_mcceq_processed) {
+                       if (phba->todo_mcc_cq)
+                               queue_work(phba->wq, &phba->work_cqs);
+
+               if ((num_mcceq_processed) && (!num_ioeq_processed))
+                               hwi_ring_eq_db(phba, eq->id, 0,
+                                             (num_ioeq_processed +
+                                              num_mcceq_processed) , 1, 1);
+                       else
+                               hwi_ring_eq_db(phba, eq->id, 0,
+                                              (num_ioeq_processed +
+                                               num_mcceq_processed), 0, 1);
+
                        return IRQ_HANDLED;
                } else
                        return IRQ_NONE;
        } else {
+               cq = &phwi_context->be_cq[0];
                while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
                                                & EQE_VALID_MASK) {
 
@@ -339,13 +474,14 @@ static irqreturn_t be_isr(int irq, void *dev_id)
                        AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
                        queue_tail_inc(eq);
                        eqe = queue_tail_node(eq);
-                       num_eq_processed++;
+                       num_ioeq_processed++;
                }
                if (phba->todo_cq || phba->todo_mcc_cq)
                        queue_work(phba->wq, &phba->work_cqs);
 
-               if (num_eq_processed) {
-                       hwi_ring_eq_db(phba, eq->id, 0, num_eq_processed, 1, 1);
+               if (num_ioeq_processed) {
+                       hwi_ring_eq_db(phba, eq->id, 0,
+                                      num_ioeq_processed, 1, 1);
                        return IRQ_HANDLED;
                } else
                        return IRQ_NONE;
@@ -355,13 +491,32 @@ static irqreturn_t be_isr(int irq, void *dev_id)
 static int beiscsi_init_irqs(struct beiscsi_hba *phba)
 {
        struct pci_dev *pcidev = phba->pcidev;
-       int ret;
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_context_memory *phwi_context;
+       int ret, msix_vec, i = 0;
+       char desc[32];
 
-       ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED, "beiscsi", phba);
-       if (ret) {
-               shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
-                            "Failed to register irq\\n");
-               return ret;
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
+
+       if (phba->msix_enabled) {
+               for (i = 0; i < phba->num_cpus; i++) {
+                       sprintf(desc, "beiscsi_msix_%04x", i);
+                       msix_vec = phba->msix_entries[i].vector;
+                       ret = request_irq(msix_vec, be_isr_msix, 0, desc,
+                                         &phwi_context->be_eq[i]);
+               }
+               msix_vec = phba->msix_entries[i].vector;
+               ret = request_irq(msix_vec, be_isr_mcc, 0, "beiscsi_msix_mcc",
+                                 &phwi_context->be_eq[i]);
+       } else {
+               ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED,
+                                 "beiscsi", phba);
+               if (ret) {
+                       shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
+                                    "Failed to register irq\\n");
+                       return ret;
+               }
        }
        return 0;
 }
@@ -378,15 +533,6 @@ static void hwi_ring_cq_db(struct beiscsi_hba *phba,
        iowrite32(val, phba->db_va + DB_CQ_OFFSET);
 }
 
-/*
- * async pdus include
- * a. unsolicited NOP-In (target initiated NOP-In)
- * b. Async Messages
- * c. Reject PDU
- * d. Login response
- * These headers arrive unprocessed by the EP firmware and iSCSI layer
- * process them
- */
 static unsigned int
 beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
                          struct beiscsi_hba *phba,
@@ -397,6 +543,9 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
 {
        struct iscsi_conn *conn = beiscsi_conn->conn;
        struct iscsi_session *session = conn->session;
+       struct iscsi_task *task;
+       struct beiscsi_io_task *io_task;
+       struct iscsi_hdr *login_hdr;
 
        switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
                                                PDUBASE_OPCODE_MASK) {
@@ -412,6 +561,10 @@ beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
                SE_DEBUG(DBG_LVL_1, "In ISCSI_OP_REJECT\n");
                break;
        case ISCSI_OP_LOGIN_RSP:
+               task = conn->login_task;
+               io_task = task->dd_data;
+               login_hdr = (struct iscsi_hdr *)ppdu;
+               login_hdr->itt = io_task->libiscsi_itt;
                break;
        default:
                shost_printk(KERN_WARNING, phba->shost,
@@ -440,7 +593,8 @@ static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
                                                io_sgl_alloc_index];
                phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL;
                phba->io_sgl_hndl_avbl--;
-               if (phba->io_sgl_alloc_index == (phba->params.ios_per_ctrl - 1))
+               if (phba->io_sgl_alloc_index == (phba->params.
+                                                ios_per_ctrl - 1))
                        phba->io_sgl_alloc_index = 0;
                else
                        phba->io_sgl_alloc_index++;
@@ -490,9 +644,18 @@ struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
 
        phwi_ctrlr = phba->phwi_ctrlr;
        pwrb_context = &phwi_ctrlr->wrb_context[cid];
-       pwrb_handle = pwrb_context->pwrb_handle_base[index];
-       pwrb_handle->wrb_index = index;
-       pwrb_handle->nxt_wrb_index = index;
+       if (pwrb_context->wrb_handles_available) {
+               pwrb_handle = pwrb_context->pwrb_handle_base[
+                                           pwrb_context->alloc_index];
+               pwrb_context->wrb_handles_available--;
+               pwrb_handle->nxt_wrb_index = pwrb_handle->wrb_index;
+               if (pwrb_context->alloc_index ==
+                                               (phba->params.wrbs_per_cxn - 1))
+                       pwrb_context->alloc_index = 0;
+               else
+                       pwrb_context->alloc_index++;
+       } else
+               pwrb_handle = NULL;
        return pwrb_handle;
 }
 
@@ -508,11 +671,20 @@ static void
 free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context,
                struct wrb_handle *pwrb_handle)
 {
+       if (!ring_mode)
+               pwrb_context->pwrb_handle_base[pwrb_context->free_index] =
+                                              pwrb_handle;
+       pwrb_context->wrb_handles_available++;
+       if (pwrb_context->free_index == (phba->params.wrbs_per_cxn - 1))
+               pwrb_context->free_index = 0;
+       else
+               pwrb_context->free_index++;
+
        SE_DEBUG(DBG_LVL_8,
-                "FREE WRB: pwrb_handle=%p free_index=%d=0x%x"
+                "FREE WRB: pwrb_handle=%p free_index=0x%x"
                 "wrb_handles_available=%d \n",
                 pwrb_handle, pwrb_context->free_index,
-                pwrb_context->free_index, pwrb_context->wrb_handles_available);
+                pwrb_context->wrb_handles_available);
 }
 
 static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba)
@@ -540,6 +712,8 @@ void
 free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
 {
 
+       SE_DEBUG(DBG_LVL_8, "In  free_mgmt_sgl_handle,eh_sgl_free_index=%d \n",
+                            phba->eh_sgl_free_index);
        if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) {
                /*
                 * this can happen if clean_task is called on a task that
@@ -572,10 +746,10 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
        u32 resid = 0, exp_cmdsn, max_cmdsn;
        u8 rsp, status, flags;
 
-       exp_cmdsn = be32_to_cpu(psol->
+       exp_cmdsn = (psol->
                        dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
                        & SOL_EXP_CMD_SN_MASK);
-       max_cmdsn = be32_to_cpu((psol->
+       max_cmdsn = ((psol->
                        dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
                        & SOL_EXP_CMD_SN_MASK) +
                        ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
@@ -610,9 +784,9 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
        }
 
        if (status == SAM_STAT_CHECK_CONDITION) {
+               unsigned short *slen = (unsigned short *)sts_bhs->sense_info;
                sense = sts_bhs->sense_info + sizeof(unsigned short);
-               sense_len =
-                   cpu_to_be16((unsigned short)(sts_bhs->sense_info[0]));
+               sense_len =  cpu_to_be16(*slen);
                memcpy(task->sc->sense_buffer, sense,
                       min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
        }
@@ -620,8 +794,8 @@ be_complete_io(struct beiscsi_conn *beiscsi_conn,
                if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
                                                        & SOL_RES_CNT_MASK)
                         conn->rxdata_octets += (psol->
-                             dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
-                                                       & SOL_RES_CNT_MASK);
+                            dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
+                            & SOL_RES_CNT_MASK);
        }
 unmap:
        scsi_dma_unmap(io_task->scsi_cmnd);
@@ -633,6 +807,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
                   struct iscsi_task *task, struct sol_cqe *psol)
 {
        struct iscsi_logout_rsp *hdr;
+       struct beiscsi_io_task *io_task = task->dd_data;
        struct iscsi_conn *conn = beiscsi_conn->conn;
 
        hdr = (struct iscsi_logout_rsp *)task->hdr;
@@ -651,7 +826,7 @@ be_complete_logout(struct beiscsi_conn *beiscsi_conn,
                        ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
                                        / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
        hdr->hlength = 0;
-
+       hdr->itt = io_task->libiscsi_itt;
        __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
 }
 
@@ -661,6 +836,7 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
 {
        struct iscsi_tm_rsp *hdr;
        struct iscsi_conn *conn = beiscsi_conn->conn;
+       struct beiscsi_io_task *io_task = task->dd_data;
 
        hdr = (struct iscsi_tm_rsp *)task->hdr;
        hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
@@ -668,11 +844,12 @@ be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
        hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
                                        32] & SOL_RESP_MASK);
        hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
-                                    i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
+                                   i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
        hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
                        i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
                        ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
                        / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
+       hdr->itt = io_task->libiscsi_itt;
        __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
 }
 
@@ -681,18 +858,36 @@ hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
                       struct beiscsi_hba *phba, struct sol_cqe *psol)
 {
        struct hwi_wrb_context *pwrb_context;
-       struct wrb_handle *pwrb_handle;
+       struct wrb_handle *pwrb_handle = NULL;
+       struct sgl_handle *psgl_handle = NULL;
        struct hwi_controller *phwi_ctrlr;
+       struct iscsi_task *task;
+       struct beiscsi_io_task *io_task;
        struct iscsi_conn *conn = beiscsi_conn->conn;
        struct iscsi_session *session = conn->session;
 
        phwi_ctrlr = phba->phwi_ctrlr;
-       pwrb_context = &phwi_ctrlr->wrb_context[((psol->
+       if (ring_mode) {
+               psgl_handle = phba->sgl_hndl_array[((psol->
+                             dw[offsetof(struct amap_sol_cqe_ring, icd_index) /
+                               32] & SOL_ICD_INDEX_MASK) >> 6)];
+               pwrb_context = &phwi_ctrlr->wrb_context[psgl_handle->cid];
+               task = psgl_handle->task;
+               pwrb_handle = NULL;
+       } else {
+               pwrb_context = &phwi_ctrlr->wrb_context[((psol->
                                dw[offsetof(struct amap_sol_cqe, cid) / 32] &
                                SOL_CID_MASK) >> 6)];
-       pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
+               pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
                                dw[offsetof(struct amap_sol_cqe, wrb_index) /
                                32] & SOL_WRB_INDEX_MASK) >> 16)];
+               task = pwrb_handle->pio_handle;
+       }
+
+       io_task = task->dd_data;
+       spin_lock(&phba->mgmt_sgl_lock);
+       free_mgmt_sgl_handle(phba, io_task->psgl_handle);
+       spin_unlock(&phba->mgmt_sgl_lock);
        spin_lock_bh(&session->lock);
        free_wrb_handle(phba, pwrb_context, pwrb_handle);
        spin_unlock_bh(&session->lock);
@@ -704,6 +899,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
 {
        struct iscsi_nopin *hdr;
        struct iscsi_conn *conn = beiscsi_conn->conn;
+       struct beiscsi_io_task *io_task = task->dd_data;
 
        hdr = (struct iscsi_nopin *)task->hdr;
        hdr->flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
@@ -715,6 +911,7 @@ be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
                        ((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
                        / 32] & SOL_CMD_WND_MASK) >> 24) - 1);
        hdr->opcode = ISCSI_OP_NOOP_IN;
+       hdr->itt = io_task->libiscsi_itt;
        __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
 }
 
@@ -726,25 +923,33 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
        struct iscsi_wrb *pwrb = NULL;
        struct hwi_controller *phwi_ctrlr;
        struct iscsi_task *task;
-       struct beiscsi_io_task *io_task;
+       struct sgl_handle *psgl_handle = NULL;
+       unsigned int type;
        struct iscsi_conn *conn = beiscsi_conn->conn;
        struct iscsi_session *session = conn->session;
 
        phwi_ctrlr = phba->phwi_ctrlr;
-
-       pwrb_context = &phwi_ctrlr->
-               wrb_context[((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32]
-               & SOL_CID_MASK) >> 6)];
-       pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
+       if (ring_mode) {
+               psgl_handle = phba->sgl_hndl_array[((psol->
+                             dw[offsetof(struct amap_sol_cqe_ring, icd_index) /
+                             32] & SOL_ICD_INDEX_MASK) >> 6)];
+               task = psgl_handle->task;
+               type = psgl_handle->type;
+       } else {
+               pwrb_context = &phwi_ctrlr->
+                               wrb_context[((psol->dw[offsetof
+                               (struct amap_sol_cqe, cid) / 32]
+                               & SOL_CID_MASK) >> 6)];
+               pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
                                dw[offsetof(struct amap_sol_cqe, wrb_index) /
                                32] & SOL_WRB_INDEX_MASK) >> 16)];
-
-       task = pwrb_handle->pio_handle;
-       io_task = task->dd_data;
+               task = pwrb_handle->pio_handle;
+               pwrb = pwrb_handle->pwrb;
+               type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
+                        WRB_TYPE_MASK) >> 28;
+       }
        spin_lock_bh(&session->lock);
-       pwrb = pwrb_handle->pwrb;
-       switch ((pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
-                WRB_TYPE_MASK) >> 28) {
+       switch (type) {
        case HWH_TYPE_IO:
        case HWH_TYPE_IO_RD:
                if ((task->hdr->opcode & ISCSI_OPCODE_MASK) ==
@@ -773,12 +978,21 @@ static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
                break;
 
        default:
-               shost_printk(KERN_WARNING, phba->shost,
-                           "wrb_index 0x%x CID 0x%x\n",
-                           ((psol->dw[offsetof(struct amap_iscsi_wrb, type) /
-                                       32] & SOL_WRB_INDEX_MASK) >> 16),
-                           ((psol->dw[offsetof(struct amap_sol_cqe, cid) / 32]
-                                       & SOL_CID_MASK) >> 6));
+               if (ring_mode)
+                       shost_printk(KERN_WARNING, phba->shost,
+                               "In hwi_complete_cmd, unknown type = %d"
+                               "icd_index 0x%x CID 0x%x\n", type,
+                               ((psol->dw[offsetof(struct amap_sol_cqe_ring,
+                               icd_index) / 32] & SOL_ICD_INDEX_MASK) >> 6),
+                               psgl_handle->cid);
+               else
+                       shost_printk(KERN_WARNING, phba->shost,
+                               "In hwi_complete_cmd, unknown type = %d"
+                               "wrb_index 0x%x CID 0x%x\n", type,
+                               ((psol->dw[offsetof(struct amap_iscsi_wrb,
+                               type) / 32] & SOL_WRB_INDEX_MASK) >> 16),
+                               ((psol->dw[offsetof(struct amap_sol_cqe,
+                               cid) / 32] & SOL_CID_MASK) >> 6));
                break;
        }
 
@@ -1208,40 +1422,55 @@ static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
        hwi_post_async_buffers(phba, pasync_handle->is_header);
 }
 
-static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
+
+static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
 {
-       struct hwi_controller *phwi_ctrlr;
-       struct hwi_context_memory *phwi_context;
        struct be_queue_info *cq;
        struct sol_cqe *sol;
        struct dmsg_cqe *dmsg;
        unsigned int num_processed = 0;
        unsigned int tot_nump = 0;
        struct beiscsi_conn *beiscsi_conn;
+       struct sgl_handle *psgl_handle = NULL;
+       struct beiscsi_hba *phba;
 
-       phwi_ctrlr = phba->phwi_ctrlr;
-       phwi_context = phwi_ctrlr->phwi_ctxt;
-       cq = &phwi_context->be_cq;
+       cq = pbe_eq->cq;
        sol = queue_tail_node(cq);
+       phba = pbe_eq->phba;
 
        while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] &
               CQE_VALID_MASK) {
                be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
 
-               beiscsi_conn = phba->conn_table[(u32) (sol->
+               if (ring_mode) {
+                       psgl_handle = phba->sgl_hndl_array[((sol->
+                                     dw[offsetof(struct amap_sol_cqe_ring,
+                                     icd_index) / 32] & SOL_ICD_INDEX_MASK)
+                                     >> 6)];
+                       beiscsi_conn = phba->conn_table[psgl_handle->cid];
+                       if (!beiscsi_conn || !beiscsi_conn->ep) {
+                               shost_printk(KERN_WARNING, phba->shost,
+                                    "Connection table empty for cid = %d\n",
+                                     psgl_handle->cid);
+                               return 0;
+                       }
+
+               } else {
+                       beiscsi_conn = phba->conn_table[(u32) (sol->
                                 dw[offsetof(struct amap_sol_cqe, cid) / 32] &
                                 SOL_CID_MASK) >> 6];
 
-               if (!beiscsi_conn || !beiscsi_conn->ep) {
-                       shost_printk(KERN_WARNING, phba->shost,
+                       if (!beiscsi_conn || !beiscsi_conn->ep) {
+                               shost_printk(KERN_WARNING, phba->shost,
                                     "Connection table empty for cid = %d\n",
                                     (u32)(sol->dw[offsetof(struct amap_sol_cqe,
                                     cid) / 32] & SOL_CID_MASK) >> 6);
-                       return 0;
+                               return 0;
+                       }
                }
 
                if (num_processed >= 32) {
-                       hwi_ring_cq_db(phba, phwi_context->be_cq.id,
+                       hwi_ring_cq_db(phba, cq->id,
                                        num_processed, 0, 0);
                        tot_nump += num_processed;
                        num_processed = 0;
@@ -1258,8 +1487,12 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
                        hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
                        break;
                case UNSOL_HDR_NOTIFY:
+                       SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR_ NOTIFY\n");
+                       hwi_process_default_pdu_ring(beiscsi_conn, phba,
+                                            (struct i_t_dpdu_cqe *)sol);
+                       break;
                case UNSOL_DATA_NOTIFY:
-                       SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR/DATA_NOTIFY\n");
+                       SE_DEBUG(DBG_LVL_8, "Received UNSOL_DATA_NOTIFY\n");
                        hwi_process_default_pdu_ring(beiscsi_conn, phba,
                                             (struct i_t_dpdu_cqe *)sol);
                        break;
@@ -1278,13 +1511,21 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
                case CMD_CXN_KILLED_ITT_INVALID:
                case CMD_CXN_KILLED_SEQ_OUTOFORDER:
                case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
-                       SE_DEBUG(DBG_LVL_1,
+                       if (ring_mode) {
+                               SE_DEBUG(DBG_LVL_1,
+                                "CQ Error notification for cmd.. "
+                                "code %d cid 0x%x\n",
+                                sol->dw[offsetof(struct amap_sol_cqe, code) /
+                                32] & CQE_CODE_MASK, psgl_handle->cid);
+                       } else {
+                               SE_DEBUG(DBG_LVL_1,
                                 "CQ Error notification for cmd.. "
                                 "code %d cid 0x%x\n",
                                 sol->dw[offsetof(struct amap_sol_cqe, code) /
                                 32] & CQE_CODE_MASK,
                                 (sol->dw[offsetof(struct amap_sol_cqe, cid) /
                                 32] & SOL_CID_MASK));
+                       }
                        break;
                case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
                        SE_DEBUG(DBG_LVL_1,
@@ -1306,23 +1547,37 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
                case CXN_KILLED_OVER_RUN_RESIDUAL:
                case CXN_KILLED_UNDER_RUN_RESIDUAL:
                case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
-                       SE_DEBUG(DBG_LVL_1, "CQ Error %d, resetting CID "
+                       if (ring_mode) {
+                               SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
+                                "0x%x...\n",
+                                sol->dw[offsetof(struct amap_sol_cqe, code) /
+                                32] & CQE_CODE_MASK, psgl_handle->cid);
+                       } else {
+                               SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
                                 "0x%x...\n",
                                 sol->dw[offsetof(struct amap_sol_cqe, code) /
                                 32] & CQE_CODE_MASK,
                                 sol->dw[offsetof(struct amap_sol_cqe, cid) /
                                 32] & CQE_CID_MASK);
+                       }
                        iscsi_conn_failure(beiscsi_conn->conn,
                                           ISCSI_ERR_CONN_FAILED);
                        break;
                case CXN_KILLED_RST_SENT:
                case CXN_KILLED_RST_RCVD:
-                       SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset received/sent "
-                                "on CID 0x%x...\n",
+                       if (ring_mode) {
+                               SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
+                               "received/sent on CID 0x%x...\n",
+                                sol->dw[offsetof(struct amap_sol_cqe, code) /
+                                32] & CQE_CODE_MASK, psgl_handle->cid);
+                       } else {
+                               SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
+                               "received/sent on CID 0x%x...\n",
                                 sol->dw[offsetof(struct amap_sol_cqe, code) /
                                 32] & CQE_CODE_MASK,
                                 sol->dw[offsetof(struct amap_sol_cqe, cid) /
                                 32] & CQE_CID_MASK);
+                       }
                        iscsi_conn_failure(beiscsi_conn->conn,
                                           ISCSI_ERR_CONN_FAILED);
                        break;
@@ -1344,8 +1599,7 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
 
        if (num_processed > 0) {
                tot_nump += num_processed;
-               hwi_ring_cq_db(phba, phwi_context->be_cq.id, num_processed,
-                              1, 0);
+               hwi_ring_cq_db(phba, cq->id, num_processed, 1, 0);
        }
        return tot_nump;
 }
@@ -1353,21 +1607,30 @@ static unsigned int beiscsi_process_cq(struct beiscsi_hba *phba)
 static void beiscsi_process_all_cqs(struct work_struct *work)
 {
        unsigned long flags;
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_context_memory *phwi_context;
+       struct be_eq_obj *pbe_eq;
        struct beiscsi_hba *phba =
            container_of(work, struct beiscsi_hba, work_cqs);
 
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
+       if (phba->msix_enabled)
+               pbe_eq = &phwi_context->be_eq[phba->num_cpus];
+       else
+               pbe_eq = &phwi_context->be_eq[0];
+
        if (phba->todo_mcc_cq) {
                spin_lock_irqsave(&phba->isr_lock, flags);
                phba->todo_mcc_cq = 0;
                spin_unlock_irqrestore(&phba->isr_lock, flags);
-               SE_DEBUG(DBG_LVL_1, "MCC Interrupt Not expected \n");
        }
 
        if (phba->todo_cq) {
                spin_lock_irqsave(&phba->isr_lock, flags);
                phba->todo_cq = 0;
                spin_unlock_irqrestore(&phba->isr_lock, flags);
-               beiscsi_process_cq(phba);
+               beiscsi_process_cq(pbe_eq);
        }
 }
 
@@ -1375,19 +1638,15 @@ static int be_iopoll(struct blk_iopoll *iop, int budget)
 {
        static unsigned int ret;
        struct beiscsi_hba *phba;
+       struct be_eq_obj *pbe_eq;
 
-       phba = container_of(iop, struct beiscsi_hba, iopoll);
-
-       ret = beiscsi_process_cq(phba);
+       pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
+       ret = beiscsi_process_cq(pbe_eq);
        if (ret < budget) {
-               struct hwi_controller *phwi_ctrlr;
-               struct hwi_context_memory *phwi_context;
-
-               phwi_ctrlr = phba->phwi_ctrlr;
-               phwi_context = phwi_ctrlr->phwi_ctxt;
+               phba = pbe_eq->phba;
                blk_iopoll_complete(iop);
-               hwi_ring_eq_db(phba, phwi_context->be_eq.q.id, 0,
-                                                       0, 1, 1);
+               SE_DEBUG(DBG_LVL_8, "rearm pbe_eq->q.id =%d\n", pbe_eq->q.id);
+               hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
        }
        return ret;
 }
@@ -1537,14 +1796,12 @@ static void hwi_write_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
 
 static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
 {
-       unsigned int num_cq_pages, num_eq_pages, num_async_pdu_buf_pages;
+       unsigned int num_cq_pages, num_async_pdu_buf_pages;
        unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn;
        unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages;
 
        num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
                                      sizeof(struct sol_cqe));
-       num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
-                                     sizeof(struct be_eq_entry));
        num_async_pdu_buf_pages =
                        PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
                                       phba->params.defpdu_hdr_sz);
@@ -1565,8 +1822,6 @@ static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
        phba->mem_req[HWI_MEM_ADDN_CONTEXT] =
                                            sizeof(struct hwi_context_memory);
 
-       phba->mem_req[HWI_MEM_CQ] = num_cq_pages * PAGE_SIZE;
-       phba->mem_req[HWI_MEM_EQ] = num_eq_pages * PAGE_SIZE;
 
        phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb)
            * (phba->params.wrbs_per_cxn)
@@ -1751,8 +2006,6 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
 
        for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
                pwrb_context = &phwi_ctrlr->wrb_context[index];
-               SE_DEBUG(DBG_LVL_8, "cid=%d pwrb_context=%p \n", index,
-                                               pwrb_context);
                pwrb_context->pwrb_handle_base =
                                kzalloc(sizeof(struct wrb_handle *) *
                                        phba->params.wrbs_per_cxn, GFP_KERNEL);
@@ -1767,6 +2020,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
                                pwrb_context->pwrb_handle_basestd[j] =
                                                                pwrb_handle;
                                pwrb_context->wrb_handles_available++;
+                               pwrb_handle->wrb_index = j;
                                pwrb_handle++;
                        }
                        pwrb_context->free_index = 0;
@@ -1785,6 +2039,7 @@ static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
                                pwrb_context->pwrb_handle_basestd[j] =
                                    pwrb_handle;
                                pwrb_context->wrb_handles_available++;
+                               pwrb_handle->wrb_index = j;
                                pwrb_handle++;
                        }
                        pwrb_context->free_index = 0;
@@ -2042,79 +2297,126 @@ static int be_fill_queue(struct be_queue_info *q,
        return 0;
 }
 
-static int beiscsi_create_eq(struct beiscsi_hba *phba,
+static int beiscsi_create_eqs(struct beiscsi_hba *phba,
                             struct hwi_context_memory *phwi_context)
 {
-       unsigned int idx;
-       int ret;
+       unsigned int i, num_eq_pages;
+       int ret, eq_for_mcc;
        struct be_queue_info *eq;
        struct be_dma_mem *mem;
-       struct be_mem_descriptor *mem_descr;
        void *eq_vaddress;
+       dma_addr_t paddr;
 
-       idx = 0;
-       eq = &phwi_context->be_eq.q;
-       mem = &eq->dma_mem;
-       mem_descr = phba->init_mem;
-       mem_descr += HWI_MEM_EQ;
-       eq_vaddress = mem_descr->mem_array[idx].virtual_address;
-
-       ret = be_fill_queue(eq, phba->params.num_eq_entries,
-                           sizeof(struct be_eq_entry), eq_vaddress);
-       if (ret) {
-               shost_printk(KERN_ERR, phba->shost,
-                            "be_fill_queue Failed for EQ \n");
-               return ret;
-       }
+       num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
+                                     sizeof(struct be_eq_entry));
 
-       mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address;
+       if (phba->msix_enabled)
+               eq_for_mcc = 1;
+       else
+               eq_for_mcc = 0;
+       for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
+               eq = &phwi_context->be_eq[i].q;
+               mem = &eq->dma_mem;
+               phwi_context->be_eq[i].phba = phba;
+               eq_vaddress = pci_alloc_consistent(phba->pcidev,
+                                                    num_eq_pages * PAGE_SIZE,
+                                                    &paddr);
+               if (!eq_vaddress)
+                       goto create_eq_error;
+
+               mem->va = eq_vaddress;
+               ret = be_fill_queue(eq, phba->params.num_eq_entries,
+                                   sizeof(struct be_eq_entry), eq_vaddress);
+               if (ret) {
+                       shost_printk(KERN_ERR, phba->shost,
+                                    "be_fill_queue Failed for EQ \n");
+                       goto create_eq_error;
+               }
 
-       ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
-                                   phwi_context->be_eq.cur_eqd);
-       if (ret) {
-               shost_printk(KERN_ERR, phba->shost, "beiscsi_cmd_eq_create"
-                            "Failedfor EQ \n");
-               return ret;
+               mem->dma = paddr;
+               ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
+                                           phwi_context->cur_eqd);
+               if (ret) {
+                       shost_printk(KERN_ERR, phba->shost,
+                                    "beiscsi_cmd_eq_create"
+                                    "Failedfor EQ \n");
+                       goto create_eq_error;
+               }
+               SE_DEBUG(DBG_LVL_8, "eqid = %d\n", phwi_context->be_eq[i].q.id);
        }
-       SE_DEBUG(DBG_LVL_8, "eq id is %d\n", phwi_context->be_eq.q.id);
        return 0;
+create_eq_error:
+       for (i = 0; i < (phba->num_cpus + 1); i++) {
+               eq = &phwi_context->be_eq[i].q;
+               mem = &eq->dma_mem;
+               if (mem->va)
+                       pci_free_consistent(phba->pcidev, num_eq_pages
+                                           * PAGE_SIZE,
+                                           mem->va, mem->dma);
+       }
+       return ret;
 }
 
-static int beiscsi_create_cq(struct beiscsi_hba *phba,
+static int beiscsi_create_cqs(struct beiscsi_hba *phba,
                             struct hwi_context_memory *phwi_context)
 {
-       unsigned int idx;
+       unsigned int i, num_cq_pages;
        int ret;
        struct be_queue_info *cq, *eq;
        struct be_dma_mem *mem;
-       struct be_mem_descriptor *mem_descr;
+       struct be_eq_obj *pbe_eq;
        void *cq_vaddress;
+       dma_addr_t paddr;
 
-       idx = 0;
-       cq = &phwi_context->be_cq;
-       eq = &phwi_context->be_eq.q;
-       mem = &cq->dma_mem;
-       mem_descr = phba->init_mem;
-       mem_descr += HWI_MEM_CQ;
-       cq_vaddress = mem_descr->mem_array[idx].virtual_address;
-       ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
-                           sizeof(struct sol_cqe), cq_vaddress);
-       if (ret) {
-               shost_printk(KERN_ERR, phba->shost,
-                            "be_fill_queue Failed for ISCSI CQ \n");
-               return ret;
-       }
+       num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
+                                     sizeof(struct sol_cqe));
 
-       mem->dma = mem_descr->mem_array[idx].bus_address.u.a64.address;
-       ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false, false, 0);
-       if (ret) {
-               shost_printk(KERN_ERR, phba->shost,
-                            "beiscsi_cmd_eq_create Failed for ISCSI CQ \n");
-               return ret;
+       for (i = 0; i < phba->num_cpus; i++) {
+               cq = &phwi_context->be_cq[i];
+               eq = &phwi_context->be_eq[i].q;
+               pbe_eq = &phwi_context->be_eq[i];
+               pbe_eq->cq = cq;
+               pbe_eq->phba = phba;
+               mem = &cq->dma_mem;
+               cq_vaddress = pci_alloc_consistent(phba->pcidev,
+                                                    num_cq_pages * PAGE_SIZE,
+                                                    &paddr);
+               if (!cq_vaddress)
+                       goto create_cq_error;
+               ret = be_fill_queue(cq, phba->params.icds_per_ctrl / 2,
+                                   sizeof(struct sol_cqe), cq_vaddress);
+               if (ret) {
+                       shost_printk(KERN_ERR, phba->shost,
+                                    "be_fill_queue Failed for ISCSI CQ \n");
+                       goto create_cq_error;
+               }
+
+               mem->dma = paddr;
+               ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false,
+                                           false, 0);
+               if (ret) {
+                       shost_printk(KERN_ERR, phba->shost,
+                                    "beiscsi_cmd_eq_create"
+                                    "Failed for ISCSI CQ \n");
+                       goto create_cq_error;
+               }
+               SE_DEBUG(DBG_LVL_8, "iscsi cq_id is %d for eq_id %d\n",
+                                                cq->id, eq->id);
+               SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
        }
-       SE_DEBUG(DBG_LVL_8, "iscsi cq id is %d\n", phwi_context->be_cq.id);
-       SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
        return 0;
+
+create_cq_error:
+       for (i = 0; i < phba->num_cpus; i++) {
+               cq = &phwi_context->be_cq[i];
+               mem = &cq->dma_mem;
+               if (mem->va)
+                       pci_free_consistent(phba->pcidev, num_cq_pages
+                                           * PAGE_SIZE,
+                                           mem->va, mem->dma);
+       }
+       return ret;
+
 }
 
 static int
@@ -2132,7 +2434,7 @@ beiscsi_create_def_hdr(struct beiscsi_hba *phba,
 
        idx = 0;
        dq = &phwi_context->be_def_hdrq;
-       cq = &phwi_context->be_cq;
+       cq = &phwi_context->be_cq[0];
        mem = &dq->dma_mem;
        mem_descr = phba->init_mem;
        mem_descr += HWI_MEM_ASYNC_HEADER_RING;
@@ -2176,7 +2478,7 @@ beiscsi_create_def_data(struct beiscsi_hba *phba,
 
        idx = 0;
        dataq = &phwi_context->be_def_dataq;
-       cq = &phwi_context->be_cq;
+       cq = &phwi_context->be_cq[0];
        mem = &dataq->dma_mem;
        mem_descr = phba->init_mem;
        mem_descr += HWI_MEM_ASYNC_DATA_RING;
@@ -2239,6 +2541,30 @@ beiscsi_post_pages(struct beiscsi_hba *phba)
        return 0;
 }
 
+static void be_queue_free(struct beiscsi_hba *phba, struct be_queue_info *q)
+{
+       struct be_dma_mem *mem = &q->dma_mem;
+       if (mem->va)
+               pci_free_consistent(phba->pcidev, mem->size,
+                       mem->va, mem->dma);
+}
+
+static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q,
+               u16 len, u16 entry_size)
+{
+       struct be_dma_mem *mem = &q->dma_mem;
+
+       memset(q, 0, sizeof(*q));
+       q->len = len;
+       q->entry_size = entry_size;
+       mem->size = len * entry_size;
+       mem->va = pci_alloc_consistent(phba->pcidev, mem->size, &mem->dma);
+       if (!mem->va)
+               return -1;
+       memset(mem->va, 0, mem->size);
+       return 0;
+}
+
 static int
 beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
                         struct hwi_context_memory *phwi_context,
@@ -2328,13 +2654,29 @@ static void free_wrb_handles(struct beiscsi_hba *phba)
        }
 }
 
+static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
+{
+       struct be_queue_info *q;
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+
+       q = &phba->ctrl.mcc_obj.q;
+       if (q->created)
+               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
+       be_queue_free(phba, q);
+
+       q = &phba->ctrl.mcc_obj.cq;
+       if (q->created)
+               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+       be_queue_free(phba, q);
+}
+
 static void hwi_cleanup(struct beiscsi_hba *phba)
 {
        struct be_queue_info *q;
        struct be_ctrl_info *ctrl = &phba->ctrl;
        struct hwi_controller *phwi_ctrlr;
        struct hwi_context_memory *phwi_context;
-       int i;
+       int i, eq_num;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        phwi_context = phwi_ctrlr->phwi_ctxt;
@@ -2343,7 +2685,6 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
                if (q->created)
                        beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
        }
-
        free_wrb_handles(phba);
 
        q = &phwi_context->be_def_hdrq;
@@ -2356,13 +2697,76 @@ static void hwi_cleanup(struct beiscsi_hba *phba)
 
        beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
 
-       q = &phwi_context->be_cq;
-       if (q->created)
-               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+       for (i = 0; i < (phba->num_cpus); i++) {
+               q = &phwi_context->be_cq[i];
+               if (q->created)
+                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
+       }
+       if (phba->msix_enabled)
+               eq_num = 1;
+       else
+               eq_num = 0;
+       for (i = 0; i < (phba->num_cpus + eq_num); i++) {
+               q = &phwi_context->be_eq[i].q;
+               if (q->created)
+                       beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+       }
+       be_mcc_queues_destroy(phba);
+}
 
-       q = &phwi_context->be_eq.q;
-       if (q->created)
-               beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
+static int be_mcc_queues_create(struct beiscsi_hba *phba,
+                               struct hwi_context_memory *phwi_context)
+{
+       struct be_queue_info *q, *cq;
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+
+       /* Alloc MCC compl queue */
+       cq = &phba->ctrl.mcc_obj.cq;
+       if (be_queue_alloc(phba, cq, MCC_CQ_LEN,
+                       sizeof(struct be_mcc_compl)))
+               goto err;
+       /* Ask BE to create MCC compl queue; */
+       if (phba->msix_enabled) {
+               if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq
+                                        [phba->num_cpus].q, false, true, 0))
+               goto mcc_cq_free;
+       } else {
+               if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq[0].q,
+                                         false, true, 0))
+               goto mcc_cq_free;
+       }
+
+       /* Alloc MCC queue */
+       q = &phba->ctrl.mcc_obj.q;
+       if (be_queue_alloc(phba, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb)))
+               goto mcc_cq_destroy;
+
+       /* Ask BE to create MCC queue */
+       if (beiscsi_cmd_mccq_create(phba, q, cq))
+               goto mcc_q_free;
+
+       return 0;
+
+mcc_q_free:
+       be_queue_free(phba, q);
+mcc_cq_destroy:
+       beiscsi_cmd_q_destroy(ctrl, cq, QTYPE_CQ);
+mcc_cq_free:
+       be_queue_free(phba, cq);
+err:
+       return -1;
+}
+
+static int find_num_cpus(void)
+{
+       int  num_cpus = 0;
+
+       num_cpus = num_online_cpus();
+       if (num_cpus >= MAX_CPUS)
+               num_cpus = MAX_CPUS - 1;
+
+       SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", num_cpus);
+       return num_cpus;
 }
 
 static int hwi_init_port(struct beiscsi_hba *phba)
@@ -2376,26 +2780,33 @@ static int hwi_init_port(struct beiscsi_hba *phba)
        def_pdu_ring_sz =
                phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr);
        phwi_ctrlr = phba->phwi_ctrlr;
-
        phwi_context = phwi_ctrlr->phwi_ctxt;
-       phwi_context->be_eq.max_eqd = 0;
-       phwi_context->be_eq.min_eqd = 0;
-       phwi_context->be_eq.cur_eqd = 64;
-       phwi_context->be_eq.enable_aic = false;
+       phwi_context->max_eqd = 0;
+       phwi_context->min_eqd = 0;
+       phwi_context->cur_eqd = 64;
        be_cmd_fw_initialize(&phba->ctrl);
-       status = beiscsi_create_eq(phba, phwi_context);
+
+       status = beiscsi_create_eqs(phba, phwi_context);
        if (status != 0) {
                shost_printk(KERN_ERR, phba->shost, "EQ not created \n");
                goto error;
        }
 
-       status = mgmt_check_supported_fw(ctrl);
+       status = be_mcc_queues_create(phba, phwi_context);
+       if (status != 0)
+               goto error;
+
+       status = mgmt_check_supported_fw(ctrl, phba);
        if (status != 0) {
                shost_printk(KERN_ERR, phba->shost,
                             "Unsupported fw version \n");
                goto error;
        }
 
+       if (phba->fw_config.iscsi_features == 0x1)
+               ring_mode = 1;
+       else
+               ring_mode = 0;
        status = mgmt_get_fw_config(ctrl, phba);
        if (status != 0) {
                shost_printk(KERN_ERR, phba->shost,
@@ -2403,7 +2814,7 @@ static int hwi_init_port(struct beiscsi_hba *phba)
                goto error;
        }
 
-       status = beiscsi_create_cq(phba, phwi_context);
+       status = beiscsi_create_cqs(phba, phwi_context);
        if (status != 0) {
                shost_printk(KERN_ERR, phba->shost, "CQ not created\n");
                goto error;
@@ -2447,7 +2858,6 @@ error:
        return -ENOMEM;
 }
 
-
 static int hwi_init_controller(struct beiscsi_hba *phba)
 {
        struct hwi_controller *phwi_ctrlr;
@@ -2530,6 +2940,18 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
 
        phba->io_sgl_hndl_avbl = 0;
        phba->eh_sgl_hndl_avbl = 0;
+
+       if (ring_mode) {
+               phba->sgl_hndl_array = kzalloc(sizeof(struct sgl_handle *) *
+                                             phba->params.icds_per_ctrl,
+                                                GFP_KERNEL);
+               if (!phba->sgl_hndl_array) {
+                       shost_printk(KERN_ERR, phba->shost,
+                            "Mem Alloc Failed. Failing to load\n");
+                       return -ENOMEM;
+               }
+       }
+
        mem_descr_sglh = phba->init_mem;
        mem_descr_sglh += HWI_MEM_SGLH;
        if (1 == mem_descr_sglh->num_elements) {
@@ -2537,6 +2959,8 @@ static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
                                                 phba->params.ios_per_ctrl,
                                                 GFP_KERNEL);
                if (!phba->io_sgl_hndl_base) {
+                       if (ring_mode)
+                               kfree(phba->sgl_hndl_array);
                        shost_printk(KERN_ERR, phba->shost,
                                     "Mem Alloc Failed. Failing to load\n");
                        return -ENOMEM;
@@ -2656,13 +3080,12 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba)
        struct hwi_context_memory *phwi_context;
        struct be_queue_info *eq;
        u8 __iomem *addr;
-       u32 reg;
+       u32 reg, i;
        u32 enabled;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        phwi_context = phwi_ctrlr->phwi_ctxt;
 
-       eq = &phwi_context->be_eq.q;
        addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg +
                        PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET);
        reg = ioread32(addr);
@@ -2673,9 +3096,11 @@ static unsigned char hwi_enable_intr(struct beiscsi_hba *phba)
                reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
                SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p \n", reg, addr);
                iowrite32(reg, addr);
-               SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id);
-
-               hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+               for (i = 0; i <= phba->num_cpus; i++) {
+                       eq = &phwi_context->be_eq[i].q;
+                       SE_DEBUG(DBG_LVL_8, "eq->id=%d \n", eq->id);
+                       hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
+               }
        } else
                shost_printk(KERN_WARNING, phba->shost,
                             "In hwi_enable_intr, Not Enabled \n");
@@ -2720,6 +3145,8 @@ static int beiscsi_init_port(struct beiscsi_hba *phba)
        if (hba_setup_cid_tbls(phba)) {
                shost_printk(KERN_ERR, phba->shost,
                             "Failed in hba_setup_cid_tbls\n");
+               if (ring_mode)
+                       kfree(phba->sgl_hndl_array);
                kfree(phba->io_sgl_hndl_base);
                kfree(phba->eh_sgl_hndl_base);
                goto do_cleanup_ctrlr;
@@ -2738,17 +3165,25 @@ static void hwi_purge_eq(struct beiscsi_hba *phba)
        struct hwi_context_memory *phwi_context;
        struct be_queue_info *eq;
        struct be_eq_entry *eqe = NULL;
+       int i, eq_msix;
 
        phwi_ctrlr = phba->phwi_ctrlr;
        phwi_context = phwi_ctrlr->phwi_ctxt;
-       eq = &phwi_context->be_eq.q;
-       eqe = queue_tail_node(eq);
+       if (phba->msix_enabled)
+               eq_msix = 1;
+       else
+               eq_msix = 0;
 
-       while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
-                                               & EQE_VALID_MASK) {
-               AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
-               queue_tail_inc(eq);
+       for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
+               eq = &phwi_context->be_eq[i].q;
                eqe = queue_tail_node(eq);
+
+               while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
+                                       & EQE_VALID_MASK) {
+                       AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
+                       queue_tail_inc(eq);
+                       eqe = queue_tail_node(eq);
+               }
        }
 }
 
@@ -2762,6 +3197,8 @@ static void beiscsi_clean_port(struct beiscsi_hba *phba)
                             "mgmt_epfw_cleanup FAILED \n");
        hwi_cleanup(phba);
        hwi_purge_eq(phba);
+       if (ring_mode)
+               kfree(phba->sgl_hndl_array);
        kfree(phba->io_sgl_hndl_base);
        kfree(phba->eh_sgl_hndl_base);
        kfree(phba->cid_array);
@@ -2846,8 +3283,9 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
        be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb));
 
        doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
-       doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK) <<
-                                       DB_DEF_PDU_WRB_INDEX_SHIFT;
+       if (!ring_mode)
+               doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK)
+                            << DB_DEF_PDU_WRB_INDEX_SHIFT;
        doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
 
        iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
@@ -2856,7 +3294,7 @@ beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
 static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt,
                              int *index, int *age)
 {
-       *index = be32_to_cpu(itt) >> 16;
+       *index = (int)itt;
        if (age)
                *age = conn->session->age;
 }
@@ -2885,15 +3323,13 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
 
        io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool,
                                          GFP_KERNEL, &paddr);
-
        if (!io_task->cmd_bhs)
                return -ENOMEM;
-
        io_task->bhs_pa.u.a64.address = paddr;
+       io_task->libiscsi_itt = (itt_t)task->itt;
        io_task->pwrb_handle = alloc_wrb_handle(phba,
                                                beiscsi_conn->beiscsi_conn_cid,
                                                task->itt);
-       io_task->pwrb_handle->pio_handle = task;
        io_task->conn = beiscsi_conn;
 
        task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr;
@@ -2905,7 +3341,6 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
                spin_unlock(&phba->io_sgl_lock);
                if (!io_task->psgl_handle)
                        goto free_hndls;
-
        } else {
                io_task->scsi_cmnd = NULL;
                if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
@@ -2932,8 +3367,18 @@ static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
                                goto free_hndls;
                }
        }
-       itt = (itt_t) cpu_to_be32(((unsigned int)task->itt << 16) |
-                       (unsigned int)(io_task->psgl_handle->sgl_index));
+       itt = (itt_t) cpu_to_be32(((unsigned int)io_task->pwrb_handle->
+                                wrb_index << 16) | (unsigned int)
+                               (io_task->psgl_handle->sgl_index));
+       if (ring_mode) {
+               phba->sgl_hndl_array[io_task->psgl_handle->sgl_index -
+                                    phba->fw_config.iscsi_cid_start] =
+                                    io_task->psgl_handle;
+               io_task->psgl_handle->task = task;
+               io_task->psgl_handle->cid = beiscsi_conn->beiscsi_conn_cid;
+       } else
+               io_task->pwrb_handle->pio_handle = task;
+
        io_task->cmd_bhs->iscsi_hdr.itt = itt;
        return 0;
 
@@ -3006,7 +3451,6 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
        io_task->bhs_len = sizeof(struct be_cmd_bhs);
 
        if (writedir) {
-               SE_DEBUG(DBG_LVL_4, " WRITE Command \t");
                memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48);
                AMAP_SET_BITS(struct amap_pdu_data_out, itt,
                              &io_task->cmd_bhs->iscsi_data_pdu,
@@ -3016,11 +3460,18 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
                              ISCSI_OPCODE_SCSI_DATA_OUT);
                AMAP_SET_BITS(struct amap_pdu_data_out, final_bit,
                              &io_task->cmd_bhs->iscsi_data_pdu, 1);
-               AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD);
+               if (ring_mode)
+                       io_task->psgl_handle->type = INI_WR_CMD;
+               else
+                       AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+                                     INI_WR_CMD);
                AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
        } else {
-               SE_DEBUG(DBG_LVL_4, "READ Command \t");
-               AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD);
+               if (ring_mode)
+                       io_task->psgl_handle->type = INI_RD_CMD;
+               else
+                       AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+                                     INI_RD_CMD);
                AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
        }
        memcpy(&io_task->cmd_bhs->iscsi_data_pdu.
@@ -3045,7 +3496,8 @@ static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
        be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
 
        doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
-       doorbell |= (io_task->pwrb_handle->wrb_index &
+       if (!ring_mode)
+               doorbell |= (io_task->pwrb_handle->wrb_index &
                     DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
        doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
 
@@ -3059,10 +3511,16 @@ static int beiscsi_mtask(struct iscsi_task *task)
        struct iscsi_conn *conn = task->conn;
        struct beiscsi_conn *beiscsi_conn = conn->dd_data;
        struct beiscsi_hba *phba = beiscsi_conn->phba;
+       struct iscsi_session *session;
        struct iscsi_wrb *pwrb = NULL;
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_wrb_context *pwrb_context;
+       struct wrb_handle *pwrb_handle;
        unsigned int doorbell = 0;
+       unsigned int i, cid;
        struct iscsi_task *aborted_task;
 
+       cid = beiscsi_conn->beiscsi_conn_cid;
        pwrb = io_task->pwrb_handle->pwrb;
        AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
                      be32_to_cpu(task->cmdsn));
@@ -3073,38 +3531,63 @@ static int beiscsi_mtask(struct iscsi_task *task)
 
        switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
        case ISCSI_OP_LOGIN:
-               AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, TGT_DM_CMD);
+               if (ring_mode)
+                       io_task->psgl_handle->type = TGT_DM_CMD;
+               else
+                       AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+                                     TGT_DM_CMD);
                AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
                AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1);
                hwi_write_buffer(pwrb, task);
                break;
        case ISCSI_OP_NOOP_OUT:
-               AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_RD_CMD);
+               if (ring_mode)
+                       io_task->psgl_handle->type = INI_RD_CMD;
+               else
+                       AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+                                     INI_RD_CMD);
                hwi_write_buffer(pwrb, task);
                break;
        case ISCSI_OP_TEXT:
-               AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_WR_CMD);
+               if (ring_mode)
+                       io_task->psgl_handle->type = INI_WR_CMD;
+               else
+                       AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+                                     INI_WR_CMD);
                AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
                hwi_write_buffer(pwrb, task);
                break;
        case ISCSI_OP_SCSI_TMFUNC:
-               aborted_task = iscsi_itt_to_task(conn,
-                                       ((struct iscsi_tm *)task->hdr)->rtt);
+               session = conn->session;
+               i = ((struct iscsi_tm *)task->hdr)->rtt;
+               phwi_ctrlr = phba->phwi_ctrlr;
+               pwrb_context = &phwi_ctrlr->wrb_context[cid];
+               pwrb_handle = pwrb_context->pwrb_handle_basestd[be32_to_cpu(i)
+                                                               >> 16];
+               aborted_task = pwrb_handle->pio_handle;
                 if (!aborted_task)
                        return 0;
+
                aborted_io_task = aborted_task->dd_data;
                if (!aborted_io_task->scsi_cmnd)
                        return 0;
 
                mgmt_invalidate_icds(phba,
                                     aborted_io_task->psgl_handle->sgl_index,
-                                    beiscsi_conn->beiscsi_conn_cid);
-               AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb, INI_TMF_CMD);
+                                    cid);
+               if (ring_mode)
+                       io_task->psgl_handle->type = INI_TMF_CMD;
+               else
+                       AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
+                                     INI_TMF_CMD);
                AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
                hwi_write_buffer(pwrb, task);
                break;
        case ISCSI_OP_LOGOUT:
                AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
+               if (ring_mode)
+                       io_task->psgl_handle->type = HWH_TYPE_LOGOUT;
+               else
                AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
                                HWH_TYPE_LOGOUT);
                hwi_write_buffer(pwrb, task);
@@ -3122,8 +3605,9 @@ static int beiscsi_mtask(struct iscsi_task *task)
                      io_task->pwrb_handle->nxt_wrb_index);
        be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
 
-       doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
-       doorbell |= (io_task->pwrb_handle->wrb_index &
+       doorbell |= cid & DB_WRB_POST_CID_MASK;
+       if (!ring_mode)
+               doorbell |= (io_task->pwrb_handle->wrb_index &
                     DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
        doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
        iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
@@ -3165,9 +3649,14 @@ static int beiscsi_task_xmit(struct iscsi_task *task)
        return beiscsi_iotask(task, sg, num_sg, xferlen, writedir);
 }
 
+
 static void beiscsi_remove(struct pci_dev *pcidev)
 {
        struct beiscsi_hba *phba = NULL;
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_context_memory *phwi_context;
+       struct be_eq_obj *pbe_eq;
+       unsigned int i, msix_vec;
 
        phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev);
        if (!phba) {
@@ -3175,12 +3664,24 @@ static void beiscsi_remove(struct pci_dev *pcidev)
                return;
        }
 
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
        hwi_disable_intr(phba);
-       if (phba->pcidev->irq)
-               free_irq(phba->pcidev->irq, phba);
+       if (phba->msix_enabled) {
+               for (i = 0; i <= phba->num_cpus; i++) {
+                       msix_vec = phba->msix_entries[i].vector;
+                       free_irq(msix_vec, &phwi_context->be_eq[i]);
+               }
+       } else
+               if (phba->pcidev->irq)
+                       free_irq(phba->pcidev->irq, phba);
+       pci_disable_msix(phba->pcidev);
        destroy_workqueue(phba->wq);
        if (blk_iopoll_enabled)
-               blk_iopoll_disable(&phba->iopoll);
+               for (i = 0; i < phba->num_cpus; i++) {
+                       pbe_eq = &phwi_context->be_eq[i];
+                       blk_iopoll_disable(&pbe_eq->iopoll);
+               }
 
        beiscsi_clean_port(phba);
        beiscsi_free_mem(phba);
@@ -3194,11 +3695,29 @@ static void beiscsi_remove(struct pci_dev *pcidev)
        iscsi_host_free(phba->shost);
 }
 
+static void beiscsi_msix_enable(struct beiscsi_hba *phba)
+{
+       int i, status;
+
+       for (i = 0; i <= phba->num_cpus; i++)
+               phba->msix_entries[i].entry = i;
+
+       status = pci_enable_msix(phba->pcidev, phba->msix_entries,
+                                (phba->num_cpus + 1));
+       if (!status)
+               phba->msix_enabled = true;
+
+       return;
+}
+
 static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
                                const struct pci_device_id *id)
 {
        struct beiscsi_hba *phba = NULL;
-       int ret;
+       struct hwi_controller *phwi_ctrlr;
+       struct hwi_context_memory *phwi_context;
+       struct be_eq_obj *pbe_eq;
+       int ret, msix_vec, num_cpus, i;
 
        ret = beiscsi_enable_pci(pcidev);
        if (ret < 0) {
@@ -3213,8 +3732,18 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
                        " Failed in beiscsi_hba_alloc \n");
                goto disable_pci;
        }
+       SE_DEBUG(DBG_LVL_8, " phba = %p \n", phba);
 
        pci_set_drvdata(pcidev, phba);
+       if (enable_msix)
+               num_cpus = find_num_cpus();
+       else
+               num_cpus = 1;
+       phba->num_cpus = num_cpus;
+       SE_DEBUG(DBG_LVL_8, "num_cpus = %d \n", phba->num_cpus);
+
+       if (enable_msix)
+               beiscsi_msix_enable(phba);
        ret = be_ctrl_init(phba, pcidev);
        if (ret) {
                shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3235,7 +3764,7 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
 
        snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
                 phba->shost->host_no);
-       phba->wq = create_singlethread_workqueue(phba->wq_name);
+       phba->wq = create_workqueue(phba->wq_name);
        if (!phba->wq) {
                shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
                                "Failed to allocate work queue\n");
@@ -3244,11 +3773,16 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
 
        INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs);
 
+       phwi_ctrlr = phba->phwi_ctrlr;
+       phwi_context = phwi_ctrlr->phwi_ctxt;
        if (blk_iopoll_enabled) {
-               blk_iopoll_init(&phba->iopoll, be_iopoll_budget, be_iopoll);
-               blk_iopoll_enable(&phba->iopoll);
+               for (i = 0; i < phba->num_cpus; i++) {
+                       pbe_eq = &phwi_context->be_eq[i];
+                       blk_iopoll_init(&pbe_eq->iopoll, be_iopoll_budget,
+                                       be_iopoll);
+                       blk_iopoll_enable(&pbe_eq->iopoll);
+               }
        }
-
        ret = beiscsi_init_irqs(phba);
        if (ret < 0) {
                shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
@@ -3261,17 +3795,26 @@ static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
                             "Failed to hwi_enable_intr\n");
                goto free_ctrlr;
        }
-
        SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED \n\n\n");
        return 0;
 
 free_ctrlr:
-       if (phba->pcidev->irq)
-               free_irq(phba->pcidev->irq, phba);
+       if (phba->msix_enabled) {
+               for (i = 0; i <= phba->num_cpus; i++) {
+                       msix_vec = phba->msix_entries[i].vector;
+                       free_irq(msix_vec, &phwi_context->be_eq[i]);
+               }
+       } else
+               if (phba->pcidev->irq)
+                       free_irq(phba->pcidev->irq, phba);
+       pci_disable_msix(phba->pcidev);
 free_blkenbld:
        destroy_workqueue(phba->wq);
        if (blk_iopoll_enabled)
-               blk_iopoll_disable(&phba->iopoll);
+               for (i = 0; i < phba->num_cpus; i++) {
+                       pbe_eq = &phwi_context->be_eq[i];
+                       blk_iopoll_disable(&pbe_eq->iopoll);
+               }
 free_twq:
        beiscsi_clean_port(phba);
        beiscsi_free_mem(phba);
@@ -3316,7 +3859,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
                ISCSI_USERNAME | ISCSI_PASSWORD |
                ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
                ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
-               ISCSI_LU_RESET_TMO |
+               ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
                ISCSI_PING_TMO | ISCSI_RECV_TMO |
                ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
        .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
@@ -3351,6 +3894,7 @@ static struct pci_driver beiscsi_pci_driver = {
        .id_table = beiscsi_pci_id_table
 };
 
+
 static int __init beiscsi_module_init(void)
 {
        int ret;
@@ -3373,6 +3917,7 @@ static int __init beiscsi_module_init(void)
                         "beiscsi pci driver.\n");
                goto unregister_iscsi_transport;
        }
+       ring_mode = 0;
        return 0;
 
 unregister_iscsi_transport:
index 53c9b70..25e6b20 100644 (file)
 #ifndef _BEISCSI_MAIN_
 #define _BEISCSI_MAIN_
 
-
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/in.h>
-#include <linux/blk-iopoll.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_transport_iscsi.h>
 
 #include "be.h"
-
-
-
 #define DRV_NAME               "be2iscsi"
 #define BUILD_STR              "2.0.527.0"
-
 #define BE_NAME                        "ServerEngines BladeEngine2" \
                                "Linux iSCSI Driver version" BUILD_STR
 #define DRV_DESC               BE_NAME " " "Driver"
@@ -49,6 +43,8 @@
 #define BE_DEVICE_ID1          0x212
 #define OC_DEVICE_ID1          0x702
 #define OC_DEVICE_ID2          0x703
+#define OC_DEVICE_ID3          0x712
+#define OC_DEVICE_ID4          0x222
 
 #define BE2_MAX_SESSIONS       64
 #define BE2_CMDS_PER_CXN       128
@@ -63,6 +59,7 @@
 #define BE2_IO_DEPTH \
        (BE2_MAX_ICDS / 2 - (BE2_LOGOUTS + BE2_TMFS + BE2_NOPOUT_REQ))
 
+#define MAX_CPUS               31
 #define BEISCSI_SGLIST_ELEMENTS        BE2_SGE
 
 #define BEISCSI_MAX_CMNDS      1024    /* Max IO's per Ctrlr sht->can_queue */
@@ -79,7 +76,7 @@
 #define BE_SENSE_INFO_SIZE             258
 #define BE_ISCSI_PDU_HEADER_SIZE       64
 #define BE_MIN_MEM_SIZE                        16384
-
+#define MAX_CMD_SZ                     65536
 #define IIOC_SCSI_DATA                  0x05   /* Write Operation */
 
 #define DBG_LVL                                0x00000001
@@ -100,6 +97,8 @@ do {                                                 \
        }                                               \
 } while (0);
 
+#define BE_ADAPTER_UP          0x00000000
+#define BE_ADAPTER_LINK_DOWN   0x00000001
 /**
  * hardware needs the async PDU buffers to be posted in multiples of 8
  * So have atleast 8 of them by default
@@ -160,21 +159,19 @@ do {                                                      \
 
 enum be_mem_enum {
        HWI_MEM_ADDN_CONTEXT,
-       HWI_MEM_CQ,
-       HWI_MEM_EQ,
        HWI_MEM_WRB,
        HWI_MEM_WRBH,
-       HWI_MEM_SGLH,   /* 5 */
+       HWI_MEM_SGLH,
        HWI_MEM_SGE,
-       HWI_MEM_ASYNC_HEADER_BUF,
+       HWI_MEM_ASYNC_HEADER_BUF,       /* 5 */
        HWI_MEM_ASYNC_DATA_BUF,
        HWI_MEM_ASYNC_HEADER_RING,
-       HWI_MEM_ASYNC_DATA_RING,        /* 10 */
+       HWI_MEM_ASYNC_DATA_RING,
        HWI_MEM_ASYNC_HEADER_HANDLE,
-       HWI_MEM_ASYNC_DATA_HANDLE,
+       HWI_MEM_ASYNC_DATA_HANDLE,      /* 10 */
        HWI_MEM_ASYNC_PDU_CONTEXT,
        ISCSI_MEM_GLOBAL_HEADER,
-       SE_MEM_MAX      /* 15 */
+       SE_MEM_MAX
 };
 
 struct be_bus_address32 {
@@ -212,6 +209,9 @@ struct be_mem_descriptor {
 
 struct sgl_handle {
        unsigned int sgl_index;
+       unsigned int type;
+       unsigned int cid;
+       struct iscsi_task *task;
        struct iscsi_sge *pfrag;
 };
 
@@ -274,13 +274,17 @@ struct beiscsi_hba {
        struct pci_dev *pcidev;
        unsigned int state;
        unsigned short asic_revision;
-       struct blk_iopoll       iopoll;
+       unsigned int num_cpus;
+       unsigned int nxt_cqid;
+       struct msix_entry msix_entries[MAX_CPUS + 1];
+       bool msix_enabled;
        struct be_mem_descriptor *init_mem;
 
        unsigned short io_sgl_alloc_index;
        unsigned short io_sgl_free_index;
        unsigned short io_sgl_hndl_avbl;
        struct sgl_handle **io_sgl_hndl_base;
+       struct sgl_handle **sgl_hndl_array;
 
        unsigned short eh_sgl_alloc_index;
        unsigned short eh_sgl_free_index;
@@ -315,6 +319,7 @@ struct beiscsi_hba {
                unsigned short cid_alloc;
                unsigned short cid_free;
                unsigned short avlbl_cids;
+               unsigned short iscsi_features;
                spinlock_t cid_lock;
        } fw_config;
 
@@ -343,6 +348,7 @@ struct beiscsi_conn {
        unsigned short login_in_progress;
        struct sgl_handle *plogin_sgl_handle;
        struct beiscsi_session *beiscsi_sess;
+       struct iscsi_task *task;
 };
 
 /* This structure is used by the chip */
@@ -390,7 +396,7 @@ struct beiscsi_io_task {
        unsigned int flags;
        unsigned short cid;
        unsigned short header_len;
-
+       itt_t libiscsi_itt;
        struct be_cmd_bhs *cmd_bhs;
        struct be_bus_address bhs_pa;
        unsigned short bhs_len;
@@ -599,7 +605,6 @@ struct amap_cq_db {
 
 void beiscsi_process_eq(struct beiscsi_hba *phba);
 
-
 struct iscsi_wrb {
        u32 dw[16];
 } __packed;
@@ -820,10 +825,12 @@ struct wrb_handle {
 };
 
 struct hwi_context_memory {
-       struct be_eq_obj be_eq;
-       struct be_queue_info be_cq;
-       struct be_queue_info be_mcc_cq;
-       struct be_queue_info be_mcc;
+       /* Adaptive interrupt coalescing (AIC) info */
+       u16 min_eqd;            /* in usecs */
+       u16 max_eqd;            /* in usecs */
+       u16 cur_eqd;            /* in usecs */
+       struct be_eq_obj be_eq[MAX_CPUS];
+       struct be_queue_info be_cq[MAX_CPUS];
 
        struct be_queue_info be_def_hdrq;
        struct be_queue_info be_def_dataq;
index 12e644f..79c2bd5 100644 (file)
@@ -35,7 +35,6 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
 
        be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
                           OPCODE_COMMON_QUERY_FIRMWARE_CONFIG, sizeof(*req));
-
        status = be_mbox_notify(ctrl);
        if (!status) {
                struct be_fw_cfg *pfw_cfg;
@@ -58,7 +57,8 @@ unsigned char mgmt_get_fw_config(struct be_ctrl_info *ctrl,
        return status;
 }
 
-unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
+unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl,
+                                     struct beiscsi_hba *phba)
 {
        struct be_dma_mem nonemb_cmd;
        struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
@@ -85,7 +85,6 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
        sge->pa_hi = cpu_to_le32(upper_32_bits(nonemb_cmd.dma));
        sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
        sge->len = cpu_to_le32(nonemb_cmd.size);
-
        status = be_mbox_notify(ctrl);
        if (!status) {
                struct be_mgmt_controller_attributes_resp *resp = nonemb_cmd.va;
@@ -95,21 +94,25 @@ unsigned char mgmt_check_supported_fw(struct be_ctrl_info *ctrl)
                        resp->params.hba_attribs.firmware_version_string);
                SE_DEBUG(DBG_LVL_8,
                        "Developer Build, not performing version check...\n");
-
+               phba->fw_config.iscsi_features =
+                               resp->params.hba_attribs.iscsi_features;
+               SE_DEBUG(DBG_LVL_8, " phba->fw_config.iscsi_features = %d\n",
+                                     phba->fw_config.iscsi_features);
        } else
                SE_DEBUG(DBG_LVL_1, " Failed in mgmt_check_supported_fw\n");
+       spin_unlock(&ctrl->mbox_lock);
        if (nonemb_cmd.va)
                pci_free_consistent(ctrl->pdev, nonemb_cmd.size,
                                    nonemb_cmd.va, nonemb_cmd.dma);
 
-       spin_unlock(&ctrl->mbox_lock);
        return status;
 }
 
+
 unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
 {
        struct be_ctrl_info *ctrl = &phba->ctrl;
-       struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+       struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
        struct iscsi_cleanup_req *req = embedded_payload(wrb);
        int status = 0;
 
@@ -124,7 +127,7 @@ unsigned char mgmt_epfw_cleanup(struct beiscsi_hba *phba, unsigned short chute)
        req->hdr_ring_id = 0;
        req->data_ring_id = 0;
 
-       status = be_mbox_notify(ctrl);
+       status =  be_mcc_notify_wait(phba);
        if (status)
                shost_printk(KERN_WARNING, phba->shost,
                             " mgmt_epfw_cleanup , FAILED\n");
@@ -137,7 +140,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
 {
        struct be_dma_mem nonemb_cmd;
        struct be_ctrl_info *ctrl = &phba->ctrl;
-       struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+       struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
        struct be_sge *sge = nonembedded_sgl(wrb);
        struct invalidate_commands_params_in *req;
        int status = 0;
@@ -169,7 +172,7 @@ unsigned char mgmt_invalidate_icds(struct beiscsi_hba *phba,
        sge->pa_lo = cpu_to_le32(nonemb_cmd.dma & 0xFFFFFFFF);
        sge->len = cpu_to_le32(nonemb_cmd.size);
 
-       status = be_mbox_notify(ctrl);
+       status = be_mcc_notify_wait(phba);
        if (status)
                SE_DEBUG(DBG_LVL_1, "ICDS Invalidation Failed\n");
        spin_unlock(&ctrl->mbox_lock);
@@ -186,7 +189,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
                                         unsigned short savecfg_flag)
 {
        struct be_ctrl_info *ctrl = &phba->ctrl;
-       struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+       struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
        struct iscsi_invalidate_connection_params_in *req =
                                                embedded_payload(wrb);
        int status = 0;
@@ -205,7 +208,7 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
        else
                req->cleanup_type = CMD_ISCSI_CONNECTION_INVALIDATE;
        req->save_cfg = savecfg_flag;
-       status = be_mbox_notify(ctrl);
+       status =  be_mcc_notify_wait(phba);
        if (status)
                SE_DEBUG(DBG_LVL_1, "Invalidation Failed\n");
 
@@ -217,7 +220,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
                                unsigned short cid, unsigned int upload_flag)
 {
        struct be_ctrl_info *ctrl = &phba->ctrl;
-       struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+       struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
        struct tcp_upload_params_in *req = embedded_payload(wrb);
        int status = 0;
 
@@ -229,7 +232,7 @@ unsigned char mgmt_upload_connection(struct beiscsi_hba *phba,
                           OPCODE_COMMON_TCP_UPLOAD, sizeof(*req));
        req->id = (unsigned short)cid;
        req->upload_type = (unsigned char)upload_flag;
-       status = be_mbox_notify(ctrl);
+       status = be_mcc_notify_wait(phba);
        if (status)
                SE_DEBUG(DBG_LVL_1, "mgmt_upload_connection Failed\n");
        spin_unlock(&ctrl->mbox_lock);
@@ -245,13 +248,14 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
        struct sockaddr_in *daddr_in = (struct sockaddr_in *)dst_addr;
        struct sockaddr_in6 *daddr_in6 = (struct sockaddr_in6 *)dst_addr;
        struct be_ctrl_info *ctrl = &phba->ctrl;
-       struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem);
+       struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
        struct tcp_connect_and_offload_in *req = embedded_payload(wrb);
        unsigned short def_hdr_id;
        unsigned short def_data_id;
        struct phys_addr template_address = { 0, 0 };
        struct phys_addr *ptemplate_address;
        int status = 0;
+       unsigned int i;
        unsigned short cid = beiscsi_ep->ep_cid;
 
        phwi_ctrlr = phba->phwi_ctrlr;
@@ -296,14 +300,18 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
 
        }
        req->cid = cid;
-       req->cq_id = phwi_context->be_cq.id;
+       i = phba->nxt_cqid++;
+       if (phba->nxt_cqid == phba->num_cpus)
+               phba->nxt_cqid = 0;
+       req->cq_id = phwi_context->be_cq[i].id;
+       SE_DEBUG(DBG_LVL_8, "i=%d cq_id=%d \n", i, req->cq_id);
        req->defq_id = def_hdr_id;
        req->hdr_ring_id = def_hdr_id;
        req->data_ring_id = def_data_id;
        req->do_offload = 1;
        req->dataout_template_pa.lo = ptemplate_address->lo;
        req->dataout_template_pa.hi = ptemplate_address->hi;
-       status = be_mbox_notify(ctrl);
+       status = be_mcc_notify_wait(phba);
        if (!status) {
                struct iscsi_endpoint *ep;
                struct tcp_connect_and_offload_out *ptcpcnct_out =
@@ -311,7 +319,7 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
 
                ep = phba->ep_array[ptcpcnct_out->cid];
                beiscsi_ep = ep->dd_data;
-               beiscsi_ep->fw_handle = 0;
+               beiscsi_ep->fw_handle = ptcpcnct_out->connection_handle;
                beiscsi_ep->cid_vld = 1;
                SE_DEBUG(DBG_LVL_8, "mgmt_open_connection Success\n");
        } else
@@ -319,3 +327,30 @@ int mgmt_open_connection(struct beiscsi_hba *phba,
        spin_unlock(&ctrl->mbox_lock);
        return status;
 }
+
+int be_cmd_get_mac_addr(struct beiscsi_hba *phba, u8 *mac_addr)
+{
+       struct be_ctrl_info *ctrl = &phba->ctrl;
+       struct be_mcc_wrb *wrb = wrb_from_mccq(phba);
+       struct be_cmd_req_get_mac_addr *req = embedded_payload(wrb);
+       int status;
+
+       SE_DEBUG(DBG_LVL_8, "In be_cmd_get_mac_addr\n");
+       spin_lock(&ctrl->mbox_lock);
+       memset(wrb, 0, sizeof(*wrb));
+       be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0);
+       be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ISCSI,
+                          OPCODE_COMMON_ISCSI_NTWK_GET_NIC_CONFIG,
+                          sizeof(*req));
+
+       status = be_mcc_notify_wait(phba);
+       if (!status) {
+               struct be_cmd_resp_get_mac_addr *resp = embedded_payload(wrb);
+
+               memcpy(mac_addr, resp->mac_address, ETH_ALEN);
+       }
+
+       spin_unlock(&ctrl->mbox_lock);
+       return status;
+}
+
index 00e816e..24eaff9 100644 (file)
@@ -175,7 +175,9 @@ struct mgmt_hba_attributes {
        u8 phy_port;
        u32 firmware_post_status;
        u32 hba_mtu[8];
-       u32 future_u32[4];
+       u8 iscsi_features;
+       u8 future_u8[3];
+       u32 future_u32[3];
 } __packed;
 
 struct mgmt_controller_attributes {
@@ -246,4 +248,8 @@ unsigned char mgmt_invalidate_connection(struct beiscsi_hba *phba,
                                         unsigned short cid,
                                         unsigned short issue_reset,
                                         unsigned short savecfg_flag);
+
+unsigned char mgmt_fw_cmd(struct be_ctrl_info *ctrl,
+                         struct beiscsi_hba *phba,
+                         char *buf, unsigned int len);
 #endif
index 0050c83..961fe43 100644 (file)
@@ -51,7 +51,7 @@ bfad_int_to_lun(u32 luno)
        lun.bfa_lun     = 0;
        lun.scsi_lun[0] = bfa_os_htons(luno);
 
-       return (lun.bfa_lun);
+       return lun.bfa_lun;
 }
 
 /**
@@ -68,7 +68,7 @@ bfa_cb_ioim_get_cdb(struct bfad_ioim_s *dio)
 {
        struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
 
-       return ((u8 *) cmnd->cmnd);
+       return (u8 *) cmnd->cmnd;
 }
 
 /**
@@ -97,7 +97,7 @@ bfa_cb_ioim_get_size(struct bfad_ioim_s *dio)
 {
        struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
 
-       return (scsi_bufflen(cmnd));
+       return scsi_bufflen(cmnd);
 }
 
 /**
@@ -129,7 +129,7 @@ bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid)
        sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid;
        addr = (u64) sg_dma_address(sge);
 
-       return (*(union bfi_addr_u *) &addr);
+       return *((union bfi_addr_u *) &addr);
 }
 
 static inline u32
@@ -197,7 +197,7 @@ bfa_cb_ioim_get_cdblen(struct bfad_ioim_s *dio)
 {
        struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio;
 
-       return (cmnd->cmd_len);
+       return cmnd->cmd_len;
 }
 
 
index 7a959c3..2b91779 100644 (file)
@@ -228,7 +228,7 @@ bfa_cee_reset_stats_isr(struct bfa_cee_s *cee, bfa_status_t status)
 u32
 bfa_cee_meminfo(void)
 {
-       return (bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo());
+       return bfa_cee_attr_meminfo() + bfa_cee_stats_meminfo();
 }
 
 /**
index 1b71d34..caeb114 100644 (file)
@@ -47,12 +47,12 @@ bfa_q_is_on_q_func(struct list_head *q, struct list_head *qe)
        tqe = bfa_q_next(q);
        while (tqe != q) {
                if (tqe == qe)
-                       return (1);
+                       return 1;
                tqe = bfa_q_next(tqe);
                if (tqe == NULL)
                        break;
        }
-       return (0);
+       return 0;
 }
 
 
index 401babe..790c945 100644 (file)
@@ -131,7 +131,7 @@ bfa_fcpim_path_tov_get(struct bfa_s *bfa)
 {
        struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
 
-       return (fcpim->path_tov / 1000);
+       return fcpim->path_tov / 1000;
 }
 
 bfa_status_t
@@ -169,7 +169,7 @@ bfa_fcpim_qdepth_get(struct bfa_s *bfa)
 {
        struct bfa_fcpim_mod_s *fcpim = BFA_FCPIM_MOD(bfa);
 
-       return (fcpim->q_depth);
+       return fcpim->q_depth;
 }
 
 
index 153206c..5cf4184 100644 (file)
@@ -35,7 +35,7 @@
 #define BFA_FCPIM_PATHTOV_MAX  (90 * 1000)     /* in millisecs */
 
 #define bfa_fcpim_stats(__fcpim, __stats)   \
-    (__fcpim)->stats.__stats ++
+    ((__fcpim)->stats.__stats++)
 
 struct bfa_fcpim_mod_s {
        struct bfa_s    *bfa;
@@ -143,7 +143,7 @@ struct bfa_itnim_s {
        struct bfa_itnim_hal_stats_s    stats;
 };
 
-#define bfa_itnim_is_online(_itnim) (_itnim)->is_online
+#define bfa_itnim_is_online(_itnim) ((_itnim)->is_online)
 #define BFA_FCPIM_MOD(_hal) (&(_hal)->modules.fcpim_mod)
 #define BFA_IOIM_FROM_TAG(_fcpim, _iotag)      \
        (&fcpim->ioim_arr[_iotag])
index 9924359..aef648b 100644 (file)
@@ -388,32 +388,29 @@ bfa_pport_sm_linkup(struct bfa_pport_s *pport, enum bfa_pport_sm_event event)
                bfa_pport_callback(pport, BFA_PPORT_LINKDOWN);
                bfa_plog_str(pport->bfa->plog, BFA_PL_MID_HAL,
                             BFA_PL_EID_PORT_ST_CHANGE, 0, "Port Linkdown");
-               if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+               if (BFA_PORT_IS_DISABLED(pport->bfa))
                        bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
-               } else {
+               else
                        bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
-               }
                break;
 
        case BFA_PPORT_SM_STOP:
                bfa_sm_set_state(pport, bfa_pport_sm_stopped);
                bfa_pport_reset_linkinfo(pport);
-               if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+               if (BFA_PORT_IS_DISABLED(pport->bfa))
                        bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
-               } else {
+               else
                        bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
-               }
                break;
 
        case BFA_PPORT_SM_HWFAIL:
                bfa_sm_set_state(pport, bfa_pport_sm_iocdown);
                bfa_pport_reset_linkinfo(pport);
                bfa_pport_callback(pport, BFA_PPORT_LINKDOWN);
-               if (BFA_PORT_IS_DISABLED(pport->bfa)) {
+               if (BFA_PORT_IS_DISABLED(pport->bfa))
                        bfa_pport_aen_post(pport, BFA_PORT_AEN_OFFLINE);
-               } else {
+               else
                        bfa_pport_aen_post(pport, BFA_PORT_AEN_DISCONNECT);
-               }
                break;
 
        default:
@@ -999,10 +996,10 @@ bfa_pport_enable(struct bfa_s *bfa)
        struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
 
        if (pport->diag_busy)
-               return (BFA_STATUS_DIAG_BUSY);
+               return BFA_STATUS_DIAG_BUSY;
        else if (bfa_sm_cmp_state
                 (BFA_PORT_MOD(bfa), bfa_pport_sm_disabling_qwait))
-               return (BFA_STATUS_DEVBUSY);
+               return BFA_STATUS_DEVBUSY;
 
        bfa_sm_send_event(BFA_PORT_MOD(bfa), BFA_PPORT_SM_ENABLE);
        return BFA_STATUS_OK;
@@ -1032,7 +1029,7 @@ bfa_pport_cfg_speed(struct bfa_s *bfa, enum bfa_pport_speed speed)
 
        pport->cfg.speed = speed;
 
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 /**
@@ -1068,7 +1065,7 @@ bfa_pport_cfg_topology(struct bfa_s *bfa, enum bfa_pport_topology topology)
        }
 
        pport->cfg.topology = topology;
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 /**
@@ -1094,7 +1091,7 @@ bfa_pport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa)
        pport->cfg.cfg_hardalpa = BFA_TRUE;
        pport->cfg.hardalpa = alpa;
 
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 bfa_status_t
@@ -1106,7 +1103,7 @@ bfa_pport_clr_hardalpa(struct bfa_s *bfa)
        bfa_trc(bfa, pport->cfg.hardalpa);
 
        pport->cfg.cfg_hardalpa = BFA_FALSE;
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 bfa_boolean_t
@@ -1138,16 +1135,16 @@ bfa_pport_cfg_maxfrsize(struct bfa_s *bfa, u16 maxfrsize)
         * with in range
         */
        if ((maxfrsize > FC_MAX_PDUSZ) || (maxfrsize < FC_MIN_PDUSZ))
-               return (BFA_STATUS_INVLD_DFSZ);
+               return BFA_STATUS_INVLD_DFSZ;
 
        /*
         * power of 2, if not the max frame size of 2112
         */
        if ((maxfrsize != FC_MAX_PDUSZ) && (maxfrsize & (maxfrsize - 1)))
-               return (BFA_STATUS_INVLD_DFSZ);
+               return BFA_STATUS_INVLD_DFSZ;
 
        pport->cfg.maxfrsize = maxfrsize;
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 u16
@@ -1415,7 +1412,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
 
        if (port->stats_busy) {
                bfa_trc(bfa, port->stats_busy);
-               return (BFA_STATUS_DEVBUSY);
+               return BFA_STATUS_DEVBUSY;
        }
 
        port->stats_busy = BFA_TRUE;
@@ -1427,7 +1424,7 @@ bfa_pport_get_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
 
        bfa_timer_start(bfa, &port->timer, bfa_port_stats_timeout, port,
                        BFA_PORT_STATS_TOV);
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 bfa_status_t
@@ -1437,7 +1434,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
 
        if (port->stats_busy) {
                bfa_trc(bfa, port->stats_busy);
-               return (BFA_STATUS_DEVBUSY);
+               return BFA_STATUS_DEVBUSY;
        }
 
        port->stats_busy = BFA_TRUE;
@@ -1448,7 +1445,7 @@ bfa_pport_clear_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
 
        bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port,
                        BFA_PORT_STATS_TOV);
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 bfa_status_t
@@ -1515,7 +1512,7 @@ bfa_pport_get_qos_stats(struct bfa_s *bfa, union bfa_pport_stats_u *stats,
        /*
         * QoS stats is embedded in port stats
         */
-       return (bfa_pport_get_stats(bfa, stats, cbfn, cbarg));
+       return bfa_pport_get_stats(bfa, stats, cbfn, cbarg);
 }
 
 bfa_status_t
@@ -1525,7 +1522,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
 
        if (port->stats_busy) {
                bfa_trc(bfa, port->stats_busy);
-               return (BFA_STATUS_DEVBUSY);
+               return BFA_STATUS_DEVBUSY;
        }
 
        port->stats_busy = BFA_TRUE;
@@ -1536,7 +1533,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
 
        bfa_timer_start(bfa, &port->timer, bfa_port_stats_clr_timeout, port,
                        BFA_PORT_STATS_TOV);
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 /**
@@ -1545,7 +1542,7 @@ bfa_pport_clear_qos_stats(struct bfa_s *bfa, bfa_cb_pport_t cbfn, void *cbarg)
 bfa_status_t
 bfa_pport_trunk_disable(struct bfa_s *bfa)
 {
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 bfa_boolean_t
@@ -1562,8 +1559,8 @@ bfa_pport_is_disabled(struct bfa_s *bfa)
 {
        struct bfa_pport_s *port = BFA_PORT_MOD(bfa);
 
-       return (bfa_sm_to_state(hal_pport_sm_table, port->sm) ==
-               BFA_PPORT_ST_DISABLED);
+       return bfa_sm_to_state(hal_pport_sm_table, port->sm) ==
+               BFA_PPORT_ST_DISABLED;
 
 }
 
@@ -1572,7 +1569,7 @@ bfa_pport_is_ratelim(struct bfa_s *bfa)
 {
        struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
 
-return (pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE);
+       return pport->cfg.ratelimit ? BFA_TRUE : BFA_FALSE;
 
 }
 
@@ -1620,7 +1617,7 @@ bfa_pport_cfg_ratelim_speed(struct bfa_s *bfa, enum bfa_pport_speed speed)
 
        pport->cfg.trl_def_speed = speed;
 
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 /**
@@ -1632,7 +1629,7 @@ bfa_pport_get_ratelim_speed(struct bfa_s *bfa)
        struct bfa_pport_s *pport = BFA_PORT_MOD(bfa);
 
        bfa_trc(bfa, pport->cfg.trl_def_speed);
-       return (pport->cfg.trl_def_speed);
+       return pport->cfg.trl_def_speed;
 
 }
 
index 8975ed0..c7ab257 100644 (file)
@@ -568,11 +568,10 @@ bfa_fcs_port_offline_actions(struct bfa_fcs_port_s *port)
 
        __port_action[port->fabric->fab_type].offline(port);
 
-       if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE) {
+       if (bfa_fcs_fabric_is_online(port->fabric) == BFA_TRUE)
                bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_DISCONNECT);
-       } else {
+       else
                bfa_fcs_port_aen_post(port, BFA_LPORT_AEN_OFFLINE);
-       }
        bfa_fcb_port_offline(port->fcs->bfad, port->port_cfg.roles,
                        port->fabric->vf_drv,
                        (port->vport == NULL) ? NULL : port->vport->vport_drv);
@@ -777,7 +776,7 @@ bfa_fcs_port_get_rport_by_pwwn(struct bfa_fcs_port_s *port, wwn_t pwwn)
        }
 
        bfa_trc(port->fcs, pwwn);
-       return (NULL);
+       return NULL;
 }
 
 /**
@@ -796,7 +795,7 @@ bfa_fcs_port_get_rport_by_nwwn(struct bfa_fcs_port_s *port, wwn_t nwwn)
        }
 
        bfa_trc(port->fcs, nwwn);
-       return (NULL);
+       return NULL;
 }
 
 /**
@@ -870,7 +869,7 @@ bfa_fcs_port_lip(struct bfa_fcs_port_s *port)
 bfa_boolean_t
 bfa_fcs_port_is_online(struct bfa_fcs_port_s *port)
 {
-       return (bfa_sm_cmp_state(port, bfa_fcs_port_sm_online));
+       return bfa_sm_cmp_state(port, bfa_fcs_port_sm_online);
 }
 
 /**
index 4754a0e..cf0ad67 100644 (file)
@@ -199,7 +199,7 @@ bfa_fcxp_get(struct bfa_fcxp_mod_s *fm)
        if (fcxp)
                list_add_tail(&fcxp->qe, &fm->fcxp_active_q);
 
-       return (fcxp);
+       return fcxp;
 }
 
 static void
@@ -503,7 +503,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
 
        fcxp = bfa_fcxp_get(BFA_FCXP_MOD(bfa));
        if (fcxp == NULL)
-               return (NULL);
+               return NULL;
 
        bfa_trc(bfa, fcxp->fcxp_tag);
 
@@ -568,7 +568,7 @@ bfa_fcxp_alloc(void *caller, struct bfa_s *bfa, int nreq_sgles,
                }
        }
 
-       return (fcxp);
+       return fcxp;
 }
 
 /**
@@ -709,7 +709,7 @@ bfa_status_t
 bfa_fcxp_abort(struct bfa_fcxp_s *fcxp)
 {
        bfa_assert(0);
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 void
index 0ca1257..b36540e 100644 (file)
@@ -59,7 +59,7 @@ bfa_intx(struct bfa_s *bfa)
        qintr = intr & __HFN_INT_RME_MASK;
        bfa_reg_write(bfa->iocfc.bfa_regs.intr_status, qintr);
 
-       for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue ++) {
+       for (queue = 0; queue < BFI_IOC_MAX_CQS_ASIC; queue++) {
                if (intr & (__HFN_INT_RME_Q0 << queue))
                        bfa_msix_rspq(bfa, queue & (BFI_IOC_MAX_CQS - 1));
        }
index 8ce6e6b..5fc301c 100644 (file)
@@ -26,9 +26,9 @@ void bfa_isr_unhandled(struct bfa_s *bfa, struct bfi_msg_s *m);
 void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func);
 
 
-#define bfa_reqq_pi(__bfa, __reqq)     (__bfa)->iocfc.req_cq_pi[__reqq]
+#define bfa_reqq_pi(__bfa, __reqq)     ((__bfa)->iocfc.req_cq_pi[__reqq])
 #define bfa_reqq_ci(__bfa, __reqq)                                     \
-       *(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva)
+       (*(u32 *)((__bfa)->iocfc.req_cq_shadow_ci[__reqq].kva))
 
 #define bfa_reqq_full(__bfa, __reqq)                           \
        (((bfa_reqq_pi(__bfa, __reqq) + 1) &                    \
@@ -50,14 +50,16 @@ void bfa_isr_bind(enum bfi_mclass mc, bfa_isr_func_t isr_func);
 } while (0)
 
 #define bfa_rspq_pi(__bfa, __rspq)                                     \
-       *(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva)
+       (*(u32 *)((__bfa)->iocfc.rsp_cq_shadow_pi[__rspq].kva))
 
-#define bfa_rspq_ci(__bfa, __rspq)     (__bfa)->iocfc.rsp_cq_ci[__rspq]
+#define bfa_rspq_ci(__bfa, __rspq)     ((__bfa)->iocfc.rsp_cq_ci[__rspq])
 #define bfa_rspq_elem(__bfa, __rspq, __ci)                             \
-       &((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci]
+       (&((struct bfi_msg_s *)((__bfa)->iocfc.rsp_cq_ba[__rspq].kva))[__ci])
 
-#define CQ_INCR(__index, __size)                                       \
-                       (__index)++; (__index) &= ((__size) - 1)
+#define CQ_INCR(__index, __size) do {                                  \
+                       (__index)++;                                    \
+                       (__index) &= ((__size) - 1);      \
+} while (0)
 
 /**
  * Queue element to wait for room in request queue. FIFO order is
@@ -94,7 +96,7 @@ bfa_reqq_winit(struct bfa_reqq_wait_s *wqe, void (*qresume) (void *cbarg),
        wqe->cbarg = cbarg;
 }
 
-#define bfa_reqq(__bfa, __reqq)        &(__bfa)->reqq_waitq[__reqq]
+#define bfa_reqq(__bfa, __reqq)        (&(__bfa)->reqq_waitq[__reqq])
 
 /**
  * static inline void
index 1493489..397d7e9 100644 (file)
@@ -51,7 +51,7 @@ BFA_TRC_FILE(HAL, IOC);
         (sizeof(struct bfa_trc_mod_s) -                        \
          BFA_TRC_MAX * sizeof(struct bfa_trc_s)))
 #define BFA_DBG_FWTRC_OFF(_fn) (BFI_IOC_TRC_OFF + BFA_DBG_FWTRC_LEN * (_fn))
-#define bfa_ioc_stats(_ioc, _stats)    (_ioc)->stats._stats ++
+#define bfa_ioc_stats(_ioc, _stats)    ((_ioc)->stats._stats++)
 
 #define BFA_FLASH_CHUNK_NO(off)         (off / BFI_FLASH_CHUNK_SZ_WORDS)
 #define BFA_FLASH_OFFSET_IN_CHUNK(off)  (off % BFI_FLASH_CHUNK_SZ_WORDS)
@@ -1953,8 +1953,8 @@ bfa_ioc_error_isr(struct bfa_ioc_s *ioc)
 bfa_boolean_t
 bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
 {
-       return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
-               || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled));
+       return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabling)
+               || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_disabled);
 }
 
 /**
@@ -1963,9 +1963,9 @@ bfa_ioc_is_disabled(struct bfa_ioc_s *ioc)
 bfa_boolean_t
 bfa_ioc_fw_mismatch(struct bfa_ioc_s *ioc)
 {
-       return (bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
+       return bfa_fsm_cmp_state(ioc, bfa_ioc_sm_reset)
                || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_fwcheck)
-               || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch));
+               || bfa_fsm_cmp_state(ioc, bfa_ioc_sm_mismatch);
 }
 
 #define bfa_ioc_state_disabled(__sm)           \
index 58efd4b..7c30f05 100644 (file)
@@ -179,16 +179,16 @@ struct bfa_ioc_s {
        struct bfa_ioc_mbox_mod_s mbox_mod;
 };
 
-#define bfa_ioc_pcifn(__ioc)           (__ioc)->pcidev.pci_func
-#define bfa_ioc_devid(__ioc)           (__ioc)->pcidev.device_id
-#define bfa_ioc_bar0(__ioc)            (__ioc)->pcidev.pci_bar_kva
+#define bfa_ioc_pcifn(__ioc)           ((__ioc)->pcidev.pci_func)
+#define bfa_ioc_devid(__ioc)           ((__ioc)->pcidev.device_id)
+#define bfa_ioc_bar0(__ioc)            ((__ioc)->pcidev.pci_bar_kva)
 #define bfa_ioc_portid(__ioc)          ((__ioc)->port_id)
 #define bfa_ioc_fetch_stats(__ioc, __stats) \
-               ((__stats)->drv_stats) = (__ioc)->stats
+               (((__stats)->drv_stats) = (__ioc)->stats)
 #define bfa_ioc_clr_stats(__ioc)       \
                bfa_os_memset(&(__ioc)->stats, 0, sizeof((__ioc)->stats))
-#define bfa_ioc_maxfrsize(__ioc)       (__ioc)->attr->maxfrsize
-#define bfa_ioc_rx_bbcredit(__ioc)     (__ioc)->attr->rx_bbcredit
+#define bfa_ioc_maxfrsize(__ioc)       ((__ioc)->attr->maxfrsize)
+#define bfa_ioc_rx_bbcredit(__ioc)     ((__ioc)->attr->rx_bbcredit)
 #define bfa_ioc_speed_sup(__ioc)       \
        BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop)
 
index 12350b0..d7ab792 100644 (file)
@@ -794,7 +794,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats,
 
        if (iocfc->stats_busy) {
                bfa_trc(bfa, iocfc->stats_busy);
-               return (BFA_STATUS_DEVBUSY);
+               return BFA_STATUS_DEVBUSY;
        }
 
        iocfc->stats_busy = BFA_TRUE;
@@ -804,7 +804,7 @@ bfa_iocfc_get_stats(struct bfa_s *bfa, struct bfa_iocfc_stats_s *stats,
 
        bfa_iocfc_stats_query(bfa);
 
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 bfa_status_t
@@ -814,7 +814,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg)
 
        if (iocfc->stats_busy) {
                bfa_trc(bfa, iocfc->stats_busy);
-               return (BFA_STATUS_DEVBUSY);
+               return BFA_STATUS_DEVBUSY;
        }
 
        iocfc->stats_busy = BFA_TRUE;
@@ -822,7 +822,7 @@ bfa_iocfc_clear_stats(struct bfa_s *bfa, bfa_cb_ioc_t cbfn, void *cbarg)
        iocfc->stats_cbarg = cbarg;
 
        bfa_iocfc_stats_clear(bfa);
-       return (BFA_STATUS_OK);
+       return BFA_STATUS_OK;
 }
 
 /**
index 7ad177e..ce9a830 100644 (file)
@@ -107,13 +107,13 @@ struct bfa_iocfc_s {
 
 #define bfa_lpuid(__bfa)               bfa_ioc_portid(&(__bfa)->ioc)
 #define bfa_msix_init(__bfa, __nvecs)  \
-       (__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs)
+       ((__bfa)->iocfc.hwif.hw_msix_init(__bfa, __nvecs))
 #define bfa_msix_install(__bfa)        \
-       (__bfa)->iocfc.hwif.hw_msix_install(__bfa)
+       ((__bfa)->iocfc.hwif.hw_msix_install(__bfa))
 #define bfa_msix_uninstall(__bfa)      \
-       (__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa)
+       ((__bfa)->iocfc.hwif.hw_msix_uninstall(__bfa))
 #define bfa_isr_mode_set(__bfa, __msix)        \
-       (__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix)
+       ((__bfa)->iocfc.hwif.hw_isr_mode_set(__bfa, __msix))
 #define bfa_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec)   \
        (__bfa)->iocfc.hwif.hw_msix_getvecs(__bfa, __vecmap, __nvecs, __maxvec)
 
index 7ae2552..f81d359 100644 (file)
@@ -105,13 +105,13 @@ bfa_ioim_sm_uninit(struct bfa_ioim_s *ioim, enum bfa_ioim_event event)
                                bfa_sm_set_state(ioim, bfa_ioim_sm_hcb);
                                list_del(&ioim->qe);
                                list_add_tail(&ioim->qe,
-                                       &ioim->fcpim->ioim_comp_q);
+                                               &ioim->fcpim->ioim_comp_q);
                                bfa_cb_queue(ioim->bfa, &ioim->hcb_qe,
                                                __bfa_cb_ioim_pathtov, ioim);
                        } else {
                                list_del(&ioim->qe);
                                list_add_tail(&ioim->qe,
-                                       &ioim->itnim->pending_q);
+                                               &ioim->itnim->pending_q);
                        }
                        break;
                }
index 4d5c61a..eabf7d3 100644 (file)
@@ -1029,7 +1029,7 @@ bfa_itnim_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void *ditn)
        bfa_stats(itnim, creates);
        bfa_sm_send_event(itnim, BFA_ITNIM_SM_CREATE);
 
-       return (itnim);
+       return itnim;
 }
 
 void
@@ -1061,7 +1061,7 @@ bfa_itnim_offline(struct bfa_itnim_s *itnim)
 bfa_boolean_t
 bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
 {
-       return (
+       return
                itnim->fcpim->path_tov && itnim->iotov_active &&
                (bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwcreate) ||
                 bfa_sm_cmp_state(itnim, bfa_itnim_sm_sler) ||
@@ -1069,7 +1069,7 @@ bfa_itnim_hold_io(struct bfa_itnim_s *itnim)
                 bfa_sm_cmp_state(itnim, bfa_itnim_sm_fwdelete) ||
                 bfa_sm_cmp_state(itnim, bfa_itnim_sm_offline) ||
                 bfa_sm_cmp_state(itnim, bfa_itnim_sm_iocdisable))
-);
+       ;
 }
 
 void
index c2735e5..e751401 100644 (file)
@@ -231,9 +231,9 @@ bfa_log_get_level(struct bfa_log_mod_s *log_mod, int mod_id)
                return BFA_LOG_INVALID;
 
        if (log_mod)
-               return (log_mod->log_level[mod_id]);
+               return log_mod->log_level[mod_id];
        else
-               return (bfa_log_info[mod_id].level);
+               return bfa_log_info[mod_id].level;
 }
 
 enum bfa_log_severity
index 4b97e27..51f698a 100644 (file)
@@ -59,8 +59,8 @@ struct bfa_pport_s {
        u8                      *stats_kva;
        u64             stats_pa;
        union bfa_pport_stats_u *stats; /*  pport stats */
-       u32             mypid : 24;
-       u32             rsvd_b : 8;
+       u32             mypid:24;
+       u32             rsvd_b:8;
        struct bfa_timer_s      timer;  /*  timer */
        union bfa_pport_stats_u         *stats_ret;
                                        /*  driver stats location */
index 16da77a..3e1990a 100644 (file)
@@ -677,7 +677,7 @@ bfa_rport_alloc(struct bfa_rport_mod_s *mod)
        if (rport)
                list_add_tail(&rport->qe, &mod->rp_active_q);
 
-       return (rport);
+       return rport;
 }
 
 static void
@@ -834,7 +834,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv)
        rp = bfa_rport_alloc(BFA_RPORT_MOD(bfa));
 
        if (rp == NULL)
-               return (NULL);
+               return NULL;
 
        rp->bfa = bfa;
        rp->rport_drv = rport_drv;
@@ -843,7 +843,7 @@ bfa_rport_create(struct bfa_s *bfa, void *rport_drv)
        bfa_assert(bfa_sm_cmp_state(rp, bfa_rport_sm_uninit));
        bfa_sm_send_event(rp, BFA_RPORT_SM_CREATE);
 
-       return (rp);
+       return rp;
 }
 
 void
index 010d40d..ff7a4dc 100644 (file)
@@ -23,13 +23,14 @@ BFA_TRC_FILE(HAL, TSKIM);
 /**
  * task management completion handling
  */
-#define bfa_tskim_qcomp(__tskim, __cbfn) do {                       \
-       bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe, __cbfn, (__tskim)); \
+#define bfa_tskim_qcomp(__tskim, __cbfn) do {                  \
+       bfa_cb_queue((__tskim)->bfa, &(__tskim)->hcb_qe,        \
+                        __cbfn, (__tskim));      \
        bfa_tskim_notify_comp(__tskim);      \
 } while (0)
 
-#define bfa_tskim_notify_comp(__tskim) do {                                 \
-       if ((__tskim)->notify)                                               \
+#define bfa_tskim_notify_comp(__tskim) do {                     \
+       if ((__tskim)->notify)                                   \
                bfa_itnim_tskdone((__tskim)->itnim);      \
 } while (0)
 
index ff5f9de..4b3c241 100644 (file)
@@ -185,7 +185,7 @@ bfa_uf_get(struct bfa_uf_mod_s *uf_mod)
        struct bfa_uf_s   *uf;
 
        bfa_q_deq(&uf_mod->uf_free_q, &uf);
-       return (uf);
+       return uf;
 }
 
 static void
index 6f2be5a..b52b773 100644 (file)
@@ -188,8 +188,8 @@ static struct bfad_port_s *
 bfad_get_drv_port(struct bfad_s *bfad, struct bfad_vf_s *vf_drv,
                  struct bfad_vport_s *vp_drv)
 {
-       return ((vp_drv) ? (&(vp_drv)->drv_port)
-               : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport)));
+       return (vp_drv) ? (&(vp_drv)->drv_port)
+               : ((vf_drv) ? (&(vf_drv)->base_port) : (&(bfad)->pport));
 }
 
 struct bfad_port_s *
@@ -716,7 +716,7 @@ bfad_drv_init(struct bfad_s *bfad)
        if ((bfad->bfad_flags & BFAD_MSIX_ON)
            && bfad_install_msix_handler(bfad)) {
                printk(KERN_WARNING "%s: install_msix failed, bfad%d\n",
-                      __FUNCTION__, bfad->inst_no);
+                      __func__, bfad->inst_no);
        }
 
        bfad_init_timer(bfad);
index bd34b0d..2ad65f2 100644 (file)
@@ -65,10 +65,10 @@ bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
        memcpy(*bfi_image, fw->data, fw->size);
        *bfi_image_size = fw->size/sizeof(u32);
 
-       return(*bfi_image);
+       return *bfi_image;
 
 error:
-       return(NULL);
+       return NULL;
 }
 
 u32 *
@@ -78,12 +78,12 @@ bfad_get_firmware_buf(struct pci_dev *pdev)
                if (bfi_image_ct_size == 0)
                        bfad_read_firmware(pdev, &bfi_image_ct,
                                &bfi_image_ct_size, BFAD_FW_FILE_CT);
-               return(bfi_image_ct);
+               return bfi_image_ct;
        } else {
                if (bfi_image_cb_size == 0)
                        bfad_read_firmware(pdev, &bfi_image_cb,
                                &bfi_image_cb_size, BFAD_FW_FILE_CB);
-               return(bfi_image_cb);
+               return bfi_image_cb;
        }
 }
 
index 55d012a..f788c2a 100644 (file)
@@ -1050,7 +1050,7 @@ bfad_im_itnim_work_handler(struct work_struct *work)
                } else {
                        printk(KERN_WARNING
                                "%s: itnim %llx is already in online state\n",
-                               __FUNCTION__,
+                               __func__,
                                bfa_fcs_itnim_get_pwwn(&itnim->fcs_itnim));
                }
 
index 1d3e74e..b36be15 100644 (file)
@@ -31,7 +31,7 @@ u32 *bfad_read_firmware(struct pci_dev *pdev, u32 **bfi_image,
 static inline u32 *
 bfad_load_fwimg(struct pci_dev *pdev)
 {
-       return(bfad_get_firmware_buf(pdev));
+       return bfad_get_firmware_buf(pdev);
 }
 
 static inline void
index f104e02..7de8832 100644 (file)
@@ -23,13 +23,12 @@ BFA_TRC_FILE(LDRV, INTR);
 /**
  *  bfa_isr BFA driver interrupt functions
  */
-irqreturn_t bfad_intx(int irq, void *dev_id);
 static int msix_disable;
 module_param(msix_disable, int, S_IRUGO | S_IWUSR);
 /**
  * Line based interrupt handler.
  */
-irqreturn_t
+static irqreturn_t
 bfad_intx(int irq, void *dev_id)
 {
        struct bfad_s         *bfad = dev_id;
index a8b14c4..a4b5dd4 100644 (file)
@@ -36,12 +36,12 @@ BFA_TRC_FILE(FCS, FABRIC);
 #define BFA_FCS_FABRIC_RETRY_DELAY     (2000)  /* Milliseconds */
 #define BFA_FCS_FABRIC_CLEANUP_DELAY   (10000) /* Milliseconds */
 
-#define bfa_fcs_fabric_set_opertype(__fabric) do {          \
-    if (bfa_pport_get_topology((__fabric)->fcs->bfa)    \
-                               == BFA_PPORT_TOPOLOGY_P2P)   \
-           (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT;       \
-    else                                                    \
-           (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT;      \
+#define bfa_fcs_fabric_set_opertype(__fabric) do {             \
+       if (bfa_pport_get_topology((__fabric)->fcs->bfa)       \
+                               == BFA_PPORT_TOPOLOGY_P2P)     \
+               (__fabric)->oper_type = BFA_PPORT_TYPE_NPORT;  \
+       else                                                   \
+               (__fabric)->oper_type = BFA_PPORT_TYPE_NLPORT; \
 } while (0)
 
 /*
@@ -887,7 +887,7 @@ bfa_fcs_fabric_modsusp(struct bfa_fcs_s *fcs)
 bfa_boolean_t
 bfa_fcs_fabric_is_loopback(struct bfa_fcs_fabric_s *fabric)
 {
-       return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback));
+       return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_loopback);
 }
 
 enum bfa_pport_type
@@ -974,7 +974,7 @@ bfa_fcs_fabric_port_delete_comp(struct bfa_fcs_fabric_s *fabric)
 int
 bfa_fcs_fabric_is_online(struct bfa_fcs_fabric_s *fabric)
 {
-       return (bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online));
+       return bfa_sm_cmp_state(fabric, bfa_fcs_fabric_sm_online);
 }
 
 
@@ -1015,7 +1015,7 @@ bfa_fcs_fabric_vport_lookup(struct bfa_fcs_fabric_s *fabric, wwn_t pwwn)
 u16
 bfa_fcs_fabric_vport_count(struct bfa_fcs_fabric_s *fabric)
 {
-       return (fabric->num_vports);
+       return fabric->num_vports;
 }
 
 /**
index d174706..fee5456 100644 (file)
@@ -188,14 +188,14 @@ fc_els_rsp_parse(struct fchs_s *fchs, int len)
        switch (els_cmd->els_code) {
        case FC_ELS_LS_RJT:
                if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
-                       return (FC_PARSE_BUSY);
+                       return FC_PARSE_BUSY;
                else
-                       return (FC_PARSE_FAILURE);
+                       return FC_PARSE_FAILURE;
 
        case FC_ELS_ACC:
-               return (FC_PARSE_OK);
+               return FC_PARSE_OK;
        }
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 }
 
 static void
@@ -228,7 +228,7 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
        bfa_os_memcpy(&plogi->port_name, &port_name, sizeof(wwn_t));
        bfa_os_memcpy(&plogi->node_name, &node_name, sizeof(wwn_t));
 
-       return (sizeof(struct fc_logi_s));
+       return sizeof(struct fc_logi_s);
 }
 
 u16
@@ -267,7 +267,7 @@ fc_flogi_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
        flogi->csp.npiv_supp    = 1; /* @todo. field name is not correct */
        vvl_info[0]     = bfa_os_htonl(FLOGI_VVL_BRCD);
 
-       return (sizeof(struct fc_logi_s));
+       return sizeof(struct fc_logi_s);
 }
 
 u16
@@ -287,7 +287,7 @@ fc_flogi_acc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
 
        flogi->csp.bbcred = bfa_os_htons(local_bb_credits);
 
-       return (sizeof(struct fc_logi_s));
+       return sizeof(struct fc_logi_s);
 }
 
 u16
@@ -306,7 +306,7 @@ fc_fdisc_build(struct fchs_s *fchs, struct fc_logi_s *flogi, u32 s_id,
        flogi->port_name = port_name;
        flogi->node_name = node_name;
 
-       return (sizeof(struct fc_logi_s));
+       return sizeof(struct fc_logi_s);
 }
 
 u16
@@ -338,26 +338,26 @@ fc_plogi_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
        case FC_ELS_LS_RJT:
                ls_rjt = (struct fc_ls_rjt_s *) (fchs + 1);
                if (ls_rjt->reason_code == FC_LS_RJT_RSN_LOGICAL_BUSY)
-                       return (FC_PARSE_BUSY);
+                       return FC_PARSE_BUSY;
                else
-                       return (FC_PARSE_FAILURE);
+                       return FC_PARSE_FAILURE;
        case FC_ELS_ACC:
                plogi = (struct fc_logi_s *) (fchs + 1);
                if (len < sizeof(struct fc_logi_s))
-                       return (FC_PARSE_FAILURE);
+                       return FC_PARSE_FAILURE;
 
                if (!wwn_is_equal(plogi->port_name, port_name))
-                       return (FC_PARSE_FAILURE);
+                       return FC_PARSE_FAILURE;
 
                if (!plogi->class3.class_valid)
-                       return (FC_PARSE_FAILURE);
+                       return FC_PARSE_FAILURE;
 
                if (bfa_os_ntohs(plogi->class3.rxsz) < (FC_MIN_PDUSZ))
-                       return (FC_PARSE_FAILURE);
+                       return FC_PARSE_FAILURE;
 
-               return (FC_PARSE_OK);
+               return FC_PARSE_OK;
        default:
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
        }
 }
 
@@ -372,7 +372,7 @@ fc_plogi_parse(struct fchs_s *fchs)
        if ((bfa_os_ntohs(plogi->class3.rxsz) < FC_MIN_PDUSZ)
            || (bfa_os_ntohs(plogi->class3.rxsz) > FC_MAX_PDUSZ)
            || (plogi->class3.rxsz == 0))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        return FC_PARSE_OK;
 }
@@ -393,7 +393,7 @@ fc_prli_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
        prli->parampage.servparams.task_retry_id = 0;
        prli->parampage.servparams.confirm       = 1;
 
-       return (sizeof(struct fc_prli_s));
+       return sizeof(struct fc_prli_s);
 }
 
 u16
@@ -414,41 +414,41 @@ fc_prli_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
 
        prli->parampage.rspcode = FC_PRLI_ACC_XQTD;
 
-       return (sizeof(struct fc_prli_s));
+       return sizeof(struct fc_prli_s);
 }
 
 enum fc_parse_status
 fc_prli_rsp_parse(struct fc_prli_s *prli, int len)
 {
        if (len < sizeof(struct fc_prli_s))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (prli->command != FC_ELS_ACC)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if ((prli->parampage.rspcode != FC_PRLI_ACC_XQTD)
            && (prli->parampage.rspcode != FC_PRLI_ACC_PREDEF_IMG))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (prli->parampage.servparams.target != 1)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 }
 
 enum fc_parse_status
 fc_prli_parse(struct fc_prli_s *prli)
 {
        if (prli->parampage.type != FC_TYPE_FCP)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (!prli->parampage.imagepair)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (!prli->parampage.servparams.initiator)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 }
 
 u16
@@ -462,7 +462,7 @@ fc_logo_build(struct fchs_s *fchs, struct fc_logo_s *logo, u32 d_id,
        logo->nport_id = (s_id);
        logo->orig_port_name = port_name;
 
-       return (sizeof(struct fc_logo_s));
+       return sizeof(struct fc_logo_s);
 }
 
 static          u16
@@ -484,7 +484,7 @@ fc_adisc_x_build(struct fchs_s *fchs, struct fc_adisc_s *adisc, u32 d_id,
        adisc->orig_node_name = node_name;
        adisc->nport_id = (s_id);
 
-       return (sizeof(struct fc_adisc_s));
+       return sizeof(struct fc_adisc_s);
 }
 
 u16
@@ -511,15 +511,15 @@ fc_adisc_rsp_parse(struct fc_adisc_s *adisc, int len, wwn_t port_name,
 {
 
        if (len < sizeof(struct fc_adisc_s))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (adisc->els_cmd.els_code != FC_ELS_ACC)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (!wwn_is_equal(adisc->orig_port_name, port_name))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 }
 
 enum fc_parse_status
@@ -529,14 +529,14 @@ fc_adisc_parse(struct fchs_s *fchs, void *pld, u32 host_dap,
        struct fc_adisc_s     *adisc = (struct fc_adisc_s *) pld;
 
        if (adisc->els_cmd.els_code != FC_ELS_ACC)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if ((adisc->nport_id == (host_dap))
            && wwn_is_equal(adisc->orig_port_name, port_name)
            && wwn_is_equal(adisc->orig_node_name, node_name))
-               return (FC_PARSE_OK);
+               return FC_PARSE_OK;
 
-       return (FC_PARSE_FAILURE);
+       return FC_PARSE_FAILURE;
 }
 
 enum fc_parse_status
@@ -550,13 +550,13 @@ fc_pdisc_parse(struct fchs_s *fchs, wwn_t node_name, wwn_t port_name)
        if ((bfa_os_ntohs(pdisc->class3.rxsz) <
                 (FC_MIN_PDUSZ - sizeof(struct fchs_s)))
            || (pdisc->class3.rxsz == 0))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (!wwn_is_equal(pdisc->port_name, port_name))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        if (!wwn_is_equal(pdisc->node_name, node_name))
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        return FC_PARSE_OK;
 }
@@ -570,7 +570,7 @@ fc_abts_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id)
        fchs->s_id = (s_id);
        fchs->ox_id = bfa_os_htons(ox_id);
 
-       return (sizeof(struct fchs_s));
+       return sizeof(struct fchs_s);
 }
 
 enum fc_parse_status
@@ -578,9 +578,9 @@ fc_abts_rsp_parse(struct fchs_s *fchs, int len)
 {
        if ((fchs->cat_info == FC_CAT_BA_ACC)
            || (fchs->cat_info == FC_CAT_BA_RJT))
-               return (FC_PARSE_OK);
+               return FC_PARSE_OK;
 
-       return (FC_PARSE_FAILURE);
+       return FC_PARSE_FAILURE;
 }
 
 u16
@@ -597,7 +597,7 @@ fc_rrq_build(struct fchs_s *fchs, struct fc_rrq_s *rrq, u32 d_id,
        rrq->ox_id = bfa_os_htons(rrq_oxid);
        rrq->rx_id = FC_RXID_ANY;
 
-       return (sizeof(struct fc_rrq_s));
+       return sizeof(struct fc_rrq_s);
 }
 
 u16
@@ -611,7 +611,7 @@ fc_logo_acc_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id,
        memset(acc, 0, sizeof(struct fc_els_cmd_s));
        acc->els_code = FC_ELS_ACC;
 
-       return (sizeof(struct fc_els_cmd_s));
+       return sizeof(struct fc_els_cmd_s);
 }
 
 u16
@@ -627,7 +627,7 @@ fc_ls_rjt_build(struct fchs_s *fchs, struct fc_ls_rjt_s *ls_rjt, u32 d_id,
        ls_rjt->reason_code_expl = reason_code_expl;
        ls_rjt->vendor_unique = 0x00;
 
-       return (sizeof(struct fc_ls_rjt_s));
+       return sizeof(struct fc_ls_rjt_s);
 }
 
 u16
@@ -643,7 +643,7 @@ fc_ba_acc_build(struct fchs_s *fchs, struct fc_ba_acc_s *ba_acc, u32 d_id,
        ba_acc->ox_id = fchs->ox_id;
        ba_acc->rx_id = fchs->rx_id;
 
-       return (sizeof(struct fc_ba_acc_s));
+       return sizeof(struct fc_ba_acc_s);
 }
 
 u16
@@ -654,7 +654,7 @@ fc_ls_acc_build(struct fchs_s *fchs, struct fc_els_cmd_s *els_cmd,
        memset(els_cmd, 0, sizeof(struct fc_els_cmd_s));
        els_cmd->els_code = FC_ELS_ACC;
 
-       return (sizeof(struct fc_els_cmd_s));
+       return sizeof(struct fc_els_cmd_s);
 }
 
 int
@@ -696,7 +696,7 @@ fc_tprlo_acc_build(struct fchs_s *fchs, struct fc_tprlo_acc_s *tprlo_acc,
                tprlo_acc->tprlo_acc_params[page].orig_process_assc = 0;
                tprlo_acc->tprlo_acc_params[page].resp_process_assc = 0;
        }
-       return (bfa_os_ntohs(tprlo_acc->payload_len));
+       return bfa_os_ntohs(tprlo_acc->payload_len);
 }
 
 u16
@@ -721,7 +721,7 @@ fc_prlo_acc_build(struct fchs_s *fchs, struct fc_prlo_acc_s *prlo_acc,
                prlo_acc->prlo_acc_params[page].resp_process_assc = 0;
        }
 
-       return (bfa_os_ntohs(prlo_acc->payload_len));
+       return bfa_os_ntohs(prlo_acc->payload_len);
 }
 
 u16
@@ -735,7 +735,7 @@ fc_rnid_build(struct fchs_s *fchs, struct fc_rnid_cmd_s *rnid, u32 d_id,
        rnid->els_cmd.els_code = FC_ELS_RNID;
        rnid->node_id_data_format = data_format;
 
-       return (sizeof(struct fc_rnid_cmd_s));
+       return sizeof(struct fc_rnid_cmd_s);
 }
 
 u16
@@ -759,10 +759,10 @@ fc_rnid_acc_build(struct fchs_s *fchs, struct fc_rnid_acc_s *rnid_acc,
                rnid_acc->specific_id_data_length =
                        sizeof(struct fc_rnid_general_topology_data_s);
                bfa_os_assign(rnid_acc->gen_topology_data, *gen_topo_data);
-               return (sizeof(struct fc_rnid_acc_s));
+               return sizeof(struct fc_rnid_acc_s);
        } else {
-               return (sizeof(struct fc_rnid_acc_s) -
-                       sizeof(struct fc_rnid_general_topology_data_s));
+               return sizeof(struct fc_rnid_acc_s) -
+                       sizeof(struct fc_rnid_general_topology_data_s);
        }
 
 }
@@ -776,7 +776,7 @@ fc_rpsc_build(struct fchs_s *fchs, struct fc_rpsc_cmd_s *rpsc, u32 d_id,
        memset(rpsc, 0, sizeof(struct fc_rpsc_cmd_s));
 
        rpsc->els_cmd.els_code = FC_ELS_RPSC;
-       return (sizeof(struct fc_rpsc_cmd_s));
+       return sizeof(struct fc_rpsc_cmd_s);
 }
 
 u16
@@ -797,8 +797,8 @@ fc_rpsc2_build(struct fchs_s *fchs, struct fc_rpsc2_cmd_s *rpsc2,
        for (i = 0; i < npids; i++)
                rpsc2->pid_list[i].pid = pid_list[i];
 
-       return (sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) *
-                       (sizeof(u32))));
+       return sizeof(struct fc_rpsc2_cmd_s) + ((npids - 1) *
+                       (sizeof(u32)));
 }
 
 u16
@@ -819,7 +819,7 @@ fc_rpsc_acc_build(struct fchs_s *fchs, struct fc_rpsc_acc_s *rpsc_acc,
        rpsc_acc->speed_info[0].port_op_speed =
                bfa_os_htons(oper_speed->port_op_speed);
 
-       return (sizeof(struct fc_rpsc_acc_s));
+       return sizeof(struct fc_rpsc_acc_s);
 
 }
 
@@ -856,7 +856,7 @@ fc_pdisc_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
        pdisc->port_name = port_name;
        pdisc->node_name = node_name;
 
-       return (sizeof(struct fc_logi_s));
+       return sizeof(struct fc_logi_s);
 }
 
 u16
@@ -865,21 +865,21 @@ fc_pdisc_rsp_parse(struct fchs_s *fchs, int len, wwn_t port_name)
        struct fc_logi_s     *pdisc = (struct fc_logi_s *) (fchs + 1);
 
        if (len < sizeof(struct fc_logi_s))
-               return (FC_PARSE_LEN_INVAL);
+               return FC_PARSE_LEN_INVAL;
 
        if (pdisc->els_cmd.els_code != FC_ELS_ACC)
-               return (FC_PARSE_ACC_INVAL);
+               return FC_PARSE_ACC_INVAL;
 
        if (!wwn_is_equal(pdisc->port_name, port_name))
-               return (FC_PARSE_PWWN_NOT_EQUAL);
+               return FC_PARSE_PWWN_NOT_EQUAL;
 
        if (!pdisc->class3.class_valid)
-               return (FC_PARSE_NWWN_NOT_EQUAL);
+               return FC_PARSE_NWWN_NOT_EQUAL;
 
        if (bfa_os_ntohs(pdisc->class3.rxsz) < (FC_MIN_PDUSZ))
-               return (FC_PARSE_RXSZ_INVAL);
+               return FC_PARSE_RXSZ_INVAL;
 
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 }
 
 u16
@@ -903,7 +903,7 @@ fc_prlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id, u16 ox_id,
                prlo->prlo_params[page].resp_process_assc = 0;
        }
 
-       return (bfa_os_ntohs(prlo->payload_len));
+       return bfa_os_ntohs(prlo->payload_len);
 }
 
 u16
@@ -916,7 +916,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
        len = len;
 
        if (prlo->command != FC_ELS_ACC)
-               return (FC_PARSE_FAILURE);
+               return FC_PARSE_FAILURE;
 
        num_pages = ((bfa_os_ntohs(prlo->payload_len)) - 4) / 16;
 
@@ -936,7 +936,7 @@ fc_prlo_rsp_parse(struct fchs_s *fchs, int len)
                if (prlo->prlo_acc_params[page].resp_process_assc != 0)
                        return FC_PARSE_FAILURE;
        }
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 
 }
 
@@ -968,7 +968,7 @@ fc_tprlo_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
                }
        }
 
-       return (bfa_os_ntohs(tprlo->payload_len));
+       return bfa_os_ntohs(tprlo->payload_len);
 }
 
 u16
@@ -981,23 +981,23 @@ fc_tprlo_rsp_parse(struct fchs_s *fchs, int len)
        len = len;
 
        if (tprlo->command != FC_ELS_ACC)
-               return (FC_PARSE_ACC_INVAL);
+               return FC_PARSE_ACC_INVAL;
 
        num_pages = (bfa_os_ntohs(tprlo->payload_len) - 4) / 16;
 
        for (page = 0; page < num_pages; page++) {
                if (tprlo->tprlo_acc_params[page].type != FC_TYPE_FCP)
-                       return (FC_PARSE_NOT_FCP);
+                       return FC_PARSE_NOT_FCP;
                if (tprlo->tprlo_acc_params[page].opa_valid != 0)
-                       return (FC_PARSE_OPAFLAG_INVAL);
+                       return FC_PARSE_OPAFLAG_INVAL;
                if (tprlo->tprlo_acc_params[page].rpa_valid != 0)
-                       return (FC_PARSE_RPAFLAG_INVAL);
+                       return FC_PARSE_RPAFLAG_INVAL;
                if (tprlo->tprlo_acc_params[page].orig_process_assc != 0)
-                       return (FC_PARSE_OPA_INVAL);
+                       return FC_PARSE_OPA_INVAL;
                if (tprlo->tprlo_acc_params[page].resp_process_assc != 0)
-                       return (FC_PARSE_RPA_INVAL);
+                       return FC_PARSE_RPA_INVAL;
        }
-       return (FC_PARSE_OK);
+       return FC_PARSE_OK;
 }
 
 enum fc_parse_status
@@ -1024,7 +1024,7 @@ fc_ba_rjt_build(struct fchs_s *fchs, u32 d_id, u32 s_id,
        fchs->cat_info = FC_CAT_BA_RJT;
        ba_rjt->reason_code = reason_code;
        ba_rjt->reason_expl = reason_expl;
-       return (sizeof(struct fc_ba_rjt_s));
+       return sizeof(struct fc_ba_rjt_s);
 }
 
 static void
@@ -1073,7 +1073,7 @@ fc_gidpn_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
 
        bfa_os_memset(gidpn, 0, sizeof(struct fcgs_gidpn_req_s));
        gidpn->port_name = port_name;
-       return (sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_gidpn_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1090,7 +1090,7 @@ fc_gpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
 
        bfa_os_memset(gpnid, 0, sizeof(fcgs_gpnid_req_t));
        gpnid->dap = port_id;
-       return (sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s));
+       return sizeof(fcgs_gpnid_req_t) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1107,7 +1107,7 @@ fc_gnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
 
        bfa_os_memset(gnnid, 0, sizeof(fcgs_gnnid_req_t));
        gnnid->dap = port_id;
-       return (sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s));
+       return sizeof(fcgs_gnnid_req_t) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1137,7 +1137,7 @@ fc_scr_build(struct fchs_s *fchs, struct fc_scr_s *scr, u8 set_br_reg,
        if (set_br_reg)
                scr->vu_reg_func = FC_VU_SCR_REG_FUNC_FABRIC_NAME_CHANGE;
 
-       return (sizeof(struct fc_scr_s));
+       return sizeof(struct fc_scr_s);
 }
 
 u16
@@ -1157,7 +1157,7 @@ fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, u32 s_id,
        rscn->event[0].format = FC_RSCN_FORMAT_PORTID;
        rscn->event[0].portid = s_id;
 
-       return (sizeof(struct fc_rscn_pl_s));
+       return sizeof(struct fc_rscn_pl_s);
 }
 
 u16
@@ -1188,7 +1188,7 @@ fc_rftid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
                rftid->fc4_type[index] |= bfa_os_htonl(type_value);
        }
 
-       return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1210,7 +1210,7 @@ fc_rftid_build_sol(struct fchs_s *fchs, void *pyld, u32 s_id,
        bfa_os_memcpy((void *)rftid->fc4_type, (void *)fc4_bitmap,
                        (bitmap_size < 32 ? bitmap_size : 32));
 
-       return (sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rftid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1231,7 +1231,7 @@ fc_rffid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
        rffid->fc4ftr_bits  = fc4_ftrs;
        rffid->fc4_type         = fc4_type;
 
-       return (sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rffid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1253,7 +1253,7 @@ fc_rspnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u16 ox_id,
        rspnid->spn_len = (u8) strlen((char *)name);
        strncpy((char *)rspnid->spn, (char *)name, rspnid->spn_len);
 
-       return (sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rspnid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1275,7 +1275,7 @@ fc_gid_ft_build(struct fchs_s *fchs, void *pyld, u32 s_id,
        gidft->domain_id = 0;
        gidft->area_id = 0;
 
-       return (sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_gidft_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1294,7 +1294,7 @@ fc_rpnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
        rpnid->port_id = port_id;
        rpnid->port_name = port_name;
 
-       return (sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rpnid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1313,7 +1313,7 @@ fc_rnnid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
        rnnid->port_id = port_id;
        rnnid->node_name = node_name;
 
-       return (sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rnnid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1332,7 +1332,7 @@ fc_rcsid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
        rcsid->port_id = port_id;
        rcsid->cos = cos;
 
-       return (sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rcsid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1351,7 +1351,7 @@ fc_rptid_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id,
        rptid->port_id = port_id;
        rptid->port_type = port_type;
 
-       return (sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s));
+       return sizeof(struct fcgs_rptid_req_s) + sizeof(struct ct_hdr_s);
 }
 
 u16
@@ -1368,7 +1368,7 @@ fc_ganxt_build(struct fchs_s *fchs, void *pyld, u32 s_id, u32 port_id)
        bfa_os_memset(ganxt, 0, sizeof(struct fcgs_ganxt_req_s));
        ganxt->port_id = port_id;
 
-       return (sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s));
+       return sizeof(struct ct_hdr_s) + sizeof(struct fcgs_ganxt_req_s);
 }
 
 /*
@@ -1385,7 +1385,7 @@ fc_fdmi_reqhdr_build(struct fchs_s *fchs, void *pyld, u32 s_id,
        fc_gs_fchdr_build(fchs, d_id, s_id, 0);
        fc_gs_fdmi_cthdr_build(cthdr, s_id, cmd_code);
 
-       return (sizeof(struct ct_hdr_s));
+       return sizeof(struct ct_hdr_s);
 }
 
 /*
@@ -1425,7 +1425,7 @@ fc_gmal_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn)
        bfa_os_memset(gmal, 0, sizeof(fcgs_gmal_req_t));
        gmal->wwn = wwn;
 
-       return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t));
+       return sizeof(struct ct_hdr_s) + sizeof(fcgs_gmal_req_t);
 }
 
 /*
@@ -1445,5 +1445,5 @@ fc_gfn_req_build(struct fchs_s *fchs, void *pyld, u32 s_id, wwn_t wwn)
        bfa_os_memset(gfn, 0, sizeof(fcgs_gfn_req_t));
        gfn->wwn = wwn;
 
-       return (sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t));
+       return sizeof(struct ct_hdr_s) + sizeof(fcgs_gfn_req_t);
 }
index 4d24842..8fa7f27 100644 (file)
@@ -32,8 +32,8 @@
  * Utility Macros/functions
  */
 
-#define fcif_sof_set(_ifhdr, _sof)     (_ifhdr)->sof = FC_ ## _sof
-#define fcif_eof_set(_ifhdr, _eof)     (_ifhdr)->eof = FC_ ## _eof
+#define fcif_sof_set(_ifhdr, _sof)     ((_ifhdr)->sof = FC_ ## _sof)
+#define fcif_eof_set(_ifhdr, _eof)     ((_ifhdr)->eof = FC_ ## _eof)
 
 #define wwn_is_equal(_wwn1, _wwn2)             \
        (memcmp(&(_wwn1), &(_wwn2), sizeof(wwn_t)) == 0)
@@ -49,7 +49,7 @@
 static inline   u32
 fc_get_ctresp_pyld_len(u32 resp_len)
 {
-       return (resp_len - sizeof(struct ct_hdr_s));
+       return resp_len - sizeof(struct ct_hdr_s);
 }
 
 /*
index 8ce5d89..1f3c06e 100644 (file)
@@ -286,11 +286,10 @@ bfa_fcs_itnim_sm_online(struct bfa_fcs_itnim_s *itnim,
                bfa_sm_set_state(itnim, bfa_fcs_itnim_sm_hcb_offline);
                bfa_fcb_itnim_offline(itnim->itnim_drv);
                bfa_itnim_offline(itnim->bfa_itnim);
-               if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE) {
+               if (bfa_fcs_port_is_online(itnim->rport->port) == BFA_TRUE)
                        bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_DISCONNECT);
-               } else {
+               else
                        bfa_fcs_itnim_aen_post(itnim, BFA_ITNIM_AEN_OFFLINE);
-               }
                break;
 
        case BFA_FCS_ITNIM_SM_DELETE:
@@ -732,7 +731,7 @@ bfa_fcs_itnim_lookup(struct bfa_fcs_port_s *port, wwn_t rpwwn)
                return NULL;
 
        bfa_assert(rport->itnim != NULL);
-       return (rport->itnim);
+       return rport->itnim;
 }
 
 bfa_status_t
index deee685..8d08230 100644 (file)
@@ -23,7 +23,7 @@
 #ifndef __FCS_H__
 #define __FCS_H__
 
-#define __fcs_min_cfg(__fcs)       (__fcs)->min_cfg
+#define __fcs_min_cfg(__fcs)       ((__fcs)->min_cfg)
 
 void bfa_fcs_modexit_comp(struct bfa_fcs_s *fcs);
 
index b845eb2..df2a1e5 100644 (file)
@@ -72,9 +72,9 @@ static u16 bfa_fcs_port_fdmi_build_rpa_pyld(
                        struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld);
 static u16 bfa_fcs_port_fdmi_build_portattr_block(
                        struct bfa_fcs_port_fdmi_s *fdmi, u8 *pyld);
-void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
+static void bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
                        struct bfa_fcs_fdmi_hba_attr_s *hba_attr);
-void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
+static void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
                        struct bfa_fcs_fdmi_port_attr_s *port_attr);
 /**
  *  fcs_fdmi_sm FCS FDMI state machine
@@ -1091,7 +1091,7 @@ bfa_fcs_port_fdmi_timeout(void *arg)
        bfa_sm_send_event(fdmi, FDMISM_EVENT_TIMEOUT);
 }
 
-void
+static void
 bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
                         struct bfa_fcs_fdmi_hba_attr_s *hba_attr)
 {
@@ -1145,7 +1145,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_port_fdmi_s *fdmi,
 
 }
 
-void
+static void
 bfa_fcs_fdmi_get_portattr(struct bfa_fcs_port_fdmi_s *fdmi,
                          struct bfa_fcs_fdmi_port_attr_s *port_attr)
 {
index da8cac0..d9cbc2a 100644 (file)
@@ -54,7 +54,7 @@ bfa_aen_get_max_cfg_entry(void)
 static inline s32
 bfa_aen_get_meminfo(void)
 {
-       return (sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry());
+       return sizeof(struct bfa_aen_entry_s) * bfa_aen_get_max_cfg_entry();
 }
 
 static inline s32
index 64c1412..d4bc0d9 100644 (file)
@@ -76,11 +76,11 @@ struct bfa_meminfo_s {
        struct bfa_mem_elem_s meminfo[BFA_MEM_TYPE_MAX];
 };
 #define bfa_meminfo_kva(_m)    \
-       (_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp
+       ((_m)->meminfo[BFA_MEM_TYPE_KVA - 1].kva_curp)
 #define bfa_meminfo_dma_virt(_m)       \
-       (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp
+       ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].kva_curp)
 #define bfa_meminfo_dma_phys(_m)       \
-       (_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp
+       ((_m)->meminfo[BFA_MEM_TYPE_DMA - 1].dma_curp)
 
 /**
  * Generic Scatter Gather Element used by driver
@@ -100,7 +100,7 @@ struct bfa_sge_s {
 /*
  * bfa stats interfaces
  */
-#define bfa_stats(_mod, _stats)        (_mod)->stats._stats ++
+#define bfa_stats(_mod, _stats)        ((_mod)->stats._stats++)
 
 #define bfa_ioc_get_stats(__bfa, __ioc_stats)  \
        bfa_ioc_fetch_stats(&(__bfa)->ioc, __ioc_stats)
@@ -136,7 +136,7 @@ void bfa_isr_enable(struct bfa_s *bfa);
 void bfa_isr_disable(struct bfa_s *bfa);
 void bfa_msix_getvecs(struct bfa_s *bfa, u32 *msix_vecs_bmap,
                        u32 *num_vecs, u32 *max_vec_bit);
-#define bfa_msix(__bfa, __vec) (__bfa)->msix.handler[__vec](__bfa, __vec)
+#define bfa_msix(__bfa, __vec) ((__bfa)->msix.handler[__vec](__bfa, __vec))
 
 void bfa_comp_deq(struct bfa_s *bfa, struct list_head *comp_q);
 void bfa_comp_process(struct bfa_s *bfa, struct list_head *comp_q);
index 0c80b74..268d956 100644 (file)
@@ -34,10 +34,10 @@ struct bfa_fcxp_s;
  */
 struct bfa_rport_info_s {
        u16        max_frmsz;   /*  max rcv pdu size               */
-       u32        pid : 24,    /*  remote port ID                 */
-                       lp_tag : 8;
-       u32        local_pid : 24,      /*  local port ID                   */
-                       cisc : 8;       /*  CIRO supported                  */
+       u32        pid:24,              /*  remote port ID                 */
+                       lp_tag:8;
+       u32        local_pid:24,        /*  local port ID                   */
+                       cisc:8;         /*  CIRO supported                  */
        u8         fc_class;    /*  supported FC classes. enum fc_cos */
        u8         vf_en;               /*  virtual fabric enable          */
        u16        vf_id;               /*  virtual fabric ID              */
index 6cadfe0..7042c18 100644 (file)
@@ -93,13 +93,13 @@ union bfi_addr_u {
  */
 struct bfi_sge_s {
 #ifdef __BIGENDIAN
-       u32        flags        : 2,
-                       rsvd    : 2,
-                       sg_len  : 28;
+       u32        flags:2,
+                       rsvd:2,
+                       sg_len:28;
 #else
-       u32        sg_len       : 28,
-                       rsvd    : 2,
-                       flags   : 2;
+       u32        sg_len:28,
+                       rsvd:2,
+                       flags:2;
 #endif
        union bfi_addr_u sga;
 };
index 026e9c0..96ef056 100644 (file)
@@ -142,7 +142,7 @@ enum {
        BFI_ADAPTER_UNSUPP    = 0x400000,       /*  unknown adapter type  */
 };
 
-#define BFI_ADAPTER_GETP(__prop,__adap_prop)                   \
+#define BFI_ADAPTER_GETP(__prop, __adap_prop)                          \
     (((__adap_prop) & BFI_ADAPTER_ ## __prop ## _MK) >>         \
      BFI_ADAPTER_ ## __prop ## _SH)
 #define BFI_ADAPTER_SETP(__prop, __val)                        \
index 414b0e3..c59d47b 100644 (file)
@@ -55,8 +55,8 @@ struct bfi_lps_login_rsp_s {
        u16     bb_credit;
        u8              f_port;
        u8              npiv_en;
-       u32     lp_pid : 24;
-       u32     auth_req : 8;
+       u32     lp_pid:24;
+       u32     auth_req:8;
        mac_t           lp_mac;
        mac_t           fcf_mac;
        u8              ext_status;
index 3520f55..e1cd83b 100644 (file)
@@ -38,10 +38,10 @@ struct bfi_rport_create_req_s {
        struct bfi_mhdr_s  mh;          /*  common msg header           */
        u16        bfa_handle;  /*  host rport handle           */
        u16        max_frmsz;   /*  max rcv pdu size            */
-       u32        pid       : 24,      /*  remote port ID              */
-                       lp_tag    : 8;  /*  local port tag              */
-       u32        local_pid : 24,      /*  local port ID               */
-                       cisc      : 8;
+       u32        pid:24,              /*  remote port ID              */
+                       lp_tag:8;       /*  local port tag              */
+       u32        local_pid:24,        /*  local port ID               */
+                       cisc:8;
        u8         fc_class;    /*  supported FC classes        */
        u8         vf_en;               /*  virtual fabric enable       */
        u16        vf_id;               /*  virtual fabric ID           */
index af8c1d5..650f8d0 100644 (file)
@@ -31,7 +31,7 @@ bfa_checksum_u32(u32 *buf, int sz)
        for (i = 0; i < m; i++)
                sum ^= buf[i];
 
-       return (sum);
+       return sum;
 }
 
 static inline u16
@@ -43,7 +43,7 @@ bfa_checksum_u16(u16 *buf, int sz)
        for (i = 0; i < m; i++)
                sum ^= buf[i];
 
-       return (sum);
+       return sum;
 }
 
 static inline u8
@@ -55,6 +55,6 @@ bfa_checksum_u8(u8 *buf, int sz)
        for (i = 0; i < sz; i++)
                sum ^= buf[i];
 
-       return (sum);
+       return sum;
 }
 #endif
index 9877066..b0a92ba 100644 (file)
@@ -24,8 +24,8 @@
 
 typedef void (*bfa_sm_t)(void *sm, int event);
 
-#define bfa_sm_set_state(_sm, _state)  (_sm)->sm = (bfa_sm_t)(_state)
-#define bfa_sm_send_event(_sm, _event) (_sm)->sm((_sm), (_event))
+#define bfa_sm_set_state(_sm, _state)  ((_sm)->sm = (bfa_sm_t)(_state))
+#define bfa_sm_send_event(_sm, _event) ((_sm)->sm((_sm), (_event)))
 #define bfa_sm_get_state(_sm)          ((_sm)->sm)
 #define bfa_sm_cmp_state(_sm, _state)  ((_sm)->sm == (bfa_sm_t)(_state))
 
@@ -62,7 +62,7 @@ typedef void (*bfa_fsm_t)(void *fsm, int event);
 } while (0)
 
 #define bfa_fsm_send_event(_fsm, _event)       \
-       (_fsm)->fsm((_fsm), (_event))
+       ((_fsm)->fsm((_fsm), (_event)))
 #define bfa_fsm_cmp_state(_fsm, _state)                \
        ((_fsm)->fsm == (bfa_fsm_t)(_state))
 
index 3e74392..310771c 100644 (file)
@@ -24,7 +24,7 @@
 #endif
 
 #ifndef BFA_TRC_TS
-#define BFA_TRC_TS(_trcm)      ((_trcm)->ticks ++)
+#define BFA_TRC_TS(_trcm)      ((_trcm)->ticks++)
 #endif
 
 struct bfa_trc_s {
index a000bc4..bf32041 100644 (file)
@@ -61,7 +61,7 @@ enum bfa_pport_speed {
  *             Port operational type (in sync with SNIA port type).
  */
 enum bfa_pport_type {
-       BFA_PPORT_TYPE_UNKNOWN = 1,     /*  port type is unkown */
+       BFA_PPORT_TYPE_UNKNOWN = 1,     /*  port type is unknown */
        BFA_PPORT_TYPE_TRUNKED = 2,     /*  Trunked mode */
        BFA_PPORT_TYPE_NPORT   = 5,     /*  P2P with switched fabric */
        BFA_PPORT_TYPE_NLPORT  = 6,     /*  public loop */
index 31881d2..ade763d 100644 (file)
@@ -25,7 +25,7 @@
  * Temperature sensor status values
  */
 enum bfa_tsensor_status {
-       BFA_TSENSOR_STATUS_UNKNOWN   = 1,   /*  unkown status */
+       BFA_TSENSOR_STATUS_UNKNOWN   = 1,   /*  unknown status */
        BFA_TSENSOR_STATUS_FAULTY    = 2,   /*  sensor is faulty */
        BFA_TSENSOR_STATUS_BELOW_MIN = 3,   /*  temperature below mininum */
        BFA_TSENSOR_STATUS_NOMINAL   = 4,   /*  normal temperature */
index 4ffd224..08b79d5 100644 (file)
@@ -75,7 +75,7 @@ struct bfa_fcs_fabric_s {
                                                            */
 };
 
-#define bfa_fcs_fabric_npiv_capable(__f)    (__f)->is_npiv
+#define bfa_fcs_fabric_npiv_capable(__f)    ((__f)->is_npiv)
 #define bfa_fcs_fabric_is_switched(__f)                        \
        ((__f)->fab_type == BFA_FCS_FABRIC_SWITCHED)
 
index b85cba8..967ceb0 100644 (file)
@@ -125,12 +125,12 @@ union bfa_fcs_port_topo_u {
 struct bfa_fcs_port_s {
        struct list_head         qe;    /*  used by port/vport */
        bfa_sm_t               sm;      /*  state machine */
-       struct bfa_fcs_fabric_s *fabric;        /*  parent fabric */
-       struct bfa_port_cfg_s  port_cfg;        /*  port configuration */
+       struct bfa_fcs_fabric_s *fabric;/*  parent fabric */
+       struct bfa_port_cfg_s  port_cfg;/*  port configuration */
        struct bfa_timer_s link_timer;  /*  timer for link offline */
-       u32        pid : 24;    /*  FC address */
-       u8         lp_tag;              /*  lport tag */
-       u16        num_rports;  /*  Num of r-ports */
+       u32 pid:24;     /*  FC address */
+       u8  lp_tag;     /*  lport tag */
+       u16 num_rports; /*  Num of r-ports */
        struct list_head rport_q;       /*  queue of discovered r-ports */
        struct bfa_fcs_s *fcs;  /*  FCS instance */
        union bfa_fcs_port_topo_u port_topo;    /*  fabric/loop/n2n details */
@@ -188,13 +188,14 @@ bfa_fcs_port_get_drvport(struct bfa_fcs_port_s *port)
 }
 
 
-#define bfa_fcs_port_get_opertype(_lport)      (_lport)->fabric->oper_type
+#define bfa_fcs_port_get_opertype(_lport)      ((_lport)->fabric->oper_type)
 
 
-#define bfa_fcs_port_get_fabric_name(_lport)   (_lport)->fabric->fabric_name
+#define bfa_fcs_port_get_fabric_name(_lport)   ((_lport)->fabric->fabric_name)
 
 
-#define bfa_fcs_port_get_fabric_ipaddr(_lport) (_lport)->fabric->fabric_ip_addr
+#define bfa_fcs_port_get_fabric_ipaddr(_lport) \
+               ((_lport)->fabric->fabric_ip_addr)
 
 /**
  * bfa fcs port public functions
index c59d663..b82540a 100644 (file)
@@ -82,7 +82,7 @@ enum {
 };
 
 /*
- * defintions for CT reason code
+ * definitions for CT reason code
  */
 enum {
        CT_RSN_INV_CMD          = 0x01,
@@ -129,7 +129,7 @@ enum {
 };
 
 /*
- * defintions for the explanation code for all servers
+ * definitions for the explanation code for all servers
  */
 enum {
        CT_EXP_AUTH_EXCEPTION                   = 0xF1,
@@ -193,11 +193,11 @@ struct fcgs_rftid_req_s {
 #define FC_GS_FCP_FC4_FEATURE_TARGET    0x01
 
 struct fcgs_rffid_req_s{
-    u32    rsvd          :8;
-    u32    dap           :24;          /* port identifier      */
-    u32    rsvd1         :16;
-    u32    fc4ftr_bits   :8;           /* fc4 feature bits     */
-    u32    fc4_type      :8;           /* corresponding FC4 Type */
+    u32    rsvd:8;
+    u32    dap:24;             /* port identifier      */
+    u32    rsvd1:16;
+    u32    fc4ftr_bits:8;      /* fc4 feature bits     */
+    u32    fc4_type:8; /* corresponding FC4 Type */
 };
 
 /**
index 3e39ba5..14969ee 100644 (file)
@@ -486,14 +486,14 @@ struct fc_rsi_s {
  * see FC-PH-X table 113 & 115 for explanation also FCP table 8
  */
 struct fc_prli_params_s{
-       u32        reserved: 16;
+       u32        reserved:16;
 #ifdef __BIGENDIAN
-       u32        reserved1: 5;
-       u32        rec_support : 1;
-       u32        task_retry_id : 1;
-       u32        retry : 1;
+       u32        reserved1:5;
+       u32        rec_support:1;
+       u32        task_retry_id:1;
+       u32        retry:1;
 
-       u32        confirm : 1;
+       u32        confirm:1;
        u32        doverlay:1;
        u32        initiator:1;
        u32        target:1;
@@ -502,10 +502,10 @@ struct fc_prli_params_s{
        u32        rxrdisab:1;
        u32        wxrdisab:1;
 #else
-       u32        retry : 1;
-       u32        task_retry_id : 1;
-       u32        rec_support : 1;
-       u32        reserved1: 5;
+       u32        retry:1;
+       u32        task_retry_id:1;
+       u32        rec_support:1;
+       u32        reserved1:5;
 
        u32        wxrdisab:1;
        u32        rxrdisab:1;
@@ -514,7 +514,7 @@ struct fc_prli_params_s{
        u32        target:1;
        u32        initiator:1;
        u32        doverlay:1;
-       u32        confirm : 1;
+       u32        confirm:1;
 #endif
 };
 
index a418ded..f7c7f4f 100644 (file)
@@ -58,49 +58,16 @@ static const u8   port_loop_alpa_map[] = {
 /*
  * Local Functions
  */
-bfa_status_t    bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port,
-                                            u8 alpa);
-
-void            bfa_fcs_port_loop_plogi_response(void *fcsarg,
-                                                struct bfa_fcxp_s *fcxp,
-                                                void *cbarg,
-                                                bfa_status_t req_status,
-                                                u32 rsp_len,
-                                                u32 resid_len,
-                                                struct fchs_s *rsp_fchs);
-
-bfa_status_t    bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port,
-                                            u8 alpa);
-
-void            bfa_fcs_port_loop_adisc_response(void *fcsarg,
-                                                struct bfa_fcxp_s *fcxp,
-                                                void *cbarg,
-                                                bfa_status_t req_status,
-                                                u32 rsp_len,
-                                                u32 resid_len,
-                                                struct fchs_s *rsp_fchs);
-
-bfa_status_t    bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port,
-                                                u8 alpa);
-
-void            bfa_fcs_port_loop_plogi_acc_response(void *fcsarg,
-                                                    struct bfa_fcxp_s *fcxp,
-                                                    void *cbarg,
-                                                    bfa_status_t req_status,
-                                                    u32 rsp_len,
-                                                    u32 resid_len,
-                                                    struct fchs_s *rsp_fchs);
-
-bfa_status_t    bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port,
-                                                u8 alpa);
-
-void            bfa_fcs_port_loop_adisc_acc_response(void *fcsarg,
-                                                    struct bfa_fcxp_s *fcxp,
-                                                    void *cbarg,
-                                                    bfa_status_t req_status,
-                                                    u32 rsp_len,
-                                                    u32 resid_len,
-                                                    struct fchs_s *rsp_fchs);
+static bfa_status_t bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port,
+                                       u8 alpa);
+
+static void bfa_fcs_port_loop_plogi_response(void *fcsarg,
+                                       struct bfa_fcxp_s *fcxp,
+                                       void *cbarg,
+                                       bfa_status_t req_status,
+                                       u32 rsp_len,
+                                       u32 resid_len,
+                                       struct fchs_s *rsp_fchs);
 /**
  *   Called by port to initializar in provate LOOP topology.
  */
@@ -179,7 +146,7 @@ bfa_fcs_port_loop_lip(struct bfa_fcs_port_s *port)
 /**
  * Local Functions.
  */
-bfa_status_t
+static bfa_status_t
 bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa)
 {
        struct fchs_s          fchs;
@@ -208,7 +175,7 @@ bfa_fcs_port_loop_send_plogi(struct bfa_fcs_port_s *port, u8 alpa)
 /**
  *   Called by fcxp to notify the Plogi response
  */
-void
+static void
 bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
                                 void *cbarg, bfa_status_t req_status,
                                 u32 rsp_len, u32 resid_len,
@@ -244,179 +211,3 @@ bfa_fcs_port_loop_plogi_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
                bfa_assert(0);
        }
 }
-
-bfa_status_t
-bfa_fcs_port_loop_send_plogi_acc(struct bfa_fcs_port_s *port, u8 alpa)
-{
-       struct fchs_s          fchs;
-       struct bfa_fcxp_s *fcxp;
-       int             len;
-
-       bfa_trc(port->fcs, alpa);
-
-       fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
-                                 NULL);
-       bfa_assert(fcxp);
-
-       len = fc_plogi_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
-                                bfa_fcs_port_get_fcid(port), 0,
-                                port->port_cfg.pwwn, port->port_cfg.nwwn,
-                                bfa_pport_get_maxfrsize(port->fcs->bfa));
-
-       bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
-                                FC_CLASS_3, len, &fchs,
-                                bfa_fcs_port_loop_plogi_acc_response,
-                                (void *)port, FC_MAX_PDUSZ, 0); /* No response
-                                                                 * expected
-                                                                 */
-
-       return BFA_STATUS_OK;
-}
-
-/*
- *  Plogi Acc Response
- * We donot do any processing here.
- */
-void
-bfa_fcs_port_loop_plogi_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
-                                    void *cbarg, bfa_status_t req_status,
-                                    u32 rsp_len, u32 resid_len,
-                                    struct fchs_s *rsp_fchs)
-{
-
-       struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-
-       bfa_trc(port->fcs, port->pid);
-
-       /*
-        * Sanity Checks
-        */
-       if (req_status != BFA_STATUS_OK) {
-               bfa_trc(port->fcs, req_status);
-               return;
-       }
-}
-
-bfa_status_t
-bfa_fcs_port_loop_send_adisc(struct bfa_fcs_port_s *port, u8 alpa)
-{
-       struct fchs_s          fchs;
-       struct bfa_fcxp_s *fcxp;
-       int             len;
-
-       bfa_trc(port->fcs, alpa);
-
-       fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
-                                 NULL);
-       bfa_assert(fcxp);
-
-       len = fc_adisc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
-                            bfa_fcs_port_get_fcid(port), 0,
-                            port->port_cfg.pwwn, port->port_cfg.nwwn);
-
-       bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
-                         FC_CLASS_3, len, &fchs,
-                         bfa_fcs_port_loop_adisc_response, (void *)port,
-                         FC_MAX_PDUSZ, FC_RA_TOV);
-
-       return BFA_STATUS_OK;
-}
-
-/**
- *   Called by fcxp to notify the ADISC response
- */
-void
-bfa_fcs_port_loop_adisc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
-                                void *cbarg, bfa_status_t req_status,
-                                u32 rsp_len, u32 resid_len,
-                                struct fchs_s *rsp_fchs)
-{
-       struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-       struct bfa_fcs_rport_s *rport;
-       struct fc_adisc_s     *adisc_resp;
-       struct fc_els_cmd_s   *els_cmd;
-       u32        pid = rsp_fchs->s_id;
-
-       bfa_trc(port->fcs, req_status);
-
-       /*
-        * Sanity Checks
-        */
-       if (req_status != BFA_STATUS_OK) {
-               /*
-                * TBD : we may need to retry certain requests
-                */
-               bfa_fcxp_free(fcxp);
-               return;
-       }
-
-       els_cmd = (struct fc_els_cmd_s *) BFA_FCXP_RSP_PLD(fcxp);
-       adisc_resp = (struct fc_adisc_s *) els_cmd;
-
-       if (els_cmd->els_code == FC_ELS_ACC) {
-       } else {
-               bfa_trc(port->fcs, adisc_resp->els_cmd.els_code);
-
-               /*
-                * TBD: we may need to check for reject codes and retry
-                */
-               rport = bfa_fcs_port_get_rport_by_pid(port, pid);
-               if (rport) {
-                       list_del(&rport->qe);
-                       bfa_fcs_rport_delete(rport);
-               }
-
-       }
-       return;
-}
-
-bfa_status_t
-bfa_fcs_port_loop_send_adisc_acc(struct bfa_fcs_port_s *port, u8 alpa)
-{
-       struct fchs_s          fchs;
-       struct bfa_fcxp_s *fcxp;
-       int             len;
-
-       bfa_trc(port->fcs, alpa);
-
-       fcxp = bfa_fcxp_alloc(NULL, port->fcs->bfa, 0, 0, NULL, NULL, NULL,
-                                 NULL);
-       bfa_assert(fcxp);
-
-       len = fc_adisc_acc_build(&fchs, bfa_fcxp_get_reqbuf(fcxp), alpa,
-                                bfa_fcs_port_get_fcid(port), 0,
-                                port->port_cfg.pwwn, port->port_cfg.nwwn);
-
-       bfa_fcxp_send(fcxp, NULL, port->fabric->vf_id, port->lp_tag, BFA_FALSE,
-                               FC_CLASS_3, len, &fchs,
-                               bfa_fcs_port_loop_adisc_acc_response,
-                               (void *)port, FC_MAX_PDUSZ, 0); /* no reponse
-                                                                * expected
-                                                                */
-
-       return BFA_STATUS_OK;
-}
-
-/*
- *  Adisc Acc Response
- * We donot do any processing here.
- */
-void
-bfa_fcs_port_loop_adisc_acc_response(void *fcsarg, struct bfa_fcxp_s *fcxp,
-                                    void *cbarg, bfa_status_t req_status,
-                                    u32 rsp_len, u32 resid_len,
-                                    struct fchs_s *rsp_fchs)
-{
-
-       struct bfa_fcs_port_s *port = (struct bfa_fcs_port_s *) cbarg;
-
-       bfa_trc(port->fcs, port->pid);
-
-       /*
-        * Sanity Checks
-        */
-       if (req_status != BFA_STATUS_OK) {
-               bfa_trc(port->fcs, req_status);
-               return;
-       }
-}
index 8f51a83..1e06792 100644 (file)
@@ -43,7 +43,7 @@ bfa_fcs_cfg_base_port(struct bfa_fcs_s *fcs, struct bfa_port_cfg_s *port_cfg)
 struct bfa_fcs_port_s *
 bfa_fcs_get_base_port(struct bfa_fcs_s *fcs)
 {
-       return (&fcs->fabric.bport);
+       return &fcs->fabric.bport;
 }
 
 wwn_t
@@ -88,11 +88,10 @@ bfa_fcs_port_get_rport(struct bfa_fcs_port_s *port, wwn_t wwn, int index,
        }
 
        bfa_trc(fcs, i);
-       if (rport) {
+       if (rport)
                return rport->pwwn;
-       } else {
+       else
                return (wwn_t) 0;
-       }
 }
 
 void
@@ -198,17 +197,17 @@ bfa_fcs_lookup_port(struct bfa_fcs_s *fcs, u16 vf_id, wwn_t lpwwn)
        vf = bfa_fcs_vf_lookup(fcs, vf_id);
        if (vf == NULL) {
                bfa_trc(fcs, vf_id);
-               return (NULL);
+               return NULL;
        }
 
        if (!lpwwn || (vf->bport.port_cfg.pwwn == lpwwn))
-               return (&vf->bport);
+               return &vf->bport;
 
        vport = bfa_fcs_fabric_vport_lookup(vf, lpwwn);
        if (vport)
-               return (&vport->lport);
+               return &vport->lport;
 
-       return (NULL);
+       return NULL;
 }
 
 /*
index 59fea99..2f8b880 100644 (file)
@@ -932,11 +932,10 @@ bfa_fcs_port_ns_send_rff_id(void *ns_cbarg, struct bfa_fcxp_s *fcxp_alloced)
        }
        ns->fcxp = fcxp;
 
-       if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port)) {
+       if (BFA_FCS_VPORT_IS_INITIATOR_MODE(ns->port))
                fc4_ftrs = FC_GS_FCP_FC4_FEATURE_INITIATOR;
-       } else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port)) {
+       else if (BFA_FCS_VPORT_IS_TARGET_MODE(ns->port))
                fc4_ftrs = FC_GS_FCP_FC4_FEATURE_TARGET;
-       }
 
        len = fc_rffid_build(&fchs, bfa_fcxp_get_reqbuf(fcxp),
                             bfa_fcs_port_get_fcid(port), 0, FC_TYPE_FCP,
index 86af818..fcb8864 100644 (file)
@@ -180,5 +180,5 @@ bfa_plog_disable(struct bfa_plog_s *plog)
 bfa_boolean_t
 bfa_plog_get_setting(struct bfa_plog_s *plog)
 {
-       return((bfa_boolean_t)plog->plog_enabled);
+       return (bfa_boolean_t)plog->plog_enabled;
 }
index 8a1f59d..e1932c8 100644 (file)
@@ -79,7 +79,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
        bfa_trc(rport->fcs, event);
 
        switch (event) {
-       case RPFSM_EVENT_RPORT_ONLINE :
+       case RPFSM_EVENT_RPORT_ONLINE:
                if (!BFA_FCS_PID_IS_WKA(rport->pid)) {
                        bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
                        rpf->rpsc_retries = 0;
@@ -87,7 +87,7 @@ bfa_fcs_rpf_sm_uninit(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
                        break;
                };
 
-       case RPFSM_EVENT_RPORT_OFFLINE :
+       case RPFSM_EVENT_RPORT_OFFLINE:
                break;
 
        default:
@@ -107,7 +107,7 @@ bfa_fcs_rpf_sm_rpsc_sending(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc);
                break;
 
-       case RPFSM_EVENT_RPORT_OFFLINE :
+       case RPFSM_EVENT_RPORT_OFFLINE:
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
                bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rpf->fcxp_wqe);
                rpf->rpsc_retries = 0;
@@ -130,11 +130,10 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
        case RPFSM_EVENT_RPSC_COMP:
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_online);
                /* Update speed info in f/w via BFA */
-               if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN) {
+               if (rpf->rpsc_speed != BFA_PPORT_SPEED_UNKNOWN)
                        bfa_rport_speed(rport->bfa_rport, rpf->rpsc_speed);
-               } else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN) {
+               else if (rpf->assigned_speed != BFA_PPORT_SPEED_UNKNOWN)
                        bfa_rport_speed(rport->bfa_rport, rpf->assigned_speed);
-               }
                break;
 
        case RPFSM_EVENT_RPSC_FAIL:
@@ -154,7 +153,7 @@ bfa_fcs_rpf_sm_rpsc(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
                }
                break;
 
-       case RPFSM_EVENT_RPORT_OFFLINE :
+       case RPFSM_EVENT_RPORT_OFFLINE:
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
                bfa_fcxp_discard(rpf->fcxp);
                rpf->rpsc_retries = 0;
@@ -174,13 +173,13 @@ bfa_fcs_rpf_sm_rpsc_retry(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
        bfa_trc(rport->fcs, event);
 
        switch (event) {
-       case RPFSM_EVENT_TIMEOUT :
+       case RPFSM_EVENT_TIMEOUT:
                /* re-send the RPSC */
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
                bfa_fcs_rpf_send_rpsc2(rpf, NULL);
                break;
 
-       case RPFSM_EVENT_RPORT_OFFLINE :
+       case RPFSM_EVENT_RPORT_OFFLINE:
                bfa_timer_stop(&rpf->timer);
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
                rpf->rpsc_retries = 0;
@@ -201,7 +200,7 @@ bfa_fcs_rpf_sm_online(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
        bfa_trc(rport->fcs, event);
 
        switch (event) {
-       case RPFSM_EVENT_RPORT_OFFLINE :
+       case RPFSM_EVENT_RPORT_OFFLINE:
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_offline);
                rpf->rpsc_retries = 0;
                break;
@@ -221,12 +220,12 @@ bfa_fcs_rpf_sm_offline(struct bfa_fcs_rpf_s *rpf, enum rpf_event event)
        bfa_trc(rport->fcs, event);
 
        switch (event) {
-       case RPFSM_EVENT_RPORT_ONLINE :
+       case RPFSM_EVENT_RPORT_ONLINE:
                bfa_sm_set_state(rpf, bfa_fcs_rpf_sm_rpsc_sending);
                bfa_fcs_rpf_send_rpsc2(rpf, NULL);
                break;
 
-       case RPFSM_EVENT_RPORT_OFFLINE :
+       case RPFSM_EVENT_RPORT_OFFLINE:
                break;
 
        default:
@@ -366,10 +365,9 @@ bfa_fcs_rpf_rpsc2_response(void *fcsarg, struct bfa_fcxp_s *fcxp, void *cbarg,
                bfa_trc(rport->fcs, ls_rjt->reason_code);
                bfa_trc(rport->fcs, ls_rjt->reason_code_expl);
                rport->stats.rpsc_rejects++;
-               if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP) {
+               if (ls_rjt->reason_code == FC_LS_RJT_RSN_CMD_NOT_SUPP)
                        bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_FAIL);
-               } else {
+               else
                        bfa_sm_send_event(rpf, RPFSM_EVENT_RPSC_ERROR);
-               }
        }
 }
index 31d81fe..391a479 100644 (file)
@@ -189,7 +189,7 @@ bfa_fcs_vf_lookup(struct bfa_fcs_s *fcs, u16 vf_id)
 {
        bfa_trc(fcs, vf_id);
        if (vf_id == FC_VF_ID_NULL)
-               return (&fcs->fabric);
+               return &fcs->fabric;
 
        /**
         * @todo vf support
index c10af06..e90f1e3 100644 (file)
 
 BFA_TRC_FILE(FCS, VPORT);
 
-#define __vport_fcs(__vp)       (__vp)->lport.fcs
-#define __vport_pwwn(__vp)      (__vp)->lport.port_cfg.pwwn
-#define __vport_nwwn(__vp)      (__vp)->lport.port_cfg.nwwn
-#define __vport_bfa(__vp)       (__vp)->lport.fcs->bfa
-#define __vport_fcid(__vp)      (__vp)->lport.pid
-#define __vport_fabric(__vp)    (__vp)->lport.fabric
-#define __vport_vfid(__vp)      (__vp)->lport.fabric->vf_id
+#define __vport_fcs(__vp)       ((__vp)->lport.fcs)
+#define __vport_pwwn(__vp)      ((__vp)->lport.port_cfg.pwwn)
+#define __vport_nwwn(__vp)      ((__vp)->lport.port_cfg.nwwn)
+#define __vport_bfa(__vp)       ((__vp)->lport.fcs->bfa)
+#define __vport_fcid(__vp)      ((__vp)->lport.pid)
+#define __vport_fabric(__vp)    ((__vp)->lport.fabric)
+#define __vport_vfid(__vp)      ((__vp)->lport.fabric->vf_id)
 
 #define BFA_FCS_VPORT_MAX_RETRIES  5
 /*
@@ -641,9 +641,9 @@ bfa_fcs_vport_get_max(struct bfa_fcs_s *fcs)
        bfa_get_attr(fcs->bfa, &ioc_attr);
 
        if (ioc_attr.pci_attr.device_id == BFA_PCI_DEVICE_ID_CT)
-               return (BFA_FCS_MAX_VPORTS_SUPP_CT);
+               return BFA_FCS_MAX_VPORTS_SUPP_CT;
        else
-               return (BFA_FCS_MAX_VPORTS_SUPP_CB);
+               return BFA_FCS_MAX_VPORTS_SUPP_CB;
 }
 
 
@@ -675,7 +675,7 @@ bfa_fcs_vport_create(struct bfa_fcs_vport_s *vport, struct bfa_fcs_s *fcs,
                     struct bfad_vport_s *vport_drv)
 {
        if (vport_cfg->pwwn == 0)
-               return (BFA_STATUS_INVALID_WWN);
+               return BFA_STATUS_INVALID_WWN;
 
        if (bfa_fcs_port_get_pwwn(&fcs->fabric.bport) == vport_cfg->pwwn)
                return BFA_STATUS_VPORT_WWN_BP;
index 5edde1a..2b973f3 100644 (file)
@@ -232,7 +232,6 @@ 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
index cafb888..132898c 100644 (file)
@@ -1161,9 +1161,6 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
        struct bnx2i_cmd *cmd = task->dd_data;
        struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
 
-       if (!bnx2i_conn->is_bound)
-               return -ENOTCONN;
-
        /*
         * If there is no scsi_cmnd this must be a mgmt task
         */
@@ -1371,7 +1368,6 @@ static int bnx2i_conn_bind(struct iscsi_cls_session *cls_session,
        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);
@@ -1883,7 +1879,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
 
        bnx2i_ep = ep->dd_data;
 
-       /* driver should not attempt connection cleanup untill TCP_CONNECT
+       /* driver should not attempt connection cleanup until TCP_CONNECT
         * completes either successfully or fails. Timeout is 9-secs, so
         * wait for it to complete
         */
@@ -1896,9 +1892,7 @@ static void bnx2i_ep_disconnect(struct iscsi_endpoint *ep)
                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);
+               iscsi_suspend_queue(conn);
        }
 
        hba = bnx2i_ep->hba;
@@ -2034,7 +2028,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
                                  ISCSI_USERNAME | ISCSI_PASSWORD |
                                  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
                                  ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
-                                 ISCSI_LU_RESET_TMO |
+                                 ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
                                  ISCSI_PING_TMO | ISCSI_RECV_TMO |
                                  ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
        .host_param_mask        = ISCSI_HOST_HWADDRESS | ISCSI_HOST_NETDEV_NAME,
index 63abb06..9129bcf 100644 (file)
@@ -141,6 +141,7 @@ static const struct value_name_pair serv_out12_arr[] = {
 static const struct value_name_pair serv_in16_arr[] = {
        {0x10, "Read capacity(16)"},
        {0x11, "Read long(16)"},
+       {0x12, "Get LBA status"},
 };
 #define SERV_IN16_SZ ARRAY_SIZE(serv_in16_arr)
 
index 2631bdd..969c831 100644 (file)
@@ -937,7 +937,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
                                ISCSI_USERNAME | ISCSI_PASSWORD |
                                ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
                                ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
-                               ISCSI_LU_RESET_TMO |
+                               ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
                                ISCSI_PING_TMO | ISCSI_RECV_TMO |
                                ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
        .host_param_mask        = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
index 075e239..6c59c02 100644 (file)
@@ -1509,7 +1509,7 @@ static u8 start_scsi(struct AdapterCtlBlk* acb, struct DeviceCtlBlk* dcb,
                 * Try anyway?
                 *
                 * We could, BUT: Sometimes the TRM_S1040 misses to produce a Selection
-                * Timeout, a Disconnect or a Reselction IRQ, so we would be screwed!
+                * Timeout, a Disconnect or a Reselection IRQ, so we would be screwed!
                 * (This is likely to be a bug in the hardware. Obviously, most people
                 *  only have one initiator per SCSI bus.)
                 * Instead let this fail and have the timer make sure the command is 
index 3ee1cbc..e19a1a5 100644 (file)
@@ -226,7 +226,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr,
                         * Activate a device handler
                         */
                        if (scsi_dh->activate)
-                               err = scsi_dh->activate(sdev);
+                               err = scsi_dh->activate(sdev, NULL, NULL);
                        else
                                err = 0;
                }
@@ -304,18 +304,15 @@ static int scsi_dh_notifier(struct notifier_block *nb,
        sdev = to_scsi_device(dev);
 
        if (action == BUS_NOTIFY_ADD_DEVICE) {
+               err = device_create_file(dev, &scsi_dh_state_attr);
+               /* don't care about err */
                devinfo = device_handler_match(NULL, sdev);
-               if (!devinfo)
-                       goto out;
-
-               err = scsi_dh_handler_attach(sdev, devinfo);
-               if (!err)
-                       err = device_create_file(dev, &scsi_dh_state_attr);
+               if (devinfo)
+                       err = scsi_dh_handler_attach(sdev, devinfo);
        } else if (action == BUS_NOTIFY_DEL_DEVICE) {
                device_remove_file(dev, &scsi_dh_state_attr);
                scsi_dh_handler_detach(sdev, NULL);
        }
-out:
        return err;
 }
 
@@ -423,10 +420,17 @@ EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
 /*
  * scsi_dh_activate - activate the path associated with the scsi_device
  *      corresponding to the given request queue.
- * @q - Request queue that is associated with the scsi_device to be
- *      activated.
+ *     Returns immediately without waiting for activation to be completed.
+ * @q    - Request queue that is associated with the scsi_device to be
+ *         activated.
+ * @fn   - Function to be called upon completion of the activation.
+ *         Function fn is called with data (below) and the error code.
+ *         Function fn may be called from the same calling context. So,
+ *         do not hold the lock in the caller which may be needed in fn.
+ * @data - data passed to the function fn upon completion.
+ *
  */
-int scsi_dh_activate(struct request_queue *q)
+int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
 {
        int err = 0;
        unsigned long flags;
@@ -445,7 +449,7 @@ int scsi_dh_activate(struct request_queue *q)
                return err;
 
        if (scsi_dh->activate)
-               err = scsi_dh->activate(sdev);
+               err = scsi_dh->activate(sdev, fn, data);
        put_device(&sdev->sdev_gendev);
        return err;
 }
index b5cdefa..4f0d013 100644 (file)
@@ -60,11 +60,17 @@ struct alua_dh_data {
        int                     bufflen;
        unsigned char           sense[SCSI_SENSE_BUFFERSIZE];
        int                     senselen;
+       struct scsi_device      *sdev;
+       activate_complete       callback_fn;
+       void                    *callback_data;
 };
 
 #define ALUA_POLICY_SWITCH_CURRENT     0
 #define ALUA_POLICY_SWITCH_ALL         1
 
+static char print_alua_state(int);
+static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *);
+
 static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
 {
        struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -230,19 +236,72 @@ done:
        return err;
 }
 
+/*
+ * alua_stpg - Evaluate SET TARGET GROUP STATES
+ * @sdev: the device to be evaluated
+ * @state: the new target group state
+ *
+ * Send a SET TARGET GROUP STATES command to the device.
+ * We only have to test here if we should resubmit the command;
+ * any other error is assumed as a failure.
+ */
+static void stpg_endio(struct request *req, int error)
+{
+       struct alua_dh_data *h = req->end_io_data;
+       struct scsi_sense_hdr sense_hdr;
+       unsigned err = SCSI_DH_IO;
+
+       if (error || host_byte(req->errors) != DID_OK ||
+                       msg_byte(req->errors) != COMMAND_COMPLETE)
+               goto done;
+
+       if (err == SCSI_DH_IO && h->senselen > 0) {
+               err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
+                                          &sense_hdr);
+               if (!err) {
+                       err = SCSI_DH_IO;
+                       goto done;
+               }
+               err = alua_check_sense(h->sdev, &sense_hdr);
+               if (err == ADD_TO_MLQUEUE) {
+                       err = SCSI_DH_RETRY;
+                       goto done;
+               }
+               sdev_printk(KERN_INFO, h->sdev,
+                           "%s: stpg sense code: %02x/%02x/%02x\n",
+                           ALUA_DH_NAME, sense_hdr.sense_key,
+                           sense_hdr.asc, sense_hdr.ascq);
+               err = SCSI_DH_IO;
+       }
+       if (err == SCSI_DH_OK) {
+               h->state = TPGS_STATE_OPTIMIZED;
+               sdev_printk(KERN_INFO, h->sdev,
+                           "%s: port group %02x switched to state %c\n",
+                           ALUA_DH_NAME, h->group_id,
+                           print_alua_state(h->state));
+       }
+done:
+       blk_put_request(req);
+       if (h->callback_fn) {
+               h->callback_fn(h->callback_data, err);
+               h->callback_fn = h->callback_data = NULL;
+       }
+       return;
+}
+
 /*
  * submit_stpg - Issue a SET TARGET GROUP STATES command
- * @sdev: sdev the command should be sent to
  *
  * Currently we're only setting the current target port group state
  * to 'active/optimized' and let the array firmware figure out
  * the states of the remaining groups.
  */
-static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
+static unsigned submit_stpg(struct alua_dh_data *h)
 {
        struct request *rq;
        int err = SCSI_DH_RES_TEMP_UNAVAIL;
        int stpg_len = 8;
+       struct scsi_device *sdev = h->sdev;
 
        /* Prepare the data buffer */
        memset(h->buff, 0, stpg_len);
@@ -252,7 +311,7 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
 
        rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
        if (!rq)
-               goto done;
+               return SCSI_DH_RES_TEMP_UNAVAIL;
 
        /* Prepare the command. */
        rq->cmd[0] = MAINTENANCE_OUT;
@@ -266,17 +325,9 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
        rq->sense = h->sense;
        memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
        rq->sense_len = h->senselen = 0;
+       rq->end_io_data = h;
 
-       err = blk_execute_rq(rq->q, NULL, rq, 1);
-       if (err == -EIO) {
-               sdev_printk(KERN_INFO, sdev,
-                           "%s: stpg failed with %x\n",
-                           ALUA_DH_NAME, rq->errors);
-               h->senselen = rq->sense_len;
-               err = SCSI_DH_IO;
-       }
-       blk_put_request(rq);
-done:
+       blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
        return err;
 }
 
@@ -476,50 +527,6 @@ static int alua_check_sense(struct scsi_device *sdev,
        return SCSI_RETURN_NOT_HANDLED;
 }
 
-/*
- * alua_stpg - Evaluate SET TARGET GROUP STATES
- * @sdev: the device to be evaluated
- * @state: the new target group state
- *
- * Send a SET TARGET GROUP STATES command to the device.
- * We only have to test here if we should resubmit the command;
- * any other error is assumed as a failure.
- */
-static int alua_stpg(struct scsi_device *sdev, int state,
-                    struct alua_dh_data *h)
-{
-       struct scsi_sense_hdr sense_hdr;
-       unsigned err;
-       int retry = ALUA_FAILOVER_RETRIES;
-
- retry:
-       err = submit_stpg(sdev, h);
-       if (err == SCSI_DH_IO && h->senselen > 0) {
-               err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
-                                          &sense_hdr);
-               if (!err)
-                       return SCSI_DH_IO;
-               err = alua_check_sense(sdev, &sense_hdr);
-               if (retry > 0 && err == ADD_TO_MLQUEUE) {
-                       retry--;
-                       goto retry;
-               }
-               sdev_printk(KERN_INFO, sdev,
-                           "%s: stpg sense code: %02x/%02x/%02x\n",
-                           ALUA_DH_NAME, sense_hdr.sense_key,
-                           sense_hdr.asc, sense_hdr.ascq);
-               err = SCSI_DH_IO;
-       }
-       if (err == SCSI_DH_OK) {
-               h->state = state;
-               sdev_printk(KERN_INFO, sdev,
-                           "%s: port group %02x switched to state %c\n",
-                           ALUA_DH_NAME, h->group_id,
-                           print_alua_state(h->state) );
-       }
-       return err;
-}
-
 /*
  * alua_rtpg - Evaluate REPORT TARGET GROUP STATES
  * @sdev: the device to be evaluated.
@@ -652,7 +659,8 @@ out:
  * based on a certain policy. But until we actually encounter them it
  * should be okay.
  */
-static int alua_activate(struct scsi_device *sdev)
+static int alua_activate(struct scsi_device *sdev,
+                       activate_complete fn, void *data)
 {
        struct alua_dh_data *h = get_alua_data(sdev);
        int err = SCSI_DH_OK;
@@ -663,11 +671,19 @@ static int alua_activate(struct scsi_device *sdev)
                        goto out;
        }
 
-       if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED)
-               err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h);
+       if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) {
+               h->callback_fn = fn;
+               h->callback_data = data;
+               err = submit_stpg(h);
+               if (err == SCSI_DH_OK)
+                       return 0;
+               h->callback_fn = h->callback_data = NULL;
+       }
 
 out:
-       return err;
+       if (fn)
+               fn(data, err);
+       return 0;
 }
 
 /*
@@ -745,6 +761,7 @@ static int alua_bus_attach(struct scsi_device *sdev)
        h->rel_port = -1;
        h->buff = h->inq;
        h->bufflen = ALUA_INQUIRY_SIZE;
+       h->sdev = sdev;
 
        err = alua_initialize(sdev, h);
        if (err != SCSI_DH_OK)
index 0cffe84..6196675 100644 (file)
@@ -528,7 +528,8 @@ retry:
        return err;
 }
 
-static int clariion_activate(struct scsi_device *sdev)
+static int clariion_activate(struct scsi_device *sdev,
+                               activate_complete fn, void *data)
 {
        struct clariion_dh_data *csdev = get_clariion_data(sdev);
        int result;
@@ -559,7 +560,9 @@ done:
                    csdev->port, lun_state[csdev->lun_state],
                    csdev->default_sp + 'A');
 
-       return result;
+       if (fn)
+               fn(data, result);
+       return 0;
 }
 /*
  * params - parameters in the following format
index f7da753..857fdd6 100644 (file)
@@ -39,8 +39,14 @@ struct hp_sw_dh_data {
        unsigned char sense[SCSI_SENSE_BUFFERSIZE];
        int path_state;
        int retries;
+       int retry_cnt;
+       struct scsi_device *sdev;
+       activate_complete       callback_fn;
+       void                    *callback_data;
 };
 
+static int hp_sw_start_stop(struct hp_sw_dh_data *);
+
 static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
 {
        struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -191,19 +197,53 @@ static int start_done(struct scsi_device *sdev, unsigned char *sense)
        return rc;
 }
 
+static void start_stop_endio(struct request *req, int error)
+{
+       struct hp_sw_dh_data *h = req->end_io_data;
+       unsigned err = SCSI_DH_OK;
+
+       if (error || host_byte(req->errors) != DID_OK ||
+                       msg_byte(req->errors) != COMMAND_COMPLETE) {
+               sdev_printk(KERN_WARNING, h->sdev,
+                           "%s: sending start_stop_unit failed with %x\n",
+                           HP_SW_NAME, req->errors);
+               err = SCSI_DH_IO;
+               goto done;
+       }
+
+       if (req->sense_len > 0) {
+               err = start_done(h->sdev, h->sense);
+               if (err == SCSI_DH_RETRY) {
+                       err = SCSI_DH_IO;
+                       if (--h->retry_cnt) {
+                               blk_put_request(req);
+                               err = hp_sw_start_stop(h);
+                               if (err == SCSI_DH_OK)
+                                       return;
+                       }
+               }
+       }
+done:
+       blk_put_request(req);
+       if (h->callback_fn) {
+               h->callback_fn(h->callback_data, err);
+               h->callback_fn = h->callback_data = NULL;
+       }
+       return;
+
+}
+
 /*
  * hp_sw_start_stop - Send START STOP UNIT command
  * @sdev: sdev command should be sent to
  *
  * Sending START STOP UNIT activates the SP.
  */
-static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
+static int hp_sw_start_stop(struct hp_sw_dh_data *h)
 {
        struct request *req;
-       int ret, retry;
 
-retry:
-       req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
+       req = blk_get_request(h->sdev->request_queue, WRITE, GFP_ATOMIC);
        if (!req)
                return SCSI_DH_RES_TEMP_UNAVAIL;
 
@@ -217,32 +257,10 @@ retry:
        req->sense = h->sense;
        memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
        req->sense_len = 0;
-       retry = h->retries;
-
-       ret = blk_execute_rq(req->q, NULL, req, 1);
-       if (ret == -EIO) {
-               if (req->sense_len > 0) {
-                       ret = start_done(sdev, h->sense);
-               } else {
-                       sdev_printk(KERN_WARNING, sdev,
-                                   "%s: sending start_stop_unit failed with %x\n",
-                                   HP_SW_NAME, req->errors);
-                       ret = SCSI_DH_IO;
-               }
-       } else
-               ret = SCSI_DH_OK;
+       req->end_io_data = h;
 
-       if (ret == SCSI_DH_RETRY) {
-               if (--retry) {
-                       blk_put_request(req);
-                       goto retry;
-               }
-               ret = SCSI_DH_IO;
-       }
-
-       blk_put_request(req);
-
-       return ret;
+       blk_execute_rq_nowait(req->q, NULL, req, 1, start_stop_endio);
+       return SCSI_DH_OK;
 }
 
 static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
@@ -268,7 +286,8 @@ static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
  * activate the passive path (and deactivate the
  * previously active one).
  */
-static int hp_sw_activate(struct scsi_device *sdev)
+static int hp_sw_activate(struct scsi_device *sdev,
+                               activate_complete fn, void *data)
 {
        int ret = SCSI_DH_OK;
        struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
@@ -276,14 +295,18 @@ static int hp_sw_activate(struct scsi_device *sdev)
        ret = hp_sw_tur(sdev, h);
 
        if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
-               ret = hp_sw_start_stop(sdev, h);
+               h->retry_cnt = h->retries;
+               h->callback_fn = fn;
+               h->callback_data = data;
+               ret = hp_sw_start_stop(h);
                if (ret == SCSI_DH_OK)
-                       sdev_printk(KERN_INFO, sdev,
-                                   "%s: activated path\n",
-                                   HP_SW_NAME);
+                       return 0;
+               h->callback_fn = h->callback_data = NULL;
        }
 
-       return ret;
+       if (fn)
+               fn(data, ret);
+       return 0;
 }
 
 static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
@@ -326,6 +349,7 @@ static int hp_sw_bus_attach(struct scsi_device *sdev)
        h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
        h->path_state = HP_SW_PATH_UNINITIALIZED;
        h->retries = HP_SW_RETRIES;
+       h->sdev = sdev;
 
        ret = hp_sw_tur(sdev, h);
        if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
index 268189d..47cfe1c 100644 (file)
@@ -22,6 +22,7 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_dh.h>
+#include <linux/workqueue.h>
 
 #define RDAC_NAME "rdac"
 #define RDAC_RETRY_COUNT 5
@@ -138,7 +139,13 @@ struct rdac_controller {
        } mode_select;
        u8      index;
        u8      array_name[ARRAY_LABEL_LEN];
+       spinlock_t              ms_lock;
+       int                     ms_queued;
+       struct work_struct      ms_work;
+       struct scsi_device      *ms_sdev;
+       struct list_head        ms_head;
 };
+
 struct c8_inquiry {
        u8      peripheral_info;
        u8      page_code; /* 0xC8 */
@@ -198,8 +205,17 @@ static const char *lun_state[] =
        "owned (AVT mode)",
 };
 
+struct rdac_queue_data {
+       struct list_head        entry;
+       struct rdac_dh_data     *h;
+       activate_complete       callback_fn;
+       void                    *callback_data;
+};
+
 static LIST_HEAD(ctlr_list);
 static DEFINE_SPINLOCK(list_lock);
+static struct workqueue_struct *kmpath_rdacd;
+static void send_mode_select(struct work_struct *work);
 
 /*
  * module parameter to enable rdac debug logging.
@@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
                rdac_pg->subpage_code = 0x1;
                rdac_pg->page_len[0] = 0x01;
                rdac_pg->page_len[1] = 0x28;
-               rdac_pg->lun_table[h->lun] = 0x81;
        } else {
                struct rdac_pg_legacy *rdac_pg;
 
@@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,
                common = &rdac_pg->common;
                rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
                rdac_pg->page_len = 0x68;
-               rdac_pg->lun_table[h->lun] = 0x81;
        }
        common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
        common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
@@ -325,6 +339,7 @@ static void release_controller(struct kref *kref)
        struct rdac_controller *ctlr;
        ctlr = container_of(kref, struct rdac_controller, kref);
 
+       flush_workqueue(kmpath_rdacd);
        spin_lock(&list_lock);
        list_del(&ctlr->node);
        spin_unlock(&list_lock);
@@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
 
        kref_init(&ctlr->kref);
        ctlr->use_ms10 = -1;
+       ctlr->ms_queued = 0;
+       ctlr->ms_sdev = NULL;
+       spin_lock_init(&ctlr->ms_lock);
+       INIT_WORK(&ctlr->ms_work, send_mode_select);
+       INIT_LIST_HEAD(&ctlr->ms_head);
        list_add(&ctlr->node, &ctlr_list);
 done:
        spin_unlock(&list_lock);
@@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
 }
 
 static int mode_select_handle_sense(struct scsi_device *sdev,
-                                   unsigned char *sensebuf)
+                                       unsigned char *sensebuf)
 {
        struct scsi_sense_hdr sense_hdr;
        int err = SCSI_DH_IO, ret;
@@ -533,11 +553,29 @@ done:
        return err;
 }
 
-static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h)
+static void send_mode_select(struct work_struct *work)
 {
+       struct rdac_controller *ctlr =
+               container_of(work, struct rdac_controller, ms_work);
        struct request *rq;
+       struct scsi_device *sdev = ctlr->ms_sdev;
+       struct rdac_dh_data *h = get_rdac_data(sdev);
        struct request_queue *q = sdev->request_queue;
        int err, retry_cnt = RDAC_RETRY_COUNT;
+       struct rdac_queue_data *tmp, *qdata;
+       LIST_HEAD(list);
+       u8 *lun_table;
+
+       spin_lock(&ctlr->ms_lock);
+       list_splice_init(&ctlr->ms_head, &list);
+       ctlr->ms_queued = 0;
+       ctlr->ms_sdev = NULL;
+       spin_unlock(&ctlr->ms_lock);
+
+       if (ctlr->use_ms10)
+               lun_table = ctlr->mode_select.expanded.lun_table;
+       else
+               lun_table = ctlr->mode_select.legacy.lun_table;
 
 retry:
        err = SCSI_DH_RES_TEMP_UNAVAIL;
@@ -545,6 +583,10 @@ retry:
        if (!rq)
                goto done;
 
+       list_for_each_entry(qdata, &list, entry) {
+               lun_table[qdata->h->lun] = 0x81;
+       }
+
        RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
                "%s MODE_SELECT command",
                (char *) h->ctlr->array_name, h->ctlr->index,
@@ -565,10 +607,45 @@ retry:
        }
 
 done:
-       return err;
+       list_for_each_entry_safe(qdata, tmp, &list, entry) {
+               list_del(&qdata->entry);
+               if (err == SCSI_DH_OK)
+                       qdata->h->state = RDAC_STATE_ACTIVE;
+               if (qdata->callback_fn)
+                       qdata->callback_fn(qdata->callback_data, err);
+               kfree(qdata);
+       }
+       return;
+}
+
+static int queue_mode_select(struct scsi_device *sdev,
+                               activate_complete fn, void *data)
+{
+       struct rdac_queue_data *qdata;
+       struct rdac_controller *ctlr;
+
+       qdata = kzalloc(sizeof(*qdata), GFP_KERNEL);
+       if (!qdata)
+               return SCSI_DH_RETRY;
+
+       qdata->h = get_rdac_data(sdev);
+       qdata->callback_fn = fn;
+       qdata->callback_data = data;
+
+       ctlr = qdata->h->ctlr;
+       spin_lock(&ctlr->ms_lock);
+       list_add_tail(&qdata->entry, &ctlr->ms_head);
+       if (!ctlr->ms_queued) {
+               ctlr->ms_queued = 1;
+               ctlr->ms_sdev = sdev;
+               queue_work(kmpath_rdacd, &ctlr->ms_work);
+       }
+       spin_unlock(&ctlr->ms_lock);
+       return SCSI_DH_OK;
 }
 
-static int rdac_activate(struct scsi_device *sdev)
+static int rdac_activate(struct scsi_device *sdev,
+                       activate_complete fn, void *data)
 {
        struct rdac_dh_data *h = get_rdac_data(sdev);
        int err = SCSI_DH_OK;
@@ -577,10 +654,15 @@ static int rdac_activate(struct scsi_device *sdev)
        if (err != SCSI_DH_OK)
                goto done;
 
-       if (h->lun_state == RDAC_LUN_UNOWNED)
-               err = send_mode_select(sdev, h);
+       if (h->lun_state == RDAC_LUN_UNOWNED) {
+               err = queue_mode_select(sdev, fn, data);
+               if (err == SCSI_DH_OK)
+                       return 0;
+       }
 done:
-       return err;
+       if (fn)
+               fn(data, err);
+       return 0;
 }
 
 static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
@@ -790,13 +872,26 @@ static int __init rdac_init(void)
        int r;
 
        r = scsi_register_device_handler(&rdac_dh);
-       if (r != 0)
+       if (r != 0) {
                printk(KERN_ERR "Failed to register scsi device handler.");
+               goto done;
+       }
+
+       /*
+        * Create workqueue to handle mode selects for rdac
+        */
+       kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd");
+       if (!kmpath_rdacd) {
+               scsi_unregister_device_handler(&rdac_dh);
+               printk(KERN_ERR "kmpath_rdacd creation failed.\n");
+       }
+done:
        return r;
 }
 
 static void __exit rdac_exit(void)
 {
+       destroy_workqueue(kmpath_rdacd);
        scsi_unregister_device_handler(&rdac_dh);
 }
 
index fa738ec..207352c 100644 (file)
@@ -31,7 +31,7 @@
 #include <scsi/scsi_host.h>
 
 /*
- * Defintions for the generic 5380 driver.
+ * Definitions for the generic 5380 driver.
  */
 #define AUTOSENSE
 
index 704b8e0..a30ffaa 100644 (file)
@@ -66,14 +66,14 @@ LIST_HEAD(fcoe_hostlist);
 DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
 
 /* Function Prototypes */
-static int fcoe_reset(struct Scsi_Host *shost);
+static int fcoe_reset(struct Scsi_Host *);
 static int fcoe_xmit(struct fc_lport *, struct fc_frame *);
 static int fcoe_rcv(struct sk_buff *, struct net_device *,
                    struct packet_type *, struct net_device *);
-static int fcoe_percpu_receive_thread(void *arg);
-static void fcoe_clean_pending_queue(struct fc_lport *lp);
-static void fcoe_percpu_clean(struct fc_lport *lp);
-static int fcoe_link_ok(struct fc_lport *lp);
+static int fcoe_percpu_receive_thread(void *);
+static void fcoe_clean_pending_queue(struct fc_lport *);
+static void fcoe_percpu_clean(struct fc_lport *);
+static int fcoe_link_ok(struct fc_lport *);
 
 static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
 static int fcoe_hostlist_add(const struct fc_lport *);
@@ -82,15 +82,69 @@ 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);
-static struct fcoe_interface *
-       fcoe_hostlist_lookup_port(const struct net_device *dev);
+static struct fcoe_interface
+*fcoe_hostlist_lookup_port(const struct net_device *);
+
+static int fcoe_fip_recv(struct sk_buff *, struct net_device *,
+                        struct packet_type *, struct net_device *);
+
+static void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *);
+static void fcoe_update_src_mac(struct fc_lport *, u8 *);
+static u8 *fcoe_get_src_mac(struct fc_lport *);
+static void fcoe_destroy_work(struct work_struct *);
+
+static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *,
+                         unsigned int);
+static int fcoe_ddp_done(struct fc_lport *, u16);
+
+static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *);
+
+static int fcoe_create(const char *, struct kernel_param *);
+static int fcoe_destroy(const char *, struct kernel_param *);
+
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
+                                     u32 did, struct fc_frame *,
+                                     unsigned int op,
+                                     void (*resp)(struct fc_seq *,
+                                                  struct fc_frame *,
+                                                  void *),
+                                     void *, u32 timeout);
+static void fcoe_recv_frame(struct sk_buff *skb);
+
+static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
+
+module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(create, "string");
+MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
+module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
+__MODULE_PARM_TYPE(destroy, "string");
+MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
 
-/* notification function from net device */
+/* notification function for packets from net device */
 static struct notifier_block fcoe_notifier = {
        .notifier_call = fcoe_device_notification,
 };
 
-static struct scsi_transport_template *scsi_transport_fcoe_sw;
+/* notification function for CPU hotplug events */
+static struct notifier_block fcoe_cpu_notifier = {
+       .notifier_call = fcoe_cpu_callback,
+};
+
+static struct scsi_transport_template *fcoe_transport_template;
+static struct scsi_transport_template *fcoe_vport_transport_template;
+
+static int fcoe_vport_destroy(struct fc_vport *);
+static int fcoe_vport_create(struct fc_vport *, bool disabled);
+static int fcoe_vport_disable(struct fc_vport *, bool disable);
+static void fcoe_set_vport_symbolic_name(struct fc_vport *);
+
+static struct libfc_function_template fcoe_libfc_fcn_templ = {
+       .frame_send = fcoe_xmit,
+       .ddp_setup = fcoe_ddp_setup,
+       .ddp_done = fcoe_ddp_done,
+       .elsct_send = fcoe_elsct_send,
+       .get_lesb = fcoe_get_lesb,
+};
 
 struct fc_function_template fcoe_transport_function = {
        .show_host_node_name = 1,
@@ -123,6 +177,48 @@ struct fc_function_template fcoe_transport_function = {
        .issue_fc_host_lip = fcoe_reset,
 
        .terminate_rport_io = fc_rport_terminate_io,
+
+       .vport_create = fcoe_vport_create,
+       .vport_delete = fcoe_vport_destroy,
+       .vport_disable = fcoe_vport_disable,
+       .set_vport_symbolic_name = fcoe_set_vport_symbolic_name,
+
+       .bsg_request = fc_lport_bsg_request,
+};
+
+struct fc_function_template fcoe_vport_transport_function = {
+       .show_host_node_name = 1,
+       .show_host_port_name = 1,
+       .show_host_supported_classes = 1,
+       .show_host_supported_fc4s = 1,
+       .show_host_active_fc4s = 1,
+       .show_host_maxframe_size = 1,
+
+       .show_host_port_id = 1,
+       .show_host_supported_speeds = 1,
+       .get_host_speed = fc_get_host_speed,
+       .show_host_speed = 1,
+       .show_host_port_type = 1,
+       .get_host_port_state = fc_get_host_port_state,
+       .show_host_port_state = 1,
+       .show_host_symbolic_name = 1,
+
+       .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
+       .show_rport_maxframe_size = 1,
+       .show_rport_supported_classes = 1,
+
+       .show_host_fabric_name = 1,
+       .show_starget_node_name = 1,
+       .show_starget_port_name = 1,
+       .show_starget_port_id = 1,
+       .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo,
+       .show_rport_dev_loss_tmo = 1,
+       .get_fc_host_stats = fc_get_host_stats,
+       .issue_fc_host_lip = fcoe_reset,
+
+       .terminate_rport_io = fc_rport_terminate_io,
+
+       .bsg_request = fc_lport_bsg_request,
 };
 
 static struct scsi_host_template fcoe_shost_template = {
@@ -137,20 +233,17 @@ static struct scsi_host_template fcoe_shost_template = {
        .change_queue_depth = fc_change_queue_depth,
        .change_queue_type = fc_change_queue_type,
        .this_id = -1,
-       .cmd_per_lun = 32,
+       .cmd_per_lun = 3,
        .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
        .use_clustering = ENABLE_CLUSTERING,
        .sg_tablesize = SG_ALL,
        .max_sectors = 0xffff,
 };
 
-static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
-                        struct packet_type *ptype,
-                        struct net_device *orig_dev);
 /**
- * fcoe_interface_setup()
- * @fcoe: new fcoe_interface
- * @netdev : ptr to the associated netdevice struct
+ * fcoe_interface_setup() - Setup a FCoE interface
+ * @fcoe:   The new FCoE interface
+ * @netdev: The net device that the fcoe interface is on
  *
  * Returns : 0 for success
  * Locking: must be called with the RTNL mutex held
@@ -160,23 +253,36 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
 {
        struct fcoe_ctlr *fip = &fcoe->ctlr;
        struct netdev_hw_addr *ha;
+       struct net_device *real_dev;
        u8 flogi_maddr[ETH_ALEN];
+       const struct net_device_ops *ops;
 
        fcoe->netdev = netdev;
 
+       /* Let LLD initialize for FCoE */
+       ops = netdev->netdev_ops;
+       if (ops->ndo_fcoe_enable) {
+               if (ops->ndo_fcoe_enable(netdev))
+                       FCOE_NETDEV_DBG(netdev, "Failed to enable FCoE"
+                                       " specific feature for LLD.\n");
+       }
+
        /* Do not support for bonding device */
        if ((netdev->priv_flags & IFF_MASTER_ALB) ||
            (netdev->priv_flags & IFF_SLAVE_INACTIVE) ||
            (netdev->priv_flags & IFF_MASTER_8023AD)) {
+               FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n");
                return -EOPNOTSUPP;
        }
 
        /* look for SAN MAC address, if multiple SAN MACs exist, only
         * use the first one for SPMA */
+       real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ?
+               vlan_dev_real_dev(netdev) : netdev;
        rcu_read_lock();
-       for_each_dev_addr(netdev, ha) {
+       for_each_dev_addr(real_dev, ha) {
                if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
-                   (is_valid_ether_addr(fip->ctl_src_addr))) {
+                   (is_valid_ether_addr(ha->addr))) {
                        memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
                        fip->spma = 1;
                        break;
@@ -216,19 +322,16 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
        return 0;
 }
 
-static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new);
-static void fcoe_destroy_work(struct work_struct *work);
-
 /**
- * fcoe_interface_create()
- * @netdev: network interface
+ * fcoe_interface_create() - Create a FCoE interface on a net device
+ * @netdev: The net device to create the FCoE interface on
  *
  * Returns: pointer to a struct fcoe_interface or NULL on error
  */
 static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
 {
        struct fcoe_interface *fcoe;
+       int err;
 
        fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
        if (!fcoe) {
@@ -245,15 +348,22 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
        fcoe_ctlr_init(&fcoe->ctlr);
        fcoe->ctlr.send = fcoe_fip_send;
        fcoe->ctlr.update_mac = fcoe_update_src_mac;
+       fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
 
-       fcoe_interface_setup(fcoe, netdev);
+       err = fcoe_interface_setup(fcoe, netdev);
+       if (err) {
+               fcoe_ctlr_destroy(&fcoe->ctlr);
+               kfree(fcoe);
+               dev_put(netdev);
+               return NULL;
+       }
 
        return fcoe;
 }
 
 /**
- * fcoe_interface_cleanup() - clean up netdev configurations
- * @fcoe:
+ * fcoe_interface_cleanup() - Clean up a FCoE interface
+ * @fcoe: The FCoE interface to be cleaned up
  *
  * Caller must be holding the RTNL mutex
  */
@@ -262,6 +372,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
        struct net_device *netdev = fcoe->netdev;
        struct fcoe_ctlr *fip = &fcoe->ctlr;
        u8 flogi_maddr[ETH_ALEN];
+       const struct net_device_ops *ops;
 
        /*
         * Don't listen for Ethernet packets anymore.
@@ -276,16 +387,22 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
        /* Delete secondary MAC addresses */
        memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
        dev_unicast_delete(netdev, flogi_maddr);
-       if (!is_zero_ether_addr(fip->data_src_addr))
-               dev_unicast_delete(netdev, fip->data_src_addr);
        if (fip->spma)
                dev_unicast_delete(netdev, fip->ctl_src_addr);
        dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+
+       /* Tell the LLD we are done w/ FCoE */
+       ops = netdev->netdev_ops;
+       if (ops->ndo_fcoe_disable) {
+               if (ops->ndo_fcoe_disable(netdev))
+                       FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE"
+                                       " specific feature for LLD.\n");
+       }
 }
 
 /**
  * fcoe_interface_release() - fcoe_port kref release function
- * @kref: embedded reference count in an fcoe_interface struct
+ * @kref: Embedded reference count in an fcoe_interface struct
  */
 static void fcoe_interface_release(struct kref *kref)
 {
@@ -301,8 +418,8 @@ static void fcoe_interface_release(struct kref *kref)
 }
 
 /**
- * fcoe_interface_get()
- * @fcoe:
+ * fcoe_interface_get() - Get a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be held
  */
 static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
 {
@@ -310,8 +427,8 @@ static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
 }
 
 /**
- * fcoe_interface_put()
- * @fcoe:
+ * fcoe_interface_put() - Put a reference to a FCoE interface
+ * @fcoe: The FCoE interface to be released
  */
 static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
 {
@@ -319,15 +436,16 @@ static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
 }
 
 /**
- * fcoe_fip_recv - handle a received FIP frame.
- * @skb: the receive skb
- * @dev: associated &net_device
- * @ptype: the &packet_type structure which was used to register this handler.
- * @orig_dev: original receive &net_device, in case @dev is a bond.
+ * fcoe_fip_recv() - Handler for received FIP frames
+ * @skb:      The receive skb
+ * @netdev:   The associated net device
+ * @ptype:    The packet_type structure which was used to register this handler
+ * @orig_dev: The original net_device the the skb was received on.
+ *           (in case dev is a bond)
  *
  * Returns: 0 for success
  */
-static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
+static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev,
                         struct packet_type *ptype,
                         struct net_device *orig_dev)
 {
@@ -339,9 +457,9 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
 }
 
 /**
- * fcoe_fip_send() - send an Ethernet-encapsulated FIP frame.
- * @fip: FCoE controller.
- * @skb: FIP Packet.
+ * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame
+ * @fip: The FCoE controller
+ * @skb: The FIP packet to be sent
  */
 static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
@@ -350,88 +468,101 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 }
 
 /**
- * fcoe_update_src_mac() - Update Ethernet MAC filters.
- * @fip: FCoE controller.
- * @old: Unicast MAC address to delete if the MAC is non-zero.
- * @new: Unicast MAC address to add.
+ * fcoe_update_src_mac() - Update the Ethernet MAC filters
+ * @lport: The local port to update the source MAC on
+ * @addr:  Unicast MAC address to add
  *
  * Remove any previously-set unicast MAC filter.
  * Add secondary FCoE MAC address filter for our OUI.
  */
-static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
+static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr)
 {
-       struct fcoe_interface *fcoe;
+       struct fcoe_port *port = lport_priv(lport);
+       struct fcoe_interface *fcoe = port->fcoe;
 
-       fcoe = fcoe_from_ctlr(fip);
        rtnl_lock();
-       if (!is_zero_ether_addr(old))
-               dev_unicast_delete(fcoe->netdev, old);
-       dev_unicast_add(fcoe->netdev, new);
+       if (!is_zero_ether_addr(port->data_src_addr))
+               dev_unicast_delete(fcoe->netdev, port->data_src_addr);
+       if (!is_zero_ether_addr(addr))
+               dev_unicast_add(fcoe->netdev, addr);
+       memcpy(port->data_src_addr, addr, ETH_ALEN);
        rtnl_unlock();
 }
 
 /**
- * fcoe_lport_config() - sets up the fc_lport
- * @lp: ptr to the fc_lport
+ * fcoe_get_src_mac() - return the Ethernet source address for an lport
+ * @lport: libfc lport
+ */
+static u8 *fcoe_get_src_mac(struct fc_lport *lport)
+{
+       struct fcoe_port *port = lport_priv(lport);
+
+       return port->data_src_addr;
+}
+
+/**
+ * fcoe_lport_config() - Set up a local port
+ * @lport: The local port to be setup
  *
  * Returns: 0 for success
  */
-static int fcoe_lport_config(struct fc_lport *lp)
+static int fcoe_lport_config(struct fc_lport *lport)
 {
-       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 |
-                             FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
-
-       fc_lport_init_stats(lp);
+       lport->link_up = 0;
+       lport->qfull = 0;
+       lport->max_retry_count = 3;
+       lport->max_rport_retry_count = 3;
+       lport->e_d_tov = 2 * 1000;      /* FC-FS default */
+       lport->r_a_tov = 2 * 2 * 1000;
+       lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
+                                FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL);
+       lport->does_npiv = 1;
+
+       fc_lport_init_stats(lport);
 
        /* lport fc_lport related configuration */
-       fc_lport_config(lp);
+       fc_lport_config(lport);
 
        /* offload related configuration */
-       lp->crc_offload = 0;
-       lp->seq_offload = 0;
-       lp->lro_enabled = 0;
-       lp->lro_xid = 0;
-       lp->lso_max = 0;
+       lport->crc_offload = 0;
+       lport->seq_offload = 0;
+       lport->lro_enabled = 0;
+       lport->lro_xid = 0;
+       lport->lso_max = 0;
 
        return 0;
 }
 
 /**
- * fcoe_queue_timer() - fcoe queue timer
- * @lp: the fc_lport pointer
+ * fcoe_queue_timer() - The fcoe queue timer
+ * @lport: The local port
  *
  * Calls fcoe_check_wait_queue on timeout
- *
  */
-static void fcoe_queue_timer(ulong lp)
+static void fcoe_queue_timer(ulong lport)
 {
-       fcoe_check_wait_queue((struct fc_lport *)lp, NULL);
+       fcoe_check_wait_queue((struct fc_lport *)lport, NULL);
 }
 
 /**
- * fcoe_netdev_config() - Set up netdev for SW FCoE
- * @lp : ptr to the fc_lport
- * @netdev : ptr to the associated netdevice struct
+ * fcoe_netdev_config() - Set up net devive for SW FCoE
+ * @lport:  The local port that is associated with the net device
+ * @netdev: The associated net device
  *
- * Must be called after fcoe_lport_config() as it will use lport mutex
+ * Must be called after fcoe_lport_config() as it will use local port mutex
  *
- * Returns : 0 for success
+ * Returns: 0 for success
  */
-static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
+static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 {
        u32 mfs;
        u64 wwnn, wwpn;
        struct fcoe_interface *fcoe;
        struct fcoe_port *port;
+       int vid = 0;
 
        /* Setup lport private data to point to fcoe softc */
-       port = lport_priv(lp);
+       port = lport_priv(lport);
        fcoe = port->fcoe;
 
        /*
@@ -439,86 +570,112 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
         * user-configured limit.  If the MFS is too low, fcoe_link_ok()
         * will return 0, so do this first.
         */
-       mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
-                            sizeof(struct fcoe_crc_eof));
-       if (fc_set_mfs(lp, mfs))
+       mfs = netdev->mtu;
+       if (netdev->features & NETIF_F_FCOE_MTU) {
+               mfs = FCOE_MTU;
+               FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs);
+       }
+       mfs -= (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof));
+       if (fc_set_mfs(lport, mfs))
                return -EINVAL;
 
        /* offload features support */
        if (netdev->features & NETIF_F_SG)
-               lp->sg_supp = 1;
+               lport->sg_supp = 1;
 
        if (netdev->features & NETIF_F_FCOE_CRC) {
-               lp->crc_offload = 1;
+               lport->crc_offload = 1;
                FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
        }
        if (netdev->features & NETIF_F_FSO) {
-               lp->seq_offload = 1;
-               lp->lso_max = netdev->gso_max_size;
+               lport->seq_offload = 1;
+               lport->lso_max = netdev->gso_max_size;
                FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
-                               lp->lso_max);
+                               lport->lso_max);
        }
        if (netdev->fcoe_ddp_xid) {
-               lp->lro_enabled = 1;
-               lp->lro_xid = netdev->fcoe_ddp_xid;
+               lport->lro_enabled = 1;
+               lport->lro_xid = netdev->fcoe_ddp_xid;
                FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
-                               lp->lro_xid);
+                               lport->lro_xid);
        }
        skb_queue_head_init(&port->fcoe_pending_queue);
        port->fcoe_pending_queue_active = 0;
-       setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp);
+       setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
 
-       wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0);
-       fc_set_wwnn(lp, wwnn);
-       /* XXX - 3rd arg needs to be vlan id */
-       wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0);
-       fc_set_wwpn(lp, wwpn);
+       if (!lport->vport) {
+               /*
+                * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
+                * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
+                * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
+                */
+               if (netdev->priv_flags & IFF_802_1Q_VLAN)
+                       vid = vlan_dev_vlan_id(netdev);
+               wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
+               fc_set_wwnn(lport, wwnn);
+               wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid);
+               fc_set_wwpn(lport, wwpn);
+       }
 
        return 0;
 }
 
 /**
- * fcoe_shost_config() - Sets up fc_lport->host
- * @lp : ptr to the fc_lport
- * @shost : ptr to the associated scsi host
- * @dev : device associated to scsi host
+ * fcoe_shost_config() - Set up the SCSI host associated with a local port
+ * @lport: The local port
+ * @shost: The SCSI host to associate with the local port
+ * @dev:   The device associated with the SCSI host
  *
  * Must be called after fcoe_lport_config() and fcoe_netdev_config()
  *
- * Returns : 0 for success
+ * Returns: 0 for success
  */
-static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
-                               struct device *dev)
+static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost,
+                            struct device *dev)
 {
        int rc = 0;
 
        /* lport scsi host config */
-       lp->host = shost;
-
-       lp->host->max_lun = FCOE_MAX_LUN;
-       lp->host->max_id = FCOE_MAX_FCP_TARGET;
-       lp->host->max_channel = 0;
-       lp->host->transportt = scsi_transport_fcoe_sw;
+       lport->host->max_lun = FCOE_MAX_LUN;
+       lport->host->max_id = FCOE_MAX_FCP_TARGET;
+       lport->host->max_channel = 0;
+       if (lport->vport)
+               lport->host->transportt = fcoe_vport_transport_template;
+       else
+               lport->host->transportt = fcoe_transport_template;
 
        /* add the new host to the SCSI-ml */
-       rc = scsi_add_host(lp->host, dev);
+       rc = scsi_add_host(lport->host, dev);
        if (rc) {
-               FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: "
+               FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: "
                                "error on scsi_add_host\n");
                return rc;
        }
-       sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s",
-               FCOE_NAME, FCOE_VERSION,
-               fcoe_netdev(lp)->name);
+
+       if (!lport->vport)
+               fc_host_max_npiv_vports(lport->host) = USHORT_MAX;
+
+       snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+                "%s v%s over %s", FCOE_NAME, FCOE_VERSION,
+                fcoe_netdev(lport)->name);
 
        return 0;
 }
 
-/*
- * fcoe_oem_match() - match for read types IO
- * @fp: the fc_frame for new IO.
+/**
+ * fcoe_oem_match() - The match routine for the offloaded exchange manager
+ * @fp: The I/O frame
+ *
+ * This routine will be associated with an exchange manager (EM). When
+ * the libfc exchange handling code is looking for an EM to use it will
+ * call this routine and pass it the frame that it wishes to send. This
+ * routine will return True if the associated EM is to be used and False
+ * if the echange code should continue looking for an EM.
  *
- * Returns : true for read types IO, otherwise returns false.
+ * The offload EM that this routine is associated with will handle any
+ * packets that are for SCSI read requests.
+ *
+ * Returns: True for read types I/O, otherwise returns false.
  */
 bool fcoe_oem_match(struct fc_frame *fp)
 {
@@ -527,14 +684,14 @@ bool fcoe_oem_match(struct fc_frame *fp)
 }
 
 /**
- * fcoe_em_config() - allocates em for this lport
- * @lp: the fcoe that em is to allocated for
+ * fcoe_em_config() - Allocate and configure an exchange manager
+ * @lport: The local port that the new EM will be associated with
  *
- * Returns : 0 on success
+ * Returns: 0 on success
  */
-static inline int fcoe_em_config(struct fc_lport *lp)
+static inline int fcoe_em_config(struct fc_lport *lport)
 {
-       struct fcoe_port *port = lport_priv(lp);
+       struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->fcoe;
        struct fcoe_interface *oldfcoe = NULL;
        struct net_device *old_real_dev, *cur_real_dev;
@@ -545,8 +702,9 @@ static inline int fcoe_em_config(struct fc_lport *lp)
         * Check if need to allocate an em instance for
         * offload exchange ids to be shared across all VN_PORTs/lport.
         */
-       if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) {
-               lp->lro_xid = 0;
+       if (!lport->lro_enabled || !lport->lro_xid ||
+           (lport->lro_xid >= max_xid)) {
+               lport->lro_xid = 0;
                goto skip_oem;
        }
 
@@ -572,16 +730,16 @@ static inline int fcoe_em_config(struct fc_lport *lp)
        }
 
        if (fcoe->oem) {
-               if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) {
+               if (!fc_exch_mgr_add(lport, fcoe->oem, fcoe_oem_match)) {
                        printk(KERN_ERR "fcoe_em_config: failed to add "
                               "offload em:%p on interface:%s\n",
                               fcoe->oem, fcoe->netdev->name);
                        return -ENOMEM;
                }
        } else {
-               fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3,
-                                           FCOE_MIN_XID, lp->lro_xid,
-                                           fcoe_oem_match);
+               fcoe->oem = fc_exch_mgr_alloc(lport, FC_CLASS_3,
+                                             FCOE_MIN_XID, lport->lro_xid,
+                                             fcoe_oem_match);
                if (!fcoe->oem) {
                        printk(KERN_ERR "fcoe_em_config: failed to allocate "
                               "em for offload exches on interface:%s\n",
@@ -593,10 +751,10 @@ static inline int fcoe_em_config(struct fc_lport *lp)
        /*
         * Exclude offload EM xid range from next EM xid range.
         */
-       min_xid += lp->lro_xid + 1;
+       min_xid += lport->lro_xid + 1;
 
 skip_oem:
-       if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) {
+       if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, min_xid, max_xid, NULL)) {
                printk(KERN_ERR "fcoe_em_config: failed to "
                       "allocate em on interface %s\n", fcoe->netdev->name);
                return -ENOMEM;
@@ -606,8 +764,8 @@ skip_oem:
 }
 
 /**
- * fcoe_if_destroy() - FCoE software HBA tear-down function
- * @lport: fc_lport to destroy
+ * fcoe_if_destroy() - Tear down a SW FCoE instance
+ * @lport: The local port to be destroyed
  */
 static void fcoe_if_destroy(struct fc_lport *lport)
 {
@@ -630,6 +788,11 @@ static void fcoe_if_destroy(struct fc_lport *lport)
        /* Free existing transmit skbs */
        fcoe_clean_pending_queue(lport);
 
+       rtnl_lock();
+       if (!is_zero_ether_addr(port->data_src_addr))
+               dev_unicast_delete(netdev, port->data_src_addr);
+       rtnl_unlock();
+
        /* receives may not be stopped until after this */
        fcoe_interface_put(fcoe);
 
@@ -650,82 +813,89 @@ static void fcoe_if_destroy(struct fc_lport *lport)
        scsi_host_put(lport->host);
 }
 
-/*
- * fcoe_ddp_setup - calls LLD's ddp_setup through net_device
- * @lp:        the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
- * @sgl: the scatterlist describing this transfer
- * @sgc: number of sg items
+/**
+ * fcoe_ddp_setup() - Call a LLD's ddp_setup through the net device
+ * @lport: The local port to setup DDP for
+ * @xid:   The exchange ID for this DDP transfer
+ * @sgl:   The scatterlist describing this transfer
+ * @sgc:   The number of sg items
  *
- * Returns : 0 no ddp
+ * Returns: 0 if the DDP context was not configured
  */
-static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid,
-                            struct scatterlist *sgl, unsigned int sgc)
+static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid,
+                         struct scatterlist *sgl, unsigned int sgc)
 {
-       struct net_device *n = fcoe_netdev(lp);
+       struct net_device *netdev = fcoe_netdev(lport);
 
-       if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup)
-               return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc);
+       if (netdev->netdev_ops->ndo_fcoe_ddp_setup)
+               return netdev->netdev_ops->ndo_fcoe_ddp_setup(netdev,
+                                                             xid, sgl,
+                                                             sgc);
 
        return 0;
 }
 
-/*
- * fcoe_ddp_done - calls LLD's ddp_done through net_device
- * @lp:        the corresponding fc_lport
- * @xid: the exchange id for this ddp transfer
+/**
+ * fcoe_ddp_done() - Call a LLD's ddp_done through the net device
+ * @lport: The local port to complete DDP on
+ * @xid:   The exchange ID for this DDP transfer
  *
- * Returns : the length of data that have been completed by ddp
+ * Returns: the length of data that have been completed by DDP
  */
-static int fcoe_ddp_done(struct fc_lport *lp, u16 xid)
+static int fcoe_ddp_done(struct fc_lport *lport, u16 xid)
 {
-       struct net_device *n = fcoe_netdev(lp);
+       struct net_device *netdev = fcoe_netdev(lport);
 
-       if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done)
-               return n->netdev_ops->ndo_fcoe_ddp_done(n, xid);
+       if (netdev->netdev_ops->ndo_fcoe_ddp_done)
+               return netdev->netdev_ops->ndo_fcoe_ddp_done(netdev, xid);
        return 0;
 }
 
-static struct libfc_function_template fcoe_libfc_fcn_templ = {
-       .frame_send = fcoe_xmit,
-       .ddp_setup = fcoe_ddp_setup,
-       .ddp_done = fcoe_ddp_done,
-};
-
 /**
- * fcoe_if_create() - this function creates the fcoe port
- * @fcoe: fcoe_interface structure to create an fc_lport instance on
- * @parent: device pointer to be the parent in sysfs for the SCSI host
+ * fcoe_if_create() - Create a FCoE instance on an interface
+ * @fcoe:   The FCoE interface to create a local port on
+ * @parent: The device pointer to be the parent in sysfs for the SCSI host
+ * @npiv:   Indicates if the port is a vport or not
  *
- * Creates fc_lport struct and scsi_host for lport, configures lport.
+ * Creates a fc_lport instance and a Scsi_Host instance and configure them.
  *
- * Returns : The allocated fc_lport or an error pointer
+ * Returns: The allocated fc_lport or an error pointer
  */
 static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
-                                      struct device *parent)
+                                      struct device *parent, int npiv)
 {
-       int rc;
+       struct net_device *netdev = fcoe->netdev;
        struct fc_lport *lport = NULL;
        struct fcoe_port *port;
        struct Scsi_Host *shost;
-       struct net_device *netdev = fcoe->netdev;
+       int rc;
+       /*
+        * parent is only a vport if npiv is 1,
+        * but we'll only use vport in that case so go ahead and set it
+        */
+       struct fc_vport *vport = dev_to_vport(parent);
 
        FCOE_NETDEV_DBG(netdev, "Create Interface\n");
 
-       shost = libfc_host_alloc(&fcoe_shost_template,
-                                sizeof(struct fcoe_port));
-       if (!shost) {
+       if (!npiv) {
+               lport = libfc_host_alloc(&fcoe_shost_template,
+                                        sizeof(struct fcoe_port));
+       } else  {
+               lport = libfc_vport_create(vport,
+                                          sizeof(struct fcoe_port));
+       }
+       if (!lport) {
                FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
                rc = -ENOMEM;
                goto out;
        }
-       lport = shost_priv(shost);
+       shost = lport->host;
        port = lport_priv(lport);
        port->lport = lport;
        port->fcoe = fcoe;
        INIT_WORK(&port->destroy_work, fcoe_destroy_work);
 
-       /* configure fc_lport, e.g., em */
+       /* configure a fc_lport including the exchange manager */
        rc = fcoe_lport_config(lport);
        if (rc) {
                FCOE_NETDEV_DBG(netdev, "Could not configure lport for the "
@@ -733,6 +903,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                goto out_host_put;
        }
 
+       if (npiv) {
+               FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n",
+                               vport->node_name, vport->port_name);
+               fc_set_wwnn(lport, vport->node_name);
+               fc_set_wwpn(lport, vport->port_name);
+       }
+
        /* configure lport network properties */
        rc = fcoe_netdev_config(lport, netdev);
        if (rc) {
@@ -757,21 +934,24 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
                goto out_lp_destroy;
        }
 
-       /*
-        * fcoe_em_alloc() and fcoe_hostlist_add() both
-        * need to be atomic with respect to other changes to the hostlist
-        * since fcoe_em_alloc() looks for an existing EM
-        * instance on host list updated by fcoe_hostlist_add().
-        *
-        * This is currently handled through the fcoe_config_mutex begin held.
-        */
+       if (!npiv) {
+               /*
+                * fcoe_em_alloc() and fcoe_hostlist_add() both
+                * need to be atomic with respect to other changes to the
+                * hostlist since fcoe_em_alloc() looks for an existing EM
+                * instance on host list updated by fcoe_hostlist_add().
+                *
+                * This is currently handled through the fcoe_config_mutex
+                * begin held.
+                */
 
-       /* lport exch manager allocation */
-       rc = fcoe_em_config(lport);
-       if (rc) {
-               FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
-                               "interface\n");
-               goto out_lp_destroy;
+               /* lport exch manager allocation */
+               rc = fcoe_em_config(lport);
+               if (rc) {
+                       FCOE_NETDEV_DBG(netdev, "Could not configure the EM "
+                                       "for the interface\n");
+                       goto out_lp_destroy;
+               }
        }
 
        fcoe_interface_get(fcoe);
@@ -786,17 +966,20 @@ out:
 }
 
 /**
- * fcoe_if_init() - attach to scsi transport
+ * fcoe_if_init() - Initialization routine for fcoe.ko
+ *
+ * Attaches the SW FCoE transport to the FC transport
  *
- * Returns : 0 on success
+ * Returns: 0 on success
  */
 static int __init fcoe_if_init(void)
 {
        /* attach to scsi transport */
-       scsi_transport_fcoe_sw =
-               fc_attach_transport(&fcoe_transport_function);
+       fcoe_transport_template = fc_attach_transport(&fcoe_transport_function);
+       fcoe_vport_transport_template =
+               fc_attach_transport(&fcoe_vport_transport_function);
 
-       if (!scsi_transport_fcoe_sw) {
+       if (!fcoe_transport_template) {
                printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n");
                return -ENODEV;
        }
@@ -805,20 +988,24 @@ static int __init fcoe_if_init(void)
 }
 
 /**
- * fcoe_if_exit() - detach from scsi transport
+ * fcoe_if_exit() - Tear down fcoe.ko
+ *
+ * Detaches the SW FCoE transport from the FC transport
  *
- * Returns : 0 on success
+ * Returns: 0 on success
  */
 int __exit fcoe_if_exit(void)
 {
-       fc_release_transport(scsi_transport_fcoe_sw);
-       scsi_transport_fcoe_sw = NULL;
+       fc_release_transport(fcoe_transport_template);
+       fc_release_transport(fcoe_vport_transport_template);
+       fcoe_transport_template = NULL;
+       fcoe_vport_transport_template = NULL;
        return 0;
 }
 
 /**
- * fcoe_percpu_thread_create() - Create a receive thread for an online cpu
- * @cpu: cpu index for the online cpu
+ * fcoe_percpu_thread_create() - Create a receive thread for an online CPU
+ * @cpu: The CPU index of the CPU to create a receive thread for
  */
 static void fcoe_percpu_thread_create(unsigned int cpu)
 {
@@ -841,8 +1028,8 @@ static void fcoe_percpu_thread_create(unsigned int cpu)
 }
 
 /**
- * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu
- * @cpu: cpu index the rx thread is to be removed
+ * fcoe_percpu_thread_destroy() - Remove the receive thread of a CPU
+ * @cpu: The CPU index of the CPU whose receive thread is to be destroyed
  *
  * Destroys a per-CPU Rx thread. Any pending skbs are moved to the
  * current CPU's Rx thread. If the thread being destroyed is bound to
@@ -890,7 +1077,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
                } else {
                        /*
                         * The targeted CPU is not initialized and cannot accept
-                        * new  skbs. Unlock the targeted CPU and drop the skbs
+                        * new  skbs. Unlock the targeted CPU and drop the skbs
                         * on the CPU that is going offline.
                         */
                        while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL)
@@ -931,12 +1118,12 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)
 }
 
 /**
- * fcoe_cpu_callback() - fcoe cpu hotplug event callback
- * @nfb: callback data block
- * @action: event triggering the callback
- * @hcpu: index for the cpu of this event
+ * fcoe_cpu_callback() - Handler for CPU hotplug events
+ * @nfb:    The callback data block
+ * @action: The event triggering the callback
+ * @hcpu:   The index of the CPU that the event is for
  *
- * This creates or destroys per cpu data for fcoe
+ * This creates or destroys per-CPU data for fcoe
  *
  * Returns NOTIFY_OK always.
  */
@@ -962,25 +1149,22 @@ static int fcoe_cpu_callback(struct notifier_block *nfb,
        return NOTIFY_OK;
 }
 
-static struct notifier_block fcoe_cpu_notifier = {
-       .notifier_call = fcoe_cpu_callback,
-};
-
 /**
- * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ
- * @skb: the receive skb
- * @dev: associated net device
- * @ptype: context
- * @olddev: last device
+ * fcoe_rcv() - Receive packets from a net device
+ * @skb:    The received packet
+ * @netdev: The net device that the packet was received on
+ * @ptype:  The packet type context
+ * @olddev: The last device net device
  *
- * this function will receive the packet and build fc frame and pass it up
+ * This routine is called by NET_RX_SOFTIRQ. It receives a packet, builds a
+ * FC frame and passes the frame to libfc.
  *
  * Returns: 0 for success
  */
-int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
+int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
             struct packet_type *ptype, struct net_device *olddev)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fcoe_rcv_info *fr;
        struct fcoe_interface *fcoe;
        struct fc_frame_header *fh;
@@ -988,15 +1172,15 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
        unsigned int cpu;
 
        fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
-       lp = fcoe->ctlr.lp;
-       if (unlikely(lp == NULL)) {
-               FCOE_NETDEV_DBG(dev, "Cannot find hba structure");
+       lport = fcoe->ctlr.lp;
+       if (unlikely(!lport)) {
+               FCOE_NETDEV_DBG(netdev, "Cannot find hba structure");
                goto err2;
        }
-       if (!lp->link_up)
+       if (!lport->link_up)
                goto err2;
 
-       FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p "
+       FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p "
                        "data:%p tail:%p end:%p sum:%d dev:%s",
                        skb->len, skb->data_len, skb->head, skb->data,
                        skb_tail_pointer(skb), skb_end_pointer(skb),
@@ -1004,7 +1188,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
 
        /* check for FCOE packet type */
        if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-               FCOE_NETDEV_DBG(dev, "Wrong FC type frame");
+               FCOE_NETDEV_DBG(netdev, "Wrong FC type frame");
                goto err;
        }
 
@@ -1013,14 +1197,14 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
         * and FC headers are pulled into the linear data area.
         */
        if (unlikely((skb->len < FCOE_MIN_FRAME) ||
-           !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+                    !pskb_may_pull(skb, FCOE_HEADER_LEN)))
                goto err;
 
        skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
        fh = (struct fc_frame_header *) skb_transport_header(skb);
 
        fr = fcoe_dev_from_skb(skb);
-       fr->fr_dev = lp;
+       fr->fr_dev = lport;
        fr->ptype = ptype;
 
        /*
@@ -1042,7 +1226,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
                 * the first CPU now. For non-SMP systems this
                 * will check the same CPU twice.
                 */
-               FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread "
+               FCOE_NETDEV_DBG(netdev, "CPU is online, but no receive thread "
                                "ready for incoming skb- using first online "
                                "CPU.\n");
 
@@ -1061,15 +1245,29 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
         * this skb. We also have this receive thread locked,
         * so we're free to queue skbs into it's queue.
         */
-       __skb_queue_tail(&fps->fcoe_rx_list, skb);
-       if (fps->fcoe_rx_list.qlen == 1)
-               wake_up_process(fps->thread);
 
-       spin_unlock_bh(&fps->fcoe_rx_list.lock);
+       /* If this is a SCSI-FCP frame, and this is already executing on the
+        * correct CPU, and the queue for this CPU is empty, then go ahead
+        * and process the frame directly in the softirq context.
+        * This lets us process completions without context switching from the
+        * NET_RX softirq, to our receive processing thread, and then back to
+        * BLOCK softirq context.
+        */
+       if (fh->fh_type == FC_TYPE_FCP &&
+           cpu == smp_processor_id() &&
+           skb_queue_empty(&fps->fcoe_rx_list)) {
+               spin_unlock_bh(&fps->fcoe_rx_list.lock);
+               fcoe_recv_frame(skb);
+       } else {
+               __skb_queue_tail(&fps->fcoe_rx_list, skb);
+               if (fps->fcoe_rx_list.qlen == 1)
+                       wake_up_process(fps->thread);
+               spin_unlock_bh(&fps->fcoe_rx_list.lock);
+       }
 
        return 0;
 err:
-       fc_lport_get_stats(lp)->ErrorFrames++;
+       fc_lport_get_stats(lport)->ErrorFrames++;
 
 err2:
        kfree_skb(skb);
@@ -1077,17 +1275,21 @@ err2:
 }
 
 /**
- * fcoe_start_io() - pass to netdev to start xmit for fcoe
- * @skb: the skb to be xmitted
+ * fcoe_start_io() - Start FCoE I/O
+ * @skb: The packet to be transmitted
+ *
+ * This routine is called from the net device to start transmitting
+ * FCoE packets.
  *
  * Returns: 0 for success
  */
 static inline int fcoe_start_io(struct sk_buff *skb)
 {
+       struct sk_buff *nskb;
        int rc;
 
-       skb_get(skb);
-       rc = dev_queue_xmit(skb);
+       nskb = skb_clone(skb, GFP_ATOMIC);
+       rc = dev_queue_xmit(nskb);
        if (rc != 0)
                return rc;
        kfree_skb(skb);
@@ -1095,9 +1297,15 @@ static inline int fcoe_start_io(struct sk_buff *skb)
 }
 
 /**
- * fcoe_get_paged_crc_eof() - in case we need to alloc a page for crc_eof
- * @skb: the skb to be xmitted
- * @tlen: total len
+ * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC
+ * @skb:  The packet to be transmitted
+ * @tlen: The total length of the trailer
+ *
+ * This routine allocates a page for frame trailers. The page is re-used if
+ * there is enough room left on it for the current trailer. If there isn't
+ * enough buffer left a new page is allocated for the trailer. Reference to
+ * the page from this function as well as the skbs using the page fragments
+ * ensure that the page is freed at the appropriate time.
  *
  * Returns: 0 for success
  */
@@ -1136,11 +1344,12 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
 }
 
 /**
- * fcoe_fc_crc() - calculates FC CRC in this fcoe skb
- * @fp: the fc_frame containing data to be checksummed
+ * fcoe_fc_crc() - Calculates the CRC for a given frame
+ * @fp: The frame to be checksumed
+ *
+ * This uses crc32() routine to calculate the CRC for a frame
  *
- * This uses crc32() to calculate the crc for port frame
- * Return   : 32 bit crc
+ * Return: The 32 bit CRC value
  */
 u32 fcoe_fc_crc(struct fc_frame *fp)
 {
@@ -1171,13 +1380,13 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
 }
 
 /**
- * fcoe_xmit() - FCoE frame transmit function
- * @lp:        the associated local fcoe
- * @fp: the fc_frame to be transmitted
+ * fcoe_xmit() - Transmit a FCoE frame
+ * @lport: The local port that the frame is to be transmitted for
+ * @fp:           The frame to be transmitted
  *
- * Return   : 0 for success
+ * Return: 0 for success
  */
-int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
+int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
 {
        int wlen;
        u32 crc;
@@ -1189,7 +1398,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        unsigned int hlen;              /* header length implies the version */
        unsigned int tlen;              /* trailer length */
        unsigned int elen;              /* eth header, may include vlan */
-       struct fcoe_port *port = lport_priv(lp);
+       struct fcoe_port *port = lport_priv(lport);
        struct fcoe_interface *fcoe = port->fcoe;
        u8 sof, eof;
        struct fcoe_hdr *hp;
@@ -1200,13 +1409,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        skb = fp_skb(fp);
        wlen = skb->len / FCOE_WORD_TO_BYTE;
 
-       if (!lp->link_up) {
+       if (!lport->link_up) {
                kfree_skb(skb);
                return 0;
        }
 
        if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
-           fcoe_ctlr_els_send(&fcoe->ctlr, skb))
+           fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
                return 0;
 
        sof = fr_sof(fp);
@@ -1218,7 +1427,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE;
 
        /* crc offload */
-       if (likely(lp->crc_offload)) {
+       if (likely(lport->crc_offload)) {
                skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum_start = skb_headroom(skb);
                skb->csum_offset = skb->len;
@@ -1271,7 +1480,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
                memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
        else
-               memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN);
+               memcpy(eh->h_source, port->data_src_addr, ETH_ALEN);
 
        hp = (struct fcoe_hdr *)(eh + 1);
        memset(hp, 0, sizeof(*hp));
@@ -1280,7 +1489,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
        hp->fcoe_sof = sof;
 
        /* fcoe lso, mss is in max_payload which is non-zero for FCP data */
-       if (lp->seq_offload && fr_max_payload(fp)) {
+       if (lport->seq_offload && fr_max_payload(fp)) {
                skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
                skb_shinfo(skb)->gso_size = fr_max_payload(fp);
        } else {
@@ -1288,23 +1497,23 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
                skb_shinfo(skb)->gso_size = 0;
        }
        /* update tx stats: regardless if LLD fails */
-       stats = fc_lport_get_stats(lp);
+       stats = fc_lport_get_stats(lport);
        stats->TxFrames++;
        stats->TxWords += wlen;
 
        /* send down to lld */
-       fr_dev(fp) = lp;
+       fr_dev(fp) = lport;
        if (port->fcoe_pending_queue.qlen)
-               fcoe_check_wait_queue(lp, skb);
+               fcoe_check_wait_queue(lport, skb);
        else if (fcoe_start_io(skb))
-               fcoe_check_wait_queue(lp, skb);
+               fcoe_check_wait_queue(lport, skb);
 
        return 0;
 }
 
 /**
- * fcoe_percpu_flush_done() - Indicate percpu queue flush completion.
- * @skb: the skb being completed.
+ * fcoe_percpu_flush_done() - Indicate per-CPU queue flush completion
+ * @skb: The completed skb (argument required by destructor)
  */
 static void fcoe_percpu_flush_done(struct sk_buff *skb)
 {
@@ -1312,26 +1521,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
 }
 
 /**
- * fcoe_percpu_receive_thread() - recv thread per cpu
- * @arg: ptr to the fcoe per cpu struct
- *
- * Return: 0 for success
+ * fcoe_recv_frame() - process a single received frame
+ * @skb: frame to process
  */
-int fcoe_percpu_receive_thread(void *arg)
+static void fcoe_recv_frame(struct sk_buff *skb)
 {
-       struct fcoe_percpu_s *p = arg;
        u32 fr_len;
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fcoe_rcv_info *fr;
        struct fcoe_dev_stats *stats;
        struct fc_frame_header *fh;
-       struct sk_buff *skb;
        struct fcoe_crc_eof crc_eof;
        struct fc_frame *fp;
        u8 *mac = NULL;
        struct fcoe_port *port;
        struct fcoe_hdr *hp;
 
+       fr = fcoe_dev_from_skb(skb);
+       lport = fr->fr_dev;
+       if (unlikely(!lport)) {
+               if (skb->destructor != fcoe_percpu_flush_done)
+                       FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+               kfree_skb(skb);
+               return;
+       }
+
+       FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
+                       "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+                       skb->len, skb->data_len,
+                       skb->head, skb->data, skb_tail_pointer(skb),
+                       skb_end_pointer(skb), skb->csum,
+                       skb->dev ? skb->dev->name : "<NULL>");
+
+       /*
+        * Save source MAC address before discarding header.
+        */
+       port = lport_priv(lport);
+       if (skb_is_nonlinear(skb))
+               skb_linearize(skb);     /* not ideal */
+       mac = eth_hdr(skb)->h_source;
+
+       /*
+        * Frame length checks and setting up the header pointers
+        * was done in fcoe_rcv already.
+        */
+       hp = (struct fcoe_hdr *) skb_network_header(skb);
+       fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+       stats = fc_lport_get_stats(lport);
+       if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+               if (stats->ErrorFrames < 5)
+                       printk(KERN_WARNING "fcoe: FCoE version "
+                              "mismatch: The frame has "
+                              "version %x, but the "
+                              "initiator supports version "
+                              "%x\n", FC_FCOE_DECAPS_VER(hp),
+                              FC_FCOE_VER);
+               stats->ErrorFrames++;
+               kfree_skb(skb);
+               return;
+       }
+
+       skb_pull(skb, sizeof(struct fcoe_hdr));
+       fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+       stats->RxFrames++;
+       stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+       fp = (struct fc_frame *)skb;
+       fc_frame_init(fp);
+       fr_dev(fp) = lport;
+       fr_sof(fp) = hp->fcoe_sof;
+
+       /* Copy out the CRC and EOF trailer for access */
+       if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+               kfree_skb(skb);
+               return;
+       }
+       fr_eof(fp) = crc_eof.fcoe_eof;
+       fr_crc(fp) = crc_eof.fcoe_crc32;
+       if (pskb_trim(skb, fr_len)) {
+               kfree_skb(skb);
+               return;
+       }
+
+       /*
+        * We only check CRC if no offload is available and if it is
+        * it's solicited data, in which case, the FCP layer would
+        * check it during the copy.
+        */
+       if (lport->crc_offload &&
+           skb->ip_summed == CHECKSUM_UNNECESSARY)
+               fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+       else
+               fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+       fh = fc_frame_header_get(fp);
+       if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+           fh->fh_type == FC_TYPE_FCP) {
+               fc_exch_recv(lport, fp);
+               return;
+       }
+       if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+               if (le32_to_cpu(fr_crc(fp)) !=
+                   ~crc32(~0, skb->data, fr_len)) {
+                       if (stats->InvalidCRCCount < 5)
+                               printk(KERN_WARNING "fcoe: dropping "
+                                      "frame with CRC error\n");
+                       stats->InvalidCRCCount++;
+                       stats->ErrorFrames++;
+                       fc_frame_free(fp);
+                       return;
+               }
+               fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+       }
+       fc_exch_recv(lport, fp);
+}
+
+/**
+ * fcoe_percpu_receive_thread() - The per-CPU packet receive thread
+ * @arg: The per-CPU context
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+       struct fcoe_percpu_s *p = arg;
+       struct sk_buff *skb;
+
        set_user_nice(current, -20);
 
        while (!kthread_should_stop()) {
@@ -1347,129 +1664,27 @@ int fcoe_percpu_receive_thread(void *arg)
                        spin_lock_bh(&p->fcoe_rx_list.lock);
                }
                spin_unlock_bh(&p->fcoe_rx_list.lock);
-               fr = fcoe_dev_from_skb(skb);
-               lp = fr->fr_dev;
-               if (unlikely(lp == NULL)) {
-                       if (skb->destructor != fcoe_percpu_flush_done)
-                               FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
-                       kfree_skb(skb);
-                       continue;
-               }
-
-               FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
-                               "head:%p data:%p tail:%p end:%p sum:%d dev:%s",
-                               skb->len, skb->data_len,
-                               skb->head, skb->data, skb_tail_pointer(skb),
-                               skb_end_pointer(skb), skb->csum,
-                               skb->dev ? skb->dev->name : "<NULL>");
-
-               /*
-                * Save source MAC address before discarding header.
-                */
-               port = lport_priv(lp);
-               if (skb_is_nonlinear(skb))
-                       skb_linearize(skb);     /* not ideal */
-               mac = eth_hdr(skb)->h_source;
-
-               /*
-                * Frame length checks and setting up the header pointers
-                * was done in fcoe_rcv already.
-                */
-               hp = (struct fcoe_hdr *) skb_network_header(skb);
-               fh = (struct fc_frame_header *) skb_transport_header(skb);
-
-               stats = fc_lport_get_stats(lp);
-               if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-                       if (stats->ErrorFrames < 5)
-                               printk(KERN_WARNING "fcoe: FCoE version "
-                                      "mismatch: The frame has "
-                                      "version %x, but the "
-                                      "initiator supports version "
-                                      "%x\n", FC_FCOE_DECAPS_VER(hp),
-                                      FC_FCOE_VER);
-                       stats->ErrorFrames++;
-                       kfree_skb(skb);
-                       continue;
-               }
-
-               skb_pull(skb, sizeof(struct fcoe_hdr));
-               fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
-               stats->RxFrames++;
-               stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
-               fp = (struct fc_frame *)skb;
-               fc_frame_init(fp);
-               fr_dev(fp) = lp;
-               fr_sof(fp) = hp->fcoe_sof;
-
-               /* Copy out the CRC and EOF trailer for access */
-               if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
-                       kfree_skb(skb);
-                       continue;
-               }
-               fr_eof(fp) = crc_eof.fcoe_eof;
-               fr_crc(fp) = crc_eof.fcoe_crc32;
-               if (pskb_trim(skb, fr_len)) {
-                       kfree_skb(skb);
-                       continue;
-               }
-
-               /*
-                * We only check CRC if no offload is available and if it is
-                * it's solicited data, in which case, the FCP layer would
-                * check it during the copy.
-                */
-               if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
-                       fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-               else
-                       fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
-               fh = fc_frame_header_get(fp);
-               if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
-                   fh->fh_type == FC_TYPE_FCP) {
-                       fc_exch_recv(lp, fp);
-                       continue;
-               }
-               if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
-                       if (le32_to_cpu(fr_crc(fp)) !=
-                           ~crc32(~0, skb->data, fr_len)) {
-                               if (stats->InvalidCRCCount < 5)
-                                       printk(KERN_WARNING "fcoe: dropping "
-                                              "frame with CRC error\n");
-                               stats->InvalidCRCCount++;
-                               stats->ErrorFrames++;
-                               fc_frame_free(fp);
-                               continue;
-                       }
-                       fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-               }
-               if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
-                   fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) {
-                       fc_frame_free(fp);
-                       continue;
-               }
-               fc_exch_recv(lp, fp);
+               fcoe_recv_frame(skb);
        }
        return 0;
 }
 
 /**
- * fcoe_check_wait_queue() - attempt to clear the transmit backlog
- * @lp: the fc_lport
+ * fcoe_check_wait_queue() - Attempt to clear the transmit backlog
+ * @lport: The local port whose backlog is to be cleared
  *
- * This empties the wait_queue, dequeue the head of the wait_queue queue
- * and calls fcoe_start_io() for each packet, if all skb have been
- * transmitted, return qlen or -1 if a error occurs, then restore
- * wait_queue and try again later.
+ * This empties the wait_queue, dequeues the head of the wait_queue queue
+ * and calls fcoe_start_io() for each packet. If all skb have been
+ * transmitted it returns the qlen. If an error occurs it restores
+ * wait_queue (to try again later) and returns -1.
  *
- * 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
+ * The wait_queue is used when the skb transmit fails. The failed skb
+ * will go in the wait_queue which will be emptied by the timer function or
  * by the next skb transmit.
  */
-static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
+static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb)
 {
-       struct fcoe_port *port = lport_priv(lp);
+       struct fcoe_port *port = lport_priv(lport);
        int rc;
 
        spin_lock_bh(&port->fcoe_pending_queue.lock);
@@ -1501,19 +1716,19 @@ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
        }
 
        if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
-               lp->qfull = 0;
+               lport->qfull = 0;
        if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
                mod_timer(&port->timer, jiffies + 2);
        port->fcoe_pending_queue_active = 0;
 out:
        if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
-               lp->qfull = 1;
+               lport->qfull = 1;
        spin_unlock_bh(&port->fcoe_pending_queue.lock);
        return;
 }
 
 /**
- * fcoe_dev_setup() - setup link change notification interface
+ * fcoe_dev_setup() - Setup the link change notification interface
  */
 static void fcoe_dev_setup(void)
 {
@@ -1521,7 +1736,7 @@ static void fcoe_dev_setup(void)
 }
 
 /**
- * fcoe_dev_cleanup() - cleanup link change notification interface
+ * fcoe_dev_cleanup() - Cleanup the link change notification interface
  */
 static void fcoe_dev_cleanup(void)
 {
@@ -1529,19 +1744,19 @@ static void fcoe_dev_cleanup(void)
 }
 
 /**
- * fcoe_device_notification() - netdev event notification callback
- * @notifier: context of the notification
- * @event: type of event
- * @ptr: fixed array for output parsed ifname
+ * fcoe_device_notification() - Handler for net device events
+ * @notifier: The context of the notification
+ * @event:    The type of event
+ * @ptr:      The net device that the event was on
  *
- * This function is called by the ethernet driver in case of link change event
+ * This function is called by the Ethernet driver in case of link change event.
  *
  * Returns: 0 for success
  */
 static int fcoe_device_notification(struct notifier_block *notifier,
                                    ulong event, void *ptr)
 {
-       struct fc_lport *lp = NULL;
+       struct fc_lport *lport = NULL;
        struct net_device *netdev = ptr;
        struct fcoe_interface *fcoe;
        struct fcoe_port *port;
@@ -1552,11 +1767,11 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 
        list_for_each_entry(fcoe, &fcoe_hostlist, list) {
                if (fcoe->netdev == netdev) {
-                       lp = fcoe->ctlr.lp;
+                       lport = fcoe->ctlr.lp;
                        break;
                }
        }
-       if (lp == NULL) {
+       if (!lport) {
                rc = NOTIFY_DONE;
                goto out;
        }
@@ -1570,10 +1785,12 @@ static int fcoe_device_notification(struct notifier_block *notifier,
        case NETDEV_CHANGE:
                break;
        case NETDEV_CHANGEMTU:
+               if (netdev->features & NETIF_F_FCOE_MTU)
+                       break;
                mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
                                     sizeof(struct fcoe_crc_eof));
                if (mfs >= FC_MIN_MAX_FRAME)
-                       fc_set_mfs(lp, mfs);
+                       fc_set_mfs(lport, mfs);
                break;
        case NETDEV_REGISTER:
                break;
@@ -1588,22 +1805,22 @@ static int fcoe_device_notification(struct notifier_block *notifier,
                FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
                                "from netdev netlink\n", event);
        }
-       if (link_possible && !fcoe_link_ok(lp))
+       if (link_possible && !fcoe_link_ok(lport))
                fcoe_ctlr_link_up(&fcoe->ctlr);
        else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
-               stats = fc_lport_get_stats(lp);
+               stats = fc_lport_get_stats(lport);
                stats->LinkFailureCount++;
-               fcoe_clean_pending_queue(lp);
+               fcoe_clean_pending_queue(lport);
        }
 out:
        return rc;
 }
 
 /**
- * fcoe_if_to_netdev() - parse a name buffer to get netdev
- * @buffer: incoming buffer to be copied
+ * fcoe_if_to_netdev() - Parse a name buffer to get a net device
+ * @buffer: The name of the net device
  *
- * Returns: NULL or ptr to net_device
+ * Returns: NULL or ptr to net_device
  */
 static struct net_device *fcoe_if_to_netdev(const char *buffer)
 {
@@ -1621,9 +1838,11 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
 }
 
 /**
- * fcoe_destroy() - handles the destroy from sysfs
- * @buffer: expected to be an eth if name
- * @kp: associated kernel param
+ * fcoe_destroy() - Destroy a FCoE interface
+ * @buffer: The name of the Ethernet interface to be destroyed
+ * @kp:            The associated kernel parameter
+ *
+ * Called from sysfs.
  *
  * Returns: 0 for success
  */
@@ -1631,7 +1850,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 {
        struct fcoe_interface *fcoe;
        struct net_device *netdev;
-       int rc;
+       int rc = 0;
 
        mutex_lock(&fcoe_config_mutex);
 #ifdef CONFIG_FCOE_MODULE
@@ -1670,6 +1889,10 @@ out_nodev:
        return rc;
 }
 
+/**
+ * fcoe_destroy_work() - Destroy a FCoE port in a deferred work context
+ * @work: Handle to the FCoE port to be destroyed
+ */
 static void fcoe_destroy_work(struct work_struct *work)
 {
        struct fcoe_port *port;
@@ -1681,9 +1904,11 @@ static void fcoe_destroy_work(struct work_struct *work)
 }
 
 /**
- * fcoe_create() - Handles the create call from sysfs
- * @buffer: expected to be an eth if name
- * @kp: associated kernel param
+ * fcoe_create() - Create a fcoe interface
+ * @buffer: The name of the Ethernet interface to create on
+ * @kp:            The associated kernel param
+ *
+ * Called from sysfs.
  *
  * Returns: 0 for success
  */
@@ -1726,7 +1951,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
                goto out_putdev;
        }
 
-       lport = fcoe_if_create(fcoe, &netdev->dev);
+       lport = fcoe_if_create(fcoe, &netdev->dev, 0);
        if (IS_ERR(lport)) {
                printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
                       netdev->name);
@@ -1762,16 +1987,9 @@ out_nodev:
        return rc;
 }
 
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
-module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
-__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
-
 /**
- * fcoe_link_ok() - Check if link is ok for the fc_lport
- * @lp: ptr to the fc_lport
+ * fcoe_link_ok() - Check if the link is OK for a local port
+ * @lport: The local port to check link on
  *
  * Any permanently-disqualifying conditions have been previously checked.
  * This also updates the speed setting, which may change with link for 100/1000.
@@ -1783,26 +2001,26 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
  * Returns: 0 if link is OK for use by FCoE.
  *
  */
-int fcoe_link_ok(struct fc_lport *lp)
+int fcoe_link_ok(struct fc_lport *lport)
 {
-       struct fcoe_port *port = lport_priv(lp);
-       struct net_device *dev = port->fcoe->netdev;
+       struct fcoe_port *port = lport_priv(lport);
+       struct net_device *netdev = port->fcoe->netdev;
        struct ethtool_cmd ecmd = { ETHTOOL_GSET };
 
-       if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
-           (!dev_ethtool_get_settings(dev, &ecmd))) {
-               lp->link_supported_speeds &=
+       if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) &&
+           (!dev_ethtool_get_settings(netdev, &ecmd))) {
+               lport->link_supported_speeds &=
                        ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
                if (ecmd.supported & (SUPPORTED_1000baseT_Half |
                                      SUPPORTED_1000baseT_Full))
-                       lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+                       lport->link_supported_speeds |= FC_PORTSPEED_1GBIT;
                if (ecmd.supported & SUPPORTED_10000baseT_Full)
-                       lp->link_supported_speeds |=
+                       lport->link_supported_speeds |=
                                FC_PORTSPEED_10GBIT;
                if (ecmd.speed == SPEED_1000)
-                       lp->link_speed = FC_PORTSPEED_1GBIT;
+                       lport->link_speed = FC_PORTSPEED_1GBIT;
                if (ecmd.speed == SPEED_10000)
-                       lp->link_speed = FC_PORTSPEED_10GBIT;
+                       lport->link_speed = FC_PORTSPEED_10GBIT;
 
                return 0;
        }
@@ -1810,8 +2028,8 @@ int fcoe_link_ok(struct fc_lport *lp)
 }
 
 /**
- * fcoe_percpu_clean() - Clear the pending skbs for an lport
- * @lp: the fc_lport
+ * fcoe_percpu_clean() - Clear all pending skbs for an local port
+ * @lport: The local port whose skbs are to be cleared
  *
  * Must be called with fcoe_create_mutex held to single-thread completion.
  *
@@ -1820,7 +2038,7 @@ int fcoe_link_ok(struct fc_lport *lp)
  * there no packets that will be handled by the lport, but also that any
  * threads already handling packet have returned.
  */
-void fcoe_percpu_clean(struct fc_lport *lp)
+void fcoe_percpu_clean(struct fc_lport *lport)
 {
        struct fcoe_percpu_s *pp;
        struct fcoe_rcv_info *fr;
@@ -1838,7 +2056,7 @@ void fcoe_percpu_clean(struct fc_lport *lp)
                     skb = next) {
                        next = skb->next;
                        fr = fcoe_dev_from_skb(skb);
-                       if (fr->fr_dev == lp) {
+                       if (fr->fr_dev == lport) {
                                __skb_unlink(skb, list);
                                kfree_skb(skb);
                        }
@@ -1867,13 +2085,11 @@ void fcoe_percpu_clean(struct fc_lport *lp)
 
 /**
  * fcoe_clean_pending_queue() - Dequeue a skb and free it
- * @lp: the corresponding fc_lport
- *
- * Returns: none
+ * @lport: The local port to dequeue a skb on
  */
-void fcoe_clean_pending_queue(struct fc_lport *lp)
+void fcoe_clean_pending_queue(struct fc_lport *lport)
 {
-       struct fcoe_port  *port = lport_priv(lp);
+       struct fcoe_port  *port = lport_priv(lport);
        struct sk_buff *skb;
 
        spin_lock_bh(&port->fcoe_pending_queue.lock);
@@ -1886,10 +2102,10 @@ void fcoe_clean_pending_queue(struct fc_lport *lp)
 }
 
 /**
- * fcoe_reset() - Resets the fcoe
- * @shost: shost the reset is from
+ * fcoe_reset() - Reset a local port
+ * @shost: The SCSI host associated with the local port to be reset
  *
- * Returns: always 0
+ * Returns: Always 0 (return value required by FC transport template)
  */
 int fcoe_reset(struct Scsi_Host *shost)
 {
@@ -1899,30 +2115,33 @@ int fcoe_reset(struct Scsi_Host *shost)
 }
 
 /**
- * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device
- * @dev: this is currently ptr to net_device
+ * fcoe_hostlist_lookup_port() - Find the FCoE interface associated with a net device
+ * @netdev: The net device used as a key
+ *
+ * Locking: Must be called with the RNL mutex held.
  *
- * Returns: NULL or the located fcoe_port
- * Locking: must be called with the RNL mutex held
+ * Returns: NULL or the FCoE interface
  */
 static struct fcoe_interface *
-fcoe_hostlist_lookup_port(const struct net_device *dev)
+fcoe_hostlist_lookup_port(const struct net_device *netdev)
 {
        struct fcoe_interface *fcoe;
 
        list_for_each_entry(fcoe, &fcoe_hostlist, list) {
-               if (fcoe->netdev == dev)
+               if (fcoe->netdev == netdev)
                        return fcoe;
        }
        return NULL;
 }
 
 /**
- * fcoe_hostlist_lookup() - Find the corresponding lport by netdev
- * @netdev: ptr to net_device
+ * fcoe_hostlist_lookup() - Find the local port associated with a
+ *                         given net device
+ * @netdev: The netdevice used as a key
  *
- * Returns: 0 for success
- * Locking: must be called with the RTNL mutex held
+ * Locking: Must be called with the RTNL mutex held
+ *
+ * Returns: NULL or the local port
  */
 static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
 {
@@ -1933,11 +2152,13 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
 }
 
 /**
- * fcoe_hostlist_add() - Add a lport to lports list
- * @lp: ptr to the fc_lport to be added
+ * fcoe_hostlist_add() - Add the FCoE interface identified by a local
+ *                      port to the hostlist
+ * @lport: The local port that identifies the FCoE interface to be added
  *
- * Returns: 0 for success
  * Locking: must be called with the RTNL mutex held
+ *
+ * Returns: 0 for success
  */
 static int fcoe_hostlist_add(const struct fc_lport *lport)
 {
@@ -1954,15 +2175,15 @@ static int fcoe_hostlist_add(const struct fc_lport *lport)
 }
 
 /**
- * fcoe_init() - fcoe module loading initialization
+ * fcoe_init() - Initialize fcoe.ko
  *
- * Returns 0 on success, negative on failure
+ * Returns: 0 on success, or a negative value on failure
  */
 static int __init fcoe_init(void)
 {
+       struct fcoe_percpu_s *p;
        unsigned int cpu;
        int rc = 0;
-       struct fcoe_percpu_s *p;
 
        mutex_lock(&fcoe_config_mutex);
 
@@ -1999,15 +2220,15 @@ out_free:
 module_init(fcoe_init);
 
 /**
- * fcoe_exit() - fcoe module unloading cleanup
+ * fcoe_exit() - Clean up fcoe.ko
  *
- * Returns 0 on success, negative on failure
+ * Returns: 0 on success or a  negative value on failure
  */
 static void __exit fcoe_exit(void)
 {
-       unsigned int cpu;
        struct fcoe_interface *fcoe, *tmp;
        struct fcoe_port *port;
+       unsigned int cpu;
 
        mutex_lock(&fcoe_config_mutex);
 
@@ -2033,9 +2254,238 @@ static void __exit fcoe_exit(void)
        /* flush any asyncronous interface destroys,
         * this should happen after the netdev notifier is unregistered */
        flush_scheduled_work();
+       /* That will flush out all the N_Ports on the hostlist, but now we
+        * may have NPIV VN_Ports scheduled for destruction */
+       flush_scheduled_work();
 
        /* detach from scsi transport
         * must happen after all destroys are done, therefor after the flush */
        fcoe_if_exit();
 }
 module_exit(fcoe_exit);
+
+/**
+ * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler
+ * @seq: active sequence in the FLOGI or FDISC exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc FLOGI response handler.
+ */
+static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+       struct fcoe_ctlr *fip = arg;
+       struct fc_exch *exch = fc_seq_exch(seq);
+       struct fc_lport *lport = exch->lp;
+       u8 *mac;
+
+       if (IS_ERR(fp))
+               goto done;
+
+       mac = fr_cb(fp)->granted_mac;
+       if (is_zero_ether_addr(mac)) {
+               /* pre-FIP */
+               if (fcoe_ctlr_recv_flogi(fip, lport, fp)) {
+                       fc_frame_free(fp);
+                       return;
+               }
+       }
+       fcoe_update_src_mac(lport, mac);
+done:
+       fc_lport_flogi_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_logo_resp() - FCoE specific LOGO response handler
+ * @seq: active sequence in the LOGO exchange
+ * @fp: response frame, or error encoded in a pointer (timeout)
+ * @arg: pointer the the fcoe_ctlr structure
+ *
+ * This handles MAC address managment for FCoE, then passes control on to
+ * the libfc LOGO response handler.
+ */
+static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
+{
+       struct fc_lport *lport = arg;
+       static u8 zero_mac[ETH_ALEN] = { 0 };
+
+       if (!IS_ERR(fp))
+               fcoe_update_src_mac(lport, zero_mac);
+       fc_lport_logo_resp(seq, fp, lport);
+}
+
+/**
+ * fcoe_elsct_send - FCoE specific ELS handler
+ *
+ * This does special case handling of FIP encapsualted ELS exchanges for FCoE,
+ * using FCoE specific response handlers and passing the FIP controller as
+ * the argument (the lport is still available from the exchange).
+ *
+ * Most of the work here is just handed off to the libfc routine.
+ */
+static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
+                                     struct fc_frame *fp, unsigned int op,
+                                     void (*resp)(struct fc_seq *,
+                                                  struct fc_frame *,
+                                                  void *),
+                                     void *arg, u32 timeout)
+{
+       struct fcoe_port *port = lport_priv(lport);
+       struct fcoe_interface *fcoe = port->fcoe;
+       struct fcoe_ctlr *fip = &fcoe->ctlr;
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+
+       switch (op) {
+       case ELS_FLOGI:
+       case ELS_FDISC:
+               return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp,
+                                    fip, timeout);
+       case ELS_LOGO:
+               /* only hook onto fabric logouts, not port logouts */
+               if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
+                       break;
+               return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp,
+                                    lport, timeout);
+       }
+       return fc_elsct_send(lport, did, fp, op, resp, arg, timeout);
+}
+
+/**
+ * fcoe_vport_create() - create an fc_host/scsi_host for a vport
+ * @vport: fc_vport object to create a new fc_host for
+ * @disabled: start the new fc_host in a disabled state by default?
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_vport_create(struct fc_vport *vport, bool disabled)
+{
+       struct Scsi_Host *shost = vport_to_shost(vport);
+       struct fc_lport *n_port = shost_priv(shost);
+       struct fcoe_port *port = lport_priv(n_port);
+       struct fcoe_interface *fcoe = port->fcoe;
+       struct net_device *netdev = fcoe->netdev;
+       struct fc_lport *vn_port;
+
+       mutex_lock(&fcoe_config_mutex);
+       vn_port = fcoe_if_create(fcoe, &vport->dev, 1);
+       mutex_unlock(&fcoe_config_mutex);
+
+       if (IS_ERR(vn_port)) {
+               printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n",
+                      netdev->name);
+               return -EIO;
+       }
+
+       if (disabled) {
+               fc_vport_set_state(vport, FC_VPORT_DISABLED);
+       } else {
+               vn_port->boot_time = jiffies;
+               fc_fabric_login(vn_port);
+               fc_vport_setlink(vn_port);
+       }
+       return 0;
+}
+
+/**
+ * fcoe_vport_destroy() - destroy the fc_host/scsi_host for a vport
+ * @vport: fc_vport object that is being destroyed
+ *
+ * Returns: 0 for success
+ */
+static int fcoe_vport_destroy(struct fc_vport *vport)
+{
+       struct Scsi_Host *shost = vport_to_shost(vport);
+       struct fc_lport *n_port = shost_priv(shost);
+       struct fc_lport *vn_port = vport->dd_data;
+       struct fcoe_port *port = lport_priv(vn_port);
+
+       mutex_lock(&n_port->lp_mutex);
+       list_del(&vn_port->list);
+       mutex_unlock(&n_port->lp_mutex);
+       schedule_work(&port->destroy_work);
+       return 0;
+}
+
+/**
+ * fcoe_vport_disable() - change vport state
+ * @vport: vport to bring online/offline
+ * @disable: should the vport be disabled?
+ */
+static int fcoe_vport_disable(struct fc_vport *vport, bool disable)
+{
+       struct fc_lport *lport = vport->dd_data;
+
+       if (disable) {
+               fc_vport_set_state(vport, FC_VPORT_DISABLED);
+               fc_fabric_logoff(lport);
+       } else {
+               lport->boot_time = jiffies;
+               fc_fabric_login(lport);
+               fc_vport_setlink(lport);
+       }
+
+       return 0;
+}
+
+/**
+ * fcoe_vport_set_symbolic_name() - append vport string to symbolic name
+ * @vport: fc_vport with a new symbolic name string
+ *
+ * After generating a new symbolic name string, a new RSPN_ID request is
+ * sent to the name server.  There is no response handler, so if it fails
+ * for some reason it will not be retried.
+ */
+static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
+{
+       struct fc_lport *lport = vport->dd_data;
+       struct fc_frame *fp;
+       size_t len;
+
+       snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE,
+                "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION,
+                fcoe_netdev(lport)->name, vport->symbolic_name);
+
+       if (lport->state != LPORT_ST_READY)
+               return;
+
+       len = strnlen(fc_host_symbolic_name(lport->host), 255);
+       fp = fc_frame_alloc(lport,
+                           sizeof(struct fc_ct_hdr) +
+                           sizeof(struct fc_ns_rspn) + len);
+       if (!fp)
+               return;
+       lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID,
+                            NULL, NULL, 3 * lport->r_a_tov);
+}
+
+/**
+ * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
+ * @lport: the local port
+ * @fc_lesb: the link error status block
+ */
+static void fcoe_get_lesb(struct fc_lport *lport,
+                        struct fc_els_lesb *fc_lesb)
+{
+       unsigned int cpu;
+       u32 lfc, vlfc, mdac;
+       struct fcoe_dev_stats *devst;
+       struct fcoe_fc_els_lesb *lesb;
+       struct net_device *netdev = fcoe_netdev(lport);
+
+       lfc = 0;
+       vlfc = 0;
+       mdac = 0;
+       lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
+       memset(lesb, 0, sizeof(*lesb));
+       for_each_possible_cpu(cpu) {
+               devst = per_cpu_ptr(lport->dev_stats, cpu);
+               lfc += devst->LinkFailureCount;
+               vlfc += devst->VLinkFailureCount;
+               mdac += devst->MissDiscAdvCount;
+       }
+       lesb->lesb_link_fail = htonl(lfc);
+       lesb->lesb_vlink_fail = htonl(vlfc);
+       lesb->lesb_miss_fka = htonl(mdac);
+       lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
+}
index ce7f60f..c69b2c5 100644 (file)
@@ -32,7 +32,7 @@
 #define FCOE_NAME      "fcoe"
 #define FCOE_VENDOR    "Open-FCoE.org"
 
-#define FCOE_MAX_LUN           255
+#define FCOE_MAX_LUN           0xFFFF
 #define FCOE_MAX_FCP_TARGET    256
 
 #define FCOE_MAX_OUTSTANDING_COMMANDS  1024
 #define FCOE_MIN_XID           0x0000  /* the min xid supported by fcoe_sw */
 #define FCOE_MAX_XID           0x0FFF  /* the max xid supported by fcoe_sw */
 
+/*
+ * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload)
+ * + 4 (FC CRC) + 4 (FCoE trailer) =  2158 bytes
+ */
+#define FCOE_MTU       2158
+
 unsigned int fcoe_debug_logging;
 module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
 
-#define FCOE_LOGGING        0x01 /* General logging, not categorized */
+#define FCOE_LOGGING       0x01 /* General logging, not categorized */
 #define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */
 
 #define FCOE_CHECK_LOGGING(LEVEL, CMD)                                 \
@@ -64,8 +70,13 @@ do {                                                                 \
                           printk(KERN_INFO "fcoe: %s: " fmt,   \
                                  netdev->name, ##args);)
 
-/*
- * this percpu struct for fcoe
+/**
+ * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads
+ * @thread:        The thread context
+ * @fcoe_rx_list:   The queue of pending packets to process
+ * @page:          The memory page for calculating frame trailer CRCs
+ * @crc_eof_offset: The offset into the CRC page pointing to available
+ *                 memory for a new trailer
  */
 struct fcoe_percpu_s {
        struct task_struct *thread;
@@ -74,37 +85,62 @@ struct fcoe_percpu_s {
        int crc_eof_offset;
 };
 
-/*
- * an FCoE interface, 1:1 with netdev
+/**
+ * struct fcoe_interface - A FCoE interface
+ * @list:            Handle for a list of FCoE interfaces
+ * @netdev:          The associated net device
+ * @fcoe_packet_type: FCoE packet type
+ * @fip_packet_type:  FIP packet type
+ * @ctlr:            The FCoE controller (for FIP)
+ * @oem:             The offload exchange manager for all local port
+ *                   instances associated with this port
+ * @kref:            The kernel reference
+ *
+ * This structure is 1:1 with a net devive.
  */
 struct fcoe_interface {
-       struct list_head list;
-       struct net_device *netdev;
-       struct packet_type  fcoe_packet_type;
-       struct packet_type  fip_packet_type;
-       struct fcoe_ctlr ctlr;
-       struct fc_exch_mgr *oem;                /* offload exchange manager */
-       struct kref kref;
+       struct list_head   list;
+       struct net_device  *netdev;
+       struct packet_type fcoe_packet_type;
+       struct packet_type fip_packet_type;
+       struct fcoe_ctlr   ctlr;
+       struct fc_exch_mgr *oem;
+       struct kref        kref;
 };
 
-/*
- * the FCoE private structure that's allocated along with the
- * Scsi_Host and libfc fc_lport structures
+/**
+ * struct fcoe_port - The FCoE private structure
+ * @fcoe:                     The associated fcoe interface
+ * @lport:                    The associated local port
+ * @fcoe_pending_queue:               The pending Rx queue of skbs
+ * @fcoe_pending_queue_active: Indicates if the pending queue is active
+ * @timer:                    The queue timer
+ * @destroy_work:             Handle for work context
+ *                            (to prevent RTNL deadlocks)
+ * @data_srt_addr:            Source address for data
+ *
+ * An instance of this structure is to be allocated along with the
+ * Scsi_Host and libfc fc_lport structures.
  */
 struct fcoe_port {
        struct fcoe_interface *fcoe;
-       struct fc_lport *lport;
-       struct sk_buff_head fcoe_pending_queue;
-       u8      fcoe_pending_queue_active;
-       struct timer_list timer;                /* queue timer */
-       struct work_struct destroy_work;        /* to prevent rtnl deadlocks */
+       struct fc_lport       *lport;
+       struct sk_buff_head   fcoe_pending_queue;
+       u8                    fcoe_pending_queue_active;
+       struct timer_list     timer;
+       struct work_struct    destroy_work;
+       u8                    data_src_addr[ETH_ALEN];
 };
 
 #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
 
-static inline struct net_device *fcoe_netdev(const struct fc_lport *lp)
+/**
+ * fcoe_netdev() - Return the net device associated with a local port
+ * @lport: The local port to get the net device from
+ */
+static inline struct net_device *fcoe_netdev(const struct fc_lport *lport)
 {
-       return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev;
+       return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev;
 }
 
 #endif /* _FCOE_H_ */
index 11ae5c9..9823291 100644 (file)
@@ -59,26 +59,30 @@ unsigned int libfcoe_debug_logging;
 module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
 
-#define LIBFCOE_LOGGING     0x01 /* General logging, not categorized */
+#define LIBFCOE_LOGGING            0x01 /* General logging, not categorized */
 #define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */
 
-#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD)                              \
-do {                                                                   \
-       if (unlikely(libfcoe_debug_logging & LEVEL))                    \
-               do {                                                    \
-                       CMD;                                            \
-               } while (0);                                            \
+#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD)              \
+do {                                                   \
+       if (unlikely(libfcoe_debug_logging & LEVEL))    \
+               do {                                    \
+                       CMD;                            \
+               } while (0);                            \
 } while (0)
 
 #define LIBFCOE_DBG(fmt, args...)                                      \
        LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING,                          \
                              printk(KERN_INFO "libfcoe: " fmt, ##args);)
 
-#define LIBFCOE_FIP_DBG(fmt, args...)                                  \
+#define LIBFCOE_FIP_DBG(fip, fmt, args...)                             \
        LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING,                      \
-                             printk(KERN_INFO "fip: " fmt, ##args);)
+                             printk(KERN_INFO "host%d: fip: " fmt,     \
+                                    (fip)->lp->host->host_no, ##args);)
 
-/*
+/**
+ * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
+ * @fcf: The FCF to check
+ *
  * Return non-zero if FCF fcoe_size has been validated.
  */
 static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
@@ -86,7 +90,10 @@ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf)
        return (fcf->flags & FIP_FL_SOL) != 0;
 }
 
-/*
+/**
+ * fcoe_ctlr_fcf_usable() - Check if a FCF is usable
+ * @fcf: The FCF to check
+ *
  * Return non-zero if the FCF is usable.
  */
 static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
@@ -97,12 +104,13 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
 }
 
 /**
- * fcoe_ctlr_init() - Initialize the FCoE Controller instance.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_init() - Initialize the FCoE Controller instance
+ * @fip: The FCoE controller to initialize
  */
 void fcoe_ctlr_init(struct fcoe_ctlr *fip)
 {
        fip->state = FIP_ST_LINK_WAIT;
+       fip->mode = FIP_ST_AUTO;
        INIT_LIST_HEAD(&fip->fcfs);
        spin_lock_init(&fip->lock);
        fip->flogi_oxid = FC_XID_UNKNOWN;
@@ -114,8 +122,8 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip)
 EXPORT_SYMBOL(fcoe_ctlr_init);
 
 /**
- * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller
+ * @fip: The FCoE controller whose FCFs are to be reset
  *
  * Called with &fcoe_ctlr lock held.
  */
@@ -134,8 +142,8 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
 }
 
 /**
- * fcoe_ctlr_destroy() - Disable and tear-down the FCoE controller.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller
+ * @fip: The FCoE controller to tear down
  *
  * This is called by FCoE drivers before freeing the &fcoe_ctlr.
  *
@@ -148,9 +156,7 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
 void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
 {
        cancel_work_sync(&fip->recv_work);
-       spin_lock_bh(&fip->fip_recv_list.lock);
-       __skb_queue_purge(&fip->fip_recv_list);
-       spin_unlock_bh(&fip->fip_recv_list.lock);
+       skb_queue_purge(&fip->fip_recv_list);
 
        spin_lock_bh(&fip->lock);
        fip->state = FIP_ST_DISABLED;
@@ -162,8 +168,8 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
 EXPORT_SYMBOL(fcoe_ctlr_destroy);
 
 /**
- * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port
+ * @fip: The FCoE controller to get the maximum FCoE size from
  *
  * Returns the maximum packet size including the FCoE header and trailer,
  * but not including any Ethernet or VLAN headers.
@@ -180,9 +186,9 @@ static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip)
 }
 
 /**
- * fcoe_ctlr_solicit() - Send a solicitation.
- * @fip:       FCoE controller.
- * @fcf:       Destination FCF.  If NULL, a multicast solicitation is sent.
+ * fcoe_ctlr_solicit() - Send a FIP solicitation
+ * @fip: The FCoE controller to send the solicitation on
+ * @fcf: The destination FCF (if NULL, a multicast solicitation is sent)
  */
 static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
 {
@@ -241,8 +247,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
 }
 
 /**
- * fcoe_ctlr_link_up() - Start FCoE controller.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_link_up() - Start FCoE controller
+ * @fip: The FCoE controller to start
  *
  * Called from the LLD when the network link is ready.
  */
@@ -255,11 +261,12 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
                spin_unlock_bh(&fip->lock);
                fc_linkup(fip->lp);
        } else if (fip->state == FIP_ST_LINK_WAIT) {
-               fip->state = FIP_ST_AUTO;
+               fip->state = fip->mode;
                fip->last_link = 1;
                fip->link = 1;
                spin_unlock_bh(&fip->lock);
-               LIBFCOE_FIP_DBG("%s", "setting AUTO mode.\n");
+               if (fip->state == FIP_ST_AUTO)
+                       LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
                fc_linkup(fip->lp);
                fcoe_ctlr_solicit(fip, NULL);
        } else
@@ -268,45 +275,23 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
 EXPORT_SYMBOL(fcoe_ctlr_link_up);
 
 /**
- * fcoe_ctlr_reset() - Reset FIP.
- * @fip:       FCoE controller.
- * @new_state: FIP state to be entered.
- *
- * Returns non-zero if the link was up and now isn't.
+ * fcoe_ctlr_reset() - Reset a FCoE controller
+ * @fip:       The FCoE controller to reset
  */
-static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
+static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
 {
-       struct fc_lport *lp = fip->lp;
-       int link_dropped;
-
-       spin_lock_bh(&fip->lock);
        fcoe_ctlr_reset_fcfs(fip);
        del_timer(&fip->timer);
-       fip->state = new_state;
        fip->ctlr_ka_time = 0;
        fip->port_ka_time = 0;
        fip->sol_time = 0;
        fip->flogi_oxid = FC_XID_UNKNOWN;
        fip->map_dest = 0;
-       fip->last_link = 0;
-       link_dropped = fip->link;
-       fip->link = 0;
-       spin_unlock_bh(&fip->lock);
-
-       if (link_dropped)
-               fc_linkdown(lp);
-
-       if (new_state == FIP_ST_ENABLED) {
-               fcoe_ctlr_solicit(fip, NULL);
-               fc_linkup(lp);
-               link_dropped = 0;
-       }
-       return link_dropped;
 }
 
 /**
- * fcoe_ctlr_link_down() - Stop FCoE controller.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_link_down() - Stop a FCoE controller
+ * @fip: The FCoE controller to be stopped
  *
  * Returns non-zero if the link was up and now isn't.
  *
@@ -315,15 +300,29 @@ static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state)
  */
 int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
 {
-       return fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+       int link_dropped;
+
+       LIBFCOE_FIP_DBG(fip, "link down.\n");
+       spin_lock_bh(&fip->lock);
+       fcoe_ctlr_reset(fip);
+       link_dropped = fip->link;
+       fip->link = 0;
+       fip->last_link = 0;
+       fip->state = FIP_ST_LINK_WAIT;
+       spin_unlock_bh(&fip->lock);
+
+       if (link_dropped)
+               fc_linkdown(fip->lp);
+       return link_dropped;
 }
 EXPORT_SYMBOL(fcoe_ctlr_link_down);
 
 /**
- * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF.
- * @fip:       FCoE controller.
- * @ports:     0 for controller keep-alive, 1 for port keep-alive.
- * @sa:                source MAC address.
+ * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF
+ * @fip:   The FCoE controller to send the FKA on
+ * @lport: libfc fc_lport to send from
+ * @ports: 0 for controller keep-alive, 1 for port keep-alive
+ * @sa:           The source MAC address
  *
  * A controller keep-alive is sent every fka_period (typically 8 seconds).
  * The source MAC is the native MAC address.
@@ -332,7 +331,9 @@ EXPORT_SYMBOL(fcoe_ctlr_link_down);
  * The source MAC is the assigned mapped source address.
  * The destination is the FCF's F-port.
  */
-static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
+static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
+                                     struct fc_lport *lport,
+                                     int ports, u8 *sa)
 {
        struct sk_buff *skb;
        struct fip_kal {
@@ -350,8 +351,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
        if (!fcf || !fc_host_port_id(lp->host))
                return;
 
-       len = fcoe_ctlr_fcoe_size(fip) + sizeof(struct ethhdr);
-       BUG_ON(len < sizeof(*kal) + sizeof(*vn));
+       len = sizeof(*kal) + ports * sizeof(*vn);
        skb = dev_alloc_skb(len);
        if (!skb)
                return;
@@ -366,7 +366,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
        kal->fip.fip_op = htons(FIP_OP_CTRL);
        kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE;
        kal->fip.fip_dl_len = htons((sizeof(kal->mac) +
-                                   ports * sizeof(*vn)) / FIP_BPW);
+                                    ports * sizeof(*vn)) / FIP_BPW);
        kal->fip.fip_flags = htons(FIP_FL_FPMA);
        if (fip->spma)
                kal->fip.fip_flags |= htons(FIP_FL_SPMA);
@@ -374,16 +374,14 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
        kal->mac.fd_desc.fip_dtype = FIP_DT_MAC;
        kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW;
        memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
-
        if (ports) {
                vn = (struct fip_vn_desc *)(kal + 1);
                vn->fd_desc.fip_dtype = FIP_DT_VN_ID;
                vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW;
-               memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN);
+               memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
                hton24(vn->fd_fc_id, fc_host_port_id(lp->host));
                put_unaligned_be64(lp->wwpn, &vn->fd_wwpn);
        }
-
        skb_put(skb, len);
        skb->protocol = htons(ETH_P_FIP);
        skb_reset_mac_header(skb);
@@ -392,10 +390,10 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
 }
 
 /**
- * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it.
- * @fip:       FCoE controller.
- * @dtype:     FIP descriptor type for the frame.
- * @skb:       FCoE ELS frame including FC header but no FCoE headers.
+ * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it
+ * @fip:   The FCoE controller for the ELS frame
+ * @dtype: The FIP descriptor type for the frame
+ * @skb:   The FCoE ELS frame including FC header but no FCoE headers
  *
  * Returns non-zero error code on failure.
  *
@@ -405,7 +403,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa)
  * Headroom includes the FIP encapsulation description, FIP header, and
  * Ethernet header.  The tailroom is for the FIP MAC descriptor.
  */
-static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
+static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
                            u8 dtype, struct sk_buff *skb)
 {
        struct fip_encaps_head {
@@ -449,8 +447,8 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
        memset(mac, 0, sizeof(mac));
        mac->fd_desc.fip_dtype = FIP_DT_MAC;
        mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
-       if (dtype != FIP_DT_FLOGI)
-               memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN);
+       if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC)
+               memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
        else if (fip->spma)
                memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
 
@@ -463,6 +461,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
 /**
  * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate.
  * @fip:       FCoE controller.
+ * @lport:     libfc fc_lport to send from
  * @skb:       FCoE ELS frame including FC header but no FCoE headers.
  *
  * Returns a non-zero error code if the frame should not be sent.
@@ -471,11 +470,13 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
  * The caller must check that the length is a multiple of 4.
  * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
  */
-int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
+int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
+                      struct sk_buff *skb)
 {
        struct fc_frame_header *fh;
        u16 old_xid;
        u8 op;
+       u8 mac[ETH_ALEN];
 
        fh = (struct fc_frame_header *)skb->data;
        op = *(u8 *)(fh + 1);
@@ -498,6 +499,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 
        if (fip->state == FIP_ST_NON_FIP)
                return 0;
+       if (!fip->sel_fcf)
+               goto drop;
 
        switch (op) {
        case ELS_FLOGI:
@@ -530,14 +533,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
                 * FLOGI.
                 */
                fip->flogi_oxid = FC_XID_UNKNOWN;
-               fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id);
+               fc_fcoe_set_mac(mac, fh->fh_d_id);
+               fip->update_mac(lport, mac);
                return 0;
        default:
                if (fip->state != FIP_ST_ENABLED)
                        goto drop;
                return 0;
        }
-       if (fcoe_ctlr_encaps(fip, op, skb))
+       if (fcoe_ctlr_encaps(fip, lport, op, skb))
                goto drop;
        fip->send(fip, skb);
        return -EINPROGRESS;
@@ -547,9 +551,9 @@ drop:
 }
 EXPORT_SYMBOL(fcoe_ctlr_els_send);
 
-/*
- * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller.
- * @fip:       FCoE controller.
+/**
+ * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller
+ * @fip: The FCoE controller to free FCFs on
  *
  * Called with lock held.
  *
@@ -558,14 +562,28 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send);
  * times its keep-alive period including fuzz.
  *
  * In addition, determine the time when an FCF selection can occur.
+ *
+ * Also, increment the MissDiscAdvCount when no advertisement is received
+ * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
  */
 static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 {
        struct fcoe_fcf *fcf;
        struct fcoe_fcf *next;
        unsigned long sel_time = 0;
+       unsigned long mda_time = 0;
 
        list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+               mda_time = fcf->fka_period + (fcf->fka_period >> 1);
+               if ((fip->sel_fcf == fcf) &&
+                   (time_after(jiffies, fcf->time + mda_time))) {
+                       mod_timer(&fip->timer, jiffies + mda_time);
+                       fc_lport_get_stats(fip->lp)->MissDiscAdvCount++;
+                       printk(KERN_INFO "libfcoe: host%d: Missing Discovery "
+                              "Advertisement for fab %llx count %lld\n",
+                              fip->lp->host->host_no, fcf->fabric_name,
+                              fc_lport_get_stats(fip->lp)->MissDiscAdvCount);
+               }
                if (time_after(jiffies, fcf->time + fcf->fka_period * 3 +
                               msecs_to_jiffies(FIP_FCF_FUZZ * 3))) {
                        if (fip->sel_fcf == fcf)
@@ -574,6 +592,7 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
                        WARN_ON(!fip->fcf_count);
                        fip->fcf_count--;
                        kfree(fcf);
+                       fc_lport_get_stats(fip->lp)->VLinkFailureCount++;
                } else if (fcoe_ctlr_mtu_valid(fcf) &&
                           (!sel_time || time_before(sel_time, fcf->time))) {
                        sel_time = fcf->time;
@@ -590,14 +609,16 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 }
 
 /**
- * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry.
- * @skb:       received FIP advertisement frame
- * @fcf:       resulting FCF entry.
+ * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP advertisement frame
+ * @fcf: The resulting FCF entry
  *
  * Returns zero on a valid parsed advertisement,
  * otherwise returns non zero value.
  */
-static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
+static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
+                              struct sk_buff *skb, struct fcoe_fcf *fcf)
 {
        struct fip_header *fiph;
        struct fip_desc *desc = NULL;
@@ -636,7 +657,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
                               ((struct fip_mac_desc *)desc)->fd_mac,
                               ETH_ALEN);
                        if (!is_valid_ether_addr(fcf->fcf_mac)) {
-                               LIBFCOE_FIP_DBG("Invalid MAC address "
+                               LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
                                                "in FIP adv\n");
                                return -EINVAL;
                        }
@@ -659,6 +680,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
                        if (dlen != sizeof(struct fip_fka_desc))
                                goto len_err;
                        fka = (struct fip_fka_desc *)desc;
+                       if (fka->fd_flags & FIP_FKA_ADV_D)
+                               fcf->fd_flags = 1;
                        t = ntohl(fka->fd_fka_period);
                        if (t >= FCOE_CTLR_MIN_FKA)
                                fcf->fka_period = msecs_to_jiffies(t);
@@ -670,7 +693,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
                case FIP_DT_LOGO:
                case FIP_DT_ELP:
                default:
-                       LIBFCOE_FIP_DBG("unexpected descriptor type %x "
+                       LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
                                        "in FIP adv\n", desc->fip_dtype);
                        /* standard says ignore unknown descriptors >= 128 */
                        if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
@@ -687,15 +710,15 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf)
        return 0;
 
 len_err:
-       LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+       LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
                        desc->fip_dtype, dlen);
        return -EINVAL;
 }
 
 /**
- * fcoe_ctlr_recv_adv() - Handle an incoming advertisement.
- * @fip:       FCoE controller.
- * @skb:       Received FIP packet.
+ * fcoe_ctlr_recv_adv() - Handle an incoming advertisement
+ * @fip: The FCoE controller receiving the advertisement
+ * @skb: The received FIP packet
  */
 static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
@@ -706,7 +729,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
        int first = 0;
        int mtu_valid;
 
-       if (fcoe_ctlr_parse_adv(skb, &new))
+       if (fcoe_ctlr_parse_adv(fip, skb, &new))
                return;
 
        spin_lock_bh(&fip->lock);
@@ -752,7 +775,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
        mtu_valid = fcoe_ctlr_mtu_valid(fcf);
        fcf->time = jiffies;
        if (!found) {
-               LIBFCOE_FIP_DBG("New FCF for fab %llx map %x val %d\n",
+               LIBFCOE_FIP_DBG(fip, "New FCF for fab %llx map %x val %d\n",
                                fcf->fabric_name, fcf->fc_map, mtu_valid);
        }
 
@@ -778,7 +801,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
         */
        if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) {
                fip->sel_time = jiffies +
-                               msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+                       msecs_to_jiffies(FCOE_CTLR_START_DELAY);
                if (!timer_pending(&fip->timer) ||
                    time_before(fip->sel_time, fip->timer.expires))
                        mod_timer(&fip->timer, fip->sel_time);
@@ -788,15 +811,15 @@ out:
 }
 
 /**
- * fcoe_ctlr_recv_els() - Handle an incoming FIP-encapsulated ELS frame.
- * @fip:       FCoE controller.
- * @skb:       Received FIP packet.
+ * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame
+ * @fip: The FCoE controller which received the packet
+ * @skb: The received FIP packet
  */
 static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
-       struct fc_lport *lp = fip->lp;
+       struct fc_lport *lport = fip->lp;
        struct fip_header *fiph;
-       struct fc_frame *fp;
+       struct fc_frame *fp = (struct fc_frame *)skb;
        struct fc_frame_header *fh = NULL;
        struct fip_desc *desc;
        struct fip_encaps *els;
@@ -831,10 +854,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
                               ((struct fip_mac_desc *)desc)->fd_mac,
                               ETH_ALEN);
                        if (!is_valid_ether_addr(granted_mac)) {
-                               LIBFCOE_FIP_DBG("Invalid MAC address "
+                               LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
                                                "in FIP ELS\n");
                                goto drop;
                        }
+                       memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
                        break;
                case FIP_DT_FLOGI:
                case FIP_DT_FDISC:
@@ -850,7 +874,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
                        els_dtype = desc->fip_dtype;
                        break;
                default:
-                       LIBFCOE_FIP_DBG("unexpected descriptor type %x "
+                       LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
                                        "in FIP adv\n", desc->fip_dtype);
                        /* standard says ignore unknown descriptors >= 128 */
                        if (desc->fip_dtype < FIP_DT_VENDOR_BASE)
@@ -867,11 +891,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
 
        if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP &&
            fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
-           els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) {
+           els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac))
                fip->flogi_oxid = FC_XID_UNKNOWN;
-               fip->update_mac(fip, fip->data_src_addr, granted_mac);
-               memcpy(fip->data_src_addr, granted_mac, ETH_ALEN);
-       }
 
        /*
         * Convert skb into an fc_frame containing only the ELS.
@@ -882,32 +903,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
        fc_frame_init(fp);
        fr_sof(fp) = FC_SOF_I3;
        fr_eof(fp) = FC_EOF_T;
-       fr_dev(fp) = lp;
+       fr_dev(fp) = lport;
 
-       stats = fc_lport_get_stats(lp);
+       stats = fc_lport_get_stats(lport);
        stats->RxFrames++;
        stats->RxWords += skb->len / FIP_BPW;
 
-       fc_exch_recv(lp, fp);
+       fc_exch_recv(lport, fp);
        return;
 
 len_err:
-       LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n",
+       LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
                        desc->fip_dtype, dlen);
 drop:
        kfree_skb(skb);
 }
 
 /**
- * fcoe_ctlr_recv_els() - Handle an incoming link reset frame.
- * @fip:       FCoE controller.
- * @fh:                Received FIP header.
+ * fcoe_ctlr_recv_els() - Handle an incoming link reset frame
+ * @fip: The FCoE controller that received the frame
+ * @fh:         The received FIP header
  *
  * There may be multiple VN_Port descriptors.
  * The overall length has already been checked.
  */
 static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
-                                     struct fip_header *fh)
+                                    struct fip_header *fh)
 {
        struct fip_desc *desc;
        struct fip_mac_desc *mp;
@@ -916,13 +937,13 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
        size_t rlen;
        size_t dlen;
        struct fcoe_fcf *fcf = fip->sel_fcf;
-       struct fc_lport *lp = fip->lp;
+       struct fc_lport *lport = fip->lp;
        u32     desc_mask;
 
-       LIBFCOE_FIP_DBG("Clear Virtual Link received\n");
+       LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
        if (!fcf)
                return;
-       if (!fcf || !fc_host_port_id(lp->host))
+       if (!fcf || !fc_host_port_id(lport->host))
                return;
 
        /*
@@ -958,9 +979,10 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
                        if (dlen < sizeof(*vp))
                                return;
                        if (compare_ether_addr(vp->fd_mac,
-                           fip->data_src_addr) == 0 &&
-                           get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn &&
-                           ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host))
+                                              fip->get_src_addr(lport)) == 0 &&
+                           get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn &&
+                           ntoh24(vp->fd_fc_id) ==
+                           fc_host_port_id(lport->host))
                                desc_mask &= ~BIT(FIP_DT_VN_ID);
                        break;
                default:
@@ -977,33 +999,39 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
         * reset only if all required descriptors were present and valid.
         */
        if (desc_mask) {
-               LIBFCOE_FIP_DBG("missing descriptors mask %x\n", desc_mask);
+               LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n",
+                               desc_mask);
        } else {
-               LIBFCOE_FIP_DBG("performing Clear Virtual Link\n");
-               fcoe_ctlr_reset(fip, FIP_ST_ENABLED);
+               LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
+
+               spin_lock_bh(&fip->lock);
+               fc_lport_get_stats(lport)->VLinkFailureCount++;
+               fcoe_ctlr_reset(fip);
+               spin_unlock_bh(&fip->lock);
+
+               fc_lport_reset(fip->lp);
+               fcoe_ctlr_solicit(fip, NULL);
        }
 }
 
 /**
- * fcoe_ctlr_recv() - Receive a FIP frame.
- * @fip:       FCoE controller.
- * @skb:       Received FIP packet.
+ * fcoe_ctlr_recv() - Receive a FIP packet
+ * @fip: The FCoE controller that received the packet
+ * @skb: The received FIP packet
  *
- * This is called from NET_RX_SOFTIRQ.
+ * This may be called from either NET_RX_SOFTIRQ or IRQ.
  */
 void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
-       spin_lock_bh(&fip->fip_recv_list.lock);
-       __skb_queue_tail(&fip->fip_recv_list, skb);
-       spin_unlock_bh(&fip->fip_recv_list.lock);
+       skb_queue_tail(&fip->fip_recv_list, skb);
        schedule_work(&fip->recv_work);
 }
 EXPORT_SYMBOL(fcoe_ctlr_recv);
 
 /**
- * fcoe_ctlr_recv_handler() - Receive a FIP frame.
- * @fip:       FCoE controller.
- * @skb:       Received FIP packet.
+ * fcoe_ctlr_recv_handler() - Receive a FIP frame
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
  *
  * Returns non-zero if the frame is dropped.
  */
@@ -1038,7 +1066,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
                fip->map_dest = 0;
                fip->state = FIP_ST_ENABLED;
                state = FIP_ST_ENABLED;
-               LIBFCOE_FIP_DBG("Using FIP mode\n");
+               LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
        }
        spin_unlock_bh(&fip->lock);
        if (state != FIP_ST_ENABLED)
@@ -1060,8 +1088,8 @@ drop:
 }
 
 /**
- * fcoe_ctlr_select() - Select the best FCF, if possible.
- * @fip:       FCoE controller.
+ * fcoe_ctlr_select() - Select the best FCF (if possible)
+ * @fip: The FCoE controller
  *
  * If there are conflicting advertisements, no FCF can be chosen.
  *
@@ -1073,11 +1101,11 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
        struct fcoe_fcf *best = NULL;
 
        list_for_each_entry(fcf, &fip->fcfs, list) {
-               LIBFCOE_FIP_DBG("consider FCF for fab %llx VFID %d map %x "
+               LIBFCOE_FIP_DBG(fip, "consider FCF for fab %llx VFID %d map %x "
                                "val %d\n", fcf->fabric_name, fcf->vfid,
                                fcf->fc_map, fcoe_ctlr_mtu_valid(fcf));
                if (!fcoe_ctlr_fcf_usable(fcf)) {
-                       LIBFCOE_FIP_DBG("FCF for fab %llx map %x %svalid "
+                       LIBFCOE_FIP_DBG(fip, "FCF for fab %llx map %x %svalid "
                                        "%savailable\n", fcf->fabric_name,
                                        fcf->fc_map, (fcf->flags & FIP_FL_SOL)
                                        ? "" : "in", (fcf->flags & FIP_FL_AVAIL)
@@ -1091,7 +1119,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
                if (fcf->fabric_name != best->fabric_name ||
                    fcf->vfid != best->vfid ||
                    fcf->fc_map != best->fc_map) {
-                       LIBFCOE_FIP_DBG("Conflicting fabric, VFID, "
+                       LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, "
                                        "or FC-MAP\n");
                        return;
                }
@@ -1102,8 +1130,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
 }
 
 /**
- * fcoe_ctlr_timeout() - FIP timer function.
- * @arg:       &fcoe_ctlr pointer.
+ * fcoe_ctlr_timeout() - FIP timeout handler
+ * @arg: The FCoE controller that timed out
  *
  * Ages FCFs.  Triggers FCF selection if possible.  Sends keep-alives.
  */
@@ -1113,8 +1141,6 @@ static void fcoe_ctlr_timeout(unsigned long arg)
        struct fcoe_fcf *sel;
        struct fcoe_fcf *fcf;
        unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
-       u8 send_ctlr_ka;
-       u8 send_port_ka;
 
        spin_lock_bh(&fip->lock);
        if (fip->state == FIP_ST_DISABLED) {
@@ -1140,53 +1166,47 @@ static void fcoe_ctlr_timeout(unsigned long arg)
                               fip->lp->host->host_no, sel->fcf_mac);
                        memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
                        fip->port_ka_time = jiffies +
-                                           msecs_to_jiffies(FIP_VN_KA_PERIOD);
+                               msecs_to_jiffies(FIP_VN_KA_PERIOD);
                        fip->ctlr_ka_time = jiffies + sel->fka_period;
-                       fip->link = 1;
                } else {
                        printk(KERN_NOTICE "libfcoe: host%d: "
-                              "FIP Fibre-Channel Forwarder timed out.  "
+                              "FIP Fibre-Channel Forwarder timed out.  "
                               "Starting FCF discovery.\n",
                               fip->lp->host->host_no);
-                       fip->link = 0;
+                       fip->reset_req = 1;
+                       schedule_work(&fip->link_work);
                }
-               schedule_work(&fip->link_work);
        }
 
-       send_ctlr_ka = 0;
-       send_port_ka = 0;
-       if (sel) {
+       if (sel && !sel->fd_flags) {
                if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
                        fip->ctlr_ka_time = jiffies + sel->fka_period;
-                       send_ctlr_ka = 1;
+                       fip->send_ctlr_ka = 1;
                }
                if (time_after(next_timer, fip->ctlr_ka_time))
                        next_timer = fip->ctlr_ka_time;
 
                if (time_after_eq(jiffies, fip->port_ka_time)) {
                        fip->port_ka_time += jiffies +
-                                       msecs_to_jiffies(FIP_VN_KA_PERIOD);
-                       send_port_ka = 1;
+                               msecs_to_jiffies(FIP_VN_KA_PERIOD);
+                       fip->send_port_ka = 1;
                }
                if (time_after(next_timer, fip->port_ka_time))
                        next_timer = fip->port_ka_time;
                mod_timer(&fip->timer, next_timer);
        } else if (fip->sel_time) {
                next_timer = fip->sel_time +
-                               msecs_to_jiffies(FCOE_CTLR_START_DELAY);
+                       msecs_to_jiffies(FCOE_CTLR_START_DELAY);
                mod_timer(&fip->timer, next_timer);
        }
+       if (fip->send_ctlr_ka || fip->send_port_ka)
+               schedule_work(&fip->link_work);
        spin_unlock_bh(&fip->lock);
-
-       if (send_ctlr_ka)
-               fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr);
-       if (send_port_ka)
-               fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr);
 }
 
 /**
- * fcoe_ctlr_link_work() - worker thread function for link changes.
- * @work:      pointer to link_work member inside &fcoe_ctlr.
+ * fcoe_ctlr_link_work() - Worker thread function for link changes
+ * @work: Handle to a FCoE controller
  *
  * See if the link status has changed and if so, report it.
  *
@@ -1196,27 +1216,49 @@ static void fcoe_ctlr_timeout(unsigned long arg)
 static void fcoe_ctlr_link_work(struct work_struct *work)
 {
        struct fcoe_ctlr *fip;
+       struct fc_lport *vport;
+       u8 *mac;
        int link;
        int last_link;
+       int reset;
 
        fip = container_of(work, struct fcoe_ctlr, link_work);
        spin_lock_bh(&fip->lock);
        last_link = fip->last_link;
        link = fip->link;
        fip->last_link = link;
+       reset = fip->reset_req;
+       fip->reset_req = 0;
        spin_unlock_bh(&fip->lock);
 
        if (last_link != link) {
                if (link)
                        fc_linkup(fip->lp);
                else
-                       fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT);
+                       fc_linkdown(fip->lp);
+       } else if (reset && link)
+               fc_lport_reset(fip->lp);
+
+       if (fip->send_ctlr_ka) {
+               fip->send_ctlr_ka = 0;
+               fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
+       }
+       if (fip->send_port_ka) {
+               fip->send_port_ka = 0;
+               mutex_lock(&fip->lp->lp_mutex);
+               mac = fip->get_src_addr(fip->lp);
+               fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
+               list_for_each_entry(vport, &fip->lp->vports, list) {
+                       mac = fip->get_src_addr(vport);
+                       fcoe_ctlr_send_keep_alive(fip, vport, 1, mac);
+               }
+               mutex_unlock(&fip->lp->lp_mutex);
        }
 }
 
 /**
- * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames.
- * @recv_work: pointer to recv_work member inside &fcoe_ctlr.
+ * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames
+ * @recv_work: Handle to a FCoE controller
  */
 static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
 {
@@ -1224,20 +1266,14 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
        struct sk_buff *skb;
 
        fip = container_of(recv_work, struct fcoe_ctlr, recv_work);
-       spin_lock_bh(&fip->fip_recv_list.lock);
-       while ((skb = __skb_dequeue(&fip->fip_recv_list))) {
-               spin_unlock_bh(&fip->fip_recv_list.lock);
+       while ((skb = skb_dequeue(&fip->fip_recv_list)))
                fcoe_ctlr_recv_handler(fip, skb);
-               spin_lock_bh(&fip->fip_recv_list.lock);
-       }
-       spin_unlock_bh(&fip->fip_recv_list.lock);
 }
 
 /**
- * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request.
- * @fip:       FCoE controller.
- * @fp:                FC frame.
- * @sa:                Ethernet source MAC address from received FCoE frame.
+ * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response
+ * @fip: The FCoE controller
+ * @fp:         The FC frame to snoop
  *
  * Snoop potential response to FLOGI or even incoming FLOGI.
  *
@@ -1245,15 +1281,18 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work)
  * by fip->flogi_oxid != FC_XID_UNKNOWN.
  *
  * The caller is responsible for freeing the frame.
+ * Fill in the granted_mac address.
  *
  * Return non-zero if the frame should not be delivered to libfc.
  */
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
+                        struct fc_frame *fp)
 {
        struct fc_frame_header *fh;
        u8 op;
-       u8 mac[ETH_ALEN];
+       u8 *sa;
 
+       sa = eth_hdr(&fp->skb)->h_source;
        fh = fc_frame_header_get(fp);
        if (fh->fh_type != FC_TYPE_ELS)
                return 0;
@@ -1268,7 +1307,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
                        return -EINVAL;
                }
                fip->state = FIP_ST_NON_FIP;
-               LIBFCOE_FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n");
+               LIBFCOE_FIP_DBG(fip,
+                               "received FLOGI LS_ACC using non-FIP mode\n");
 
                /*
                 * FLOGI accepted.
@@ -1283,11 +1323,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
                        fip->map_dest = 0;
                }
                fip->flogi_oxid = FC_XID_UNKNOWN;
-               memcpy(mac, fip->data_src_addr, ETH_ALEN);
-               fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id);
                spin_unlock_bh(&fip->lock);
-
-               fip->update_mac(fip, mac, fip->data_src_addr);
+               fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
        } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
                /*
                 * Save source MAC for point-to-point responses.
@@ -1297,7 +1334,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
                        memcpy(fip->dest_addr, sa, ETH_ALEN);
                        fip->map_dest = 0;
                        if (fip->state == FIP_ST_NON_FIP)
-                               LIBFCOE_FIP_DBG("received FLOGI REQ, "
+                               LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, "
                                                "using non-FIP mode\n");
                        fip->state = FIP_ST_NON_FIP;
                }
@@ -1308,10 +1345,10 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa)
 EXPORT_SYMBOL(fcoe_ctlr_recv_flogi);
 
 /**
- * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN.
- * @mac: mac address
- * @scheme: check port
- * @port: port indicator for converting
+ * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN
+ * @mac:    The MAC address to convert
+ * @scheme: The scheme to use when converting
+ * @port:   The port indicator for converting
  *
  * Returns: u64 fc world wide name
  */
@@ -1349,24 +1386,26 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
 EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
 
 /**
- * fcoe_libfc_config() - sets up libfc related properties for lport
- * @lp: ptr to the fc_lport
- * @tt: libfc function template
+ * fcoe_libfc_config() - Sets up libfc related properties for local port
+ * @lp: The local port to configure libfc for
+ * @tt: The libfc function template
  *
  * Returns : 0 for success
  */
-int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt)
+int fcoe_libfc_config(struct fc_lport *lport,
+                     struct libfc_function_template *tt)
 {
        /* Set the function pointers set by the LLDD */
-       memcpy(&lp->tt, tt, sizeof(*tt));
-       if (fc_fcp_init(lp))
+       memcpy(&lport->tt, tt, sizeof(*tt));
+       if (fc_fcp_init(lport))
                return -ENOMEM;
-       fc_exch_init(lp);
-       fc_elsct_init(lp);
-       fc_lport_init(lp);
-       fc_rport_init(lp);
-       fc_disc_init(lp);
+       fc_exch_init(lport);
+       fc_elsct_init(lport);
+       fc_lport_init(lport);
+       fc_rport_init(lport);
+       fc_disc_init(lport);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(fcoe_libfc_config);
+
index e4c0a3d..bb208a6 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/netdevice.h>
 #include <linux/workqueue.h>
 #include <scsi/libfc.h>
+#include <scsi/libfcoe.h>
 #include "fnic_io.h"
 #include "fnic_res.h"
 #include "vnic_dev.h"
@@ -44,7 +45,7 @@
 #define        FNIC_IO_LOCKS           64 /* IO locks: power of 2 */
 #define FNIC_DFLT_QUEUE_DEPTH  32
 #define        FNIC_STATS_RATE_LIMIT   4 /* limit rate at which stats are pulled up */
-
+#define FNIC_MAX_CMD_LEN        16 /* Supported CDB length */
 /*
  * Tag bits used for special requests.
  */
@@ -145,6 +146,7 @@ struct mempool;
 /* Per-instance private data structure */
 struct fnic {
        struct fc_lport *lport;
+       struct fcoe_ctlr ctlr;          /* FIP FCoE controller structure */
        struct vnic_dev_bar bar0;
 
        struct msix_entry msix_entry[FNIC_MSIX_INTR_MAX];
@@ -162,23 +164,16 @@ struct fnic {
        unsigned int wq_count;
        unsigned int cq_count;
 
-       u32 fcoui_mode:1;               /* use fcoui address*/
        u32 vlan_hw_insert:1;           /* let hw insert the tag */
        u32 in_remove:1;                /* fnic device in removal */
        u32 stop_rx_link_events:1;      /* stop proc. rx frames, link events */
 
        struct completion *remove_wait; /* device remove thread blocks */
 
-       struct fc_frame *flogi;
-       struct fc_frame *flogi_resp;
-       u16 flogi_oxid;
-       unsigned long s_id;
        enum fnic_state state;
        spinlock_t fnic_lock;
 
        u16 vlan_id;                    /* VLAN tag including priority */
-       u8 mac_addr[ETH_ALEN];
-       u8 dest_addr[ETH_ALEN];
        u8 data_src_addr[ETH_ALEN];
        u64 fcp_input_bytes;            /* internal statistic */
        u64 fcp_output_bytes;           /* internal statistic */
@@ -205,6 +200,7 @@ struct fnic {
        struct work_struct link_work;
        struct work_struct frame_work;
        struct sk_buff_head frame_queue;
+       struct sk_buff_head tx_queue;
 
        /* copy work queue cache line section */
        ____cacheline_aligned struct vnic_wq_copy wq_copy[FNIC_WQ_COPY_MAX];
@@ -224,6 +220,11 @@ struct fnic {
        ____cacheline_aligned struct vnic_intr intr[FNIC_MSIX_INTR_MAX];
 };
 
+static inline struct fnic *fnic_from_ctlr(struct fcoe_ctlr *fip)
+{
+       return container_of(fip, struct fnic, ctlr);
+}
+
 extern struct workqueue_struct *fnic_event_queue;
 extern struct device_attribute *fnic_attrs[];
 
@@ -239,7 +240,11 @@ void fnic_handle_link(struct work_struct *work);
 int fnic_rq_cmpl_handler(struct fnic *fnic, int);
 int fnic_alloc_rq_frame(struct vnic_rq *rq);
 void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf);
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp);
+void fnic_flush_tx(struct fnic *);
+void fnic_eth_send(struct fcoe_ctlr *, struct sk_buff *skb);
+void fnic_set_port_id(struct fc_lport *, u32, struct fc_frame *);
+void fnic_update_mac(struct fc_lport *, u8 *new);
+void fnic_update_mac_locked(struct fnic *, u8 *new);
 
 int fnic_queuecommand(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
 int fnic_abort_cmd(struct scsi_cmnd *);
@@ -252,7 +257,7 @@ void fnic_empty_scsi_cleanup(struct fc_lport *);
 void fnic_exch_mgr_reset(struct fc_lport *, u32, u32);
 int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int);
 int fnic_wq_cmpl_handler(struct fnic *fnic, int);
-int fnic_flogi_reg_handler(struct fnic *fnic);
+int fnic_flogi_reg_handler(struct fnic *fnic, u32);
 void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq,
                                  struct fcpio_host_req *desc);
 int fnic_fw_reset_handler(struct fnic *fnic);
index 50db3e3..54f8d0e 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/if_ether.h>
 #include <linux/if_vlan.h>
 #include <linux/workqueue.h>
+#include <scsi/fc/fc_fip.h>
 #include <scsi/fc/fc_els.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/fc_frame.h>
@@ -34,6 +35,8 @@
 
 struct workqueue_struct *fnic_event_queue;
 
+static void fnic_set_eth_mode(struct fnic *);
+
 void fnic_handle_link(struct work_struct *work)
 {
        struct fnic *fnic = container_of(work, struct fnic, link_work);
@@ -64,10 +67,10 @@ void fnic_handle_link(struct work_struct *work)
                                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
                                FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
                                             "link down\n");
-                               fc_linkdown(fnic->lport);
+                               fcoe_ctlr_link_down(&fnic->ctlr);
                                FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
                                             "link up\n");
-                               fc_linkup(fnic->lport);
+                               fcoe_ctlr_link_up(&fnic->ctlr);
                        } else
                                /* UP -> UP */
                                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
@@ -76,13 +79,13 @@ void fnic_handle_link(struct work_struct *work)
                /* DOWN -> UP */
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
                FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link up\n");
-               fc_linkup(fnic->lport);
+               fcoe_ctlr_link_up(&fnic->ctlr);
        } else {
                /* UP -> DOWN */
                fnic->lport->host_stats.link_failure_count++;
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
                FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "link down\n");
-               fc_linkdown(fnic->lport);
+               fcoe_ctlr_link_down(&fnic->ctlr);
        }
 
 }
@@ -107,197 +110,179 @@ void fnic_handle_frame(struct work_struct *work)
                        return;
                }
                fp = (struct fc_frame *)skb;
-               /* if Flogi resp frame, register the address */
-               if (fr_flags(fp)) {
-                       vnic_dev_add_addr(fnic->vdev,
-                                         fnic->data_src_addr);
-                       fr_flags(fp) = 0;
+
+               /*
+                * If we're in a transitional state, just re-queue and return.
+                * The queue will be serviced when we get to a stable state.
+                */
+               if (fnic->state != FNIC_IN_FC_MODE &&
+                   fnic->state != FNIC_IN_ETH_MODE) {
+                       skb_queue_head(&fnic->frame_queue, skb);
+                       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+                       return;
                }
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
                fc_exch_recv(lp, fp);
        }
-
-}
-
-static inline void fnic_import_rq_fc_frame(struct sk_buff *skb,
-                                          u32 len, u8 sof, u8 eof)
-{
-       struct fc_frame *fp = (struct fc_frame *)skb;
-
-       skb_trim(skb, len);
-       fr_eof(fp) = eof;
-       fr_sof(fp) = sof;
 }
 
-
-static inline int fnic_import_rq_eth_pkt(struct sk_buff *skb, u32 len)
+/**
+ * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame.
+ * @fnic:      fnic instance.
+ * @skb:       Ethernet Frame.
+ */
+static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb)
 {
        struct fc_frame *fp;
        struct ethhdr *eh;
-       struct vlan_ethhdr *vh;
        struct fcoe_hdr *fcoe_hdr;
        struct fcoe_crc_eof *ft;
-       u32    transport_len = 0;
 
+       /*
+        * Undo VLAN encapsulation if present.
+        */
        eh = (struct ethhdr *)skb->data;
-       vh = (struct vlan_ethhdr *)skb->data;
-       if (vh->h_vlan_proto == htons(ETH_P_8021Q) &&
-           vh->h_vlan_encapsulated_proto == htons(ETH_P_FCOE)) {
-               skb_pull(skb, sizeof(struct vlan_ethhdr));
-               transport_len += sizeof(struct vlan_ethhdr);
-       } else if (eh->h_proto == htons(ETH_P_FCOE)) {
-               transport_len += sizeof(struct ethhdr);
-               skb_pull(skb, sizeof(struct ethhdr));
-       } else
-               return -1;
+       if (eh->h_proto == htons(ETH_P_8021Q)) {
+               memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2);
+               eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN);
+               skb_reset_mac_header(skb);
+       }
+       if (eh->h_proto == htons(ETH_P_FIP)) {
+               skb_pull(skb, sizeof(*eh));
+               fcoe_ctlr_recv(&fnic->ctlr, skb);
+               return 1;               /* let caller know packet was used */
+       }
+       if (eh->h_proto != htons(ETH_P_FCOE))
+               goto drop;
+       skb_set_network_header(skb, sizeof(*eh));
+       skb_pull(skb, sizeof(*eh));
 
        fcoe_hdr = (struct fcoe_hdr *)skb->data;
        if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER)
-               return -1;
+               goto drop;
 
        fp = (struct fc_frame *)skb;
        fc_frame_init(fp);
        fr_sof(fp) = fcoe_hdr->fcoe_sof;
        skb_pull(skb, sizeof(struct fcoe_hdr));
-       transport_len += sizeof(struct fcoe_hdr);
+       skb_reset_transport_header(skb);
 
-       ft = (struct fcoe_crc_eof *)(skb->data + len -
-                                    transport_len - sizeof(*ft));
+       ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft));
        fr_eof(fp) = ft->fcoe_eof;
-       skb_trim(skb, len - transport_len - sizeof(*ft));
+       skb_trim(skb, skb->len - sizeof(*ft));
        return 0;
+drop:
+       dev_kfree_skb_irq(skb);
+       return -1;
 }
 
-static inline int fnic_handle_flogi_resp(struct fnic *fnic,
-                                        struct fc_frame *fp)
+/**
+ * fnic_update_mac_locked() - set data MAC address and filters.
+ * @fnic:      fnic instance.
+ * @new:       newly-assigned FCoE MAC address.
+ *
+ * Called with the fnic lock held.
+ */
+void fnic_update_mac_locked(struct fnic *fnic, u8 *new)
 {
-       u8 mac[ETH_ALEN] = FC_FCOE_FLOGI_MAC;
-       struct ethhdr *eth_hdr;
-       struct fc_frame_header *fh;
-       int ret = 0;
-       unsigned long flags;
-       struct fc_frame *old_flogi_resp = NULL;
+       u8 *ctl = fnic->ctlr.ctl_src_addr;
+       u8 *data = fnic->data_src_addr;
 
-       fh = (struct fc_frame_header *)fr_hdr(fp);
+       if (is_zero_ether_addr(new))
+               new = ctl;
+       if (!compare_ether_addr(data, new))
+               return;
+       FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host, "update_mac %pM\n", new);
+       if (!is_zero_ether_addr(data) && compare_ether_addr(data, ctl))
+               vnic_dev_del_addr(fnic->vdev, data);
+       memcpy(data, new, ETH_ALEN);
+       if (compare_ether_addr(new, ctl))
+               vnic_dev_add_addr(fnic->vdev, new);
+}
 
-       spin_lock_irqsave(&fnic->fnic_lock, flags);
+/**
+ * fnic_update_mac() - set data MAC address and filters.
+ * @lport:     local port.
+ * @new:       newly-assigned FCoE MAC address.
+ */
+void fnic_update_mac(struct fc_lport *lport, u8 *new)
+{
+       struct fnic *fnic = lport_priv(lport);
 
-       if (fnic->state == FNIC_IN_ETH_MODE) {
+       spin_lock_irq(&fnic->fnic_lock);
+       fnic_update_mac_locked(fnic, new);
+       spin_unlock_irq(&fnic->fnic_lock);
+}
 
-               /*
-                * Check if oxid matches on taking the lock. A new Flogi
-                * issued by libFC might have changed the fnic cached oxid
-                */
-               if (fnic->flogi_oxid != ntohs(fh->fh_ox_id)) {
-                       FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
-                                    "Flogi response oxid not"
-                                    " matching cached oxid, dropping frame"
-                                    "\n");
-                       ret = -1;
-                       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-                       dev_kfree_skb_irq(fp_skb(fp));
-                       goto handle_flogi_resp_end;
-               }
+/**
+ * fnic_set_port_id() - set the port_ID after successful FLOGI.
+ * @lport:     local port.
+ * @port_id:   assigned FC_ID.
+ * @fp:                received frame containing the FLOGI accept or NULL.
+ *
+ * This is called from libfc when a new FC_ID has been assigned.
+ * This causes us to reset the firmware to FC_MODE and setup the new MAC
+ * address and FC_ID.
+ *
+ * It is also called with FC_ID 0 when we're logged off.
+ *
+ * If the FC_ID is due to point-to-point, fp may be NULL.
+ */
+void fnic_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
+{
+       struct fnic *fnic = lport_priv(lport);
+       u8 *mac;
+       int ret;
 
-               /* Drop older cached flogi response frame, cache this frame */
-               old_flogi_resp = fnic->flogi_resp;
-               fnic->flogi_resp = fp;
-               fnic->flogi_oxid = FC_XID_UNKNOWN;
+       FNIC_FCS_DBG(KERN_DEBUG, lport->host, "set port_id %x fp %p\n",
+                    port_id, fp);
 
-               /*
-                * this frame is part of flogi get the src mac addr from this
-                * frame if the src mac is fcoui based then we mark the
-                * address mode flag to use fcoui base for dst mac addr
-                * otherwise we have to store the fcoe gateway addr
-                */
-               eth_hdr = (struct ethhdr *)skb_mac_header(fp_skb(fp));
-               memcpy(mac, eth_hdr->h_source, ETH_ALEN);
+       /*
+        * If we're clearing the FC_ID, change to use the ctl_src_addr.
+        * Set ethernet mode to send FLOGI.
+        */
+       if (!port_id) {
+               fnic_update_mac(lport, fnic->ctlr.ctl_src_addr);
+               fnic_set_eth_mode(fnic);
+               return;
+       }
 
-               if (ntoh24(mac) == FC_FCOE_OUI)
-                       fnic->fcoui_mode = 1;
-               else {
-                       fnic->fcoui_mode = 0;
-                       memcpy(fnic->dest_addr, mac, ETH_ALEN);
+       if (fp) {
+               mac = fr_cb(fp)->granted_mac;
+               if (is_zero_ether_addr(mac)) {
+                       /* non-FIP - FLOGI already accepted - ignore return */
+                       fcoe_ctlr_recv_flogi(&fnic->ctlr, lport, fp);
                }
+               fnic_update_mac(lport, mac);
+       }
 
-               /*
-                * Except for Flogi frame, all outbound frames from us have the
-                * Eth Src address as FC_FCOE_OUI"our_sid". Flogi frame uses
-                * the vnic MAC address as the Eth Src address
-                */
-               fc_fcoe_set_mac(fnic->data_src_addr, fh->fh_d_id);
-
-               /* We get our s_id from the d_id of the flogi resp frame */
-               fnic->s_id = ntoh24(fh->fh_d_id);
-
-               /* Change state to reflect transition from Eth to FC mode */
+       /* Change state to reflect transition to FC mode */
+       spin_lock_irq(&fnic->fnic_lock);
+       if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE)
                fnic->state = FNIC_IN_ETH_TRANS_FC_MODE;
-
-       } else {
+       else {
                FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
                             "Unexpected fnic state %s while"
                             " processing flogi resp\n",
                             fnic_state_to_str(fnic->state));
-               ret = -1;
-               spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-               dev_kfree_skb_irq(fp_skb(fp));
-               goto handle_flogi_resp_end;
+               spin_unlock_irq(&fnic->fnic_lock);
+               return;
        }
-
-       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
-       /* Drop older cached frame */
-       if (old_flogi_resp)
-               dev_kfree_skb_irq(fp_skb(old_flogi_resp));
+       spin_unlock_irq(&fnic->fnic_lock);
 
        /*
-        * send flogi reg request to firmware, this will put the fnic in
-        * in FC mode
+        * Send FLOGI registration to firmware to set up FC mode.
+        * The new address will be set up when registration completes.
         */
-       ret = fnic_flogi_reg_handler(fnic);
+       ret = fnic_flogi_reg_handler(fnic, port_id);
 
        if (ret < 0) {
-               int free_fp = 1;
-               spin_lock_irqsave(&fnic->fnic_lock, flags);
-               /*
-                * free the frame is some other thread is not
-                * pointing to it
-                */
-               if (fnic->flogi_resp != fp)
-                       free_fp = 0;
-               else
-                       fnic->flogi_resp = NULL;
-
+               spin_lock_irq(&fnic->fnic_lock);
                if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE)
                        fnic->state = FNIC_IN_ETH_MODE;
-               spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-               if (free_fp)
-                       dev_kfree_skb_irq(fp_skb(fp));
+               spin_unlock_irq(&fnic->fnic_lock);
        }
-
- handle_flogi_resp_end:
-       return ret;
-}
-
-/* Returns 1 for a response that matches cached flogi oxid */
-static inline int is_matching_flogi_resp_frame(struct fnic *fnic,
-                                              struct fc_frame *fp)
-{
-       struct fc_frame_header *fh;
-       int ret = 0;
-       u32 f_ctl;
-
-       fh = fc_frame_header_get(fp);
-       f_ctl = ntoh24(fh->fh_f_ctl);
-
-       if (fnic->flogi_oxid == ntohs(fh->fh_ox_id) &&
-           fh->fh_r_ctl == FC_RCTL_ELS_REP &&
-           (f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX &&
-           fh->fh_type == FC_TYPE_ELS)
-               ret = 1;
-
-       return ret;
 }
 
 static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
@@ -326,6 +311,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
        pci_unmap_single(fnic->pdev, buf->dma_addr, buf->len,
                         PCI_DMA_FROMDEVICE);
        skb = buf->os_buf;
+       fp = (struct fc_frame *)skb;
        buf->os_buf = NULL;
 
        cq_desc_dec(cq_desc, &type, &color, &q_number, &completed_index);
@@ -338,6 +324,9 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
                                   &fcoe_enc_error, &fcs_ok, &vlan_stripped,
                                   &vlan);
                eth_hdrs_stripped = 1;
+               skb_trim(skb, fcp_bytes_written);
+               fr_sof(fp) = sof;
+               fr_eof(fp) = eof;
 
        } else if (type == CQ_DESC_TYPE_RQ_ENET) {
                cq_enet_rq_desc_dec((struct cq_enet_rq_desc *)cq_desc,
@@ -352,6 +341,14 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
                                    &ipv4_csum_ok, &ipv6, &ipv4,
                                    &ipv4_fragment, &fcs_ok);
                eth_hdrs_stripped = 0;
+               skb_trim(skb, bytes_written);
+               if (!fcs_ok) {
+                       FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+                                    "fcs error.  dropping packet.\n");
+                       goto drop;
+               }
+               if (fnic_import_rq_eth_pkt(fnic, skb))
+                       return;
 
        } else {
                /* wrong CQ type*/
@@ -370,43 +367,11 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc
                goto drop;
        }
 
-       if (eth_hdrs_stripped)
-               fnic_import_rq_fc_frame(skb, fcp_bytes_written, sof, eof);
-       else if (fnic_import_rq_eth_pkt(skb, bytes_written))
-               goto drop;
-
-       fp = (struct fc_frame *)skb;
-
-       /*
-        * If frame is an ELS response that matches the cached FLOGI OX_ID,
-        * and is accept, issue flogi_reg_request copy wq request to firmware
-        * to register the S_ID and determine whether FC_OUI mode or GW mode.
-        */
-       if (is_matching_flogi_resp_frame(fnic, fp)) {
-               if (!eth_hdrs_stripped) {
-                       if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
-                               fnic_handle_flogi_resp(fnic, fp);
-                               return;
-                       }
-                       /*
-                        * Recd. Flogi reject. No point registering
-                        * with fw, but forward to libFC
-                        */
-                       goto forward;
-               }
-               goto drop;
-       }
-       if (!eth_hdrs_stripped)
-               goto drop;
-
-forward:
        spin_lock_irqsave(&fnic->fnic_lock, flags);
        if (fnic->stop_rx_link_events) {
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
                goto drop;
        }
-       /* Use fr_flags to indicate whether succ. flogi resp or not */
-       fr_flags(fp) = 0;
        fr_dev(fp) = fnic->lport;
        spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
@@ -494,12 +459,49 @@ void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf)
        buf->os_buf = NULL;
 }
 
-static inline int is_flogi_frame(struct fc_frame_header *fh)
+/**
+ * fnic_eth_send() - Send Ethernet frame.
+ * @fip:       fcoe_ctlr instance.
+ * @skb:       Ethernet Frame, FIP, without VLAN encapsulation.
+ */
+void fnic_eth_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
 {
-       return fh->fh_r_ctl == FC_RCTL_ELS_REQ && *(u8 *)(fh + 1) == ELS_FLOGI;
+       struct fnic *fnic = fnic_from_ctlr(fip);
+       struct vnic_wq *wq = &fnic->wq[0];
+       dma_addr_t pa;
+       struct ethhdr *eth_hdr;
+       struct vlan_ethhdr *vlan_hdr;
+       unsigned long flags;
+
+       if (!fnic->vlan_hw_insert) {
+               eth_hdr = (struct ethhdr *)skb_mac_header(skb);
+               vlan_hdr = (struct vlan_ethhdr *)skb_push(skb,
+                               sizeof(*vlan_hdr) - sizeof(*eth_hdr));
+               memcpy(vlan_hdr, eth_hdr, 2 * ETH_ALEN);
+               vlan_hdr->h_vlan_proto = htons(ETH_P_8021Q);
+               vlan_hdr->h_vlan_encapsulated_proto = eth_hdr->h_proto;
+               vlan_hdr->h_vlan_TCI = htons(fnic->vlan_id);
+       }
+
+       pa = pci_map_single(fnic->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
+
+       spin_lock_irqsave(&fnic->wq_lock[0], flags);
+       if (!vnic_wq_desc_avail(wq)) {
+               pci_unmap_single(fnic->pdev, pa, skb->len, PCI_DMA_TODEVICE);
+               spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
+               kfree_skb(skb);
+               return;
+       }
+
+       fnic_queue_wq_eth_desc(wq, skb, pa, skb->len,
+                              fnic->vlan_hw_insert, fnic->vlan_id, 1);
+       spin_unlock_irqrestore(&fnic->wq_lock[0], flags);
 }
 
-int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
+/*
+ * Send FC frame.
+ */
+static int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
 {
        struct vnic_wq *wq = &fnic->wq[0];
        struct sk_buff *skb;
@@ -515,6 +517,10 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
        fh = fc_frame_header_get(fp);
        skb = fp_skb(fp);
 
+       if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
+           fcoe_ctlr_els_send(&fnic->ctlr, fnic->lport, skb))
+               return 0;
+
        if (!fnic->vlan_hw_insert) {
                eth_hdr_len = sizeof(*vlan_hdr) + sizeof(*fcoe_hdr);
                vlan_hdr = (struct vlan_ethhdr *)skb_push(skb, eth_hdr_len);
@@ -530,16 +536,11 @@ int fnic_send_frame(struct fnic *fnic, struct fc_frame *fp)
                fcoe_hdr = (struct fcoe_hdr *)(eth_hdr + 1);
        }
 
-       if (is_flogi_frame(fh)) {
+       if (fnic->ctlr.map_dest)
                fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
-               memcpy(eth_hdr->h_source, fnic->mac_addr, ETH_ALEN);
-       } else {
-               if (fnic->fcoui_mode)
-                       fc_fcoe_set_mac(eth_hdr->h_dest, fh->fh_d_id);
-               else
-                       memcpy(eth_hdr->h_dest, fnic->dest_addr, ETH_ALEN);
-               memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
-       }
+       else
+               memcpy(eth_hdr->h_dest, fnic->ctlr.dest_addr, ETH_ALEN);
+       memcpy(eth_hdr->h_source, fnic->data_src_addr, ETH_ALEN);
 
        tot_len = skb->len;
        BUG_ON(tot_len % 4);
@@ -578,109 +579,85 @@ fnic_send_frame_end:
 int fnic_send(struct fc_lport *lp, struct fc_frame *fp)
 {
        struct fnic *fnic = lport_priv(lp);
-       struct fc_frame_header *fh;
-       int ret = 0;
-       enum fnic_state old_state;
        unsigned long flags;
-       struct fc_frame *old_flogi = NULL;
-       struct fc_frame *old_flogi_resp = NULL;
 
        if (fnic->in_remove) {
                dev_kfree_skb(fp_skb(fp));
-               ret = -1;
-               goto fnic_send_end;
+               return -1;
        }
 
-       fh = fc_frame_header_get(fp);
-       /* if not an Flogi frame, send it out, this is the common case */
-       if (!is_flogi_frame(fh))
-               return fnic_send_frame(fnic, fp);
+       /*
+        * Queue frame if in a transitional state.
+        * This occurs while registering the Port_ID / MAC address after FLOGI.
+        */
+       spin_lock_irqsave(&fnic->fnic_lock, flags);
+       if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) {
+               skb_queue_tail(&fnic->tx_queue, fp_skb(fp));
+               spin_unlock_irqrestore(&fnic->fnic_lock, flags);
+               return 0;
+       }
+       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-       /* Flogi frame, now enter the state machine */
+       return fnic_send_frame(fnic, fp);
+}
 
-       spin_lock_irqsave(&fnic->fnic_lock, flags);
-again:
-       /* Get any old cached frames, free them after dropping lock */
-       old_flogi = fnic->flogi;
-       fnic->flogi = NULL;
-       old_flogi_resp = fnic->flogi_resp;
-       fnic->flogi_resp = NULL;
+/**
+ * fnic_flush_tx() - send queued frames.
+ * @fnic: fnic device
+ *
+ * Send frames that were waiting to go out in FC or Ethernet mode.
+ * Whenever changing modes we purge queued frames, so these frames should
+ * be queued for the stable mode that we're in, either FC or Ethernet.
+ *
+ * Called without fnic_lock held.
+ */
+void fnic_flush_tx(struct fnic *fnic)
+{
+       struct sk_buff *skb;
+       struct fc_frame *fp;
 
-       fnic->flogi_oxid = FC_XID_UNKNOWN;
+       while ((skb = skb_dequeue(&fnic->frame_queue))) {
+               fp = (struct fc_frame *)skb;
+               fnic_send_frame(fnic, fp);
+       }
+}
 
+/**
+ * fnic_set_eth_mode() - put fnic into ethernet mode.
+ * @fnic: fnic device
+ *
+ * Called without fnic lock held.
+ */
+static void fnic_set_eth_mode(struct fnic *fnic)
+{
+       unsigned long flags;
+       enum fnic_state old_state;
+       int ret;
+
+       spin_lock_irqsave(&fnic->fnic_lock, flags);
+again:
        old_state = fnic->state;
        switch (old_state) {
        case FNIC_IN_FC_MODE:
        case FNIC_IN_ETH_TRANS_FC_MODE:
        default:
                fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
-               vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-               if (old_flogi) {
-                       dev_kfree_skb(fp_skb(old_flogi));
-                       old_flogi = NULL;
-               }
-               if (old_flogi_resp) {
-                       dev_kfree_skb(fp_skb(old_flogi_resp));
-                       old_flogi_resp = NULL;
-               }
-
                ret = fnic_fw_reset_handler(fnic);
 
                spin_lock_irqsave(&fnic->fnic_lock, flags);
                if (fnic->state != FNIC_IN_FC_TRANS_ETH_MODE)
                        goto again;
-               if (ret) {
+               if (ret)
                        fnic->state = old_state;
-                       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-                       dev_kfree_skb(fp_skb(fp));
-                       goto fnic_send_end;
-               }
-               old_flogi = fnic->flogi;
-               fnic->flogi = fp;
-               fnic->flogi_oxid = ntohs(fh->fh_ox_id);
-               old_flogi_resp = fnic->flogi_resp;
-               fnic->flogi_resp = NULL;
-               spin_unlock_irqrestore(&fnic->fnic_lock, flags);
                break;
 
        case FNIC_IN_FC_TRANS_ETH_MODE:
-               /*
-                * A reset is pending with the firmware. Store the flogi
-                * and its oxid. The transition out of this state happens
-                * only when Firmware completes the reset, either with
-                * success or failed. If success, transition to
-                * FNIC_IN_ETH_MODE, if fail, then transition to
-                * FNIC_IN_FC_MODE
-                */
-               fnic->flogi = fp;
-               fnic->flogi_oxid = ntohs(fh->fh_ox_id);
-               spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-               break;
-
        case FNIC_IN_ETH_MODE:
-               /*
-                * The fw/hw is already in eth mode. Store the oxid,
-                * and send the flogi frame out. The transition out of this
-                * state happens only we receive flogi response from the
-                * network, and the oxid matches the cached oxid when the
-                * flogi frame was sent out. If they match, then we issue
-                * a flogi_reg request and transition to state
-                * FNIC_IN_ETH_TRANS_FC_MODE
-                */
-               fnic->flogi_oxid = ntohs(fh->fh_ox_id);
-               spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-               ret = fnic_send_frame(fnic, fp);
                break;
        }
-
-fnic_send_end:
-       if (old_flogi)
-               dev_kfree_skb(fp_skb(old_flogi));
-       if (old_flogi_resp)
-               dev_kfree_skb(fp_skb(old_flogi_resp));
-       return ret;
+       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 }
 
 static void fnic_wq_complete_frame_send(struct vnic_wq *wq,
index 2b30648..5c1f223 100644 (file)
@@ -48,9 +48,9 @@ static irqreturn_t fnic_isr_legacy(int irq, void *data)
        }
 
        if (pba & (1 << FNIC_INTX_WQ_RQ_COPYWQ)) {
-               work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
-               work_done += fnic_wq_cmpl_handler(fnic, 4);
-               work_done += fnic_rq_cmpl_handler(fnic, 4);
+               work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+               work_done += fnic_wq_cmpl_handler(fnic, -1);
+               work_done += fnic_rq_cmpl_handler(fnic, -1);
 
                vnic_intr_return_credits(&fnic->intr[FNIC_INTX_WQ_RQ_COPYWQ],
                                         work_done,
@@ -66,9 +66,9 @@ static irqreturn_t fnic_isr_msi(int irq, void *data)
        struct fnic *fnic = data;
        unsigned long work_done = 0;
 
-       work_done += fnic_wq_copy_cmpl_handler(fnic, 8);
-       work_done += fnic_wq_cmpl_handler(fnic, 4);
-       work_done += fnic_rq_cmpl_handler(fnic, 4);
+       work_done += fnic_wq_copy_cmpl_handler(fnic, -1);
+       work_done += fnic_wq_cmpl_handler(fnic, -1);
+       work_done += fnic_rq_cmpl_handler(fnic, -1);
 
        vnic_intr_return_credits(&fnic->intr[0],
                                 work_done,
@@ -83,7 +83,7 @@ static irqreturn_t fnic_isr_msix_rq(int irq, void *data)
        struct fnic *fnic = data;
        unsigned long rq_work_done = 0;
 
-       rq_work_done = fnic_rq_cmpl_handler(fnic, 4);
+       rq_work_done = fnic_rq_cmpl_handler(fnic, -1);
        vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_RQ],
                                 rq_work_done,
                                 1 /* unmask intr */,
@@ -97,7 +97,7 @@ static irqreturn_t fnic_isr_msix_wq(int irq, void *data)
        struct fnic *fnic = data;
        unsigned long wq_work_done = 0;
 
-       wq_work_done = fnic_wq_cmpl_handler(fnic, 4);
+       wq_work_done = fnic_wq_cmpl_handler(fnic, -1);
        vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ],
                                 wq_work_done,
                                 1 /* unmask intr */,
@@ -110,7 +110,7 @@ static irqreturn_t fnic_isr_msix_wq_copy(int irq, void *data)
        struct fnic *fnic = data;
        unsigned long wq_copy_work_done = 0;
 
-       wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, 8);
+       wq_copy_work_done = fnic_wq_copy_cmpl_handler(fnic, -1);
        vnic_intr_return_credits(&fnic->intr[FNIC_MSIX_WQ_COPY],
                                 wq_copy_work_done,
                                 1 /* unmask intr */,
index 71c7bbe..fe1b103 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/interrupt.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include <scsi/fc/fc_fip.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
@@ -68,6 +70,7 @@ MODULE_PARM_DESC(fnic_log_level, "bit mask of fnic logging levels");
 
 static struct libfc_function_template fnic_transport_template = {
        .frame_send = fnic_send,
+       .lport_set_port_id = fnic_set_port_id,
        .fcp_abort_io = fnic_empty_scsi_cleanup,
        .fcp_cleanup = fnic_empty_scsi_cleanup,
        .exch_mgr_reset = fnic_exch_mgr_reset
@@ -140,6 +143,7 @@ static struct fc_function_template fnic_fc_functions = {
        .get_fc_host_stats = fnic_get_stats,
        .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv),
        .terminate_rport_io = fnic_terminate_rport_io,
+       .bsg_request = fc_lport_bsg_request,
 };
 
 static void fnic_get_host_speed(struct Scsi_Host *shost)
@@ -324,9 +328,6 @@ static int fnic_cleanup(struct fnic *fnic)
 {
        unsigned int i;
        int err;
-       unsigned long flags;
-       struct fc_frame *flogi = NULL;
-       struct fc_frame *flogi_resp = NULL;
 
        vnic_dev_disable(fnic->vdev);
        for (i = 0; i < fnic->intr_count; i++)
@@ -367,24 +368,6 @@ static int fnic_cleanup(struct fnic *fnic)
        for (i = 0; i < fnic->intr_count; i++)
                vnic_intr_clean(&fnic->intr[i]);
 
-       /*
-        * Remove cached flogi and flogi resp frames if any
-        * These frames are not in any queue, and therefore queue
-        * cleanup does not clean them. So clean them explicitly
-        */
-       spin_lock_irqsave(&fnic->fnic_lock, flags);
-       flogi = fnic->flogi;
-       fnic->flogi = NULL;
-       flogi_resp = fnic->flogi_resp;
-       fnic->flogi_resp = NULL;
-       spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-
-       if (flogi)
-               dev_kfree_skb(fp_skb(flogi));
-
-       if (flogi_resp)
-               dev_kfree_skb(fp_skb(flogi_resp));
-
        mempool_destroy(fnic->io_req_pool);
        for (i = 0; i < FNIC_SGL_NUM_CACHES; i++)
                mempool_destroy(fnic->io_sgl_pool[i]);
@@ -409,6 +392,17 @@ static void *fnic_alloc_slab_dma(gfp_t gfp_mask, void *pool_data)
        return kmem_cache_alloc(mem, gfp_mask | GFP_ATOMIC | GFP_DMA);
 }
 
+/**
+ * fnic_get_mac() - get assigned data MAC address for FIP code.
+ * @lport:     local port.
+ */
+static u8 *fnic_get_mac(struct fc_lport *lport)
+{
+       struct fnic *fnic = lport_priv(lport);
+
+       return fnic->data_src_addr;
+}
+
 static int __devinit fnic_probe(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
@@ -424,17 +418,16 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
         * Allocate SCSI Host and set up association between host,
         * local port, and fnic
         */
-       host = scsi_host_alloc(&fnic_host_template,
-                              sizeof(struct fc_lport) + sizeof(struct fnic));
-       if (!host) {
-               printk(KERN_ERR PFX "Unable to alloc SCSI host\n");
+       lp = libfc_host_alloc(&fnic_host_template, sizeof(struct fnic));
+       if (!lp) {
+               printk(KERN_ERR PFX "Unable to alloc libfc local port\n");
                err = -ENOMEM;
                goto err_out;
        }
-       lp = shost_priv(host);
-       lp->host = host;
+       host = lp->host;
        fnic = lport_priv(lp);
        fnic->lport = lp;
+       fnic->ctlr.lp = lp;
 
        snprintf(fnic->name, sizeof(fnic->name) - 1, "%s%d", DRV_NAME,
                 host->host_no);
@@ -543,12 +536,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
                goto err_out_dev_close;
        }
 
-       err = vnic_dev_mac_addr(fnic->vdev, fnic->mac_addr);
+       err = vnic_dev_mac_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
        if (err) {
                shost_printk(KERN_ERR, fnic->lport->host,
                             "vNIC get MAC addr failed \n");
                goto err_out_dev_close;
        }
+       /* set data_src for point-to-point mode and to keep it non-zero */
+       memcpy(fnic->data_src_addr, fnic->ctlr.ctl_src_addr, ETH_ALEN);
 
        /* Get vNIC configuration */
        err = fnic_get_vnic_config(fnic);
@@ -560,6 +555,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
        }
        host->max_lun = fnic->config.luns_per_tgt;
        host->max_id = FNIC_MAX_FCP_TARGET;
+       host->max_cmd_len = FNIC_MAX_CMD_LEN;
 
        fnic_get_res_counts(fnic);
 
@@ -571,19 +567,12 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
                goto err_out_dev_close;
        }
 
-       err = fnic_request_intr(fnic);
-       if (err) {
-               shost_printk(KERN_ERR, fnic->lport->host,
-                            "Unable to request irq.\n");
-               goto err_out_clear_intr;
-       }
-
        err = fnic_alloc_vnic_resources(fnic);
        if (err) {
                shost_printk(KERN_ERR, fnic->lport->host,
                             "Failed to alloc vNIC resources, "
                             "aborting.\n");
-               goto err_out_free_intr;
+               goto err_out_clear_intr;
        }
 
 
@@ -623,9 +612,21 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
        fnic->vlan_hw_insert = 1;
        fnic->vlan_id = 0;
 
-       fnic->flogi_oxid = FC_XID_UNKNOWN;
-       fnic->flogi = NULL;
-       fnic->flogi_resp = NULL;
+       /* Initialize the FIP fcoe_ctrl struct */
+       fnic->ctlr.send = fnic_eth_send;
+       fnic->ctlr.update_mac = fnic_update_mac;
+       fnic->ctlr.get_src_addr = fnic_get_mac;
+       fcoe_ctlr_init(&fnic->ctlr);
+       if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+               shost_printk(KERN_INFO, fnic->lport->host,
+                            "firmware supports FIP\n");
+               vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
+               vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
+       } else {
+               shost_printk(KERN_INFO, fnic->lport->host,
+                            "firmware uses non-FIP mode\n");
+               fnic->ctlr.mode = FIP_ST_NON_FIP;
+       }
        fnic->state = FNIC_IN_FC_MODE;
 
        /* Enable hardware stripping of vlan header on ingress */
@@ -716,6 +717,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
        INIT_WORK(&fnic->link_work, fnic_handle_link);
        INIT_WORK(&fnic->frame_work, fnic_handle_frame);
        skb_queue_head_init(&fnic->frame_queue);
+       skb_queue_head_init(&fnic->tx_queue);
 
        /* Enable all queues */
        for (i = 0; i < fnic->raw_wq_count; i++)
@@ -728,6 +730,14 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
        fc_fabric_login(lp);
 
        vnic_dev_enable(fnic->vdev);
+
+       err = fnic_request_intr(fnic);
+       if (err) {
+               shost_printk(KERN_ERR, fnic->lport->host,
+                            "Unable to request irq.\n");
+               goto err_out_free_exch_mgr;
+       }
+
        for (i = 0; i < fnic->intr_count; i++)
                vnic_intr_unmask(&fnic->intr[i]);
 
@@ -738,8 +748,8 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 err_out_free_exch_mgr:
        fc_exch_mgr_free(lp);
 err_out_remove_scsi_host:
-       fc_remove_host(fnic->lport->host);
-       scsi_remove_host(fnic->lport->host);
+       fc_remove_host(lp->host);
+       scsi_remove_host(lp->host);
 err_out_free_rq_buf:
        for (i = 0; i < fnic->rq_count; i++)
                vnic_rq_clean(&fnic->rq[i], fnic_free_rq_buf);
@@ -752,8 +762,6 @@ err_out_free_ioreq_pool:
        mempool_destroy(fnic->io_req_pool);
 err_out_free_resources:
        fnic_free_vnic_resources(fnic);
-err_out_free_intr:
-       fnic_free_intr(fnic);
 err_out_clear_intr:
        fnic_clear_intr_mode(fnic);
 err_out_dev_close:
@@ -775,6 +783,7 @@ err_out:
 static void __devexit fnic_remove(struct pci_dev *pdev)
 {
        struct fnic *fnic = pci_get_drvdata(pdev);
+       struct fc_lport *lp = fnic->lport;
        unsigned long flags;
 
        /*
@@ -796,6 +805,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
         */
        flush_workqueue(fnic_event_queue);
        skb_queue_purge(&fnic->frame_queue);
+       skb_queue_purge(&fnic->tx_queue);
 
        /*
         * Log off the fabric. This stops all remote ports, dns port,
@@ -808,7 +818,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
        fnic->in_remove = 1;
        spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-       fc_lport_destroy(fnic->lport);
+       fcoe_ctlr_destroy(&fnic->ctlr);
+       fc_lport_destroy(lp);
 
        /*
         * This stops the fnic device, masks all interrupts. Completed
@@ -818,6 +829,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
        fnic_cleanup(fnic);
 
        BUG_ON(!skb_queue_empty(&fnic->frame_queue));
+       BUG_ON(!skb_queue_empty(&fnic->tx_queue));
 
        spin_lock_irqsave(&fnic_list_lock, flags);
        list_del(&fnic->list);
@@ -827,8 +839,8 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
        scsi_remove_host(fnic->lport->host);
        fc_exch_mgr_free(fnic->lport);
        vnic_dev_notify_unset(fnic->vdev);
-       fnic_free_vnic_resources(fnic);
        fnic_free_intr(fnic);
+       fnic_free_vnic_resources(fnic);
        fnic_clear_intr_mode(fnic);
        vnic_dev_close(fnic->vdev);
        vnic_dev_unregister(fnic->vdev);
@@ -836,7 +848,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
        pci_release_regions(pdev);
        pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
-       scsi_host_put(fnic->lport->host);
+       scsi_host_put(lp->host);
 }
 
 static struct pci_driver fnic_driver = {
index 7ba61ec..50488f8 100644 (file)
@@ -144,10 +144,9 @@ int fnic_get_vnic_config(struct fnic *fnic)
        c->intr_timer_type = c->intr_timer_type;
 
        shost_printk(KERN_INFO, fnic->lport->host,
-                    "vNIC MAC addr %02x:%02x:%02x:%02x:%02x:%02x "
+                    "vNIC MAC addr %pM "
                     "wq/wq_copy/rq %d/%d/%d\n",
-                    fnic->mac_addr[0], fnic->mac_addr[1], fnic->mac_addr[2],
-                    fnic->mac_addr[3], fnic->mac_addr[4], fnic->mac_addr[5],
+                    fnic->ctlr.ctl_src_addr,
                     c->wq_enet_desc_count, c->wq_copy_desc_count,
                     c->rq_desc_count);
        shost_printk(KERN_INFO, fnic->lport->host,
index b6f3102..ef8aaf2 100644 (file)
@@ -51,6 +51,31 @@ static inline void fnic_queue_wq_desc(struct vnic_wq *wq,
        vnic_wq_post(wq, os_buf, dma_addr, len, sop, eop);
 }
 
+static inline void fnic_queue_wq_eth_desc(struct vnic_wq *wq,
+                                     void *os_buf, dma_addr_t dma_addr,
+                                     unsigned int len,
+                                     int vlan_tag_insert,
+                                     unsigned int vlan_tag,
+                                     int cq_entry)
+{
+       struct wq_enet_desc *desc = vnic_wq_next_desc(wq);
+
+       wq_enet_desc_enc(desc,
+                        (u64)dma_addr | VNIC_PADDR_TARGET,
+                        (u16)len,
+                        0, /* mss_or_csum_offset */
+                        0, /* fc_eof */
+                        0, /* offload_mode */
+                        1, /* eop */
+                        (u8)cq_entry,
+                        0, /* fcoe_encap */
+                        (u8)vlan_tag_insert,
+                        (u16)vlan_tag,
+                        0 /* loopback */);
+
+       vnic_wq_post(wq, os_buf, dma_addr, len, 1, 1);
+}
+
 static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
                                                    u32 req_id,
                                                    u32 lunmap_id, u8 spl_flags,
@@ -58,6 +83,7 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
                                                    u64 sgl_addr, u64 sns_addr,
                                                    u8 crn, u8 pri_ta,
                                                    u8 flags, u8 *scsi_cdb,
+                                                   u8 cdb_len,
                                                    u32 data_len, u8 *lun,
                                                    u32 d_id, u16 mss,
                                                    u32 ratov, u32 edtov)
@@ -82,7 +108,8 @@ static inline void fnic_queue_wq_copy_desc_icmnd_16(struct vnic_wq_copy *wq,
        desc->u.icmnd_16.pri_ta = pri_ta;       /* SCSI Pri & Task attribute */
        desc->u.icmnd_16._resvd1 = 0;           /* reserved: should be 0 */
        desc->u.icmnd_16.flags = flags;         /* command flags */
-       memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, CDB_16);    /* SCSI CDB */
+       memset(desc->u.icmnd_16.scsi_cdb, 0, CDB_16);
+       memcpy(desc->u.icmnd_16.scsi_cdb, scsi_cdb, cdb_len);    /* SCSI CDB */
        desc->u.icmnd_16.data_len = data_len;   /* length of data expected */
        memcpy(desc->u.icmnd_16.lun, lun, LUN_ADDRESS);  /* LUN address */
        desc->u.icmnd_16._resvd2 = 0;           /* reserved */
@@ -132,12 +159,37 @@ static inline void fnic_queue_wq_copy_desc_flogi_reg(struct vnic_wq_copy *wq,
        desc->hdr.tag.u.req_id = req_id;      /* id for this request */
 
        desc->u.flogi_reg.format = format;
+       desc->u.flogi_reg._resvd = 0;
        hton24(desc->u.flogi_reg.s_id, s_id);
        memcpy(desc->u.flogi_reg.gateway_mac, gw_mac, ETH_ALEN);
 
        vnic_wq_copy_post(wq);
 }
 
+static inline void fnic_queue_wq_copy_desc_fip_reg(struct vnic_wq_copy *wq,
+                                                  u32 req_id, u32 s_id,
+                                                  u8 *fcf_mac, u8 *ha_mac,
+                                                  u32 r_a_tov, u32 e_d_tov)
+{
+       struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq);
+
+       desc->hdr.type = FCPIO_FLOGI_FIP_REG; /* enum fcpio_type */
+       desc->hdr.status = 0;                 /* header status entry */
+       desc->hdr._resvd = 0;                 /* reserved */
+       desc->hdr.tag.u.req_id = req_id;      /* id for this request */
+
+       desc->u.flogi_fip_reg._resvd0 = 0;
+       hton24(desc->u.flogi_fip_reg.s_id, s_id);
+       memcpy(desc->u.flogi_fip_reg.fcf_mac, fcf_mac, ETH_ALEN);
+       desc->u.flogi_fip_reg._resvd1 = 0;
+       desc->u.flogi_fip_reg.r_a_tov = r_a_tov;
+       desc->u.flogi_fip_reg.e_d_tov = e_d_tov;
+       memcpy(desc->u.flogi_fip_reg.ha_mac, ha_mac, ETH_ALEN);
+       desc->u.flogi_fip_reg._resvd2 = 0;
+
+       vnic_wq_copy_post(wq);
+}
+
 static inline void fnic_queue_wq_copy_desc_fw_reset(struct vnic_wq_copy *wq,
                                                    u32 req_id)
 {
index bfc9969..65a39b0 100644 (file)
@@ -174,6 +174,9 @@ int fnic_fw_reset_handler(struct fnic *fnic)
        int ret = 0;
        unsigned long flags;
 
+       skb_queue_purge(&fnic->frame_queue);
+       skb_queue_purge(&fnic->tx_queue);
+
        spin_lock_irqsave(&fnic->wq_copy_lock[0], flags);
 
        if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[0])
@@ -200,9 +203,11 @@ int fnic_fw_reset_handler(struct fnic *fnic)
  * fnic_flogi_reg_handler
  * Routine to send flogi register msg to fw
  */
-int fnic_flogi_reg_handler(struct fnic *fnic)
+int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id)
 {
        struct vnic_wq_copy *wq = &fnic->wq_copy[0];
+       enum fcpio_flogi_reg_format_type format;
+       struct fc_lport *lp = fnic->lport;
        u8 gw_mac[ETH_ALEN];
        int ret = 0;
        unsigned long flags;
@@ -217,23 +222,32 @@ int fnic_flogi_reg_handler(struct fnic *fnic)
                goto flogi_reg_ioreq_end;
        }
 
-       if (fnic->fcoui_mode)
+       if (fnic->ctlr.map_dest) {
                memset(gw_mac, 0xff, ETH_ALEN);
-       else
-               memcpy(gw_mac, fnic->dest_addr, ETH_ALEN);
+               format = FCPIO_FLOGI_REG_DEF_DEST;
+       } else {
+               memcpy(gw_mac, fnic->ctlr.dest_addr, ETH_ALEN);
+               format = FCPIO_FLOGI_REG_GW_DEST;
+       }
 
-       fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
-                                         FCPIO_FLOGI_REG_GW_DEST,
-                                         fnic->s_id,
-                                         gw_mac);
+       if ((fnic->config.flags & VFCF_FIP_CAPABLE) && !fnic->ctlr.map_dest) {
+               fnic_queue_wq_copy_desc_fip_reg(wq, SCSI_NO_TAG,
+                                               fc_id, gw_mac,
+                                               fnic->data_src_addr,
+                                               lp->r_a_tov, lp->e_d_tov);
+               FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+                             "FLOGI FIP reg issued fcid %x src %pM dest %pM\n",
+                             fc_id, fnic->data_src_addr, gw_mac);
+       } else {
+               fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG,
+                                                 format, fc_id, gw_mac);
+               FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+                             "FLOGI reg issued fcid %x map %d dest %pM\n",
+                             fc_id, fnic->ctlr.map_dest, gw_mac);
+       }
 
 flogi_reg_ioreq_end:
        spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags);
-
-       if (!ret)
-               FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
-                             "flog reg issued\n");
-
        return ret;
 }
 
@@ -319,7 +333,8 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic,
                                         0, /* scsi cmd ref, always 0 */
                                         pri_tag, /* scsi pri and tag */
                                         flags, /* command flags */
-                                        sc->cmnd, scsi_bufflen(sc),
+                                        sc->cmnd, sc->cmd_len,
+                                        scsi_bufflen(sc),
                                         fc_lun.scsi_lun, io_req->port_id,
                                         rport->maxframe_size, rp->r_a_tov,
                                         rp->e_d_tov);
@@ -452,7 +467,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
        u8 hdr_status;
        struct fcpio_tag tag;
        int ret = 0;
-       struct fc_frame *flogi;
        unsigned long flags;
 
        fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
@@ -462,9 +476,6 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
 
        spin_lock_irqsave(&fnic->fnic_lock, flags);
 
-       flogi = fnic->flogi;
-       fnic->flogi = NULL;
-
        /* fnic should be in FC_TRANS_ETH_MODE */
        if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) {
                /* Check status of reset completion */
@@ -505,17 +516,14 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic,
         * free the flogi frame. Else, send it out
         */
        if (fnic->remove_wait || ret) {
-               fnic->flogi_oxid = FC_XID_UNKNOWN;
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-               if (flogi)
-                       dev_kfree_skb_irq(fp_skb(flogi));
+               skb_queue_purge(&fnic->tx_queue);
                goto reset_cmpl_handler_end;
        }
 
        spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-       if (flogi)
-               ret = fnic_send_frame(fnic, flogi);
+       fnic_flush_tx(fnic);
 
  reset_cmpl_handler_end:
        return ret;
@@ -532,18 +540,13 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
        u8 hdr_status;
        struct fcpio_tag tag;
        int ret = 0;
-       struct fc_frame *flogi_resp = NULL;
        unsigned long flags;
-       struct sk_buff *skb;
 
        fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag);
 
        /* Update fnic state based on status of flogi reg completion */
        spin_lock_irqsave(&fnic->fnic_lock, flags);
 
-       flogi_resp = fnic->flogi_resp;
-       fnic->flogi_resp = NULL;
-
        if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) {
 
                /* Check flogi registration completion status */
@@ -567,25 +570,17 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic,
                ret = -1;
        }
 
-       /* Successful flogi reg cmpl, pass frame to LibFC */
-       if (!ret && flogi_resp) {
+       if (!ret) {
                if (fnic->stop_rx_link_events) {
                        spin_unlock_irqrestore(&fnic->fnic_lock, flags);
                        goto reg_cmpl_handler_end;
                }
-               skb = (struct sk_buff *)flogi_resp;
-               /* Use fr_flags to indicate whether flogi resp or not */
-               fr_flags(flogi_resp) = 1;
-               fr_dev(flogi_resp) = fnic->lport;
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
-               skb_queue_tail(&fnic->frame_queue, skb);
+               fnic_flush_tx(fnic);
                queue_work(fnic_event_queue, &fnic->frame_work);
-
        } else {
                spin_unlock_irqrestore(&fnic->fnic_lock, flags);
-               if (flogi_resp)
-                       dev_kfree_skb_irq(fp_skb(flogi_resp));
        }
 
 reg_cmpl_handler_end:
@@ -907,6 +902,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev,
                break;
 
        case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */
+       case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */
                ret = fnic_fcpio_flogi_reg_cmpl_handler(fnic, desc);
                break;
 
@@ -1224,22 +1220,6 @@ void fnic_terminate_rport_io(struct fc_rport *rport)
 
 }
 
-static void fnic_block_error_handler(struct scsi_cmnd *sc)
-{
-       struct Scsi_Host *shost = sc->device->host;
-       struct fc_rport *rport = starget_to_rport(scsi_target(sc->device));
-       unsigned long flags;
-
-       spin_lock_irqsave(shost->host_lock, flags);
-       while (rport->port_state == FC_PORTSTATE_BLOCKED) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               msleep(1000);
-               spin_lock_irqsave(shost->host_lock, flags);
-       }
-       spin_unlock_irqrestore(shost->host_lock, flags);
-
-}
-
 /*
  * This function is exported to SCSI for sending abort cmnds.
  * A SCSI IO is represented by a io_req in the driver.
@@ -1259,7 +1239,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc)
        DECLARE_COMPLETION_ONSTACK(tm_done);
 
        /* Wait for rport to unblock */
-       fnic_block_error_handler(sc);
+       fc_block_scsi_eh(sc);
 
        /* Get local-port, check ready and link up */
        lp = shost_priv(sc->device->host);
@@ -1541,7 +1521,7 @@ int fnic_device_reset(struct scsi_cmnd *sc)
        DECLARE_COMPLETION_ONSTACK(tm_done);
 
        /* Wait for rport to unblock */
-       fnic_block_error_handler(sc);
+       fc_block_scsi_eh(sc);
 
        /* Get local-port, check ready and link up */
        lp = shost_priv(sc->device->host);
@@ -1762,7 +1742,7 @@ void fnic_scsi_abort_io(struct fc_lport *lp)
        fnic->remove_wait = &remove_wait;
        old_state = fnic->state;
        fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
-       vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+       fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
        spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
        err = fnic_fw_reset_handler(fnic);
@@ -1802,7 +1782,7 @@ void fnic_scsi_cleanup(struct fc_lport *lp)
        spin_lock_irqsave(&fnic->fnic_lock, flags);
        old_state = fnic->state;
        fnic->state = FNIC_IN_FC_TRANS_ETH_MODE;
-       vnic_dev_del_addr(fnic->vdev, fnic->data_src_addr);
+       fnic_update_mac_locked(fnic, fnic->ctlr.ctl_src_addr);
        spin_unlock_irqrestore(&fnic->fnic_lock, flags);
 
        if (fnic_fw_reset_handler(fnic)) {
index 46baa52..fbb5536 100644 (file)
@@ -95,5 +95,6 @@ struct vnic_fc_config {
 
 #define VFCF_FCP_SEQ_LVL_ERR   0x1     /* Enable FCP-2 Error Recovery */
 #define VFCF_PERBI             0x2     /* persistent binding info available */
+#define VFCF_FIP_CAPABLE       0x4     /* firmware can handle FIP */
 
 #endif /* _VNIC_SCSI_H_ */
index c968cc3..554626e 100644 (file)
@@ -180,14 +180,20 @@ void scsi_remove_host(struct Scsi_Host *shost)
 EXPORT_SYMBOL(scsi_remove_host);
 
 /**
- * scsi_add_host - add a scsi host
+ * scsi_add_host_with_dma - add a scsi host with dma device
  * @shost:     scsi host pointer to add
  * @dev:       a struct device of type scsi class
+ * @dma_dev:   dma device for the host
+ *
+ * Note: You rarely need to worry about this unless you're in a
+ * virtualised host environments, so use the simpler scsi_add_host()
+ * function instead.
  *
  * Return value: 
  *     0 on success / != 0 for error
  **/
-int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
+int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
+                          struct device *dma_dev)
 {
        struct scsi_host_template *sht = shost->hostt;
        int error = -EINVAL;
@@ -207,6 +213,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
 
        if (!shost->shost_gendev.parent)
                shost->shost_gendev.parent = dev ? dev : &platform_bus;
+       shost->dma_dev = dma_dev;
 
        error = device_add(&shost->shost_gendev);
        if (error)
@@ -262,7 +269,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
  fail:
        return error;
 }
-EXPORT_SYMBOL(scsi_add_host);
+EXPORT_SYMBOL(scsi_add_host_with_dma);
 
 static void scsi_host_dev_release(struct device *dev)
 {
index a0e7e71..4f05565 100644 (file)
@@ -834,7 +834,7 @@ static int hptiop_reset_hba(struct hptiop_hba *hba)
                        atomic_read(&hba->resetting) == 0, 60 * HZ);
 
        if (atomic_read(&hba->resetting)) {
-               /* IOP is in unkown state, abort reset */
+               /* IOP is in unknown state, abort reset */
                printk(KERN_ERR "scsi%d: reset failed\n", hba->host->host_no);
                return -1;
        }
@@ -861,10 +861,13 @@ static int hptiop_reset(struct scsi_cmnd *scp)
 }
 
 static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev,
-                                               int queue_depth)
+                                         int queue_depth, int reason)
 {
        struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata;
 
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (queue_depth > hba->max_requests)
                queue_depth = hba->max_requests;
        scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
index bb2c696..87b536a 100644 (file)
@@ -39,6 +39,7 @@
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
 #include "ibmvfc.h"
 
 static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT;
@@ -558,12 +559,11 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
 /**
  * ibmvfc_init_host - Start host initialization
  * @vhost:             ibmvfc host struct
- * @relogin:   is this a re-login?
  *
  * Return value:
  *     nothing
  **/
-static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
+static void ibmvfc_init_host(struct ibmvfc_host *vhost)
 {
        struct ibmvfc_target *tgt;
 
@@ -577,10 +577,8 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost, int relogin)
        }
 
        if (!ibmvfc_set_host_state(vhost, IBMVFC_INITIALIZING)) {
-               if (!relogin) {
-                       memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
-                       vhost->async_crq.cur = 0;
-               }
+               memset(vhost->async_crq.msgs, 0, PAGE_SIZE);
+               vhost->async_crq.cur = 0;
 
                list_for_each_entry(tgt, &vhost->targets, queue)
                        ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
@@ -1677,6 +1675,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt)
        complete(&evt->comp);
 }
 
+/**
+ * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands
+ * @evt:       struct ibmvfc_event
+ *
+ **/
+static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt)
+{
+       struct ibmvfc_host *vhost = evt->vhost;
+
+       ibmvfc_free_event(evt);
+       vhost->aborting_passthru = 0;
+       dev_info(vhost->dev, "Passthru command cancelled\n");
+}
+
+/**
+ * ibmvfc_bsg_timeout - Handle a BSG timeout
+ * @job:       struct fc_bsg_job that timed out
+ *
+ * Returns:
+ *     0 on success / other on failure
+ **/
+static int ibmvfc_bsg_timeout(struct fc_bsg_job *job)
+{
+       struct ibmvfc_host *vhost = shost_priv(job->shost);
+       unsigned long port_id = (unsigned long)job->dd_data;
+       struct ibmvfc_event *evt;
+       struct ibmvfc_tmf *tmf;
+       unsigned long flags;
+       int rc;
+
+       ENTER;
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) {
+               __ibmvfc_reset_host(vhost);
+               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               return 0;
+       }
+
+       vhost->aborting_passthru = 1;
+       evt = ibmvfc_get_event(vhost);
+       ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT);
+
+       tmf = &evt->iu.tmf;
+       memset(tmf, 0, sizeof(*tmf));
+       tmf->common.version = 1;
+       tmf->common.opcode = IBMVFC_TMF_MAD;
+       tmf->common.length = sizeof(*tmf);
+       tmf->scsi_id = port_id;
+       tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+       tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY;
+       rc = ibmvfc_send_event(evt, vhost, default_timeout);
+
+       if (rc != 0) {
+               vhost->aborting_passthru = 0;
+               dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc);
+               rc = -EIO;
+       } else
+               dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n",
+                        port_id);
+
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+       LEAVE;
+       return rc;
+}
+
+/**
+ * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command
+ * @vhost:             struct ibmvfc_host to send command
+ * @port_id:   port ID to send command
+ *
+ * Returns:
+ *     0 on success / other on failure
+ **/
+static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
+{
+       struct ibmvfc_port_login *plogi;
+       struct ibmvfc_target *tgt;
+       struct ibmvfc_event *evt;
+       union ibmvfc_iu rsp_iu;
+       unsigned long flags;
+       int rc = 0, issue_login = 1;
+
+       ENTER;
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       list_for_each_entry(tgt, &vhost->targets, queue) {
+               if (tgt->scsi_id == port_id) {
+                       issue_login = 0;
+                       break;
+               }
+       }
+
+       if (!issue_login)
+               goto unlock_out;
+       if (unlikely((rc = ibmvfc_host_chkready(vhost))))
+               goto unlock_out;
+
+       evt = ibmvfc_get_event(vhost);
+       ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+       plogi = &evt->iu.plogi;
+       memset(plogi, 0, sizeof(*plogi));
+       plogi->common.version = 1;
+       plogi->common.opcode = IBMVFC_PORT_LOGIN;
+       plogi->common.length = sizeof(*plogi);
+       plogi->scsi_id = port_id;
+       evt->sync_iu = &rsp_iu;
+       init_completion(&evt->comp);
+
+       rc = ibmvfc_send_event(evt, vhost, default_timeout);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+       if (rc)
+               return -EIO;
+
+       wait_for_completion(&evt->comp);
+
+       if (rsp_iu.plogi.common.status)
+               rc = -EIO;
+
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       ibmvfc_free_event(evt);
+unlock_out:
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+       LEAVE;
+       return rc;
+}
+
+/**
+ * ibmvfc_bsg_request - Handle a BSG request
+ * @job:       struct fc_bsg_job to be executed
+ *
+ * Returns:
+ *     0 on success / other on failure
+ **/
+static int ibmvfc_bsg_request(struct fc_bsg_job *job)
+{
+       struct ibmvfc_host *vhost = shost_priv(job->shost);
+       struct fc_rport *rport = job->rport;
+       struct ibmvfc_passthru_mad *mad;
+       struct ibmvfc_event *evt;
+       union ibmvfc_iu rsp_iu;
+       unsigned long flags, port_id = -1;
+       unsigned int code = job->request->msgcode;
+       int rc = 0, req_seg, rsp_seg, issue_login = 0;
+       u32 fc_flags, rsp_len;
+
+       ENTER;
+       job->reply->reply_payload_rcv_len = 0;
+       if (rport)
+               port_id = rport->port_id;
+
+       switch (code) {
+       case FC_BSG_HST_ELS_NOLOGIN:
+               port_id = (job->request->rqst_data.h_els.port_id[0] << 16) |
+                       (job->request->rqst_data.h_els.port_id[1] << 8) |
+                       job->request->rqst_data.h_els.port_id[2];
+       case FC_BSG_RPT_ELS:
+               fc_flags = IBMVFC_FC_ELS;
+               break;
+       case FC_BSG_HST_CT:
+               issue_login = 1;
+               port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) |
+                       (job->request->rqst_data.h_ct.port_id[1] << 8) |
+                       job->request->rqst_data.h_ct.port_id[2];
+       case FC_BSG_RPT_CT:
+               fc_flags = IBMVFC_FC_CT_IU;
+               break;
+       default:
+               return -ENOTSUPP;
+       };
+
+       if (port_id == -1)
+               return -EINVAL;
+       if (!mutex_trylock(&vhost->passthru_mutex))
+               return -EBUSY;
+
+       job->dd_data = (void *)port_id;
+       req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list,
+                            job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+       if (!req_seg) {
+               mutex_unlock(&vhost->passthru_mutex);
+               return -ENOMEM;
+       }
+
+       rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list,
+                            job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+       if (!rsp_seg) {
+               dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+                            job->request_payload.sg_cnt, DMA_TO_DEVICE);
+               mutex_unlock(&vhost->passthru_mutex);
+               return -ENOMEM;
+       }
+
+       if (req_seg > 1 || rsp_seg > 1) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       if (issue_login)
+               rc = ibmvfc_bsg_plogi(vhost, port_id);
+
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+
+       if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) ||
+           unlikely((rc = ibmvfc_host_chkready(vhost)))) {
+               spin_unlock_irqrestore(vhost->host->host_lock, flags);
+               goto out;
+       }
+
+       evt = ibmvfc_get_event(vhost);
+       ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT);
+       mad = &evt->iu.passthru;
+
+       memset(mad, 0, sizeof(*mad));
+       mad->common.version = 1;
+       mad->common.opcode = IBMVFC_PASSTHRU;
+       mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu);
+
+       mad->cmd_ioba.va = (u64)evt->crq.ioba +
+               offsetof(struct ibmvfc_passthru_mad, iu);
+       mad->cmd_ioba.len = sizeof(mad->iu);
+
+       mad->iu.cmd_len = job->request_payload.payload_len;
+       mad->iu.rsp_len = job->reply_payload.payload_len;
+       mad->iu.flags = fc_flags;
+       mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY;
+
+       mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list);
+       mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list);
+       mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list);
+       mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list);
+       mad->iu.scsi_id = port_id;
+       mad->iu.tag = (u64)evt;
+       rsp_len = mad->iu.rsp.len;
+
+       evt->sync_iu = &rsp_iu;
+       init_completion(&evt->comp);
+       rc = ibmvfc_send_event(evt, vhost, 0);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+       if (rc) {
+               rc = -EIO;
+               goto out;
+       }
+
+       wait_for_completion(&evt->comp);
+
+       if (rsp_iu.passthru.common.status)
+               rc = -EIO;
+       else
+               job->reply->reply_payload_rcv_len = rsp_len;
+
+       spin_lock_irqsave(vhost->host->host_lock, flags);
+       ibmvfc_free_event(evt);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+       job->reply->result = rc;
+       job->job_done(job);
+       rc = 0;
+out:
+       dma_unmap_sg(vhost->dev, job->request_payload.sg_list,
+                    job->request_payload.sg_cnt, DMA_TO_DEVICE);
+       dma_unmap_sg(vhost->dev, job->reply_payload.sg_list,
+                    job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+       mutex_unlock(&vhost->passthru_mutex);
+       LEAVE;
+       return rc;
+}
+
 /**
  * ibmvfc_reset_device - Reset the device with the specified reset type
  * @sdev:      scsi device to reset
@@ -1731,7 +1999,10 @@ static int ibmvfc_reset_device(struct scsi_device *sdev, int type, char *desc)
        sdev_printk(KERN_INFO, sdev, "Resetting %s\n", desc);
        wait_for_completion(&evt->comp);
 
-       if (rsp_iu.cmd.status) {
+       if (rsp_iu.cmd.status)
+               rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+       if (rsp_code) {
                if (fc_rsp->flags & FCP_RSP_LEN_VALID)
                        rsp_code = fc_rsp->data.info.rsp_code;
 
@@ -1820,7 +2091,10 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
        sdev_printk(KERN_INFO, sdev, "Aborting outstanding commands\n");
        wait_for_completion(&evt->comp);
 
-       if (rsp_iu.cmd.status) {
+       if (rsp_iu.cmd.status)
+               rsp_code = ibmvfc_get_err_result(&rsp_iu.cmd);
+
+       if (rsp_code) {
                if (fc_rsp->flags & FCP_RSP_LEN_VALID)
                        rsp_code = fc_rsp->data.info.rsp_code;
 
@@ -2061,12 +2335,24 @@ static int ibmvfc_eh_device_reset_handler(struct scsi_cmnd *cmd)
 }
 
 /**
- * ibmvfc_dev_cancel_all - Device iterated cancel all function
+ * ibmvfc_dev_cancel_all_abts - Device iterated cancel all function
  * @sdev:      scsi device struct
  * @data:      return code
  *
  **/
-static void ibmvfc_dev_cancel_all(struct scsi_device *sdev, void *data)
+static void ibmvfc_dev_cancel_all_abts(struct scsi_device *sdev, void *data)
+{
+       unsigned long *rc = data;
+       *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_ABORT_TASK_SET);
+}
+
+/**
+ * ibmvfc_dev_cancel_all_reset - Device iterated cancel all function
+ * @sdev:      scsi device struct
+ * @data:      return code
+ *
+ **/
+static void ibmvfc_dev_cancel_all_reset(struct scsi_device *sdev, void *data)
 {
        unsigned long *rc = data;
        *rc |= ibmvfc_cancel_all(sdev, IBMVFC_TMF_TGT_RESET);
@@ -2102,7 +2388,7 @@ static int ibmvfc_eh_target_reset_handler(struct scsi_cmnd *cmd)
 
        ENTER;
        ibmvfc_wait_while_resetting(vhost);
-       starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+       starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_reset);
        reset_rc = ibmvfc_reset_device(sdev, IBMVFC_TARGET_RESET, "target");
 
        if (!cancel_rc && !reset_rc)
@@ -2144,7 +2430,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
        int rc = FAILED;
 
        ENTER;
-       starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all);
+       starget_for_each_device(starget, &cancel_rc, ibmvfc_dev_cancel_all_abts);
        starget_for_each_device(starget, &abort_rc, ibmvfc_dev_abort_all);
 
        if (!cancel_rc && !abort_rc)
@@ -2297,13 +2583,13 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost)
                        /* Send back a response */
                        rc = ibmvfc_send_crq_init_complete(vhost);
                        if (rc == 0)
-                               ibmvfc_init_host(vhost, 0);
+                               ibmvfc_init_host(vhost);
                        else
                                dev_err(vhost->dev, "Unable to send init rsp. rc=%ld\n", rc);
                        break;
                case IBMVFC_CRQ_INIT_COMPLETE:
                        dev_info(vhost->dev, "Partner initialization complete\n");
-                       ibmvfc_init_host(vhost, 0);
+                       ibmvfc_init_host(vhost);
                        break;
                default:
                        dev_err(vhost->dev, "Unknown crq message type: %d\n", crq->format);
@@ -2478,12 +2764,17 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
  * ibmvfc_change_queue_depth - Change the device's queue depth
  * @sdev:      scsi device struct
  * @qdepth:    depth to set
+ * @reason:    calling context
  *
  * Return value:
  *     actual depth set
  **/
-static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                    int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
                qdepth = IBMVFC_MAX_CMDS_PER_LUN;
 
@@ -3725,7 +4016,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
        case IBMVFC_MAD_SUCCESS:
                if (list_empty(&vhost->sent) &&
                    vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
-                       ibmvfc_init_host(vhost, 0);
+                       ibmvfc_init_host(vhost);
                        return;
                }
                break;
@@ -3903,6 +4194,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt)
                        rport->supported_classes |= FC_COS_CLASS2;
                if (tgt->service_parms.class3_parms[0] & 0x80000000)
                        rport->supported_classes |= FC_COS_CLASS3;
+               if (rport->rqst_q)
+                       blk_queue_max_hw_segments(rport->rqst_q, 1);
        } else
                tgt_dbg(tgt, "rport add failed\n");
        spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4342,6 +4635,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        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);
+       mutex_init(&vhost->passthru_mutex);
 
        if ((rc = ibmvfc_alloc_mem(vhost)))
                goto free_scsi_host;
@@ -4374,6 +4668,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
                goto remove_shost;
        }
 
+       if (shost_to_fc_host(shost)->rqst_q)
+               blk_queue_max_hw_segments(shost_to_fc_host(shost)->rqst_q, 1);
        dev_set_drvdata(dev, vhost);
        spin_lock(&ibmvfc_driver_lock);
        list_add_tail(&vhost->queue, &ibmvfc_head);
@@ -4414,7 +4710,11 @@ static int ibmvfc_remove(struct vio_dev *vdev)
 
        ENTER;
        ibmvfc_remove_trace_file(&vhost->host->shost_dev.kobj, &ibmvfc_trace_attr);
+
+       spin_lock_irqsave(vhost->host->host_lock, flags);
        ibmvfc_link_down(vhost, IBMVFC_HOST_OFFLINE);
+       spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
        ibmvfc_wait_while_resetting(vhost);
        ibmvfc_release_crq_queue(vhost);
        kthread_stop(vhost->work_thread);
@@ -4498,6 +4798,9 @@ static struct fc_function_template ibmvfc_transport_functions = {
 
        .get_starget_port_id = ibmvfc_get_starget_port_id,
        .show_starget_port_id = 1,
+
+       .bsg_request = ibmvfc_bsg_request,
+       .bsg_timeout = ibmvfc_bsg_timeout,
 };
 
 /**
index 007fa1c..d25106a 100644 (file)
@@ -29,8 +29,8 @@
 #include "viosrp.h"
 
 #define IBMVFC_NAME    "ibmvfc"
-#define IBMVFC_DRIVER_VERSION          "1.0.6"
-#define IBMVFC_DRIVER_DATE             "(May 28, 2009)"
+#define IBMVFC_DRIVER_VERSION          "1.0.7"
+#define IBMVFC_DRIVER_DATE             "(October 16, 2009)"
 
 #define IBMVFC_DEFAULT_TIMEOUT 60
 #define IBMVFC_ADISC_CANCEL_TIMEOUT    45
  * 1 for ERP
  * 1 for initialization
  * 1 for NPIV Logout
+ * 2 for BSG passthru
  * 2 for each discovery thread
  */
-#define IBMVFC_NUM_INTERNAL_REQ        (1 + 1 + 1 + (disc_threads * 2))
+#define IBMVFC_NUM_INTERNAL_REQ        (1 + 1 + 1 + 2 + (disc_threads * 2))
 
 #define IBMVFC_MAD_SUCCESS             0x00
 #define IBMVFC_MAD_NOT_SUPPORTED       0xF1
@@ -466,7 +467,10 @@ struct ibmvfc_passthru_iu {
        u16 error;
        u32 flags;
 #define IBMVFC_FC_ELS          0x01
+#define IBMVFC_FC_CT_IU                0x02
        u32 cancel_key;
+#define IBMVFC_PASSTHRU_CANCEL_KEY     0x80000000
+#define IBMVFC_INTERNAL_CANCEL_KEY     0x80000001
        u32 reserved;
        struct srp_direct_buf cmd;
        struct srp_direct_buf rsp;
@@ -693,6 +697,7 @@ struct ibmvfc_host {
        int disc_buf_sz;
        int log_level;
        struct ibmvfc_discover_targets_buf *disc_buf;
+       struct mutex passthru_mutex;
        int task_set;
        int init_retries;
        int discovery_threads;
@@ -702,6 +707,7 @@ struct ibmvfc_host {
        int delay_init;
        int scan_complete;
        int logged_in;
+       int aborting_passthru;
        int events_to_log;
 #define IBMVFC_AE_LINKUP       0x0001
 #define IBMVFC_AE_LINKDOWN     0x0002
index d9b0e9d..e475b79 100644 (file)
@@ -1637,12 +1637,17 @@ static int ibmvscsi_slave_configure(struct scsi_device *sdev)
  * ibmvscsi_change_queue_depth - Change the device's queue depth
  * @sdev:      scsi device struct
  * @qdepth:    depth to set
+ * @reason:    calling context
  *
  * Return value:
  *     actual depth set
  **/
-static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                      int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN)
                qdepth = IBMVSCSI_MAX_CMDS_PER_LUN;
 
index 76d294f..206c2fa 100644 (file)
@@ -3367,16 +3367,21 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; };
  * ipr_change_queue_depth - Change the device's queue depth
  * @sdev:      scsi device struct
  * @qdepth:    depth to set
+ * @reason:    calling context
  *
  * Return value:
  *     actual depth set
  **/
-static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                 int reason)
 {
        struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
        struct ipr_resource_entry *res;
        unsigned long lock_flags = 0;
 
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
        res = (struct ipr_resource_entry *)sdev->hostdata;
 
index edc49ca..517da3f 100644 (file)
@@ -903,7 +903,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = {
                                  ISCSI_USERNAME | ISCSI_PASSWORD |
                                  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
                                  ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
-                                 ISCSI_LU_RESET_TMO |
+                                 ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO |
                                  ISCSI_PING_TMO | ISCSI_RECV_TMO |
                                  ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
        .host_param_mask        = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
index 55f982d..4bb23ac 100644 (file)
@@ -3,10 +3,12 @@
 obj-$(CONFIG_LIBFC) += libfc.o
 
 libfc-objs := \
+       fc_libfc.o \
        fc_disc.o \
        fc_exch.o \
        fc_elsct.o \
        fc_frame.o \
        fc_lport.o \
        fc_rport.o \
-       fc_fcp.o
+       fc_fcp.o \
+       fc_npiv.o
index c48799e..9b0a519 100644 (file)
@@ -40,6 +40,8 @@
 
 #include <scsi/libfc.h>
 
+#include "fc_libfc.h"
+
 #define FC_DISC_RETRY_LIMIT    3       /* max retries */
 #define FC_DISC_RETRY_DELAY    500UL   /* (msecs) delay */
 
@@ -51,8 +53,8 @@ static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
 static void fc_disc_restart(struct fc_disc *);
 
 /**
- * fc_disc_stop_rports() - delete all the remote ports associated with the lport
- * @disc: The discovery job to stop rports on
+ * fc_disc_stop_rports() - Delete all the remote ports associated with the lport
+ * @disc: The discovery job to stop remote ports on
  *
  * Locking Note: This function expects that the lport mutex is locked before
  * calling it.
@@ -72,9 +74,9 @@ void fc_disc_stop_rports(struct fc_disc *disc)
 
 /**
  * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
- * @sp: Current sequence of the RSCN exchange
- * @fp: RSCN Frame
- * @lport: Fibre Channel host port instance
+ * @sp:           The sequence of the RSCN exchange
+ * @fp:           The RSCN frame
+ * @lport: The local port that the request will be sent on
  *
  * Locking Note: This function expects that the disc_mutex is locked
  *              before it is called.
@@ -183,9 +185,9 @@ reject:
 
 /**
  * fc_disc_recv_req() - Handle incoming requests
- * @sp: Current sequence of the request exchange
- * @fp: The frame
- * @lport: The FC local port
+ * @sp:           The sequence of the request exchange
+ * @fp:           The request frame
+ * @lport: The local port receiving the request
  *
  * Locking Note: This function is called from the EM and will lock
  *              the disc_mutex before calling the handler for the
@@ -213,7 +215,7 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 
 /**
  * fc_disc_restart() - Restart discovery
- * @lport: FC discovery context
+ * @disc: The discovery object to be restarted
  *
  * Locking Note: This function expects that the disc mutex
  *              is already locked.
@@ -240,9 +242,9 @@ static void fc_disc_restart(struct fc_disc *disc)
 }
 
 /**
- * fc_disc_start() - Fibre Channel Target discovery
- * @lport: FC local port
- * @disc_callback: function to be called when discovery is complete
+ * fc_disc_start() - Start discovery on a local port
+ * @lport:        The local port to have discovery started on
+ * @disc_callback: Callback function to be called when discovery is complete
  */
 static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
                                                enum fc_disc_event),
@@ -263,8 +265,8 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
 
 /**
  * fc_disc_done() - Discovery has been completed
- * @disc: FC discovery context
- * @event: discovery completion status
+ * @disc:  The discovery context
+ * @event: The discovery completion status
  *
  * Locking Note: This function expects that the disc mutex is locked before
  * it is called. The discovery callback is then made with the lock released,
@@ -284,8 +286,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
        }
 
        /*
-        * Go through all remote ports.  If they were found in the latest
-        * discovery, reverify or log them in.  Otherwise, log them out.
+        * Go through all remote ports.  If they were found in the latest
+        * discovery, reverify or log them in.  Otherwise, log them out.
         * Skip ports which were never discovered.  These are the dNS port
         * and ports which were created by PLOGI.
         */
@@ -305,8 +307,8 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
 
 /**
  * fc_disc_error() - Handle error on dNS request
- * @disc: FC discovery context
- * @fp: The frame pointer
+ * @disc: The discovery context
+ * @fp:          The error code encoded as a frame pointer
  */
 static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
 {
@@ -342,7 +344,7 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
 
 /**
  * fc_disc_gpn_ft_req() - Send Get Port Names by FC-4 type (GPN_FT) request
- * @lport: FC discovery context
+ * @lport: The discovery context
  *
  * Locking Note: This function expects that the disc_mutex is locked
  *              before it is called.
@@ -368,17 +370,17 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
        if (lport->tt.elsct_send(lport, 0, fp,
                                 FC_NS_GPN_FT,
                                 fc_disc_gpn_ft_resp,
-                                disc, lport->e_d_tov))
+                                disc, 3 * lport->r_a_tov))
                return;
 err:
-       fc_disc_error(disc, fp);
+       fc_disc_error(disc, NULL);
 }
 
 /**
  * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
- * @lport: Fibre Channel host port instance
- * @buf: GPN_FT response buffer
- * @len: size of response buffer
+ * @lport: The local port the GPN_FT was received on
+ * @buf:   The GPN_FT response buffer
+ * @len:   The size of response buffer
  *
  * Goes through the list of IDs and names resulting from a request.
  */
@@ -477,10 +479,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 }
 
 /**
- * fc_disc_timeout() - Retry handler for the disc component
- * @work: Structure holding disc obj that needs retry discovery
- *
- * Handle retry of memory allocation for remote ports.
+ * fc_disc_timeout() - Handler for discovery timeouts
+ * @work: Structure holding discovery context that needs to retry discovery
  */
 static void fc_disc_timeout(struct work_struct *work)
 {
@@ -494,9 +494,9 @@ static void fc_disc_timeout(struct work_struct *work)
 
 /**
  * fc_disc_gpn_ft_resp() - Handle a response frame from Get Port Names (GPN_FT)
- * @sp: Current sequence of GPN_FT exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel host port instance
+ * @sp:            The sequence that the GPN_FT response was received on
+ * @fp:            The GPN_FT response frame
+ * @lp_arg: The discovery context
  *
  * Locking Note: This function is called without disc mutex held, and
  *              should do all its processing with the mutex held
@@ -567,9 +567,9 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
 
 /**
  * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
- * @sp: exchange sequence
- * @fp: response frame
- * @rdata_arg: remote port private data
+ * @sp:               The sequence the GPN_ID is on
+ * @fp:               The response frame
+ * @rdata_arg: The remote port that sent the GPN_ID response
  *
  * Locking Note: This function is called without disc mutex held.
  */
@@ -637,7 +637,7 @@ out:
 
 /**
  * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
- * @lport: local port
+ * @lport: The local port to initiate discovery on
  * @rdata: remote port private data
  *
  * Locking Note: This function expects that the disc_mutex is locked
@@ -654,7 +654,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
        if (!fp)
                return -ENOMEM;
        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
-                                fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
+                                 fc_disc_gpn_id_resp, rdata,
+                                 3 * lport->r_a_tov))
                return -ENOMEM;
        kref_get(&rdata->kref);
        return 0;
@@ -662,8 +663,8 @@ static int fc_disc_gpn_id_req(struct fc_lport *lport,
 
 /**
  * fc_disc_single() - Discover the directory information for a single target
- * @lport: local port
- * @dp: The port to rediscover
+ * @lport: The local port the remote port is associated with
+ * @dp:           The port to rediscover
  *
  * Locking Note: This function expects that the disc_mutex is locked
  *              before it is called.
@@ -681,7 +682,7 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
 
 /**
  * fc_disc_stop() - Stop discovery for a given lport
- * @lport: The lport that discovery should stop for
+ * @lport: The local port that discovery should stop on
  */
 void fc_disc_stop(struct fc_lport *lport)
 {
@@ -695,7 +696,7 @@ void fc_disc_stop(struct fc_lport *lport)
 
 /**
  * fc_disc_stop_final() - Stop discovery for a given lport
- * @lport: The lport that discovery should stop for
+ * @lport: The lport that discovery should stop on
  *
  * This function will block until discovery has been
  * completely stopped and all rports have been deleted.
@@ -707,8 +708,8 @@ void fc_disc_stop_final(struct fc_lport *lport)
 }
 
 /**
- * fc_disc_init() - Initialize the discovery block
- * @lport: FC local port
+ * fc_disc_init() - Initialize the discovery layer for a local port
+ * @lport: The local port that needs the discovery layer to be initialized
  */
 int fc_disc_init(struct fc_lport *lport)
 {
index 5cfa687..5374872 100644 (file)
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
 
-/*
- * fc_elsct_send - sends ELS/CT frame
+/**
+ * fc_elsct_send() - Send an ELS or CT frame
+ * @lport:     The local port to send the frame on
+ * @did:       The destination ID for the frame
+ * @fp:                The frame to be sent
+ * @op:                The operational code
+ * @resp:      The callback routine when the response is received
+ * @arg:       The argument to pass to the response callback routine
+ * @timer_msec: The timeout period for the frame (in msecs)
  */
-static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
-                                   u32 did,
-                                   struct fc_frame *fp,
-                                   unsigned int op,
-                                   void (*resp)(struct fc_seq *,
-                                                struct fc_frame *fp,
-                                                void *arg),
-                                   void *arg, u32 timer_msec)
+struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
+                            struct fc_frame *fp, unsigned int op,
+                            void (*resp)(struct fc_seq *,
+                                         struct fc_frame *,
+                                         void *),
+                            void *arg, u32 timer_msec)
 {
        enum fc_rctl r_ctl;
        enum fc_fh_type fh_type;
@@ -53,15 +58,22 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
                did = FC_FID_DIR_SERV;
        }
 
-       if (rc)
+       if (rc) {
+               fc_frame_free(fp);
                return NULL;
+       }
 
        fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type,
                       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
 
        return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
 }
+EXPORT_SYMBOL(fc_elsct_send);
 
+/**
+ * fc_elsct_init() - Initialize the ELS/CT layer
+ * @lport: The local port to initialize the ELS/CT layer for
+ */
 int fc_elsct_init(struct fc_lport *lport)
 {
        if (!lport->tt.elsct_send)
@@ -72,12 +84,15 @@ int fc_elsct_init(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_elsct_init);
 
 /**
- * fc_els_resp_type() - return string describing ELS response for debug.
- * @fp: frame pointer with possible error code.
+ * fc_els_resp_type() - Return a string describing the ELS response
+ * @fp: The frame pointer or possible error code
  */
 const char *fc_els_resp_type(struct fc_frame *fp)
 {
        const char *msg;
+       struct fc_frame_header *fh;
+       struct fc_ct_hdr *ct;
+
        if (IS_ERR(fp)) {
                switch (-PTR_ERR(fp)) {
                case FC_NO_ERR:
@@ -94,15 +109,41 @@ const char *fc_els_resp_type(struct fc_frame *fp)
                        break;
                }
        } else {
-               switch (fc_frame_payload_op(fp)) {
-               case ELS_LS_ACC:
-                       msg = "accept";
+               fh = fc_frame_header_get(fp);
+               switch (fh->fh_type) {
+               case FC_TYPE_ELS:
+                       switch (fc_frame_payload_op(fp)) {
+                       case ELS_LS_ACC:
+                               msg = "accept";
+                               break;
+                       case ELS_LS_RJT:
+                               msg = "reject";
+                               break;
+                       default:
+                               msg = "response unknown ELS";
+                               break;
+                       }
                        break;
-               case ELS_LS_RJT:
-                       msg = "reject";
+               case FC_TYPE_CT:
+                       ct = fc_frame_payload_get(fp, sizeof(*ct));
+                       if (ct) {
+                               switch (ntohs(ct->ct_cmd)) {
+                               case FC_FS_ACC:
+                                       msg = "CT accept";
+                                       break;
+                               case FC_FS_RJT:
+                                       msg = "CT reject";
+                                       break;
+                               default:
+                                       msg = "response unknown CT";
+                                       break;
+                               }
+                       } else {
+                               msg = "short CT response";
+                       }
                        break;
                default:
-                       msg = "response unknown ELS";
+                       msg = "response not ELS or CT";
                        break;
                }
        }
index c1c1574..19d711c 100644 (file)
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
 
+#include "fc_libfc.h"
+
 u16    fc_cpu_mask;            /* cpu mask for possible cpus */
 EXPORT_SYMBOL(fc_cpu_mask);
 static u16     fc_cpu_order;   /* 2's power to represent total possible cpus */
-static struct kmem_cache *fc_em_cachep;        /* cache for exchanges */
+static struct kmem_cache *fc_em_cachep;               /* cache for exchanges */
+struct workqueue_struct *fc_exch_workqueue;
 
 /*
  * Structure and function definitions for managing Fibre Channel Exchanges
@@ -50,35 +53,46 @@ static struct kmem_cache *fc_em_cachep;        /* cache for exchanges */
  * fc_seq holds the state for an individual sequence.
  */
 
-/*
- * Per cpu exchange pool
+/**
+ * struct fc_exch_pool - Per cpu exchange pool
+ * @next_index:          Next possible free exchange index
+ * @total_exches: Total allocated exchanges
+ * @lock:        Exch pool lock
+ * @ex_list:     List of exchanges
  *
  * This structure manages per cpu exchanges in array of exchange pointers.
  * This array is allocated followed by struct fc_exch_pool memory for
  * assigned range of exchanges to per cpu pool.
  */
 struct fc_exch_pool {
-       u16             next_index;     /* next possible free exchange index */
-       u16             total_exches;   /* total allocated exchanges */
-       spinlock_t      lock;           /* exch pool lock */
-       struct list_head        ex_list;        /* allocated exchanges list */
+       u16              next_index;
+       u16              total_exches;
+       spinlock_t       lock;
+       struct list_head ex_list;
 };
 
-/*
- * Exchange manager.
+/**
+ * struct fc_exch_mgr - The Exchange Manager (EM).
+ * @class:         Default class for new sequences
+ * @kref:          Reference counter
+ * @min_xid:       Minimum exchange ID
+ * @max_xid:       Maximum exchange ID
+ * @ep_pool:       Reserved exchange pointers
+ * @pool_max_index: Max exch array index in exch pool
+ * @pool:          Per cpu exch pool
+ * @stats:         Statistics structure
  *
  * This structure is the center for creating exchanges and sequences.
  * It manages the allocation of exchange IDs.
  */
 struct fc_exch_mgr {
-       enum fc_class   class;          /* default class for sequences */
-       struct kref     kref;           /* exchange mgr reference count */
-       u16             min_xid;        /* min exchange ID */
-       u16             max_xid;        /* max exchange ID */
-       struct list_head        ex_list;        /* allocated exchanges list */
-       mempool_t       *ep_pool;       /* reserve ep's */
-       u16             pool_max_index; /* max exch array index in exch pool */
-       struct fc_exch_pool *pool;      /* per cpu exch pool */
+       enum fc_class   class;
+       struct kref     kref;
+       u16             min_xid;
+       u16             max_xid;
+       mempool_t       *ep_pool;
+       u16             pool_max_index;
+       struct fc_exch_pool *pool;
 
        /*
         * currently exchange mgr stats are updated but not used.
@@ -96,6 +110,18 @@ struct fc_exch_mgr {
 };
 #define        fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
 
+/**
+ * struct fc_exch_mgr_anchor - primary structure for list of EMs
+ * @ema_list: Exchange Manager Anchor list
+ * @mp:              Exchange Manager associated with this anchor
+ * @match:    Routine to determine if this anchor's EM should be used
+ *
+ * When walking the list of anchors the match routine will be called
+ * for each anchor to determine if that EM should be used. The last
+ * anchor in the list will always match to handle any exchanges not
+ * handled by other EMs. The non-default EMs would be added to the
+ * anchor list by HW that provides FCoE offloads.
+ */
 struct fc_exch_mgr_anchor {
        struct list_head ema_list;
        struct fc_exch_mgr *mp;
@@ -108,7 +134,6 @@ static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
                          enum fc_els_rjt_explan);
 static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
 static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
-static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
 
 /*
  * Internal implementation notes.
@@ -196,6 +221,15 @@ static char *fc_exch_rctl_names[] = FC_RCTL_NAMES_INIT;
 
 #define FC_TABLE_SIZE(x)   (sizeof(x) / sizeof(x[0]))
 
+/**
+ * fc_exch_name_lookup() - Lookup name by opcode
+ * @op:               Opcode to be looked up
+ * @table:     Opcode/name table
+ * @max_index: Index not to be exceeded
+ *
+ * This routine is used to determine a human-readable string identifying
+ * a R_CTL opcode.
+ */
 static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
                                              unsigned int max_index)
 {
@@ -208,25 +242,34 @@ static inline const char *fc_exch_name_lookup(unsigned int op, char **table,
        return name;
 }
 
+/**
+ * fc_exch_rctl_name() - Wrapper routine for fc_exch_name_lookup()
+ * @op: The opcode to be looked up
+ */
 static const char *fc_exch_rctl_name(unsigned int op)
 {
        return fc_exch_name_lookup(op, fc_exch_rctl_names,
                                   FC_TABLE_SIZE(fc_exch_rctl_names));
 }
 
-/*
- * Hold an exchange - keep it from being freed.
+/**
+ * fc_exch_hold() - Increment an exchange's reference count
+ * @ep: Echange to be held
  */
-static void fc_exch_hold(struct fc_exch *ep)
+static inline void fc_exch_hold(struct fc_exch *ep)
 {
        atomic_inc(&ep->ex_refcnt);
 }
 
-/*
- * setup fc hdr by initializing few more FC header fields and sof/eof.
- * Initialized fields by this func:
- *     - fh_ox_id, fh_rx_id, fh_seq_id, fh_seq_cnt
- *     - sof and eof
+/**
+ * fc_exch_setup_hdr() - Initialize a FC header by initializing some fields
+ *                      and determine SOF and EOF.
+ * @ep:           The exchange to that will use the header
+ * @fp:           The frame whose header is to be modified
+ * @f_ctl: F_CTL bits that will be used for the frame header
+ *
+ * The fields initialized by this routine are: fh_ox_id, fh_rx_id,
+ * fh_seq_id, fh_seq_cnt and the SOF and EOF.
  */
 static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
                              u32 f_ctl)
@@ -243,7 +286,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
                if (fc_sof_needs_ack(ep->class))
                        fr_eof(fp) = FC_EOF_N;
                /*
-                * Form f_ctl.
+                * From F_CTL.
                 * The number of fill bytes to make the length a 4-byte
                 * multiple is the low order 2-bits of the f_ctl.
                 * The fill itself will have been cleared by the frame
@@ -273,10 +316,12 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,
        fh->fh_seq_cnt = htons(ep->seq.cnt);
 }
 
-
-/*
- * Release a reference to an exchange.
- * If the refcnt goes to zero and the exchange is complete, it is freed.
+/**
+ * fc_exch_release() - Decrement an exchange's reference count
+ * @ep: Exchange to be released
+ *
+ * If the reference count reaches zero and the exchange is complete,
+ * it is freed.
  */
 static void fc_exch_release(struct fc_exch *ep)
 {
@@ -291,6 +336,10 @@ static void fc_exch_release(struct fc_exch *ep)
        }
 }
 
+/**
+ * fc_exch_done_locked() - Complete an exchange with the exchange lock held
+ * @ep: The exchange that is complete
+ */
 static int fc_exch_done_locked(struct fc_exch *ep)
 {
        int rc = 1;
@@ -315,6 +364,15 @@ static int fc_exch_done_locked(struct fc_exch *ep)
        return rc;
 }
 
+/**
+ * fc_exch_ptr_get() - Return an exchange from an exchange pool
+ * @pool:  Exchange Pool to get an exchange from
+ * @index: Index of the exchange within the pool
+ *
+ * Use the index to get an exchange from within an exchange pool. exches
+ * will point to an array of exchange pointers. The index will select
+ * the exchange within the array.
+ */
 static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
                                              u16 index)
 {
@@ -322,12 +380,22 @@ static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
        return exches[index];
 }
 
+/**
+ * fc_exch_ptr_set() - Assign an exchange to a slot in an exchange pool
+ * @pool:  The pool to assign the exchange to
+ * @index: The index in the pool where the exchange will be assigned
+ * @ep:           The exchange to assign to the pool
+ */
 static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
                                   struct fc_exch *ep)
 {
        ((struct fc_exch **)(pool + 1))[index] = ep;
 }
 
+/**
+ * fc_exch_delete() - Delete an exchange
+ * @ep: The exchange to be deleted
+ */
 static void fc_exch_delete(struct fc_exch *ep)
 {
        struct fc_exch_pool *pool;
@@ -343,8 +411,14 @@ static void fc_exch_delete(struct fc_exch *ep)
        fc_exch_release(ep);    /* drop hold for exch in mp */
 }
 
-/*
- * Internal version of fc_exch_timer_set - used with lock held.
+/**
+ * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the
+ *                             the exchange lock held
+ * @ep:                The exchange whose timer will start
+ * @timer_msec: The timeout period
+ *
+ * Used for upper level protocols to time out the exchange.
+ * The timer is cancelled when it fires or when the exchange completes.
  */
 static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
                                            unsigned int timer_msec)
@@ -354,17 +428,15 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
 
        FC_EXCH_DBG(ep, "Exchange timer armed\n");
 
-       if (schedule_delayed_work(&ep->timeout_work,
-                                 msecs_to_jiffies(timer_msec)))
+       if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work,
+                              msecs_to_jiffies(timer_msec)))
                fc_exch_hold(ep);               /* hold for timer */
 }
 
-/*
- * Set timer for an exchange.
- * The time is a minimum delay in milliseconds until the timer fires.
- * Used for upper level protocols to time out the exchange.
- * The timer is cancelled when it fires or when the exchange completes.
- * Returns non-zero if a timer couldn't be allocated.
+/**
+ * fc_exch_timer_set() - Lock the exchange and set the timer
+ * @ep:                The exchange whose timer will start
+ * @timer_msec: The timeout period
  */
 static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
 {
@@ -373,7 +445,115 @@ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec)
        spin_unlock_bh(&ep->ex_lock);
 }
 
-int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
+/**
+ * fc_seq_send() - Send a frame using existing sequence/exchange pair
+ * @lport: The local port that the exchange will be sent on
+ * @sp:           The sequence to be sent
+ * @fp:           The frame to be sent on the exchange
+ */
+static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
+                      struct fc_frame *fp)
+{
+       struct fc_exch *ep;
+       struct fc_frame_header *fh = fc_frame_header_get(fp);
+       int error;
+       u32 f_ctl;
+
+       ep = fc_seq_exch(sp);
+       WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
+
+       f_ctl = ntoh24(fh->fh_f_ctl);
+       fc_exch_setup_hdr(ep, fp, f_ctl);
+
+       /*
+        * update sequence count if this frame is carrying
+        * multiple FC frames when sequence offload is enabled
+        * by LLD.
+        */
+       if (fr_max_payload(fp))
+               sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
+                                       fr_max_payload(fp));
+       else
+               sp->cnt++;
+
+       /*
+        * Send the frame.
+        */
+       error = lport->tt.frame_send(lport, fp);
+
+       /*
+        * Update the exchange and sequence flags,
+        * assuming all frames for the sequence have been sent.
+        * We can only be called to send once for each sequence.
+        */
+       spin_lock_bh(&ep->ex_lock);
+       ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;   /* not first seq */
+       if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
+               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
+       return error;
+}
+
+/**
+ * fc_seq_alloc() - Allocate a sequence for a given exchange
+ * @ep:            The exchange to allocate a new sequence for
+ * @seq_id: The sequence ID to be used
+ *
+ * We don't support multiple originated sequences on the same exchange.
+ * By implication, any previously originated sequence on this exchange
+ * is complete, and we reallocate the same sequence.
+ */
+static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
+{
+       struct fc_seq *sp;
+
+       sp = &ep->seq;
+       sp->ssb_stat = 0;
+       sp->cnt = 0;
+       sp->id = seq_id;
+       return sp;
+}
+
+/**
+ * fc_seq_start_next_locked() - Allocate a new sequence on the same
+ *                             exchange as the supplied sequence
+ * @sp: The sequence/exchange to get a new sequence for
+ */
+static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
+{
+       struct fc_exch *ep = fc_seq_exch(sp);
+
+       sp = fc_seq_alloc(ep, ep->seq_id++);
+       FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
+                   ep->f_ctl, sp->id);
+       return sp;
+}
+
+/**
+ * fc_seq_start_next() - Lock the exchange and get a new sequence
+ *                      for a given sequence/exchange pair
+ * @sp: The sequence/exchange to get a new exchange for
+ */
+static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
+{
+       struct fc_exch *ep = fc_seq_exch(sp);
+
+       spin_lock_bh(&ep->ex_lock);
+       sp = fc_seq_start_next_locked(sp);
+       spin_unlock_bh(&ep->ex_lock);
+
+       return sp;
+}
+
+/**
+ * fc_seq_exch_abort() - Abort an exchange and sequence
+ * @req_sp:    The sequence to be aborted
+ * @timer_msec: The period of time to wait before aborting
+ *
+ * Generally called because of a timeout or an abort from the upper layer.
+ */
+static int fc_seq_exch_abort(const struct fc_seq *req_sp,
+                            unsigned int timer_msec)
 {
        struct fc_seq *sp;
        struct fc_exch *ep;
@@ -422,11 +602,10 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec)
                error = -ENOBUFS;
        return error;
 }
-EXPORT_SYMBOL(fc_seq_exch_abort);
 
-/*
- * Exchange timeout - handle exchange timer expiration.
- * The timer will have been cancelled before this is called.
+/**
+ * fc_exch_timeout() - Handle exchange timer expiration
+ * @work: The work_struct identifying the exchange that timed out
  */
 static void fc_exch_timeout(struct work_struct *work)
 {
@@ -474,28 +653,10 @@ done:
        fc_exch_release(ep);
 }
 
-/*
- * Allocate a sequence.
- *
- * We don't support multiple originated sequences on the same exchange.
- * By implication, any previously originated sequence on this exchange
- * is complete, and we reallocate the same sequence.
- */
-static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
-{
-       struct fc_seq *sp;
-
-       sp = &ep->seq;
-       sp->ssb_stat = 0;
-       sp->cnt = 0;
-       sp->id = seq_id;
-       return sp;
-}
-
 /**
- * fc_exch_em_alloc() - allocate an exchange from a specified EM.
- * @lport:     ptr to the local port
- * @mp:                ptr to the exchange manager
+ * fc_exch_em_alloc() - Allocate an exchange from a specified EM.
+ * @lport: The local port that the exchange is for
+ * @mp:           The exchange manager that will allocate the exchange
  *
  * Returns pointer to allocated fc_exch with exch lock held.
  */
@@ -563,16 +724,18 @@ err:
 }
 
 /**
- * fc_exch_alloc() - allocate an exchange.
- * @lport:     ptr to the local port
- * @fp:                ptr to the FC frame
+ * fc_exch_alloc() - Allocate an exchange from an EM on a
+ *                  local port's list of EMs.
+ * @lport: The local port that will own the exchange
+ * @fp:           The FC frame that the exchange will be for
  *
- * This function walks the list of the exchange manager(EM)
- * anchors to select a EM for new exchange allocation. The
- * EM is selected having either a NULL match function pointer
- * or call to match function returning true.
+ * This function walks the list of exchange manager(EM)
+ * anchors to select an EM for a new exchange allocation. The
+ * EM is selected when a NULL match function pointer is encountered
+ * or when a call to a match function returns true.
  */
-struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
+static struct fc_exch *fc_exch_alloc(struct fc_lport *lport,
+                                    struct fc_frame *fp)
 {
        struct fc_exch_mgr_anchor *ema;
        struct fc_exch *ep;
@@ -586,10 +749,11 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
        }
        return NULL;
 }
-EXPORT_SYMBOL(fc_exch_alloc);
 
-/*
- * Lookup and hold an exchange.
+/**
+ * fc_exch_find() - Lookup and hold an exchange
+ * @mp:         The exchange manager to lookup the exchange from
+ * @xid: The XID of the exchange to look up
  */
 static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
 {
@@ -609,7 +773,13 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
        return ep;
 }
 
-void fc_exch_done(struct fc_seq *sp)
+
+/**
+ * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and
+ *                 the memory allocated for the related objects may be freed.
+ * @sp: The sequence that has completed
+ */
+static void fc_exch_done(struct fc_seq *sp)
 {
        struct fc_exch *ep = fc_seq_exch(sp);
        int rc;
@@ -620,10 +790,13 @@ void fc_exch_done(struct fc_seq *sp)
        if (!rc)
                fc_exch_delete(ep);
 }
-EXPORT_SYMBOL(fc_exch_done);
 
-/*
- * Allocate a new exchange as responder.
+/**
+ * fc_exch_resp() - Allocate a new exchange for a response frame
+ * @lport: The local port that the exchange was for
+ * @mp:           The exchange manager to allocate the exchange from
+ * @fp:           The response frame
+ *
  * Sets the responder ID in the frame header.
  */
 static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
@@ -664,8 +837,13 @@ static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
        return ep;
 }
 
-/*
- * Find a sequence for receive where the other end is originating the sequence.
+/**
+ * fc_seq_lookup_recip() - Find a sequence where the other end
+ *                        originated the sequence
+ * @lport: The local port that the frame was sent to
+ * @mp:           The Exchange Manager to lookup the exchange from
+ * @fp:           The frame associated with the sequence we're looking for
+ *
  * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
  * on the ep that should be released by the caller.
  */
@@ -771,10 +949,12 @@ rel:
        return reject;
 }
 
-/*
- * Find the sequence for a frame being received.
- * We originated the sequence, so it should be found.
- * We may or may not have originated the exchange.
+/**
+ * fc_seq_lookup_orig() - Find a sequence where this end
+ *                       originated the sequence
+ * @mp:           The Exchange Manager to lookup the exchange from
+ * @fp:           The frame associated with the sequence we're looking for
+ *
  * Does not hold the sequence for the caller.
  */
 static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
@@ -806,8 +986,12 @@ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp,
        return sp;
 }
 
-/*
- * Set addresses for an exchange.
+/**
+ * fc_exch_set_addr() - Set the source and destination IDs for an exchange
+ * @ep:             The exchange to set the addresses for
+ * @orig_id: The originator's ID
+ * @resp_id: The responder's ID
+ *
  * Note this must be done before the first sequence of the exchange is sent.
  */
 static void fc_exch_set_addr(struct fc_exch *ep,
@@ -823,76 +1007,15 @@ static void fc_exch_set_addr(struct fc_exch *ep,
        }
 }
 
-static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp)
-{
-       struct fc_exch *ep = fc_seq_exch(sp);
-
-       sp = fc_seq_alloc(ep, ep->seq_id++);
-       FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n",
-                   ep->f_ctl, sp->id);
-       return sp;
-}
-/*
- * Allocate a new sequence on the same exchange as the supplied sequence.
- * This will never return NULL.
+/**
+ * fc_seq_els_rsp_send() - Send an ELS response using infomation from
+ *                        the existing sequence/exchange.
+ * @sp:              The sequence/exchange to get information from
+ * @els_cmd:  The ELS command to be sent
+ * @els_data: The ELS data to be sent
  */
-struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
-{
-       struct fc_exch *ep = fc_seq_exch(sp);
-
-       spin_lock_bh(&ep->ex_lock);
-       sp = fc_seq_start_next_locked(sp);
-       spin_unlock_bh(&ep->ex_lock);
-
-       return sp;
-}
-EXPORT_SYMBOL(fc_seq_start_next);
-
-int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp)
-{
-       struct fc_exch *ep;
-       struct fc_frame_header *fh = fc_frame_header_get(fp);
-       int error;
-       u32     f_ctl;
-
-       ep = fc_seq_exch(sp);
-       WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT);
-
-       f_ctl = ntoh24(fh->fh_f_ctl);
-       fc_exch_setup_hdr(ep, fp, f_ctl);
-
-       /*
-        * update sequence count if this frame is carrying
-        * multiple FC frames when sequence offload is enabled
-        * by LLD.
-        */
-       if (fr_max_payload(fp))
-               sp->cnt += DIV_ROUND_UP((fr_len(fp) - sizeof(*fh)),
-                                       fr_max_payload(fp));
-       else
-               sp->cnt++;
-
-       /*
-        * Send the frame.
-        */
-       error = lp->tt.frame_send(lp, fp);
-
-       /*
-        * Update the exchange and sequence flags,
-        * assuming all frames for the sequence have been sent.
-        * We can only be called to send once for each sequence.
-        */
-       spin_lock_bh(&ep->ex_lock);
-       ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;   /* not first seq */
-       if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
-               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
-       spin_unlock_bh(&ep->ex_lock);
-       return error;
-}
-EXPORT_SYMBOL(fc_seq_send);
-
-void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
-                        struct fc_seq_els_data *els_data)
+static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+                               struct fc_seq_els_data *els_data)
 {
        switch (els_cmd) {
        case ELS_LS_RJT:
@@ -911,10 +1034,13 @@ void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
                FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd);
        }
 }
-EXPORT_SYMBOL(fc_seq_els_rsp_send);
 
-/*
- * Send a sequence, which is also the last sequence in the exchange.
+/**
+ * fc_seq_send_last() - Send a sequence that is the last in the exchange
+ * @sp:             The sequence that is to be sent
+ * @fp:             The frame that will be sent on the sequence
+ * @rctl:    The R_CTL information to be sent
+ * @fh_type: The frame header type
  */
 static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
                             enum fc_rctl rctl, enum fc_fh_type fh_type)
@@ -928,9 +1054,12 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,
        fc_seq_send(ep->lp, sp, fp);
 }
 
-/*
+/**
+ * fc_seq_send_ack() - Send an acknowledgement that we've received a frame
+ * @sp:           The sequence to send the ACK on
+ * @rx_fp: The received frame that is being acknoledged
+ *
  * Send ACK_1 (or equiv.) indicating we received something.
- * The frame we're acking is supplied.
  */
 static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
 {
@@ -938,14 +1067,14 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
        struct fc_frame_header *rx_fh;
        struct fc_frame_header *fh;
        struct fc_exch *ep = fc_seq_exch(sp);
-       struct fc_lport *lp = ep->lp;
+       struct fc_lport *lport = ep->lp;
        unsigned int f_ctl;
 
        /*
         * Don't send ACKs for class 3.
         */
        if (fc_sof_needs_ack(fr_sof(rx_fp))) {
-               fp = fc_frame_alloc(lp, 0);
+               fp = fc_frame_alloc(lport, 0);
                if (!fp)
                        return;
 
@@ -980,12 +1109,16 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)
                else
                        fr_eof(fp) = FC_EOF_N;
 
-               (void) lp->tt.frame_send(lp, fp);
+               lport->tt.frame_send(lport, fp);
        }
 }
 
-/*
- * Send BLS Reject.
+/**
+ * fc_exch_send_ba_rjt() - Send BLS Reject
+ * @rx_fp:  The frame being rejected
+ * @reason: The reason the frame is being rejected
+ * @explan: The explaination for the rejection
+ *
  * This is for rejecting BA_ABTS only.
  */
 static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
@@ -996,11 +1129,11 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
        struct fc_frame_header *rx_fh;
        struct fc_frame_header *fh;
        struct fc_ba_rjt *rp;
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        unsigned int f_ctl;
 
-       lp = fr_dev(rx_fp);
-       fp = fc_frame_alloc(lp, sizeof(*rp));
+       lport = fr_dev(rx_fp);
+       fp = fc_frame_alloc(lport, sizeof(*rp));
        if (!fp)
                return;
        fh = fc_frame_header_get(fp);
@@ -1045,13 +1178,17 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
        if (fc_sof_needs_ack(fr_sof(fp)))
                fr_eof(fp) = FC_EOF_N;
 
-       (void) lp->tt.frame_send(lp, fp);
+       lport->tt.frame_send(lport, fp);
 }
 
-/*
- * Handle an incoming ABTS.  This would be for target mode usually,
- * but could be due to lost FCP transfer ready, confirm or RRQ.
- * We always handle this as an exchange abort, ignoring the parameter.
+/**
+ * fc_exch_recv_abts() - Handle an incoming ABTS
+ * @ep:           The exchange the abort was on
+ * @rx_fp: The ABTS frame
+ *
+ * This would be for target mode usually, but could be due to lost
+ * FCP transfer ready, confirm or RRQ. We always handle this as an
+ * exchange abort, ignoring the parameter.
  */
 static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)
 {
@@ -1100,10 +1237,14 @@ free:
        fc_frame_free(rx_fp);
 }
 
-/*
- * Handle receive where the other end is originating the sequence.
+/**
+ * fc_exch_recv_req() - Handler for an incoming request where is other
+ *                     end is originating the sequence
+ * @lport: The local port that received the request
+ * @mp:           The EM that the exchange is on
+ * @fp:           The request frame
  */
-static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
+static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
                             struct fc_frame *fp)
 {
        struct fc_frame_header *fh = fc_frame_header_get(fp);
@@ -1114,8 +1255,17 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
        u32 f_ctl;
        enum fc_pf_rjt_reason reject;
 
+       /* We can have the wrong fc_lport at this point with NPIV, which is a
+        * problem now that we know a new exchange needs to be allocated
+        */
+       lport = fc_vport_id_lookup(lport, ntoh24(fh->fh_d_id));
+       if (!lport) {
+               fc_frame_free(fp);
+               return;
+       }
+
        fr_seq(fp) = NULL;
-       reject = fc_seq_lookup_recip(lp, mp, fp);
+       reject = fc_seq_lookup_recip(lport, mp, fp);
        if (reject == FC_RJT_NONE) {
                sp = fr_seq(fp);        /* sequence will be held */
                ep = fc_seq_exch(sp);
@@ -1138,17 +1288,21 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
                if (ep->resp)
                        ep->resp(sp, fp, ep->arg);
                else
-                       lp->tt.lport_recv(lp, sp, fp);
+                       lport->tt.lport_recv(lport, sp, fp);
                fc_exch_release(ep);    /* release from lookup */
        } else {
-               FC_LPORT_DBG(lp, "exch/seq lookup failed: reject %x\n", reject);
+               FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n",
+                            reject);
                fc_frame_free(fp);
        }
 }
 
-/*
- * Handle receive where the other end is originating the sequence in
- * response to our exchange.
+/**
+ * fc_exch_recv_seq_resp() - Handler for an incoming response where the other
+ *                          end is the originator of the sequence that is a
+ *                          response to our initial exchange
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
  */
 static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
 {
@@ -1239,8 +1393,11 @@ out:
        fc_frame_free(fp);
 }
 
-/*
- * Handle receive for a sequence where other end is responding to our sequence.
+/**
+ * fc_exch_recv_resp() - Handler for a sequence where other end is
+ *                      responding to our sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The response frame
  */
 static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
 {
@@ -1256,9 +1413,13 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
        fc_frame_free(fp);
 }
 
-/*
- * Handle the response to an ABTS for exchange or sequence.
- * This can be BA_ACC or BA_RJT.
+/**
+ * fc_exch_abts_resp() - Handler for a response to an ABT
+ * @ep: The exchange that the frame is on
+ * @fp: The response frame
+ *
+ * This response would be to an ABTS cancelling an exchange or sequence.
+ * The response can be either BA_ACC or BA_RJT
  */
 static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
 {
@@ -1333,9 +1494,12 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
 
 }
 
-/*
- * Receive BLS sequence.
- * This is always a sequence initiated by the remote side.
+/**
+ * fc_exch_recv_bls() - Handler for a BLS sequence
+ * @mp: The EM that the exchange is on
+ * @fp: The request frame
+ *
+ * The BLS frame is always a sequence initiated by the remote side.
  * We may be either the originator or recipient of the exchange.
  */
 static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
@@ -1392,8 +1556,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
                fc_exch_release(ep);    /* release hold taken by fc_exch_find */
 }
 
-/*
- * Accept sequence with LS_ACC.
+/**
+ * fc_seq_ls_acc() - Accept sequence with LS_ACC
+ * @req_sp: The request sequence
+ *
  * If this fails due to allocation or transmit congestion, assume the
  * originator will repeat the sequence.
  */
@@ -1413,8 +1579,12 @@ static void fc_seq_ls_acc(struct fc_seq *req_sp)
        }
 }
 
-/*
- * Reject sequence with ELS LS_RJT.
+/**
+ * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT
+ * @req_sp: The request sequence
+ * @reason: The reason the sequence is being rejected
+ * @explan: The explaination for the rejection
+ *
  * If this fails due to allocation or transmit congestion, assume the
  * originator will repeat the sequence.
  */
@@ -1437,6 +1607,10 @@ static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
        }
 }
 
+/**
+ * fc_exch_reset() - Reset an exchange
+ * @ep: The exchange to be reset
+ */
 static void fc_exch_reset(struct fc_exch *ep)
 {
        struct fc_seq *sp;
@@ -1446,12 +1620,6 @@ static void fc_exch_reset(struct fc_exch *ep)
 
        spin_lock_bh(&ep->ex_lock);
        ep->state |= FC_EX_RST_CLEANUP;
-       /*
-        * we really want to call del_timer_sync, but cannot due
-        * to the lport calling with the lport lock held (some resp
-        * functions can also grab the lport lock which could cause
-        * a deadlock).
-        */
        if (cancel_delayed_work(&ep->timeout_work))
                atomic_dec(&ep->ex_refcnt);     /* drop hold for timer */
        resp = ep->resp;
@@ -1471,16 +1639,16 @@ static void fc_exch_reset(struct fc_exch *ep)
 }
 
 /**
- * fc_exch_pool_reset() - Resets an per cpu exches pool.
- * @lport:     ptr to the local port
- * @pool:      ptr to the per cpu exches pool
- * @sid:       source FC ID
- * @did:       destination FC ID
+ * fc_exch_pool_reset() - Reset a per cpu exchange pool
+ * @lport: The local port that the exchange pool is on
+ * @pool:  The exchange pool to be reset
+ * @sid:   The source ID
+ * @did:   The destination ID
  *
- * Resets an per cpu exches pool, releasing its all sequences
- * and exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
+ * Resets a per cpu exches pool, releasing all of its sequences
+ * and exchanges. If sid is non-zero then reset only exchanges
+ * we sourced from the local port's FID. If did is non-zero then
+ * only reset exchanges destined for the local port's FID.
  */
 static void fc_exch_pool_reset(struct fc_lport *lport,
                               struct fc_exch_pool *pool,
@@ -1514,15 +1682,15 @@ restart:
 }
 
 /**
- * fc_exch_mgr_reset() - Resets all EMs of a lport
- * @lport:     ptr to the local port
- * @sid:       source FC ID
- * @did:       destination FC ID
+ * fc_exch_mgr_reset() - Reset all EMs of a local port
+ * @lport: The local port whose EMs are to be reset
+ * @sid:   The source ID
+ * @did:   The destination ID
  *
- * Reset all EMs of a lport, releasing its all sequences and
- * exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
+ * Reset all EMs associated with a given local port. Release all
+ * sequences and exchanges. If sid is non-zero then reset only the
+ * exchanges sent from the local port's FID. If did is non-zero then
+ * reset only exchanges destined for the local port's FID.
  */
 void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
 {
@@ -1538,8 +1706,11 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
 }
 EXPORT_SYMBOL(fc_exch_mgr_reset);
 
-/*
- * Handle incoming ELS REC - Read Exchange Concise.
+/**
+ * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests
+ * @sp:         The sequence the REC is on
+ * @rfp: The REC frame
+ *
  * Note that the requesting port may be different than the S_ID in the request.
  */
 static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
@@ -1621,10 +1792,11 @@ reject:
        fc_frame_free(rfp);
 }
 
-/*
- * Handle response from RRQ.
- * Not much to do here, really.
- * Should report errors.
+/**
+ * fc_exch_rrq_resp() - Handler for RRQ responses
+ * @sp:         The sequence that the RRQ is on
+ * @fp:         The RRQ frame
+ * @arg: The exchange that the RRQ is on
  *
  * TODO: fix error handler.
  */
@@ -1664,21 +1836,99 @@ cleanup:
        fc_exch_release(aborted_ep);
 }
 
-/*
- * Send ELS RRQ - Reinstate Recovery Qualifier.
+
+/**
+ * fc_exch_seq_send() - Send a frame using a new exchange and sequence
+ * @lport:     The local port to send the frame on
+ * @fp:                The frame to be sent
+ * @resp:      The response handler for this request
+ * @destructor: The destructor for the exchange
+ * @arg:       The argument to be passed to the response handler
+ * @timer_msec: The timeout period for the exchange
+ *
+ * The frame pointer with some of the header's fields must be
+ * filled before calling this routine, those fields are:
+ *
+ * - routing control
+ * - FC port did
+ * - FC port sid
+ * - FC header type
+ * - frame control
+ * - parameter or relative offset
+ */
+static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,
+                                      struct fc_frame *fp,
+                                      void (*resp)(struct fc_seq *,
+                                                   struct fc_frame *fp,
+                                                   void *arg),
+                                      void (*destructor)(struct fc_seq *,
+                                                         void *),
+                                      void *arg, u32 timer_msec)
+{
+       struct fc_exch *ep;
+       struct fc_seq *sp = NULL;
+       struct fc_frame_header *fh;
+       int rc = 1;
+
+       ep = fc_exch_alloc(lport, fp);
+       if (!ep) {
+               fc_frame_free(fp);
+               return NULL;
+       }
+       ep->esb_stat |= ESB_ST_SEQ_INIT;
+       fh = fc_frame_header_get(fp);
+       fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
+       ep->resp = resp;
+       ep->destructor = destructor;
+       ep->arg = arg;
+       ep->r_a_tov = FC_DEF_R_A_TOV;
+       ep->lp = lport;
+       sp = &ep->seq;
+
+       ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
+       ep->f_ctl = ntoh24(fh->fh_f_ctl);
+       fc_exch_setup_hdr(ep, fp, ep->f_ctl);
+       sp->cnt++;
+
+       if (ep->xid <= lport->lro_xid)
+               fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+
+       if (unlikely(lport->tt.frame_send(lport, fp)))
+               goto err;
+
+       if (timer_msec)
+               fc_exch_timer_set_locked(ep, timer_msec);
+       ep->f_ctl &= ~FC_FC_FIRST_SEQ;  /* not first seq */
+
+       if (ep->f_ctl & FC_FC_SEQ_INIT)
+               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
+       spin_unlock_bh(&ep->ex_lock);
+       return sp;
+err:
+       rc = fc_exch_done_locked(ep);
+       spin_unlock_bh(&ep->ex_lock);
+       if (!rc)
+               fc_exch_delete(ep);
+       return NULL;
+}
+
+/**
+ * fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command
+ * @ep: The exchange to send the RRQ on
+ *
  * This tells the remote port to stop blocking the use of
  * the exchange and the seq_cnt range.
  */
 static void fc_exch_rrq(struct fc_exch *ep)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fc_els_rrq *rrq;
        struct fc_frame *fp;
        u32 did;
 
-       lp = ep->lp;
+       lport = ep->lp;
 
-       fp = fc_frame_alloc(lp, sizeof(*rrq));
+       fp = fc_frame_alloc(lport, sizeof(*rrq));
        if (!fp)
                goto retry;
 
@@ -1694,10 +1944,11 @@ static void fc_exch_rrq(struct fc_exch *ep)
                did = ep->sid;
 
        fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did,
-                      fc_host_port_id(lp->host), FC_TYPE_ELS,
+                      fc_host_port_id(lport->host), FC_TYPE_ELS,
                       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
 
-       if (fc_exch_seq_send(lp, fp, fc_exch_rrq_resp, NULL, ep, lp->e_d_tov))
+       if (fc_exch_seq_send(lport, fp, fc_exch_rrq_resp, NULL, ep,
+                            lport->e_d_tov))
                return;
 
 retry:
@@ -1714,12 +1965,14 @@ retry:
 }
 
 
-/*
- * Handle incoming ELS RRQ - Reset Recovery Qualifier.
+/**
+ * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests
+ * @sp: The sequence that the RRQ is on
+ * @fp: The RRQ frame
  */
 static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
 {
-       struct fc_exch *ep;             /* request or subject exchange */
+       struct fc_exch *ep = NULL;      /* request or subject exchange */
        struct fc_els_rrq *rp;
        u32 sid;
        u16 xid;
@@ -1769,17 +2022,24 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
         * Send LS_ACC.
         */
        fc_seq_ls_acc(sp);
-       fc_frame_free(fp);
-       return;
+       goto out;
 
 unlock_reject:
        spin_unlock_bh(&ep->ex_lock);
-       fc_exch_release(ep);    /* drop hold from fc_exch_find */
 reject:
        fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan);
+out:
        fc_frame_free(fp);
+       if (ep)
+               fc_exch_release(ep);    /* drop hold from fc_exch_find */
 }
 
+/**
+ * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs
+ * @lport: The local port to add the exchange manager to
+ * @mp:           The exchange manager to be added to the local port
+ * @match: The match routine that indicates when this EM should be used
+ */
 struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
                                           struct fc_exch_mgr *mp,
                                           bool (*match)(struct fc_frame *))
@@ -1799,6 +2059,10 @@ struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
 }
 EXPORT_SYMBOL(fc_exch_mgr_add);
 
+/**
+ * fc_exch_mgr_destroy() - Destroy an exchange manager
+ * @kref: The reference to the EM to be destroyed
+ */
 static void fc_exch_mgr_destroy(struct kref *kref)
 {
        struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
@@ -1808,6 +2072,10 @@ static void fc_exch_mgr_destroy(struct kref *kref)
        kfree(mp);
 }
 
+/**
+ * fc_exch_mgr_del() - Delete an EM from a local port's list
+ * @ema: The exchange manager anchor identifying the EM to be deleted
+ */
 void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
 {
        /* remove EM anchor from EM anchors list */
@@ -1817,7 +2085,35 @@ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
 }
 EXPORT_SYMBOL(fc_exch_mgr_del);
 
-struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
+/**
+ * fc_exch_mgr_list_clone() - Share all exchange manager objects
+ * @src: Source lport to clone exchange managers from
+ * @dst: New lport that takes references to all the exchange managers
+ */
+int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst)
+{
+       struct fc_exch_mgr_anchor *ema, *tmp;
+
+       list_for_each_entry(ema, &src->ema_list, ema_list) {
+               if (!fc_exch_mgr_add(dst, ema->mp, ema->match))
+                       goto err;
+       }
+       return 0;
+err:
+       list_for_each_entry_safe(ema, tmp, &dst->ema_list, ema_list)
+               fc_exch_mgr_del(ema);
+       return -ENOMEM;
+}
+
+/**
+ * fc_exch_mgr_alloc() - Allocate an exchange manager
+ * @lport:   The local port that the new EM will be associated with
+ * @class:   The default FC class for new exchanges
+ * @min_xid: The minimum XID for exchanges from the new EM
+ * @max_xid: The maximum XID for exchanges from the new EM
+ * @match:   The match routine for the new EM
+ */
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,
                                      enum fc_class class,
                                      u16 min_xid, u16 max_xid,
                                      bool (*match)(struct fc_frame *))
@@ -1830,7 +2126,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
 
        if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN ||
            (min_xid & fc_cpu_mask) != 0) {
-               FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
+               FC_LPORT_DBG(lport, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
                             min_xid, max_xid);
                return NULL;
        }
@@ -1873,7 +2169,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
        }
 
        kref_init(&mp->kref);
-       if (!fc_exch_mgr_add(lp, mp, match)) {
+       if (!fc_exch_mgr_add(lport, mp, match)) {
                free_percpu(mp->pool);
                goto free_mempool;
        }
@@ -1894,76 +2190,26 @@ free_mp:
 }
 EXPORT_SYMBOL(fc_exch_mgr_alloc);
 
+/**
+ * fc_exch_mgr_free() - Free all exchange managers on a local port
+ * @lport: The local port whose EMs are to be freed
+ */
 void fc_exch_mgr_free(struct fc_lport *lport)
 {
        struct fc_exch_mgr_anchor *ema, *next;
 
+       flush_workqueue(fc_exch_workqueue);
        list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list)
                fc_exch_mgr_del(ema);
 }
 EXPORT_SYMBOL(fc_exch_mgr_free);
 
-
-struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
-                               struct fc_frame *fp,
-                               void (*resp)(struct fc_seq *,
-                                            struct fc_frame *fp,
-                                            void *arg),
-                               void (*destructor)(struct fc_seq *, void *),
-                               void *arg, u32 timer_msec)
-{
-       struct fc_exch *ep;
-       struct fc_seq *sp = NULL;
-       struct fc_frame_header *fh;
-       int rc = 1;
-
-       ep = fc_exch_alloc(lp, fp);
-       if (!ep) {
-               fc_frame_free(fp);
-               return NULL;
-       }
-       ep->esb_stat |= ESB_ST_SEQ_INIT;
-       fh = fc_frame_header_get(fp);
-       fc_exch_set_addr(ep, ntoh24(fh->fh_s_id), ntoh24(fh->fh_d_id));
-       ep->resp = resp;
-       ep->destructor = destructor;
-       ep->arg = arg;
-       ep->r_a_tov = FC_DEF_R_A_TOV;
-       ep->lp = lp;
-       sp = &ep->seq;
-
-       ep->fh_type = fh->fh_type; /* save for possbile timeout handling */
-       ep->f_ctl = ntoh24(fh->fh_f_ctl);
-       fc_exch_setup_hdr(ep, fp, ep->f_ctl);
-       sp->cnt++;
-
-       if (ep->xid <= lp->lro_xid)
-               fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
-
-       if (unlikely(lp->tt.frame_send(lp, fp)))
-               goto err;
-
-       if (timer_msec)
-               fc_exch_timer_set_locked(ep, timer_msec);
-       ep->f_ctl &= ~FC_FC_FIRST_SEQ;  /* not first seq */
-
-       if (ep->f_ctl & FC_FC_SEQ_INIT)
-               ep->esb_stat &= ~ESB_ST_SEQ_INIT;
-       spin_unlock_bh(&ep->ex_lock);
-       return sp;
-err:
-       rc = fc_exch_done_locked(ep);
-       spin_unlock_bh(&ep->ex_lock);
-       if (!rc)
-               fc_exch_delete(ep);
-       return NULL;
-}
-EXPORT_SYMBOL(fc_exch_seq_send);
-
-/*
- * Receive a frame
+/**
+ * fc_exch_recv() - Handler for received frames
+ * @lport: The local port the frame was received on
+ * @fp:           The received frame
  */
-void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
+void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
 {
        struct fc_frame_header *fh = fc_frame_header_get(fp);
        struct fc_exch_mgr_anchor *ema;
@@ -1971,8 +2217,8 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
        u16 oxid;
 
        /* lport lock ? */
-       if (!lp || lp->state == LPORT_ST_DISABLED) {
-               FC_LPORT_DBG(lp, "Receiving frames for an lport that "
+       if (!lport || lport->state == LPORT_ST_DISABLED) {
+               FC_LPORT_DBG(lport, "Receiving frames for an lport that "
                             "has not been initialized correctly\n");
                fc_frame_free(fp);
                return;
@@ -1981,7 +2227,7 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
        f_ctl = ntoh24(fh->fh_f_ctl);
        oxid = ntohs(fh->fh_ox_id);
        if (f_ctl & FC_FC_EX_CTX) {
-               list_for_each_entry(ema, &lp->ema_list, ema_list) {
+               list_for_each_entry(ema, &lport->ema_list, ema_list) {
                        if ((oxid >= ema->mp->min_xid) &&
                            (oxid <= ema->mp->max_xid)) {
                                found = 1;
@@ -1990,13 +2236,13 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
                }
 
                if (!found) {
-                       FC_LPORT_DBG(lp, "Received response for out "
+                       FC_LPORT_DBG(lport, "Received response for out "
                                     "of range oxid:%hx\n", oxid);
                        fc_frame_free(fp);
                        return;
                }
        } else
-               ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list);
+               ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list);
 
        /*
         * If frame is marked invalid, just drop it.
@@ -2015,37 +2261,56 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
                else if (f_ctl & FC_FC_SEQ_CTX)
                        fc_exch_recv_resp(ema->mp, fp);
                else
-                       fc_exch_recv_req(lp, ema->mp, fp);
+                       fc_exch_recv_req(lport, ema->mp, fp);
                break;
        default:
-               FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp));
+               FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)",
+                            fr_eof(fp));
                fc_frame_free(fp);
        }
 }
 EXPORT_SYMBOL(fc_exch_recv);
 
-int fc_exch_init(struct fc_lport *lp)
+/**
+ * fc_exch_init() - Initialize the exchange layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_exch_init(struct fc_lport *lport)
 {
-       if (!lp->tt.seq_start_next)
-               lp->tt.seq_start_next = fc_seq_start_next;
+       if (!lport->tt.seq_start_next)
+               lport->tt.seq_start_next = fc_seq_start_next;
 
-       if (!lp->tt.exch_seq_send)
-               lp->tt.exch_seq_send = fc_exch_seq_send;
+       if (!lport->tt.exch_seq_send)
+               lport->tt.exch_seq_send = fc_exch_seq_send;
 
-       if (!lp->tt.seq_send)
-               lp->tt.seq_send = fc_seq_send;
+       if (!lport->tt.seq_send)
+               lport->tt.seq_send = fc_seq_send;
 
-       if (!lp->tt.seq_els_rsp_send)
-               lp->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
+       if (!lport->tt.seq_els_rsp_send)
+               lport->tt.seq_els_rsp_send = fc_seq_els_rsp_send;
 
-       if (!lp->tt.exch_done)
-               lp->tt.exch_done = fc_exch_done;
+       if (!lport->tt.exch_done)
+               lport->tt.exch_done = fc_exch_done;
 
-       if (!lp->tt.exch_mgr_reset)
-               lp->tt.exch_mgr_reset = fc_exch_mgr_reset;
+       if (!lport->tt.exch_mgr_reset)
+               lport->tt.exch_mgr_reset = fc_exch_mgr_reset;
 
-       if (!lp->tt.seq_exch_abort)
-               lp->tt.seq_exch_abort = fc_seq_exch_abort;
+       if (!lport->tt.seq_exch_abort)
+               lport->tt.seq_exch_abort = fc_seq_exch_abort;
+
+       return 0;
+}
+EXPORT_SYMBOL(fc_exch_init);
+
+/**
+ * fc_setup_exch_mgr() - Setup an exchange manager
+ */
+int fc_setup_exch_mgr()
+{
+       fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
+                                        0, SLAB_HWCACHE_ALIGN, NULL);
+       if (!fc_em_cachep)
+               return -ENOMEM;
 
        /*
         * Initialize fc_cpu_mask and fc_cpu_order. The
@@ -2069,20 +2334,17 @@ int fc_exch_init(struct fc_lport *lp)
        }
        fc_cpu_mask--;
 
-       return 0;
-}
-EXPORT_SYMBOL(fc_exch_init);
-
-int fc_setup_exch_mgr(void)
-{
-       fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),
-                                        0, SLAB_HWCACHE_ALIGN, NULL);
-       if (!fc_em_cachep)
+       fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");
+       if (!fc_exch_workqueue)
                return -ENOMEM;
        return 0;
 }
 
-void fc_destroy_exch_mgr(void)
+/**
+ * fc_destroy_exch_mgr() - Destroy an exchange manager
+ */
+void fc_destroy_exch_mgr()
 {
+       destroy_workqueue(fc_exch_workqueue);
        kmem_cache_destroy(fc_em_cachep);
 }
index 59a4408..c4b58d0 100644 (file)
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
 
-MODULE_AUTHOR("Open-FCoE.org");
-MODULE_DESCRIPTION("libfc");
-MODULE_LICENSE("GPL v2");
+#include "fc_libfc.h"
 
-unsigned int fc_debug_logging;
-module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
-
-static struct kmem_cache *scsi_pkt_cachep;
+struct kmem_cache *scsi_pkt_cachep;
 
 /* SRB state definitions */
 #define FC_SRB_FREE            0               /* cmd is free */
@@ -58,7 +52,6 @@ static struct kmem_cache *scsi_pkt_cachep;
 #define FC_SRB_DISCONTIG       (1 << 4)        /* non-sequential data recvd */
 #define FC_SRB_COMPL           (1 << 5)        /* fc_io_compl has been run */
 #define FC_SRB_FCP_PROCESSING_TMO (1 << 6)     /* timer function processing */
-#define FC_SRB_NOMEM           (1 << 7)        /* dropped to out of mem */
 
 #define FC_SRB_READ            (1 << 1)
 #define FC_SRB_WRITE           (1 << 0)
@@ -73,10 +66,20 @@ static struct kmem_cache *scsi_pkt_cachep;
 #define CMD_SCSI_STATUS(Cmnd)      ((Cmnd)->SCp.Status)
 #define CMD_RESID_LEN(Cmnd)        ((Cmnd)->SCp.buffers_residual)
 
+/**
+ * struct fc_fcp_internal - FCP layer internal data
+ * @scsi_pkt_pool:  Memory pool to draw FCP packets from
+ * @scsi_pkt_queue: Current FCP packets
+ * @last_can_queue_ramp_down_time: ramp down time
+ * @last_can_queue_ramp_up_time: ramp up time
+ * @max_can_queue: max can_queue size
+ */
 struct fc_fcp_internal {
-       mempool_t       *scsi_pkt_pool;
+       mempool_t        *scsi_pkt_pool;
        struct list_head scsi_pkt_queue;
-       u8              throttled;
+       unsigned long last_can_queue_ramp_down_time;
+       unsigned long last_can_queue_ramp_up_time;
+       int max_can_queue;
 };
 
 #define fc_get_scsi_internal(x)        ((struct fc_fcp_internal *)(x)->scsi_priv)
@@ -90,9 +93,9 @@ static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
 static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
 static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
 static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
-static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp);
+static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *);
 static void fc_timeout_error(struct fc_fcp_pkt *);
-static void fc_fcp_timeout(unsigned long data);
+static void fc_fcp_timeout(unsigned long);
 static void fc_fcp_rec(struct fc_fcp_pkt *);
 static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
 static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
@@ -124,6 +127,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
 #define FC_SCSI_TM_TOV         (10 * HZ)
 #define FC_SCSI_REC_TOV                (2 * HZ)
 #define FC_HOST_RESET_TIMEOUT  (30 * HZ)
+#define FC_CAN_QUEUE_PERIOD    (60 * HZ)
 
 #define FC_MAX_ERROR_CNT       5
 #define FC_MAX_RECOV_RETRY     3
@@ -131,23 +135,22 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);
 #define FC_FCP_DFLT_QUEUE_DEPTH 32
 
 /**
- * fc_fcp_pkt_alloc - allocation routine for scsi_pkt packet
- * @lp:                fc lport struct
- * @gfp:       gfp flags for allocation
+ * fc_fcp_pkt_alloc() - Allocate a fcp_pkt
+ * @lport: The local port that the FCP packet is for
+ * @gfp:   GFP flags for allocation
  *
- * This is used by upper layer scsi driver.
- * Return Value : scsi_pkt structure or null on allocation failure.
- * Context     : call from process context. no locking required.
+ * Return value: fcp_pkt structure or null on allocation failure.
+ * Context:     Can be called from process context, no lock is required.
  */
-static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
+static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)
 {
-       struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+       struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
        struct fc_fcp_pkt *fsp;
 
        fsp = mempool_alloc(si->scsi_pkt_pool, gfp);
        if (fsp) {
                memset(fsp, 0, sizeof(*fsp));
-               fsp->lp = lp;
+               fsp->lp = lport;
                atomic_set(&fsp->ref_cnt, 1);
                init_timer(&fsp->timer);
                INIT_LIST_HEAD(&fsp->list);
@@ -157,12 +160,11 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lp, gfp_t gfp)
 }
 
 /**
- * fc_fcp_pkt_release() - release hold on scsi_pkt packet
- * @fsp:       fcp packet struct
+ * fc_fcp_pkt_release() - Release hold on a fcp_pkt
+ * @fsp: The FCP packet to be released
  *
- * This is used by upper layer scsi driver.
- * Context     : call from process  and interrupt context.
- *               no locking required
+ * Context: Can be called from process or interrupt context,
+ *         no lock is required.
  */
 static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
 {
@@ -173,20 +175,25 @@ static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp)
        }
 }
 
+/**
+ * fc_fcp_pkt_hold() - Hold a fcp_pkt
+ * @fsp: The FCP packet to be held
+ */
 static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp)
 {
        atomic_inc(&fsp->ref_cnt);
 }
 
 /**
- * fc_fcp_pkt_destory() - release hold on scsi_pkt packet
- * @seq:               exchange sequence
- * @fsp:       fcp packet struct
+ * fc_fcp_pkt_destory() - Release hold on a fcp_pkt
+ * @seq: The sequence that the FCP packet is on (required by destructor API)
+ * @fsp: The FCP packet to be released
+ *
+ * This routine is called by a destructor callback in the exch_seq_send()
+ * routine of the libfc Transport Template. The 'struct fc_seq' is a required
+ * argument even though it is not used by this routine.
  *
- * Release hold on scsi_pkt packet set to keep scsi_pkt
- * till EM layer exch resource is not freed.
- * Context     : called from from EM layer.
- *               no locking required
+ * Context: No locking required.
  */
 static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
 {
@@ -194,10 +201,10 @@ static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp)
 }
 
 /**
- * fc_fcp_lock_pkt() - lock a packet and get a ref to it.
- * @fsp:       fcp packet
+ * fc_fcp_lock_pkt() - Lock a fcp_pkt and increase its reference count
+ * @fsp: The FCP packet to be locked and incremented
  *
- * We should only return error if we return a command to scsi-ml before
+ * We should only return error if we return a command to SCSI-ml before
  * getting a response. This can happen in cases where we send a abort, but
  * do not wait for the response and the abort and command can be passing
  * each other on the wire/network-layer.
@@ -222,18 +229,33 @@ static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp)
        return 0;
 }
 
+/**
+ * fc_fcp_unlock_pkt() - Release a fcp_pkt's lock and decrement its
+ *                      reference count
+ * @fsp: The FCP packet to be unlocked and decremented
+ */
 static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)
 {
        spin_unlock_bh(&fsp->scsi_pkt_lock);
        fc_fcp_pkt_release(fsp);
 }
 
+/**
+ * fc_fcp_timer_set() - Start a timer for a fcp_pkt
+ * @fsp:   The FCP packet to start a timer for
+ * @delay: The timeout period for the timer
+ */
 static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)
 {
        if (!(fsp->state & FC_SRB_COMPL))
                mod_timer(&fsp->timer, jiffies + delay);
 }
 
+/**
+ * fc_fcp_send_abort() - Send an abort for exchanges associated with a
+ *                      fcp_pkt
+ * @fsp: The FCP packet to abort exchanges on
+ */
 static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
 {
        if (!fsp->seq_ptr)
@@ -243,9 +265,14 @@ static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)
        return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);
 }
 
-/*
- * Retry command.
- * An abort isn't needed.
+/**
+ * fc_fcp_retry_cmd() - Retry a fcp_pkt
+ * @fsp: The FCP packet to be retried
+ *
+ * Sets the status code to be FC_ERROR and then calls
+ * fc_fcp_complete_locked() which in turn calls fc_io_compl().
+ * fc_io_compl() will notify the SCSI-ml that the I/O is done.
+ * The SCSI-ml will retry the command.
  */
 static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
 {
@@ -260,64 +287,146 @@ static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp)
        fc_fcp_complete_locked(fsp);
 }
 
-/*
- * fc_fcp_ddp_setup - calls to LLD's ddp_setup to set up DDP
- * transfer for a read I/O indicated by the fc_fcp_pkt.
- * @fsp: ptr to the fc_fcp_pkt
- *
- * This is called in exch_seq_send() when we have a newly allocated
- * exchange with a valid exchange id to setup ddp.
- *
- * returns: none
+/**
+ * fc_fcp_ddp_setup() - Calls a LLD's ddp_setup routine to set up DDP context
+ * @fsp: The FCP packet that will manage the DDP frames
+ * @xid: The XID that will be used for the DDP exchange
  */
 void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
 
        if (!fsp)
                return;
 
-       lp = fsp->lp;
+       lport = fsp->lp;
        if ((fsp->req_flags & FC_SRB_READ) &&
-           (lp->lro_enabled) && (lp->tt.ddp_setup)) {
-               if (lp->tt.ddp_setup(lp, xid, scsi_sglist(fsp->cmd),
-                                    scsi_sg_count(fsp->cmd)))
+           (lport->lro_enabled) && (lport->tt.ddp_setup)) {
+               if (lport->tt.ddp_setup(lport, xid, scsi_sglist(fsp->cmd),
+                                       scsi_sg_count(fsp->cmd)))
                        fsp->xfer_ddp = xid;
        }
 }
-EXPORT_SYMBOL(fc_fcp_ddp_setup);
 
-/*
- * fc_fcp_ddp_done - calls to LLD's ddp_done to release any
- * DDP related resources for this I/O if it is initialized
- * as a ddp transfer
- * @fsp: ptr to the fc_fcp_pkt
- *
- * returns: none
+/**
+ * fc_fcp_ddp_done() - Calls a LLD's ddp_done routine to release any
+ *                    DDP related resources for a fcp_pkt
+ * @fsp: The FCP packet that DDP had been used on
  */
 static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
 
        if (!fsp)
                return;
 
-       lp = fsp->lp;
-       if (fsp->xfer_ddp && lp->tt.ddp_done) {
-               fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp);
-               fsp->xfer_ddp = 0;
+       if (fsp->xfer_ddp == FC_XID_UNKNOWN)
+               return;
+
+       lport = fsp->lp;
+       if (lport->tt.ddp_done) {
+               fsp->xfer_len = lport->tt.ddp_done(lport, fsp->xfer_ddp);
+               fsp->xfer_ddp = FC_XID_UNKNOWN;
        }
 }
 
+/**
+ * fc_fcp_can_queue_ramp_up() - increases can_queue
+ * @lport: lport to ramp up can_queue
+ *
+ * Locking notes: Called with Scsi_Host lock held
+ */
+static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)
+{
+       struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+       int can_queue;
+
+       if (si->last_can_queue_ramp_up_time &&
+           (time_before(jiffies, si->last_can_queue_ramp_up_time +
+                        FC_CAN_QUEUE_PERIOD)))
+               return;
+
+       if (time_before(jiffies, si->last_can_queue_ramp_down_time +
+                       FC_CAN_QUEUE_PERIOD))
+               return;
+
+       si->last_can_queue_ramp_up_time = jiffies;
+
+       can_queue = lport->host->can_queue << 1;
+       if (can_queue >= si->max_can_queue) {
+               can_queue = si->max_can_queue;
+               si->last_can_queue_ramp_down_time = 0;
+       }
+       lport->host->can_queue = can_queue;
+       shost_printk(KERN_ERR, lport->host, "libfc: increased "
+                    "can_queue to %d.\n", can_queue);
+}
+
+/**
+ * fc_fcp_can_queue_ramp_down() - reduces can_queue
+ * @lport: lport to reduce can_queue
+ *
+ * If we are getting memory allocation failures, then we may
+ * be trying to execute too many commands. We let the running
+ * commands complete or timeout, then try again with a reduced
+ * can_queue. Eventually we will hit the point where we run
+ * on all reserved structs.
+ *
+ * Locking notes: Called with Scsi_Host lock held
+ */
+static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)
+{
+       struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
+       int can_queue;
+
+       if (si->last_can_queue_ramp_down_time &&
+           (time_before(jiffies, si->last_can_queue_ramp_down_time +
+                        FC_CAN_QUEUE_PERIOD)))
+               return;
+
+       si->last_can_queue_ramp_down_time = jiffies;
+
+       can_queue = lport->host->can_queue;
+       can_queue >>= 1;
+       if (!can_queue)
+               can_queue = 1;
+       lport->host->can_queue = can_queue;
+       shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"
+                    "Reducing can_queue to %d.\n", can_queue);
+}
 
 /*
- * Receive SCSI data from target.
- * Called after receiving solicited data.
+ * fc_fcp_frame_alloc() -  Allocates fc_frame structure and buffer.
+ * @lport:     fc lport struct
+ * @len:       payload length
+ *
+ * Allocates fc_frame structure and buffer but if fails to allocate
+ * then reduce can_queue.
+ */
+static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,
+                                                 size_t len)
+{
+       struct fc_frame *fp;
+       unsigned long flags;
+
+       fp = fc_frame_alloc(lport, len);
+       if (!fp) {
+               spin_lock_irqsave(lport->host->host_lock, flags);
+               fc_fcp_can_queue_ramp_down(lport);
+               spin_unlock_irqrestore(lport->host->host_lock, flags);
+       }
+       return fp;
+}
+
+/**
+ * fc_fcp_recv_data() - Handler for receiving SCSI-FCP data from a target
+ * @fsp: The FCP packet the data is on
+ * @fp:         The data frame
  */
 static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 {
        struct scsi_cmnd *sc = fsp->cmd;
-       struct fc_lport *lp = fsp->lp;
+       struct fc_lport *lport = fsp->lp;
        struct fcoe_dev_stats *stats;
        struct fc_frame_header *fh;
        size_t start_offset;
@@ -327,7 +436,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
        size_t len;
        void *buf;
        struct scatterlist *sg;
-       size_t remaining;
+       u32 nents;
 
        fh = fc_frame_header_get(fp);
        offset = ntohl(fh->fh_parm_offset);
@@ -351,65 +460,29 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
        if (offset != fsp->xfer_len)
                fsp->state |= FC_SRB_DISCONTIG;
 
-       crc = 0;
-       if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
-               crc = crc32(~0, (u8 *) fh, sizeof(*fh));
-
        sg = scsi_sglist(sc);
-       remaining = len;
-
-       while (remaining > 0 && sg) {
-               size_t off;
-               void *page_addr;
-               size_t sg_bytes;
+       nents = scsi_sg_count(sc);
 
-               if (offset >= sg->length) {
-                       offset -= sg->length;
-                       sg = sg_next(sg);
-                       continue;
-               }
-               sg_bytes = min(remaining, sg->length - offset);
-
-               /*
-                * The scatterlist item may be bigger than PAGE_SIZE,
-                * but we are limited to mapping PAGE_SIZE at a time.
-                */
-               off = offset + sg->offset;
-               sg_bytes = min(sg_bytes, (size_t)
-                              (PAGE_SIZE - (off & ~PAGE_MASK)));
-               page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
-                                       KM_SOFTIRQ0);
-               if (!page_addr)
-                       break;          /* XXX panic? */
-
-               if (fr_flags(fp) & FCPHF_CRC_UNCHECKED)
-                       crc = crc32(crc, buf, sg_bytes);
-               memcpy((char *)page_addr + (off & ~PAGE_MASK), buf,
-                      sg_bytes);
-
-               kunmap_atomic(page_addr, KM_SOFTIRQ0);
-               buf += sg_bytes;
-               offset += sg_bytes;
-               remaining -= sg_bytes;
-               copy_len += sg_bytes;
-       }
-
-       if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+       if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
+               copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
+                                                   &offset, KM_SOFTIRQ0, NULL);
+       } else {
+               crc = crc32(~0, (u8 *) fh, sizeof(*fh));
+               copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents,
+                                                   &offset, KM_SOFTIRQ0, &crc);
                buf = fc_frame_payload_get(fp, 0);
-               if (len % 4) {
+               if (len % 4)
                        crc = crc32(crc, buf + len, 4 - (len % 4));
-                       len += 4 - (len % 4);
-               }
 
                if (~crc != le32_to_cpu(fr_crc(fp))) {
 crc_err:
-                       stats = fc_lport_get_stats(lp);
+                       stats = fc_lport_get_stats(lport);
                        stats->ErrorFrames++;
                        /* FIXME - per cpu count, not total count! */
                        if (stats->InvalidCRCCount++ < 5)
                                printk(KERN_WARNING "libfc: CRC error on data "
                                       "frame for port (%6x)\n",
-                                      fc_host_port_id(lp->host));
+                                      fc_host_port_id(lport->host));
                        /*
                         * Assume the frame is total garbage.
                         * We may have copied it over the good part
@@ -437,18 +510,17 @@ crc_err:
 }
 
 /**
- * fc_fcp_send_data() -  Send SCSI data to target.
- * @fsp: ptr to fc_fcp_pkt
- * @sp: ptr to this sequence
- * @offset: starting offset for this data request
- * @seq_blen: the burst length for this data request
+ * fc_fcp_send_data() - Send SCSI data to a target
+ * @fsp:      The FCP packet the data is on
+ * @sp:              The sequence the data is to be sent on
+ * @offset:   The starting offset for this data request
+ * @seq_blen: The burst length for this data request
  *
  * Called after receiving a Transfer Ready data descriptor.
- * if LLD is capable of seq offload then send down seq_blen
- * size of data in single frame, otherwise send multiple FC
- * frames of max FC frame payload supported by target port.
- *
- * Returns : 0 for success.
+ * If the LLD is capable of sequence offload then send down the
+ * seq_blen ammount of data in single frame, otherwise send
+ * multiple frames of the maximum frame payload supported by
+ * the target port.
  */
 static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
                            size_t offset, size_t seq_blen)
@@ -457,16 +529,18 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
        struct scsi_cmnd *sc;
        struct scatterlist *sg;
        struct fc_frame *fp = NULL;
-       struct fc_lport *lp = fsp->lp;
+       struct fc_lport *lport = fsp->lp;
+       struct page *page;
        size_t remaining;
        size_t t_blen;
        size_t tlen;
        size_t sg_bytes;
        size_t frame_offset, fh_parm_offset;
+       size_t off;
        int error;
        void *data = NULL;
        void *page_addr;
-       int using_sg = lp->sg_supp;
+       int using_sg = lport->sg_supp;
        u32 f_ctl;
 
        WARN_ON(seq_blen <= 0);
@@ -488,10 +562,10 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
         * to max FC frame payload previously set in fsp->max_payload.
         */
        t_blen = fsp->max_payload;
-       if (lp->seq_offload) {
-               t_blen = min(seq_blen, (size_t)lp->lso_max);
+       if (lport->seq_offload) {
+               t_blen = min(seq_blen, (size_t)lport->lso_max);
                FC_FCP_DBG(fsp, "fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n",
-                          fsp, seq_blen, lp->lso_max, t_blen);
+                          fsp, seq_blen, lport->lso_max, t_blen);
        }
 
        WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
@@ -503,7 +577,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
        remaining = seq_blen;
        fh_parm_offset = frame_offset = offset;
        tlen = 0;
-       seq = lp->tt.seq_start_next(seq);
+       seq = lport->tt.seq_start_next(seq);
        f_ctl = FC_FC_REL_OFF;
        WARN_ON(!seq);
 
@@ -525,43 +599,34 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
                         */
                        if (tlen % 4)
                                using_sg = 0;
-                       if (using_sg) {
-                               fp = _fc_frame_alloc(lp, 0);
-                               if (!fp)
-                                       return -ENOMEM;
-                       } else {
-                               fp = fc_frame_alloc(lp, tlen);
-                               if (!fp)
-                                       return -ENOMEM;
+                       fp = fc_frame_alloc(lport, using_sg ? 0 : tlen);
+                       if (!fp)
+                               return -ENOMEM;
 
-                               data = (void *)(fr_hdr(fp)) +
-                                       sizeof(struct fc_frame_header);
-                       }
+                       data = fc_frame_header_get(fp) + 1;
                        fh_parm_offset = frame_offset;
                        fr_max_payload(fp) = fsp->max_payload;
                }
+
+               off = offset + sg->offset;
                sg_bytes = min(tlen, sg->length - offset);
+               sg_bytes = min(sg_bytes,
+                              (size_t) (PAGE_SIZE - (off & ~PAGE_MASK)));
+               page = sg_page(sg) + (off >> PAGE_SHIFT);
                if (using_sg) {
-                       get_page(sg_page(sg));
+                       get_page(page);
                        skb_fill_page_desc(fp_skb(fp),
                                           skb_shinfo(fp_skb(fp))->nr_frags,
-                                          sg_page(sg), sg->offset + offset,
-                                          sg_bytes);
+                                          page, off & ~PAGE_MASK, sg_bytes);
                        fp_skb(fp)->data_len += sg_bytes;
                        fr_len(fp) += sg_bytes;
                        fp_skb(fp)->truesize += PAGE_SIZE;
                } else {
-                       size_t off = offset + sg->offset;
-
                        /*
                         * The scatterlist item may be bigger than PAGE_SIZE,
                         * but we must not cross pages inside the kmap.
                         */
-                       sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE -
-                                                          (off & ~PAGE_MASK)));
-                       page_addr = kmap_atomic(sg_page(sg) +
-                                               (off >> PAGE_SHIFT),
-                                               KM_SOFTIRQ0);
+                       page_addr = kmap_atomic(page, KM_SOFTIRQ0);
                        memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),
                               sg_bytes);
                        kunmap_atomic(page_addr, KM_SOFTIRQ0);
@@ -572,7 +637,8 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
                tlen -= sg_bytes;
                remaining -= sg_bytes;
 
-               if (tlen)
+               if ((skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN) &&
+                   (tlen))
                        continue;
 
                /*
@@ -589,7 +655,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
                /*
                 * send fragment using for a sequence.
                 */
-               error = lp->tt.seq_send(lp, seq, fp);
+               error = lport->tt.seq_send(lport, seq, fp);
                if (error) {
                        WARN_ON(1);             /* send error should be rare */
                        fc_fcp_retry_cmd(fsp);
@@ -601,6 +667,11 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
        return 0;
 }
 
+/**
+ * fc_fcp_abts_resp() - Send an ABTS response
+ * @fsp: The FCP packet that is being aborted
+ * @fp:         The response frame
+ */
 static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 {
        int ba_done = 1;
@@ -637,46 +708,13 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 }
 
 /**
- * fc_fcp_reduce_can_queue() - drop can_queue
- * @lp: lport to drop queueing for
- *
- * If we are getting memory allocation failures, then we may
- * be trying to execute too many commands. We let the running
- * commands complete or timeout, then try again with a reduced
- * can_queue. Eventually we will hit the point where we run
- * on all reserved structs.
- */
-static void fc_fcp_reduce_can_queue(struct fc_lport *lp)
-{
-       struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
-       unsigned long flags;
-       int can_queue;
-
-       spin_lock_irqsave(lp->host->host_lock, flags);
-       if (si->throttled)
-               goto done;
-       si->throttled = 1;
-
-       can_queue = lp->host->can_queue;
-       can_queue >>= 1;
-       if (!can_queue)
-               can_queue = 1;
-       lp->host->can_queue = can_queue;
-       shost_printk(KERN_ERR, lp->host, "libfc: Could not allocate frame.\n"
-                    "Reducing can_queue to %d.\n", can_queue);
-done:
-       spin_unlock_irqrestore(lp->host->host_lock, flags);
-}
-
-/**
- * fc_fcp_recv() - Reveive FCP frames
+ * fc_fcp_recv() - Reveive an FCP frame
  * @seq: The sequence the frame is on
- * @fp: The FC frame
+ * @fp:         The received frame
  * @arg: The related FCP packet
  *
- * Return   : None
- * Context  : called from Soft IRQ context
- *           can not called holding list lock
+ * Context: Called from Soft IRQ context. Can not be called
+ *         holding the FCP packet list lock.
  */
 static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
@@ -687,8 +725,10 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
        u8 r_ctl;
        int rc = 0;
 
-       if (IS_ERR(fp))
-               goto errout;
+       if (IS_ERR(fp)) {
+               fc_fcp_error(fsp, fp);
+               return;
+       }
 
        fh = fc_frame_header_get(fp);
        r_ctl = fh->fh_r_ctl;
@@ -721,8 +761,6 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
                                      (size_t) ntohl(dd->ft_burst_len));
                if (!rc)
                        seq->rec_data = fsp->xfer_len;
-               else if (rc == -ENOMEM)
-                       fsp->state |= FC_SRB_NOMEM;
        } else if (r_ctl == FC_RCTL_DD_SOL_DATA) {
                /*
                 * received a DATA frame
@@ -742,13 +780,13 @@ unlock:
        fc_fcp_unlock_pkt(fsp);
 out:
        fc_frame_free(fp);
-errout:
-       if (IS_ERR(fp))
-               fc_fcp_error(fsp, fp);
-       else if (rc == -ENOMEM)
-               fc_fcp_reduce_can_queue(lport);
 }
 
+/**
+ * fc_fcp_resp() - Handler for FCP responses
+ * @fsp: The FCP packet the response is for
+ * @fp:         The response frame
+ */
 static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 {
        struct fc_frame_header *fh;
@@ -862,15 +900,16 @@ err:
 }
 
 /**
- * fc_fcp_complete_locked() - complete processing of a fcp packet
- * @fsp:       fcp packet
+ * fc_fcp_complete_locked() - Complete processing of a fcp_pkt with the
+ *                           fcp_pkt lock held
+ * @fsp: The FCP packet to be completed
  *
  * This function may sleep if a timer is pending. The packet lock must be
  * held, and the host lock must not be held.
  */
 static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
 {
-       struct fc_lport *lp = fsp->lp;
+       struct fc_lport *lport = fsp->lp;
        struct fc_seq *seq;
        struct fc_exch *ep;
        u32 f_ctl;
@@ -901,8 +940,8 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
                        struct fc_frame *conf_frame;
                        struct fc_seq *csp;
 
-                       csp = lp->tt.seq_start_next(seq);
-                       conf_frame = fc_frame_alloc(fsp->lp, 0);
+                       csp = lport->tt.seq_start_next(seq);
+                       conf_frame = fc_fcp_frame_alloc(fsp->lp, 0);
                        if (conf_frame) {
                                f_ctl = FC_FC_SEQ_INIT;
                                f_ctl |= FC_FC_LAST_SEQ | FC_FC_END_SEQ;
@@ -910,43 +949,48 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)
                                fc_fill_fc_hdr(conf_frame, FC_RCTL_DD_SOL_CTL,
                                               ep->did, ep->sid,
                                               FC_TYPE_FCP, f_ctl, 0);
-                               lp->tt.seq_send(lp, csp, conf_frame);
+                               lport->tt.seq_send(lport, csp, conf_frame);
                        }
                }
-               lp->tt.exch_done(seq);
+               lport->tt.exch_done(seq);
        }
        fc_io_compl(fsp);
 }
 
+/**
+ * fc_fcp_cleanup_cmd() - Cancel the active exchange on a fcp_pkt
+ * @fsp:   The FCP packet whose exchanges should be canceled
+ * @error: The reason for the cancellation
+ */
 static void fc_fcp_cleanup_cmd(struct fc_fcp_pkt *fsp, int error)
 {
-       struct fc_lport *lp = fsp->lp;
+       struct fc_lport *lport = fsp->lp;
 
        if (fsp->seq_ptr) {
-               lp->tt.exch_done(fsp->seq_ptr);
+               lport->tt.exch_done(fsp->seq_ptr);
                fsp->seq_ptr = NULL;
        }
        fsp->status_code = error;
 }
 
 /**
- * fc_fcp_cleanup_each_cmd() - Cleanup active commads
- * @lp:                logical port
- * @id:                target id
- * @lun:       lun
- * @error:     fsp status code
+ * fc_fcp_cleanup_each_cmd() - Cancel all exchanges on a local port
+ * @lport: The local port whose exchanges should be canceled
+ * @id:           The target's ID
+ * @lun:   The LUN
+ * @error: The reason for cancellation
  *
  * If lun or id is -1, they are ignored.
  */
-static void fc_fcp_cleanup_each_cmd(struct fc_lport *lp, unsigned int id,
+static void fc_fcp_cleanup_each_cmd(struct fc_lport *lport, unsigned int id,
                                    unsigned int lun, int error)
 {
-       struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+       struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
        struct fc_fcp_pkt *fsp;
        struct scsi_cmnd *sc_cmd;
        unsigned long flags;
 
-       spin_lock_irqsave(lp->host->host_lock, flags);
+       spin_lock_irqsave(lport->host->host_lock, flags);
 restart:
        list_for_each_entry(fsp, &si->scsi_pkt_queue, list) {
                sc_cmd = fsp->cmd;
@@ -957,7 +1001,7 @@ restart:
                        continue;
 
                fc_fcp_pkt_hold(fsp);
-               spin_unlock_irqrestore(lp->host->host_lock, flags);
+               spin_unlock_irqrestore(lport->host->host_lock, flags);
 
                if (!fc_fcp_lock_pkt(fsp)) {
                        fc_fcp_cleanup_cmd(fsp, error);
@@ -966,35 +1010,36 @@ restart:
                }
 
                fc_fcp_pkt_release(fsp);
-               spin_lock_irqsave(lp->host->host_lock, flags);
+               spin_lock_irqsave(lport->host->host_lock, flags);
                /*
                 * while we dropped the lock multiple pkts could
                 * have been released, so we have to start over.
                 */
                goto restart;
        }
-       spin_unlock_irqrestore(lp->host->host_lock, flags);
+       spin_unlock_irqrestore(lport->host->host_lock, flags);
 }
 
-static void fc_fcp_abort_io(struct fc_lport *lp)
+/**
+ * fc_fcp_abort_io() - Abort all FCP-SCSI exchanges on a local port
+ * @lport: The local port whose exchanges are to be aborted
+ */
+static void fc_fcp_abort_io(struct fc_lport *lport)
 {
-       fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_HRD_ERROR);
+       fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_HRD_ERROR);
 }
 
 /**
- * fc_fcp_pkt_send() - send a fcp packet to the lower level.
- * @lp:                fc lport
- * @fsp:       fc packet.
+ * fc_fcp_pkt_send() - Send a fcp_pkt
+ * @lport: The local port to send the FCP packet on
+ * @fsp:   The FCP packet to send
  *
- * This is called by upper layer protocol.
- * Return   : zero for success and -1 for failure
- * Context  : called from queuecommand which can be called from process
- *           or scsi soft irq.
- * Locks    : called with the host lock and irqs disabled.
+ * Return:  Zero for success and -1 for failure
+ * Locks:   Called with the host lock and irqs disabled.
  */
-static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
 {
-       struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+       struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
        int rc;
 
        fsp->cmd->SCp.ptr = (char *)fsp;
@@ -1006,16 +1051,22 @@ static int fc_fcp_pkt_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
        memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
        list_add_tail(&fsp->list, &si->scsi_pkt_queue);
 
-       spin_unlock_irq(lp->host->host_lock);
-       rc = lp->tt.fcp_cmd_send(lp, fsp, fc_fcp_recv);
-       spin_lock_irq(lp->host->host_lock);
+       spin_unlock_irq(lport->host->host_lock);
+       rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);
+       spin_lock_irq(lport->host->host_lock);
        if (rc)
                list_del(&fsp->list);
 
        return rc;
 }
 
-static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+/**
+ * fc_fcp_cmd_send() - Send a FCP command
+ * @lport: The local port to send the command on
+ * @fsp:   The FCP packet the command is on
+ * @resp:  The handler for the response
+ */
+static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
                           void (*resp)(struct fc_seq *,
                                        struct fc_frame *fp,
                                        void *arg))
@@ -1023,14 +1074,14 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
        struct fc_frame *fp;
        struct fc_seq *seq;
        struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport_libfc_priv *rpriv;
        const size_t len = sizeof(fsp->cdb_cmd);
        int rc = 0;
 
        if (fc_fcp_lock_pkt(fsp))
                return 0;
 
-       fp = fc_frame_alloc(lp, sizeof(fsp->cdb_cmd));
+       fp = fc_fcp_frame_alloc(lport, sizeof(fsp->cdb_cmd));
        if (!fp) {
                rc = -1;
                goto unlock;
@@ -1040,15 +1091,15 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
        fr_fsp(fp) = fsp;
        rport = fsp->rport;
        fsp->max_payload = rport->maxframe_size;
-       rp = rport->dd_data;
+       rpriv = rport->dd_data;
 
        fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
-                      fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+                      fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP,
                       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
 
-       seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
+       seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy,
+                                     fsp, 0);
        if (!seq) {
-               fc_frame_free(fp);
                rc = -1;
                goto unlock;
        }
@@ -1065,8 +1116,10 @@ unlock:
        return rc;
 }
 
-/*
- * transport error handler
+/**
+ * fc_fcp_error() - Handler for FCP layer errors
+ * @fsp: The FCP packet the error is on
+ * @fp:         The frame that has errored
  */
 static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 {
@@ -1091,11 +1144,13 @@ unlock:
        fc_fcp_unlock_pkt(fsp);
 }
 
-/*
- * Scsi abort handler- calls to send an abort
- * and then wait for abort completion
+/**
+ * fc_fcp_pkt_abort() - Abort a fcp_pkt
+ * @fsp:   The FCP packet to abort on
+ *
+ * Called to send an abort and then wait for abort completion
  */
-static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
 {
        int rc = FAILED;
 
@@ -1122,14 +1177,15 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
        return rc;
 }
 
-/*
- * Retry LUN reset after resource allocation failed.
+/**
+ * fc_lun_reset_send() - Send LUN reset command
+ * @data: The FCP packet that identifies the LUN to be reset
  */
 static void fc_lun_reset_send(unsigned long data)
 {
        struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
-       struct fc_lport *lp = fsp->lp;
-       if (lp->tt.fcp_cmd_send(lp, fsp, fc_tm_done)) {
+       struct fc_lport *lport = fsp->lp;
+       if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {
                if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)
                        return;
                if (fc_fcp_lock_pkt(fsp))
@@ -1140,11 +1196,15 @@ static void fc_lun_reset_send(unsigned long data)
        }
 }
 
-/*
- * Scsi device reset handler- send a LUN RESET to the device
- * and wait for reset reply
+/**
+ * fc_lun_reset() - Send a LUN RESET command to a device
+ *                 and wait for the reply
+ * @lport: The local port to sent the comand on
+ * @fsp:   The FCP packet that identifies the LUN to be reset
+ * @id:           The SCSI command ID
+ * @lun:   The LUN ID to be reset
  */
-static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
+static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
                        unsigned int id, unsigned int lun)
 {
        int rc;
@@ -1172,14 +1232,14 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
 
        spin_lock_bh(&fsp->scsi_pkt_lock);
        if (fsp->seq_ptr) {
-               lp->tt.exch_done(fsp->seq_ptr);
+               lport->tt.exch_done(fsp->seq_ptr);
                fsp->seq_ptr = NULL;
        }
        fsp->wait_for_comp = 0;
        spin_unlock_bh(&fsp->scsi_pkt_lock);
 
        if (!rc) {
-               FC_SCSI_DBG(lp, "lun reset failed\n");
+               FC_SCSI_DBG(lport, "lun reset failed\n");
                return FAILED;
        }
 
@@ -1187,13 +1247,16 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
        if (fsp->cdb_status != FCP_TMF_CMPL)
                return FAILED;
 
-       FC_SCSI_DBG(lp, "lun reset to lun %u completed\n", lun);
-       fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED);
+       FC_SCSI_DBG(lport, "lun reset to lun %u completed\n", lun);
+       fc_fcp_cleanup_each_cmd(lport, id, lun, FC_CMD_ABORTED);
        return SUCCESS;
 }
 
-/*
- * Task Managment response handler
+/**
+ * fc_tm_done() - Task Managment response handler
+ * @seq: The sequence that the response is on
+ * @fp:         The response frame
+ * @arg: The FCP packet the response is for
  */
 static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
@@ -1230,34 +1293,31 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)
        fc_fcp_unlock_pkt(fsp);
 }
 
-static void fc_fcp_cleanup(struct fc_lport *lp)
+/**
+ * fc_fcp_cleanup() - Cleanup all FCP exchanges on a local port
+ * @lport: The local port to be cleaned up
+ */
+static void fc_fcp_cleanup(struct fc_lport *lport)
 {
-       fc_fcp_cleanup_each_cmd(lp, -1, -1, FC_ERROR);
+       fc_fcp_cleanup_each_cmd(lport, -1, -1, FC_ERROR);
 }
 
-/*
- * fc_fcp_timeout: called by OS timer function.
- *
- * The timer has been inactivated and must be reactivated if desired
- * using fc_fcp_timer_set().
- *
- * Algorithm:
- *
- * If REC is supported, just issue it, and return.  The REC exchange will
- * complete or time out, and recovery can continue at that point.
- *
- * Otherwise, if the response has been received without all the data,
- * it has been ER_TIMEOUT since the response was received.
+/**
+ * fc_fcp_timeout() - Handler for fcp_pkt timeouts
+ * @data: The FCP packet that has timed out
  *
- * If the response has not been received,
- * we see if data was received recently.  If it has been, we continue waiting,
- * otherwise, we abort the command.
+ * If REC is supported then just issue it and return. The REC exchange will
+ * complete or time out and recovery can continue at that point. Otherwise,
+ * if the response has been received without all the data it has been
+ * ER_TIMEOUT since the response was received. If the response has not been
+ * received we see if data was received recently. If it has been then we
+ * continue waiting, otherwise, we abort the command.
  */
 static void fc_fcp_timeout(unsigned long data)
 {
        struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;
        struct fc_rport *rport = fsp->rport;
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
+       struct fc_rport_libfc_priv *rpriv = rport->dd_data;
 
        if (fc_fcp_lock_pkt(fsp))
                return;
@@ -1267,7 +1327,7 @@ static void fc_fcp_timeout(unsigned long data)
 
        fsp->state |= FC_SRB_FCP_PROCESSING_TMO;
 
-       if (rp->flags & FC_RP_FLAGS_REC_SUPPORTED)
+       if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)
                fc_fcp_rec(fsp);
        else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2),
                               jiffies))
@@ -1281,39 +1341,40 @@ unlock:
        fc_fcp_unlock_pkt(fsp);
 }
 
-/*
- * Send a REC ELS request
+/**
+ * fc_fcp_rec() - Send a REC ELS request
+ * @fsp: The FCP packet to send the REC request on
  */
 static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fc_frame *fp;
        struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport_libfc_priv *rpriv;
 
-       lp = fsp->lp;
+       lport = fsp->lp;
        rport = fsp->rport;
-       rp = rport->dd_data;
-       if (!fsp->seq_ptr || rp->rp_state != RPORT_ST_READY) {
+       rpriv = rport->dd_data;
+       if (!fsp->seq_ptr || rpriv->rp_state != RPORT_ST_READY) {
                fsp->status_code = FC_HRD_ERROR;
                fsp->io_status = 0;
                fc_fcp_complete_locked(fsp);
                return;
        }
-       fp = fc_frame_alloc(lp, sizeof(struct fc_els_rec));
+       fp = fc_fcp_frame_alloc(lport, sizeof(struct fc_els_rec));
        if (!fp)
                goto retry;
 
        fr_seq(fp) = fsp->seq_ptr;
        fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
-                      fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
+                      fc_host_port_id(rpriv->local_port->host), FC_TYPE_ELS,
                       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
-       if (lp->tt.elsct_send(lp, rport->port_id, fp, ELS_REC, fc_fcp_rec_resp,
-                             fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
+       if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC,
+                                fc_fcp_rec_resp, fsp,
+                                jiffies_to_msecs(FC_SCSI_REC_TOV))) {
                fc_fcp_pkt_hold(fsp);           /* hold while REC outstanding */
                return;
        }
-       fc_frame_free(fp);
 retry:
        if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
                fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
@@ -1321,12 +1382,16 @@ retry:
                fc_timeout_error(fsp);
 }
 
-/*
- * Receive handler for REC ELS frame
- * if it is a reject then let the scsi layer to handle
- * the timeout. if it is a LS_ACC then if the io was not completed
- * then set the timeout and return otherwise complete the exchange
- * and tell the scsi layer to restart the I/O.
+/**
+ * fc_fcp_rec_resp() - Handler for REC ELS responses
+ * @seq: The sequence the response is on
+ * @fp:         The response frame
+ * @arg: The FCP packet the response is on
+ *
+ * If the response is a reject then the scsi layer will handle
+ * the timeout. If the response is a LS_ACC then if the I/O was not completed
+ * set the timeout and return. If the I/O was completed then complete the
+ * exchange and tell the SCSI layer.
  */
 static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
@@ -1338,7 +1403,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
        u32 offset;
        enum dma_data_direction data_dir;
        enum fc_rctl r_ctl;
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport_libfc_priv *rpriv;
 
        if (IS_ERR(fp)) {
                fc_fcp_rec_error(fsp, fp);
@@ -1361,13 +1426,13 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
                        /* fall through */
                case ELS_RJT_UNSUP:
                        FC_FCP_DBG(fsp, "device does not support REC\n");
-                       rp = fsp->rport->dd_data;
+                       rpriv = fsp->rport->dd_data;
                        /*
                         * if we do not spport RECs or got some bogus
                         * reason then resetup timer so we check for
                         * making progress.
                         */
-                       rp->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
+                       rpriv->flags &= ~FC_RP_FLAGS_REC_SUPPORTED;
                        fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);
                        break;
                case ELS_RJT_LOGIC:
@@ -1464,8 +1529,10 @@ out:
        fc_frame_free(fp);
 }
 
-/*
- * Handle error response or timeout for REC exchange.
+/**
+ * fc_fcp_rec_error() - Handler for REC errors
+ * @fsp: The FCP packet the error is on
+ * @fp:         The REC frame
  */
 static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 {
@@ -1504,10 +1571,9 @@ out:
        fc_fcp_pkt_release(fsp);        /* drop hold for outstanding REC */
 }
 
-/*
- * Time out error routine:
- * abort's the I/O close the exchange and
- * send completion notification to scsi layer
+/**
+ * fc_timeout_error() - Handler for fcp_pkt timeouts
+ * @fsp: The FCP packt that has timed out
  */
 static void fc_timeout_error(struct fc_fcp_pkt *fsp)
 {
@@ -1521,16 +1587,18 @@ static void fc_timeout_error(struct fc_fcp_pkt *fsp)
        fc_fcp_send_abort(fsp);
 }
 
-/*
- * Sequence retransmission request.
+/**
+ * fc_fcp_srr() - Send a SRR request (Sequence Retransmission Request)
+ * @fsp:   The FCP packet the SRR is to be sent on
+ * @r_ctl: The R_CTL field for the SRR request
  * This is called after receiving status but insufficient data, or
  * when expecting status but the request has timed out.
  */
 static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
 {
-       struct fc_lport *lp = fsp->lp;
+       struct fc_lport *lport = fsp->lp;
        struct fc_rport *rport;
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport_libfc_priv *rpriv;
        struct fc_exch *ep = fc_seq_exch(fsp->seq_ptr);
        struct fc_seq *seq;
        struct fcp_srr *srr;
@@ -1538,12 +1606,13 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
        u8 cdb_op;
 
        rport = fsp->rport;
-       rp = rport->dd_data;
+       rpriv = rport->dd_data;
        cdb_op = fsp->cdb_cmd.fc_cdb[0];
 
-       if (!(rp->flags & FC_RP_FLAGS_RETRY) || rp->rp_state != RPORT_ST_READY)
+       if (!(rpriv->flags & FC_RP_FLAGS_RETRY) ||
+           rpriv->rp_state != RPORT_ST_READY)
                goto retry;                     /* shouldn't happen */
-       fp = fc_frame_alloc(lp, sizeof(*srr));
+       fp = fc_fcp_frame_alloc(lport, sizeof(*srr));
        if (!fp)
                goto retry;
 
@@ -1556,15 +1625,14 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
        srr->srr_rel_off = htonl(offset);
 
        fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
-                      fc_host_port_id(rp->local_port->host), FC_TYPE_FCP,
+                      fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP,
                       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
 
-       seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
-                                  fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
-       if (!seq) {
-               fc_frame_free(fp);
+       seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL,
+                                     fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
+       if (!seq)
                goto retry;
-       }
+
        fsp->recov_seq = seq;
        fsp->xfer_len = offset;
        fsp->xfer_contig_end = offset;
@@ -1575,8 +1643,11 @@ retry:
        fc_fcp_retry_cmd(fsp);
 }
 
-/*
- * Handle response from SRR.
+/**
+ * fc_fcp_srr_resp() - Handler for SRR response
+ * @seq: The sequence the SRR is on
+ * @fp:         The SRR frame
+ * @arg: The FCP packet the SRR is on
  */
 static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)
 {
@@ -1622,6 +1693,11 @@ out:
        fc_fcp_pkt_release(fsp);        /* drop hold for outstanding SRR */
 }
 
+/**
+ * fc_fcp_srr_error() - Handler for SRR errors
+ * @fsp: The FCP packet that the SRR error is on
+ * @fp:         The SRR frame
+ */
 static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
 {
        if (fc_fcp_lock_pkt(fsp))
@@ -1646,31 +1722,36 @@ out:
        fc_fcp_pkt_release(fsp);        /* drop hold for outstanding SRR */
 }
 
-static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
+/**
+ * fc_fcp_lport_queue_ready() - Determine if the lport and it's queue is ready
+ * @lport: The local port to be checked
+ */
+static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)
 {
        /* lock ? */
-       return (lp->state == LPORT_ST_READY) && lp->link_up && !lp->qfull;
+       return (lport->state == LPORT_ST_READY) &&
+               lport->link_up && !lport->qfull;
 }
 
 /**
- * fc_queuecommand - The queuecommand function of the scsi template
- * @cmd:       struct scsi_cmnd to be executed
- * @done:      Callback function to be called when cmd is completed
+ * fc_queuecommand() - The queuecommand function of the SCSI template
+ * @cmd:   The scsi_cmnd to be executed
+ * @done:  The callback function to be called when the scsi_cmnd is complete
  *
- * this is the i/o strategy routine, called by the scsi layer
- * this routine is called with holding the host_lock.
+ * This is the i/o strategy routine, called by the SCSI layer. This routine
+ * is called with the host_lock held.
  */
 int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
        struct fc_fcp_pkt *fsp;
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport_libfc_priv *rpriv;
        int rval;
        int rc = 0;
        struct fcoe_dev_stats *stats;
 
-       lp = shost_priv(sc_cmd->device->host);
+       lport = shost_priv(sc_cmd->device->host);
 
        rval = fc_remote_port_chkready(rport);
        if (rval) {
@@ -1689,14 +1770,16 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
                goto out;
        }
 
-       rp = rport->dd_data;
+       rpriv = rport->dd_data;
 
-       if (!fc_fcp_lport_queue_ready(lp)) {
+       if (!fc_fcp_lport_queue_ready(lport)) {
+               if (lport->qfull)
+                       fc_fcp_can_queue_ramp_down(lport);
                rc = SCSI_MLQUEUE_HOST_BUSY;
                goto out;
        }
 
-       fsp = fc_fcp_pkt_alloc(lp, GFP_ATOMIC);
+       fsp = fc_fcp_pkt_alloc(lport, GFP_ATOMIC);
        if (fsp == NULL) {
                rc = SCSI_MLQUEUE_HOST_BUSY;
                goto out;
@@ -1706,8 +1789,9 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
         * build the libfc request pkt
         */
        fsp->cmd = sc_cmd;      /* save the cmd */
-       fsp->lp = lp;           /* save the softc ptr */
+       fsp->lp = lport;        /* save the softc ptr */
        fsp->rport = rport;     /* set the remote port ptr */
+       fsp->xfer_ddp = FC_XID_UNKNOWN;
        sc_cmd->scsi_done = done;
 
        /*
@@ -1719,7 +1803,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
        /*
         * setup the data direction
         */
-       stats = fc_lport_get_stats(lp);
+       stats = fc_lport_get_stats(lport);
        if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {
                fsp->req_flags = FC_SRB_READ;
                stats->InputRequests++;
@@ -1733,7 +1817,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
                stats->ControlRequests++;
        }
 
-       fsp->tgt_flags = rp->flags;
+       fsp->tgt_flags = rpriv->flags;
 
        init_timer(&fsp->timer);
        fsp->timer.data = (unsigned long)fsp;
@@ -1743,7 +1827,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
         * if we get -1 return then put the request in the pending
         * queue.
         */
-       rval = fc_fcp_pkt_send(lp, fsp);
+       rval = fc_fcp_pkt_send(lport, fsp);
        if (rval != 0) {
                fsp->state = FC_SRB_FREE;
                fc_fcp_pkt_release(fsp);
@@ -1755,18 +1839,17 @@ out:
 EXPORT_SYMBOL(fc_queuecommand);
 
 /**
- * fc_io_compl() -  Handle responses for completed commands
- * @fsp:       scsi packet
- *
- * Translates a error to a Linux SCSI error.
+ * fc_io_compl() - Handle responses for completed commands
+ * @fsp: The FCP packet that is complete
  *
+ * Translates fcp_pkt errors to a Linux SCSI errors.
  * The fcp packet lock must be held when calling.
  */
 static void fc_io_compl(struct fc_fcp_pkt *fsp)
 {
        struct fc_fcp_internal *si;
        struct scsi_cmnd *sc_cmd;
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        unsigned long flags;
 
        /* release outstanding ddp context */
@@ -1779,28 +1862,26 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
                spin_lock_bh(&fsp->scsi_pkt_lock);
        }
 
-       lp = fsp->lp;
-       si = fc_get_scsi_internal(lp);
-       spin_lock_irqsave(lp->host->host_lock, flags);
+       lport = fsp->lp;
+       si = fc_get_scsi_internal(lport);
+       spin_lock_irqsave(lport->host->host_lock, flags);
        if (!fsp->cmd) {
-               spin_unlock_irqrestore(lp->host->host_lock, flags);
+               spin_unlock_irqrestore(lport->host->host_lock, flags);
                return;
        }
 
        /*
-        * if a command timed out while we had to try and throttle IO
-        * and it is now getting cleaned up, then we are about to
-        * try again so clear the throttled flag incase we get more
-        * time outs.
+        * if can_queue ramp down is done then try can_queue ramp up
+        * since commands are completing now.
         */
-       if (si->throttled && fsp->state & FC_SRB_NOMEM)
-               si->throttled = 0;
+       if (si->last_can_queue_ramp_down_time)
+               fc_fcp_can_queue_ramp_up(lport);
 
        sc_cmd = fsp->cmd;
        fsp->cmd = NULL;
 
        if (!sc_cmd->SCp.ptr) {
-               spin_unlock_irqrestore(lp->host->host_lock, flags);
+               spin_unlock_irqrestore(lport->host->host_lock, flags);
                return;
        }
 
@@ -1814,21 +1895,6 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
                        sc_cmd->result = DID_OK << 16;
                        if (fsp->scsi_resid)
                                CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid;
-               } else if (fsp->cdb_status == QUEUE_FULL) {
-                       struct scsi_device *tmp_sdev;
-                       struct scsi_device *sdev = sc_cmd->device;
-
-                       shost_for_each_device(tmp_sdev, sdev->host) {
-                               if (tmp_sdev->id != sdev->id)
-                                       continue;
-
-                               if (tmp_sdev->queue_depth > 1) {
-                                       scsi_track_queue_full(tmp_sdev,
-                                                             tmp_sdev->
-                                                             queue_depth - 1);
-                               }
-                       }
-                       sc_cmd->result = (DID_OK << 16) | fsp->cdb_status;
                } else {
                        /*
                         * transport level I/O was ok but scsi
@@ -1846,7 +1912,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
                         * scsi status is good but transport level
                         * underrun.
                         */
-                       sc_cmd->result = DID_OK << 16;
+                       sc_cmd->result = (fsp->state & FC_SRB_RCV_STATUS ?
+                                         DID_OK : DID_ERROR) << 16;
                } else {
                        /*
                         * scsi got underrun, this is an error
@@ -1881,60 +1948,42 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
        list_del(&fsp->list);
        sc_cmd->SCp.ptr = NULL;
        sc_cmd->scsi_done(sc_cmd);
-       spin_unlock_irqrestore(lp->host->host_lock, flags);
+       spin_unlock_irqrestore(lport->host->host_lock, flags);
 
        /* release ref from initial allocation in queue command */
        fc_fcp_pkt_release(fsp);
 }
 
-/**
- * fc_fcp_complete() - complete processing of a fcp packet
- * @fsp:       fcp packet
- *
- * This function may sleep if a fsp timer is pending.
- * The host lock must not be held by caller.
- */
-void fc_fcp_complete(struct fc_fcp_pkt *fsp)
-{
-       if (fc_fcp_lock_pkt(fsp))
-               return;
-
-       fc_fcp_complete_locked(fsp);
-       fc_fcp_unlock_pkt(fsp);
-}
-EXPORT_SYMBOL(fc_fcp_complete);
-
 /**
  * fc_eh_abort() - Abort a command
- * @sc_cmd:    scsi command to abort
+ * @sc_cmd: The SCSI command to abort
  *
- * From scsi host template.
- * send ABTS to the target device  and wait for the response
- * sc_cmd is the pointer to the command to be aborted.
+ * From SCSI host template.
+ * Send an ABTS to the target device and wait for the response.
  */
 int fc_eh_abort(struct scsi_cmnd *sc_cmd)
 {
        struct fc_fcp_pkt *fsp;
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        int rc = FAILED;
        unsigned long flags;
 
-       lp = shost_priv(sc_cmd->device->host);
-       if (lp->state != LPORT_ST_READY)
+       lport = shost_priv(sc_cmd->device->host);
+       if (lport->state != LPORT_ST_READY)
                return rc;
-       else if (!lp->link_up)
+       else if (!lport->link_up)
                return rc;
 
-       spin_lock_irqsave(lp->host->host_lock, flags);
+       spin_lock_irqsave(lport->host->host_lock, flags);
        fsp = CMD_SP(sc_cmd);
        if (!fsp) {
                /* command completed while scsi eh was setting up */
-               spin_unlock_irqrestore(lp->host->host_lock, flags);
+               spin_unlock_irqrestore(lport->host->host_lock, flags);
                return SUCCESS;
        }
        /* grab a ref so the fsp and sc_cmd cannot be relased from under us */
        fc_fcp_pkt_hold(fsp);
-       spin_unlock_irqrestore(lp->host->host_lock, flags);
+       spin_unlock_irqrestore(lport->host->host_lock, flags);
 
        if (fc_fcp_lock_pkt(fsp)) {
                /* completed while we were waiting for timer to be deleted */
@@ -1942,7 +1991,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
                goto release_pkt;
        }
 
-       rc = fc_fcp_pkt_abort(lp, fsp);
+       rc = fc_fcp_pkt_abort(fsp);
        fc_fcp_unlock_pkt(fsp);
 
 release_pkt:
@@ -1952,37 +2001,34 @@ release_pkt:
 EXPORT_SYMBOL(fc_eh_abort);
 
 /**
- * fc_eh_device_reset() Reset a single LUN
- * @sc_cmd:    scsi command
+ * fc_eh_device_reset() - Reset a single LUN
+ * @sc_cmd: The SCSI command which identifies the device whose
+ *         LUN is to be reset
  *
- * Set from scsi host template to send tm cmd to the target and wait for the
- * response.
+ * Set from SCSI host template.
  */
 int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
 {
-       struct fc_lport *lp;
+       struct fc_lport *lport;
        struct fc_fcp_pkt *fsp;
        struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));
        int rc = FAILED;
-       struct fc_rport_libfc_priv *rp;
        int rval;
 
        rval = fc_remote_port_chkready(rport);
        if (rval)
                goto out;
 
-       rp = rport->dd_data;
-       lp = shost_priv(sc_cmd->device->host);
+       lport = shost_priv(sc_cmd->device->host);
 
-       if (lp->state != LPORT_ST_READY)
+       if (lport->state != LPORT_ST_READY)
                return rc;
 
-       FC_SCSI_DBG(lp, "Resetting rport (%6x)\n", rport->port_id);
+       FC_SCSI_DBG(lport, "Resetting rport (%6x)\n", rport->port_id);
 
-       fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO);
+       fsp = fc_fcp_pkt_alloc(lport, GFP_NOIO);
        if (fsp == NULL) {
                printk(KERN_WARNING "libfc: could not allocate scsi_pkt\n");
-               sc_cmd->result = DID_NO_CONNECT << 16;
                goto out;
        }
 
@@ -1991,13 +2037,13 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)
         * the sc passed in is not setup for execution like when sent
         * through the queuecommand callout.
         */
-       fsp->lp = lp;           /* save the softc ptr */
+       fsp->lp = lport;        /* save the softc ptr */
        fsp->rport = rport;     /* set the remote port ptr */
 
        /*
         * flush outstanding commands
         */
-       rc = fc_lun_reset(lp, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
+       rc = fc_lun_reset(lport, fsp, scmd_id(sc_cmd), sc_cmd->device->lun);
        fsp->state = FC_SRB_FREE;
        fc_fcp_pkt_release(fsp);
 
@@ -2007,38 +2053,39 @@ out:
 EXPORT_SYMBOL(fc_eh_device_reset);
 
 /**
- * fc_eh_host_reset() - The reset function will reset the ports on the host.
- * @sc_cmd:    scsi command
+ * fc_eh_host_reset() - Reset a Scsi_Host.
+ * @sc_cmd: The SCSI command that identifies the SCSI host to be reset
  */
 int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)
 {
        struct Scsi_Host *shost = sc_cmd->device->host;
-       struct fc_lport *lp = shost_priv(shost);
+       struct fc_lport *lport = shost_priv(shost);
        unsigned long wait_tmo;
 
-       FC_SCSI_DBG(lp, "Resetting host\n");
+       FC_SCSI_DBG(lport, "Resetting host\n");
 
-       lp->tt.lport_reset(lp);
+       lport->tt.lport_reset(lport);
        wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;
-       while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo))
+       while (!fc_fcp_lport_queue_ready(lport) && time_before(jiffies,
+                                                              wait_tmo))
                msleep(1000);
 
-       if (fc_fcp_lport_queue_ready(lp)) {
+       if (fc_fcp_lport_queue_ready(lport)) {
                shost_printk(KERN_INFO, shost, "libfc: Host reset succeeded "
-                            "on port (%6x)\n", fc_host_port_id(lp->host));
+                            "on port (%6x)\n", fc_host_port_id(lport->host));
                return SUCCESS;
        } else {
                shost_printk(KERN_INFO, shost, "libfc: Host reset failed, "
                             "port (%6x) is not ready.\n",
-                            fc_host_port_id(lp->host));
+                            fc_host_port_id(lport->host));
                return FAILED;
        }
 }
 EXPORT_SYMBOL(fc_eh_host_reset);
 
 /**
- * fc_slave_alloc() - configure queue depth
- * @sdev:      scsi device
+ * fc_slave_alloc() - Configure the queue depth of a Scsi_Host
+ * @sdev: The SCSI device that identifies the SCSI host
  *
  * Configures queue depth based on host's cmd_per_len. If not set
  * then we use the libfc default.
@@ -2046,29 +2093,50 @@ EXPORT_SYMBOL(fc_eh_host_reset);
 int fc_slave_alloc(struct scsi_device *sdev)
 {
        struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
-       int queue_depth;
 
        if (!rport || fc_remote_port_chkready(rport))
                return -ENXIO;
 
-       if (sdev->tagged_supported) {
-               if (sdev->host->hostt->cmd_per_lun)
-                       queue_depth = sdev->host->hostt->cmd_per_lun;
-               else
-                       queue_depth = FC_FCP_DFLT_QUEUE_DEPTH;
-               scsi_activate_tcq(sdev, queue_depth);
-       }
+       if (sdev->tagged_supported)
+               scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
+       else
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
+                                       FC_FCP_DFLT_QUEUE_DEPTH);
+
        return 0;
 }
 EXPORT_SYMBOL(fc_slave_alloc);
 
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth)
+/**
+ * fc_change_queue_depth() - Change a device's queue depth
+ * @sdev:   The SCSI device whose queue depth is to change
+ * @qdepth: The new queue depth
+ * @reason: The resason for the change
+ */
+int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
 {
-       scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+       switch (reason) {
+       case SCSI_QDEPTH_DEFAULT:
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+               break;
+       case SCSI_QDEPTH_QFULL:
+               scsi_track_queue_full(sdev, qdepth);
+               break;
+       case SCSI_QDEPTH_RAMP_UP:
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
        return sdev->queue_depth;
 }
 EXPORT_SYMBOL(fc_change_queue_depth);
 
+/**
+ * fc_change_queue_type() - Change a device's queue type
+ * @sdev:     The SCSI device whose queue depth is to change
+ * @tag_type: Identifier for queue type
+ */
 int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
 {
        if (sdev->tagged_supported) {
@@ -2084,38 +2152,69 @@ int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
 }
 EXPORT_SYMBOL(fc_change_queue_type);
 
-void fc_fcp_destroy(struct fc_lport *lp)
+/**
+ * fc_fcp_destory() - Tear down the FCP layer for a given local port
+ * @lport: The local port that no longer needs the FCP layer
+ */
+void fc_fcp_destroy(struct fc_lport *lport)
 {
-       struct fc_fcp_internal *si = fc_get_scsi_internal(lp);
+       struct fc_fcp_internal *si = fc_get_scsi_internal(lport);
 
        if (!list_empty(&si->scsi_pkt_queue))
                printk(KERN_ERR "libfc: Leaked SCSI packets when destroying "
-                      "port (%6x)\n", fc_host_port_id(lp->host));
+                      "port (%6x)\n", fc_host_port_id(lport->host));
 
        mempool_destroy(si->scsi_pkt_pool);
        kfree(si);
-       lp->scsi_priv = NULL;
+       lport->scsi_priv = NULL;
 }
 EXPORT_SYMBOL(fc_fcp_destroy);
 
-int fc_fcp_init(struct fc_lport *lp)
+int fc_setup_fcp()
+{
+       int rc = 0;
+
+       scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
+                                           sizeof(struct fc_fcp_pkt),
+                                           0, SLAB_HWCACHE_ALIGN, NULL);
+       if (!scsi_pkt_cachep) {
+               printk(KERN_ERR "libfc: Unable to allocate SRB cache, "
+                      "module load failed!");
+               rc = -ENOMEM;
+       }
+
+       return rc;
+}
+
+void fc_destroy_fcp()
+{
+       if (scsi_pkt_cachep)
+               kmem_cache_destroy(scsi_pkt_cachep);
+}
+
+/**
+ * fc_fcp_init() - Initialize the FCP layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
+int fc_fcp_init(struct fc_lport *lport)
 {
        int rc;
        struct fc_fcp_internal *si;
 
-       if (!lp->tt.fcp_cmd_send)
-               lp->tt.fcp_cmd_send = fc_fcp_cmd_send;
+       if (!lport->tt.fcp_cmd_send)
+               lport->tt.fcp_cmd_send = fc_fcp_cmd_send;
 
-       if (!lp->tt.fcp_cleanup)
-               lp->tt.fcp_cleanup = fc_fcp_cleanup;
+       if (!lport->tt.fcp_cleanup)
+               lport->tt.fcp_cleanup = fc_fcp_cleanup;
 
-       if (!lp->tt.fcp_abort_io)
-               lp->tt.fcp_abort_io = fc_fcp_abort_io;
+       if (!lport->tt.fcp_abort_io)
+               lport->tt.fcp_abort_io = fc_fcp_abort_io;
 
        si = kzalloc(sizeof(struct fc_fcp_internal), GFP_KERNEL);
        if (!si)
                return -ENOMEM;
-       lp->scsi_priv = si;
+       lport->scsi_priv = si;
+       si->max_can_queue = lport->host->can_queue;
        INIT_LIST_HEAD(&si->scsi_pkt_queue);
 
        si->scsi_pkt_pool = mempool_create_slab_pool(2, scsi_pkt_cachep);
@@ -2130,42 +2229,3 @@ free_internal:
        return rc;
 }
 EXPORT_SYMBOL(fc_fcp_init);
-
-static int __init libfc_init(void)
-{
-       int rc;
-
-       scsi_pkt_cachep = kmem_cache_create("libfc_fcp_pkt",
-                                           sizeof(struct fc_fcp_pkt),
-                                           0, SLAB_HWCACHE_ALIGN, NULL);
-       if (scsi_pkt_cachep == NULL) {
-               printk(KERN_ERR "libfc: Unable to allocate SRB cache, "
-                      "module load failed!");
-               return -ENOMEM;
-       }
-
-       rc = fc_setup_exch_mgr();
-       if (rc)
-               goto destroy_pkt_cache;
-
-       rc = fc_setup_rport();
-       if (rc)
-               goto destroy_em;
-
-       return rc;
-destroy_em:
-       fc_destroy_exch_mgr();
-destroy_pkt_cache:
-       kmem_cache_destroy(scsi_pkt_cachep);
-       return rc;
-}
-
-static void __exit libfc_exit(void)
-{
-       kmem_cache_destroy(scsi_pkt_cachep);
-       fc_destroy_exch_mgr();
-       fc_destroy_rport();
-}
-
-module_init(libfc_init);
-module_exit(libfc_exit);
index 63fe00c..6da01c6 100644 (file)
@@ -51,24 +51,24 @@ EXPORT_SYMBOL(fc_frame_crc_check);
  * Allocate a frame intended to be sent via fcoe_xmit.
  * Get an sk_buff for the frame and set the length.
  */
-struct fc_frame *__fc_frame_alloc(size_t len)
+struct fc_frame *_fc_frame_alloc(size_t len)
 {
        struct fc_frame *fp;
        struct sk_buff *skb;
 
        WARN_ON((len % sizeof(u32)) != 0);
        len += sizeof(struct fc_frame_header);
-       skb = dev_alloc_skb(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM);
+       skb = alloc_skb_fclone(len + FC_FRAME_HEADROOM + FC_FRAME_TAILROOM +
+                              NET_SKB_PAD, GFP_ATOMIC);
        if (!skb)
                return NULL;
+       skb_reserve(skb, NET_SKB_PAD + FC_FRAME_HEADROOM);
        fp = (struct fc_frame *) skb;
        fc_frame_init(fp);
-       skb_reserve(skb, FC_FRAME_HEADROOM);
        skb_put(skb, len);
        return fp;
 }
-EXPORT_SYMBOL(__fc_frame_alloc);
-
+EXPORT_SYMBOL(_fc_frame_alloc);
 
 struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
 {
@@ -78,7 +78,7 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
        fill = payload_len % 4;
        if (fill != 0)
                fill = 4 - fill;
-       fp = __fc_frame_alloc(payload_len + fill);
+       fp = _fc_frame_alloc(payload_len + fill);
        if (fp) {
                memset((char *) fr_hdr(fp) + payload_len, 0, fill);
                /* trim is OK, we just allocated it so there are no fragments */
@@ -87,3 +87,4 @@ struct fc_frame *fc_frame_alloc_fill(struct fc_lport *lp, size_t payload_len)
        }
        return fp;
 }
+EXPORT_SYMBOL(fc_frame_alloc_fill);
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
new file mode 100644 (file)
index 0000000..39f4b6a
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/scatterlist.h>
+#include <linux/crc32.h>
+
+#include <scsi/libfc.h>
+
+#include "fc_libfc.h"
+
+MODULE_AUTHOR("Open-FCoE.org");
+MODULE_DESCRIPTION("libfc");
+MODULE_LICENSE("GPL v2");
+
+unsigned int fc_debug_logging;
+module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+
+/**
+ * libfc_init() - Initialize libfc.ko
+ */
+static int __init libfc_init(void)
+{
+       int rc = 0;
+
+       rc = fc_setup_fcp();
+       if (rc)
+               return rc;
+
+       rc = fc_setup_exch_mgr();
+       if (rc)
+               goto destroy_pkt_cache;
+
+       rc = fc_setup_rport();
+       if (rc)
+               goto destroy_em;
+
+       return rc;
+destroy_em:
+       fc_destroy_exch_mgr();
+destroy_pkt_cache:
+       fc_destroy_fcp();
+       return rc;
+}
+module_init(libfc_init);
+
+/**
+ * libfc_exit() - Tear down libfc.ko
+ */
+static void __exit libfc_exit(void)
+{
+       fc_destroy_fcp();
+       fc_destroy_exch_mgr();
+       fc_destroy_rport();
+}
+module_exit(libfc_exit);
+
+/**
+ * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer
+ *                             into a scatter-gather list (SG list).
+ *
+ * @buf: pointer to the data buffer.
+ * @len: the byte-length of the data buffer.
+ * @sg: pointer to the pointer of the SG list.
+ * @nents: pointer to the remaining number of entries in the SG list.
+ * @offset: pointer to the current offset in the SG list.
+ * @km_type: dedicated page table slot type for kmap_atomic.
+ * @crc: pointer to the 32-bit crc value.
+ *      If crc is NULL, CRC is not calculated.
+ */
+u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+                            struct scatterlist *sg,
+                            u32 *nents, size_t *offset,
+                            enum km_type km_type, u32 *crc)
+{
+       size_t remaining = len;
+       u32 copy_len = 0;
+
+       while (remaining > 0 && sg) {
+               size_t off, sg_bytes;
+               void *page_addr;
+
+               if (*offset >= sg->length) {
+                       /*
+                        * Check for end and drop resources
+                        * from the last iteration.
+                        */
+                       if (!(*nents))
+                               break;
+                       --(*nents);
+                       *offset -= sg->length;
+                       sg = sg_next(sg);
+                       continue;
+               }
+               sg_bytes = min(remaining, sg->length - *offset);
+
+               /*
+                * The scatterlist item may be bigger than PAGE_SIZE,
+                * but we are limited to mapping PAGE_SIZE at a time.
+                */
+               off = *offset + sg->offset;
+               sg_bytes = min(sg_bytes,
+                              (size_t)(PAGE_SIZE - (off & ~PAGE_MASK)));
+               page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT),
+                                       km_type);
+               if (crc)
+                       *crc = crc32(*crc, buf, sg_bytes);
+               memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes);
+               kunmap_atomic(page_addr, km_type);
+               buf += sg_bytes;
+               *offset += sg_bytes;
+               remaining -= sg_bytes;
+               copy_len += sg_bytes;
+       }
+       return copy_len;
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
new file mode 100644 (file)
index 0000000..741fd5c
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef _FC_LIBFC_H_
+#define _FC_LIBFC_H_
+
+#define FC_LIBFC_LOGGING 0x01 /* General logging, not categorized */
+#define FC_LPORT_LOGGING 0x02 /* lport layer logging */
+#define FC_DISC_LOGGING         0x04 /* discovery layer logging */
+#define FC_RPORT_LOGGING 0x08 /* rport layer logging */
+#define FC_FCP_LOGGING  0x10 /* I/O path logging */
+#define FC_EM_LOGGING   0x20 /* Exchange Manager logging */
+#define FC_EXCH_LOGGING         0x40 /* Exchange/Sequence logging */
+#define FC_SCSI_LOGGING         0x80 /* SCSI logging (mostly error handling) */
+
+extern unsigned int fc_debug_logging;
+
+#define FC_CHECK_LOGGING(LEVEL, CMD)                   \
+       do {                                            \
+               if (unlikely(fc_debug_logging & LEVEL)) \
+                       do {                            \
+                               CMD;                    \
+                       } while (0);                    \
+       } while (0)
+
+#define FC_LIBFC_DBG(fmt, args...)                                     \
+       FC_CHECK_LOGGING(FC_LIBFC_LOGGING,                              \
+                        printk(KERN_INFO "libfc: " fmt, ##args))
+
+#define FC_LPORT_DBG(lport, fmt, args...)                              \
+       FC_CHECK_LOGGING(FC_LPORT_LOGGING,                              \
+                        printk(KERN_INFO "host%u: lport %6x: " fmt,    \
+                               (lport)->host->host_no,                 \
+                               fc_host_port_id((lport)->host), ##args))
+
+#define FC_DISC_DBG(disc, fmt, args...)                                \
+       FC_CHECK_LOGGING(FC_DISC_LOGGING,                       \
+                        printk(KERN_INFO "host%u: disc: " fmt, \
+                               (disc)->lport->host->host_no,   \
+                               ##args))
+
+#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...)                  \
+       FC_CHECK_LOGGING(FC_RPORT_LOGGING,                              \
+                        printk(KERN_INFO "host%u: rport %6x: " fmt,    \
+                               (lport)->host->host_no,                 \
+                               (port_id), ##args))
+
+#define FC_RPORT_DBG(rdata, fmt, args...)                              \
+       FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)
+
+#define FC_FCP_DBG(pkt, fmt, args...)                                  \
+       FC_CHECK_LOGGING(FC_FCP_LOGGING,                                \
+                        printk(KERN_INFO "host%u: fcp: %6x: " fmt,     \
+                               (pkt)->lp->host->host_no,               \
+                               pkt->rport->port_id, ##args))
+
+#define FC_EXCH_DBG(exch, fmt, args...)                                        \
+       FC_CHECK_LOGGING(FC_EXCH_LOGGING,                               \
+                        printk(KERN_INFO "host%u: xid %4x: " fmt,      \
+                               (exch)->lp->host->host_no,              \
+                               exch->xid, ##args))
+
+#define FC_SCSI_DBG(lport, fmt, args...)                               \
+       FC_CHECK_LOGGING(FC_SCSI_LOGGING,                               \
+                        printk(KERN_INFO "host%u: scsi: " fmt,         \
+                               (lport)->host->host_no, ##args))
+
+/*
+ * Set up direct-data placement for this I/O request
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+
+/*
+ * Module setup functions
+ */
+int fc_setup_exch_mgr(void);
+void fc_destroy_exch_mgr(void);
+int fc_setup_rport(void);
+void fc_destroy_rport(void);
+int fc_setup_fcp(void);
+void fc_destroy_fcp(void);
+
+/*
+ * Internal libfc functions
+ */
+const char *fc_els_resp_type(struct fc_frame *);
+
+/*
+ * Copies a buffer into an sg list
+ */
+u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
+                            struct scatterlist *sg,
+                            u32 *nents, size_t *offset,
+                            enum km_type km_type, u32 *crc);
+
+#endif /* _FC_LIBFC_H_ */
index bd2f771..74338c8 100644 (file)
@@ -56,7 +56,7 @@
  * at the same time.
  *
  * When discovery succeeds or fails a callback is made to the lport as
- * notification. Currently, succesful discovery causes the lport to take no
+ * notification. Currently, successful discovery causes the lport to take no
  * action. A failure will cause the lport to reset. There is likely a circular
  * locking problem with this implementation.
  */
@@ -94,6 +94,9 @@
 
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
+#include <linux/scatterlist.h>
+
+#include "fc_libfc.h"
 
 /* Fabric IDs to use for point-to-point mode, chosen on whims. */
 #define FC_LOCAL_PTP_FID_LO   0x010101
@@ -106,8 +109,7 @@ static void fc_lport_error(struct fc_lport *, struct fc_frame *);
 static void fc_lport_enter_reset(struct fc_lport *);
 static void fc_lport_enter_flogi(struct fc_lport *);
 static void fc_lport_enter_dns(struct fc_lport *);
-static void fc_lport_enter_rpn_id(struct fc_lport *);
-static void fc_lport_enter_rft_id(struct fc_lport *);
+static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);
 static void fc_lport_enter_scr(struct fc_lport *);
 static void fc_lport_enter_ready(struct fc_lport *);
 static void fc_lport_enter_logo(struct fc_lport *);
@@ -116,14 +118,40 @@ static const char *fc_lport_state_names[] = {
        [LPORT_ST_DISABLED] = "disabled",
        [LPORT_ST_FLOGI] =    "FLOGI",
        [LPORT_ST_DNS] =      "dNS",
-       [LPORT_ST_RPN_ID] =   "RPN_ID",
+       [LPORT_ST_RNN_ID] =   "RNN_ID",
+       [LPORT_ST_RSNN_NN] =  "RSNN_NN",
+       [LPORT_ST_RSPN_ID] =  "RSPN_ID",
        [LPORT_ST_RFT_ID] =   "RFT_ID",
+       [LPORT_ST_RFF_ID] =   "RFF_ID",
        [LPORT_ST_SCR] =      "SCR",
        [LPORT_ST_READY] =    "Ready",
        [LPORT_ST_LOGO] =     "LOGO",
        [LPORT_ST_RESET] =    "reset",
 };
 
+/**
+ * struct fc_bsg_info - FC Passthrough managemet structure
+ * @job:      The passthrough job
+ * @lport:    The local port to pass through a command
+ * @rsp_code: The expected response code
+ * @sg:              job->reply_payload.sg_list
+ * @nents:    job->reply_payload.sg_cnt
+ * @offset:   The offset into the response data
+ */
+struct fc_bsg_info {
+       struct fc_bsg_job *job;
+       struct fc_lport *lport;
+       u16 rsp_code;
+       struct scatterlist *sg;
+       u32 nents;
+       size_t offset;
+};
+
+/**
+ * fc_frame_drop() - Dummy frame handler
+ * @lport: The local port the frame was received on
+ * @fp:           The received frame
+ */
 static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
 {
        fc_frame_free(fp);
@@ -150,8 +178,8 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
        switch (event) {
        case RPORT_EV_READY:
                if (lport->state == LPORT_ST_DNS) {
-                       lport->dns_rp = rdata;
-                       fc_lport_enter_rpn_id(lport);
+                       lport->dns_rdata = rdata;
+                       fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
                } else {
                        FC_LPORT_DBG(lport, "Received an READY event "
                                     "on port (%6x) for the directory "
@@ -165,7 +193,7 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
        case RPORT_EV_LOGO:
        case RPORT_EV_FAILED:
        case RPORT_EV_STOP:
-               lport->dns_rp = NULL;
+               lport->dns_rdata = NULL;
                break;
        case RPORT_EV_NONE:
                break;
@@ -189,8 +217,8 @@ static const char *fc_lport_state(struct fc_lport *lport)
 
 /**
  * fc_lport_ptp_setup() - Create an rport for point-to-point mode
- * @lport: The lport to attach the ptp rport to
- * @fid: The FID of the ptp rport
+ * @lport:      The lport to attach the ptp rport to
+ * @remote_fid:         The FID of the ptp rport
  * @remote_wwpn: The WWPN of the ptp rport
  * @remote_wwnn: The WWNN of the ptp rport
  */
@@ -199,18 +227,22 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
                               u64 remote_wwnn)
 {
        mutex_lock(&lport->disc.disc_mutex);
-       if (lport->ptp_rp)
-               lport->tt.rport_logoff(lport->ptp_rp);
-       lport->ptp_rp = lport->tt.rport_create(lport, remote_fid);
-       lport->ptp_rp->ids.port_name = remote_wwpn;
-       lport->ptp_rp->ids.node_name = remote_wwnn;
+       if (lport->ptp_rdata)
+               lport->tt.rport_logoff(lport->ptp_rdata);
+       lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
+       lport->ptp_rdata->ids.port_name = remote_wwpn;
+       lport->ptp_rdata->ids.node_name = remote_wwnn;
        mutex_unlock(&lport->disc.disc_mutex);
 
-       lport->tt.rport_login(lport->ptp_rp);
+       lport->tt.rport_login(lport->ptp_rdata);
 
        fc_lport_enter_ready(lport);
 }
 
+/**
+ * fc_get_host_port_type() - Return the port type of the given Scsi_Host
+ * @shost: The SCSI host whose port type is to be determined
+ */
 void fc_get_host_port_type(struct Scsi_Host *shost)
 {
        /* TODO - currently just NPORT */
@@ -218,17 +250,33 @@ void fc_get_host_port_type(struct Scsi_Host *shost)
 }
 EXPORT_SYMBOL(fc_get_host_port_type);
 
+/**
+ * fc_get_host_port_state() - Return the port state of the given Scsi_Host
+ * @shost:  The SCSI host whose port state is to be determined
+ */
 void fc_get_host_port_state(struct Scsi_Host *shost)
 {
-       struct fc_lport *lp = shost_priv(shost);
+       struct fc_lport *lport = shost_priv(shost);
 
-       if (lp->link_up)
-               fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+       mutex_lock(&lport->lp_mutex);
+       if (!lport->link_up)
+               fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
        else
-               fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+               switch (lport->state) {
+               case LPORT_ST_READY:
+                       fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+                       break;
+               default:
+                       fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+               }
+       mutex_unlock(&lport->lp_mutex);
 }
 EXPORT_SYMBOL(fc_get_host_port_state);
 
+/**
+ * fc_get_host_speed() - Return the speed of the given Scsi_Host
+ * @shost: The SCSI host whose port speed is to be determined
+ */
 void fc_get_host_speed(struct Scsi_Host *shost)
 {
        struct fc_lport *lport = shost_priv(shost);
@@ -237,24 +285,28 @@ void fc_get_host_speed(struct Scsi_Host *shost)
 }
 EXPORT_SYMBOL(fc_get_host_speed);
 
+/**
+ * fc_get_host_stats() - Return the Scsi_Host's statistics
+ * @shost: The SCSI host whose statistics are to be returned
+ */
 struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 {
        struct fc_host_statistics *fcoe_stats;
-       struct fc_lport *lp = shost_priv(shost);
+       struct fc_lport *lport = shost_priv(shost);
        struct timespec v0, v1;
        unsigned int cpu;
 
-       fcoe_stats = &lp->host_stats;
+       fcoe_stats = &lport->host_stats;
        memset(fcoe_stats, 0, sizeof(struct fc_host_statistics));
 
        jiffies_to_timespec(jiffies, &v0);
-       jiffies_to_timespec(lp->boot_time, &v1);
+       jiffies_to_timespec(lport->boot_time, &v1);
        fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);
 
        for_each_possible_cpu(cpu) {
                struct fcoe_dev_stats *stats;
 
-               stats = per_cpu_ptr(lp->dev_stats, cpu);
+               stats = per_cpu_ptr(lport->dev_stats, cpu);
 
                fcoe_stats->tx_frames += stats->TxFrames;
                fcoe_stats->tx_words += stats->TxWords;
@@ -279,12 +331,15 @@ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
 }
 EXPORT_SYMBOL(fc_get_host_stats);
 
-/*
- * Fill in FLOGI command for request.
+/**
+ * fc_lport_flogi_fill() - Fill in FLOGI command for request
+ * @lport: The local port the FLOGI is for
+ * @flogi: The FLOGI command
+ * @op:           The opcode
  */
-static void
-fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
-                   unsigned int op)
+static void fc_lport_flogi_fill(struct fc_lport *lport,
+                               struct fc_els_flogi *flogi,
+                               unsigned int op)
 {
        struct fc_els_csp *sp;
        struct fc_els_cssp *cp;
@@ -312,8 +367,10 @@ fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi,
        }
 }
 
-/*
- * Add a supported FC-4 type.
+/**
+ * fc_lport_add_fc4_type() - Add a supported FC-4 type to a local port
+ * @lport: The local port to add a new FC-4 type to
+ * @type:  The new FC-4 type
  */
 static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
 {
@@ -325,11 +382,11 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
 
 /**
  * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
+ * @sp:           The sequence in the RLIR exchange
+ * @fp:           The RLIR request frame
  * @lport: Fibre Channel local port recieving the RLIR
- * @sp: current sequence in the RLIR exchange
- * @fp: RLIR request frame
  *
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
 static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
@@ -344,11 +401,11 @@ static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
 
 /**
  * fc_lport_recv_echo_req() - Handle received ECHO request
- * @lport: Fibre Channel local port recieving the ECHO
- * @sp: current sequence in the ECHO exchange
- * @fp: ECHO request frame
+ * @sp:           The sequence in the ECHO exchange
+ * @fp:           ECHO request frame
+ * @lport: The local port recieving the ECHO
  *
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
 static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -361,7 +418,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
        void *dp;
        u32 f_ctl;
 
-       FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n",
+       FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n",
                     fc_lport_state(lport));
 
        len = fr_len(in_fp) - sizeof(struct fc_frame_header);
@@ -374,7 +431,7 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
        if (fp) {
                dp = fc_frame_payload_get(fp, len);
                memcpy(dp, pp, len);
-               *((u32 *)dp) = htonl(ELS_LS_ACC << 24);
+               *((__be32 *)dp) = htonl(ELS_LS_ACC << 24);
                sp = lport->tt.seq_start_next(sp);
                f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
                fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
@@ -385,12 +442,12 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
 }
 
 /**
- * fc_lport_recv_echo_req() - Handle received Request Node ID data request
- * @lport: Fibre Channel local port recieving the RNID
- * @sp: current sequence in the RNID exchange
- * @fp: RNID request frame
+ * fc_lport_recv_rnid_req() - Handle received Request Node ID data request
+ * @sp:           The sequence in the RNID exchange
+ * @fp:           The RNID request frame
+ * @lport: The local port recieving the RNID
  *
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
 static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -453,9 +510,9 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 
 /**
  * fc_lport_recv_logo_req() - Handle received fabric LOGO request
- * @lport: Fibre Channel local port recieving the LOGO
- * @sp: current sequence in the LOGO exchange
- * @fp: LOGO request frame
+ * @sp:           The sequence in the LOGO exchange
+ * @fp:           The LOGO request frame
+ * @lport: The local port recieving the LOGO
  *
  * Locking Note: The lport lock is exected to be held before calling
  * this function.
@@ -470,7 +527,7 @@ static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
 
 /**
  * fc_fabric_login() - Start the lport state machine
- * @lport: The lport that should log into the fabric
+ * @lport: The local port that should log into the fabric
  *
  * Locking Note: This function should not be called
  *              with the lport lock held.
@@ -491,47 +548,69 @@ int fc_fabric_login(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_fabric_login);
 
 /**
- * fc_linkup() - Handler for transport linkup events
+ * __fc_linkup() - Handler for transport linkup events
  * @lport: The lport whose link is up
+ *
+ * Locking: must be called with the lp_mutex held
  */
-void fc_linkup(struct fc_lport *lport)
+void __fc_linkup(struct fc_lport *lport)
 {
-       printk(KERN_INFO "libfc: Link up on port (%6x)\n",
-              fc_host_port_id(lport->host));
-
-       mutex_lock(&lport->lp_mutex);
        if (!lport->link_up) {
                lport->link_up = 1;
 
                if (lport->state == LPORT_ST_RESET)
                        fc_lport_enter_flogi(lport);
        }
+}
+
+/**
+ * fc_linkup() - Handler for transport linkup events
+ * @lport: The local port whose link is up
+ */
+void fc_linkup(struct fc_lport *lport)
+{
+       printk(KERN_INFO "host%d: libfc: Link up on port (%6x)\n",
+              lport->host->host_no, fc_host_port_id(lport->host));
+
+       mutex_lock(&lport->lp_mutex);
+       __fc_linkup(lport);
        mutex_unlock(&lport->lp_mutex);
 }
 EXPORT_SYMBOL(fc_linkup);
 
 /**
- * fc_linkdown() - Handler for transport linkdown events
+ * __fc_linkdown() - Handler for transport linkdown events
  * @lport: The lport whose link is down
+ *
+ * Locking: must be called with the lp_mutex held
  */
-void fc_linkdown(struct fc_lport *lport)
+void __fc_linkdown(struct fc_lport *lport)
 {
-       mutex_lock(&lport->lp_mutex);
-       printk(KERN_INFO "libfc: Link down on port (%6x)\n",
-              fc_host_port_id(lport->host));
-
        if (lport->link_up) {
                lport->link_up = 0;
                fc_lport_enter_reset(lport);
                lport->tt.fcp_cleanup(lport);
        }
+}
+
+/**
+ * fc_linkdown() - Handler for transport linkdown events
+ * @lport: The local port whose link is down
+ */
+void fc_linkdown(struct fc_lport *lport)
+{
+       printk(KERN_INFO "host%d: libfc: Link down on port (%6x)\n",
+              lport->host->host_no, fc_host_port_id(lport->host));
+
+       mutex_lock(&lport->lp_mutex);
+       __fc_linkdown(lport);
        mutex_unlock(&lport->lp_mutex);
 }
 EXPORT_SYMBOL(fc_linkdown);
 
 /**
  * fc_fabric_logoff() - Logout of the fabric
- * @lport:           fc_lport pointer to logoff the fabric
+ * @lport: The local port to logoff the fabric
  *
  * Return value:
  *     0 for success, -1 for failure
@@ -540,8 +619,8 @@ int fc_fabric_logoff(struct fc_lport *lport)
 {
        lport->tt.disc_stop_final(lport);
        mutex_lock(&lport->lp_mutex);
-       if (lport->dns_rp)
-               lport->tt.rport_logoff(lport->dns_rp);
+       if (lport->dns_rdata)
+               lport->tt.rport_logoff(lport->dns_rdata);
        mutex_unlock(&lport->lp_mutex);
        lport->tt.rport_flush_queue();
        mutex_lock(&lport->lp_mutex);
@@ -553,11 +632,9 @@ int fc_fabric_logoff(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_fabric_logoff);
 
 /**
- * fc_lport_destroy() - unregister a fc_lport
- * @lport:           fc_lport pointer to unregister
+ * fc_lport_destroy() - Unregister a fc_lport
+ * @lport: The local port to unregister
  *
- * Return value:
- *     None
  * Note:
  * exit routine for fc_lport instance
  * clean-up all the allocated memory
@@ -580,13 +657,9 @@ int fc_lport_destroy(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_lport_destroy);
 
 /**
- * fc_set_mfs() - sets up the mfs for the corresponding fc_lport
- * @lport: fc_lport pointer to unregister
- * @mfs: the new mfs for fc_lport
- *
- * Set mfs for the given fc_lport to the new mfs.
- *
- * Return: 0 for success
+ * fc_set_mfs() - Set the maximum frame size for a local port
+ * @lport: The local port to set the MFS for
+ * @mfs:   The new MFS
  */
 int fc_set_mfs(struct fc_lport *lport, u32 mfs)
 {
@@ -617,7 +690,7 @@ EXPORT_SYMBOL(fc_set_mfs);
 
 /**
  * fc_lport_disc_callback() - Callback for discovery events
- * @lport: FC local port
+ * @lport: The local port receiving the event
  * @event: The discovery event
  */
 void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
@@ -627,8 +700,9 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
                FC_LPORT_DBG(lport, "Discovery succeeded\n");
                break;
        case DISC_EV_FAILED:
-               printk(KERN_ERR "libfc: Discovery failed for port (%6x)\n",
-                      fc_host_port_id(lport->host));
+               printk(KERN_ERR "host%d: libfc: "
+                      "Discovery failed for port (%6x)\n",
+                      lport->host->host_no, fc_host_port_id(lport->host));
                mutex_lock(&lport->lp_mutex);
                fc_lport_enter_reset(lport);
                mutex_unlock(&lport->lp_mutex);
@@ -641,7 +715,7 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
 
 /**
  * fc_rport_enter_ready() - Enter the ready state and start discovery
- * @lport: Fibre Channel local port that is ready
+ * @lport: The local port that is ready
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
@@ -652,22 +726,46 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
                     fc_lport_state(lport));
 
        fc_lport_state_enter(lport, LPORT_ST_READY);
+       if (lport->vport)
+               fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE);
+       fc_vports_linkchange(lport);
 
-       if (!lport->ptp_rp)
+       if (!lport->ptp_rdata)
                lport->tt.disc_start(fc_lport_disc_callback, lport);
 }
 
+/**
+ * fc_lport_set_port_id() - set the local port Port ID
+ * @lport: The local port which will have its Port ID set.
+ * @port_id: The new port ID.
+ * @fp: The frame containing the incoming request, or NULL.
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
+                                struct fc_frame *fp)
+{
+       if (port_id)
+               printk(KERN_INFO "host%d: Assigned Port ID %6x\n",
+                      lport->host->host_no, port_id);
+
+       fc_host_port_id(lport->host) = port_id;
+       if (lport->tt.lport_set_port_id)
+               lport->tt.lport_set_port_id(lport, port_id, fp);
+}
+
 /**
  * fc_lport_recv_flogi_req() - Receive a FLOGI request
  * @sp_in: The sequence the FLOGI is on
- * @rx_fp: The frame the FLOGI is in
- * @lport: The lport that recieved the request
+ * @rx_fp: The FLOGI frame
+ * @lport: The local port that recieved the request
  *
  * A received FLOGI request indicates a point-to-point connection.
  * Accept it with the common service parameters indicating our N port.
  * Set up to do a PLOGI if we have the higher-number WWPN.
  *
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
 static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
@@ -695,8 +793,9 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
                goto out;
        remote_wwpn = get_unaligned_be64(&flp->fl_wwpn);
        if (remote_wwpn == lport->wwpn) {
-               printk(KERN_WARNING "libfc: Received FLOGI from port "
-                      "with same WWPN %llx\n", remote_wwpn);
+               printk(KERN_WARNING "host%d: libfc: Received FLOGI from port "
+                      "with same WWPN %llx\n",
+                      lport->host->host_no, remote_wwpn);
                goto out;
        }
        FC_LPORT_DBG(lport, "FLOGI from port WWPN %llx\n", remote_wwpn);
@@ -715,7 +814,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
                remote_fid = FC_LOCAL_PTP_FID_HI;
        }
 
-       fc_host_port_id(lport->host) = local_fid;
+       fc_lport_set_port_id(lport, local_fid, rx_fp);
 
        fp = fc_frame_alloc(lport, sizeof(*flp));
        if (fp) {
@@ -747,9 +846,9 @@ out:
 
 /**
  * fc_lport_recv_req() - The generic lport request handler
- * @lport: The lport that received the request
- * @sp: The sequence the request is on
- * @fp: The frame the request is in
+ * @lport: The local port that received the request
+ * @sp:           The sequence the request is on
+ * @fp:           The request frame
  *
  * This function will see if the lport handles the request or
  * if an rport should handle the request.
@@ -817,8 +916,8 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 }
 
 /**
- * fc_lport_reset() - Reset an lport
- * @lport: The lport which should be reset
+ * fc_lport_reset() - Reset a local port
+ * @lport: The local port which should be reset
  *
  * Locking Note: This functions should not be called with the
  *              lport lock held.
@@ -834,29 +933,31 @@ int fc_lport_reset(struct fc_lport *lport)
 EXPORT_SYMBOL(fc_lport_reset);
 
 /**
- * fc_lport_reset_locked() - Reset the local port
- * @lport: Fibre Channel local port to be reset
+ * fc_lport_reset_locked() - Reset the local port w/ the lport lock held
+ * @lport: The local port to be reset
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
  */
 static void fc_lport_reset_locked(struct fc_lport *lport)
 {
-       if (lport->dns_rp)
-               lport->tt.rport_logoff(lport->dns_rp);
+       if (lport->dns_rdata)
+               lport->tt.rport_logoff(lport->dns_rdata);
 
-       lport->ptp_rp = NULL;
+       lport->ptp_rdata = NULL;
 
        lport->tt.disc_stop(lport);
 
        lport->tt.exch_mgr_reset(lport, 0, 0);
        fc_host_fabric_name(lport->host) = 0;
-       fc_host_port_id(lport->host) = 0;
+
+       if (fc_host_port_id(lport->host))
+               fc_lport_set_port_id(lport, 0, NULL);
 }
 
 /**
  * fc_lport_enter_reset() - Reset the local port
- * @lport: Fibre Channel local port to be reset
+ * @lport: The local port to be reset
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
@@ -866,15 +967,22 @@ static void fc_lport_enter_reset(struct fc_lport *lport)
        FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
                     fc_lport_state(lport));
 
+       if (lport->vport) {
+               if (lport->link_up)
+                       fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING);
+               else
+                       fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
+       }
        fc_lport_state_enter(lport, LPORT_ST_RESET);
+       fc_vports_linkchange(lport);
        fc_lport_reset_locked(lport);
        if (lport->link_up)
                fc_lport_enter_flogi(lport);
 }
 
 /**
- * fc_lport_enter_disabled() - disable the local port
- * @lport: Fibre Channel local port to be reset
+ * fc_lport_enter_disabled() - Disable the local port
+ * @lport: The local port to be reset
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
@@ -885,13 +993,14 @@ static void fc_lport_enter_disabled(struct fc_lport *lport)
                     fc_lport_state(lport));
 
        fc_lport_state_enter(lport, LPORT_ST_DISABLED);
+       fc_vports_linkchange(lport);
        fc_lport_reset_locked(lport);
 }
 
 /**
  * fc_lport_error() - Handler for any errors
- * @lport: The fc_lport object
- * @fp: The frame pointer
+ * @lport: The local port that the error was on
+ * @fp:           The error code encoded in a frame pointer
  *
  * If the error was caused by a resource allocation failure
  * then wait for half a second and retry, otherwise retry
@@ -922,8 +1031,11 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
                        case LPORT_ST_DISABLED:
                        case LPORT_ST_READY:
                        case LPORT_ST_RESET:
-                       case LPORT_ST_RPN_ID:
+                       case LPORT_ST_RNN_ID:
+                       case LPORT_ST_RSNN_NN:
+                       case LPORT_ST_RSPN_ID:
                        case LPORT_ST_RFT_ID:
+                       case LPORT_ST_RFF_ID:
                        case LPORT_ST_SCR:
                        case LPORT_ST_DNS:
                        case LPORT_ST_FLOGI:
@@ -936,33 +1048,33 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
 }
 
 /**
- * fc_lport_rft_id_resp() - Handle response to Register Fibre
- *                         Channel Types by ID (RPN_ID) request
- * @sp: current sequence in RPN_ID exchange
- * @fp: response frame
+ * fc_lport_ns_resp() - Handle response to a name server
+ *                     registration exchange
+ * @sp:            current sequence in exchange
+ * @fp:            response frame
  * @lp_arg: Fibre Channel host port instance
  *
  * Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
  * and then unlock the lport.
  */
-static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
-                                void *lp_arg)
+static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
+                            void *lp_arg)
 {
        struct fc_lport *lport = lp_arg;
        struct fc_frame_header *fh;
        struct fc_ct_hdr *ct;
 
-       FC_LPORT_DBG(lport, "Received a RFT_ID %s\n", fc_els_resp_type(fp));
+       FC_LPORT_DBG(lport, "Received a ns %s\n", fc_els_resp_type(fp));
 
        if (fp == ERR_PTR(-FC_EX_CLOSED))
                return;
 
        mutex_lock(&lport->lp_mutex);
 
-       if (lport->state != LPORT_ST_RFT_ID) {
-               FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state "
-                            "%s\n", fc_lport_state(lport));
+       if (lport->state < LPORT_ST_RNN_ID || lport->state > LPORT_ST_RFF_ID) {
+               FC_LPORT_DBG(lport, "Received a name server response, "
+                            "but in state %s\n", fc_lport_state(lport));
                if (IS_ERR(fp))
                        goto err;
                goto out;
@@ -980,63 +1092,28 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
            ct->ct_fs_type == FC_FST_DIR &&
            ct->ct_fs_subtype == FC_NS_SUBTYPE &&
            ntohs(ct->ct_cmd) == FC_FS_ACC)
-               fc_lport_enter_scr(lport);
-       else
-               fc_lport_error(lport, fp);
-out:
-       fc_frame_free(fp);
-err:
-       mutex_unlock(&lport->lp_mutex);
-}
-
-/**
- * fc_lport_rpn_id_resp() - Handle response to Register Port
- *                         Name by ID (RPN_ID) request
- * @sp: current sequence in RPN_ID exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel host port instance
- *
- * Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
- * and then unlock the lport.
- */
-static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
-                                void *lp_arg)
-{
-       struct fc_lport *lport = lp_arg;
-       struct fc_frame_header *fh;
-       struct fc_ct_hdr *ct;
-
-       FC_LPORT_DBG(lport, "Received a RPN_ID %s\n", fc_els_resp_type(fp));
-
-       if (fp == ERR_PTR(-FC_EX_CLOSED))
-               return;
-
-       mutex_lock(&lport->lp_mutex);
-
-       if (lport->state != LPORT_ST_RPN_ID) {
-               FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state "
-                            "%s\n", fc_lport_state(lport));
-               if (IS_ERR(fp))
-                       goto err;
-               goto out;
-       }
-
-       if (IS_ERR(fp)) {
-               fc_lport_error(lport, fp);
-               goto err;
-       }
-
-       fh = fc_frame_header_get(fp);
-       ct = fc_frame_payload_get(fp, sizeof(*ct));
-       if (fh && ct && fh->fh_type == FC_TYPE_CT &&
-           ct->ct_fs_type == FC_FST_DIR &&
-           ct->ct_fs_subtype == FC_NS_SUBTYPE &&
-           ntohs(ct->ct_cmd) == FC_FS_ACC)
-               fc_lport_enter_rft_id(lport);
+               switch (lport->state) {
+               case LPORT_ST_RNN_ID:
+                       fc_lport_enter_ns(lport, LPORT_ST_RSNN_NN);
+                       break;
+               case LPORT_ST_RSNN_NN:
+                       fc_lport_enter_ns(lport, LPORT_ST_RSPN_ID);
+                       break;
+               case LPORT_ST_RSPN_ID:
+                       fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+                       break;
+               case LPORT_ST_RFT_ID:
+                       fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);
+                       break;
+               case LPORT_ST_RFF_ID:
+                       fc_lport_enter_scr(lport);
+                       break;
+               default:
+                       /* should have already been caught by state checks */
+                       break;
+               }
        else
                fc_lport_error(lport, fp);
-
 out:
        fc_frame_free(fp);
 err:
@@ -1045,8 +1122,8 @@ err:
 
 /**
  * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request
- * @sp: current sequence in SCR exchange
- * @fp: response frame
+ * @sp:            current sequence in SCR exchange
+ * @fp:            response frame
  * @lp_arg: Fibre Channel lport port instance that sent the registration request
  *
  * Locking Note: This function will be called without the lport lock
@@ -1092,8 +1169,8 @@ err:
 }
 
 /**
- * fc_lport_enter_scr() - Send a State Change Register (SCR) request
- * @lport: Fibre Channel local port to register for state changes
+ * fc_lport_enter_scr() - Send a SCR (State Change Register) request
+ * @lport: The local port to register for state changes
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
@@ -1114,78 +1191,74 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
        }
 
        if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR,
-                                 fc_lport_scr_resp, lport, lport->e_d_tov))
-               fc_lport_error(lport, fp);
+                                 fc_lport_scr_resp, lport,
+                                 2 * lport->r_a_tov))
+               fc_lport_error(lport, NULL);
 }
 
 /**
- * fc_lport_enter_rft_id() - Register FC4-types with the name server
+ * fc_lport_enter_ns() - register some object with the name server
  * @lport: Fibre Channel local port to register
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
  */
-static void fc_lport_enter_rft_id(struct fc_lport *lport)
+static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
 {
        struct fc_frame *fp;
-       struct fc_ns_fts *lps;
-       int i;
+       enum fc_ns_req cmd;
+       int size = sizeof(struct fc_ct_hdr);
+       size_t len;
 
-       FC_LPORT_DBG(lport, "Entered RFT_ID state from %s state\n",
+       FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
+                    fc_lport_state_names[state],
                     fc_lport_state(lport));
 
-       fc_lport_state_enter(lport, LPORT_ST_RFT_ID);
-
-       lps = &lport->fcts;
-       i = sizeof(lps->ff_type_map) / sizeof(lps->ff_type_map[0]);
-       while (--i >= 0)
-               if (ntohl(lps->ff_type_map[i]) != 0)
-                       break;
-       if (i < 0) {
-               /* nothing to register, move on to SCR */
-               fc_lport_enter_scr(lport);
-               return;
-       }
+       fc_lport_state_enter(lport, state);
 
-       fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
-                           sizeof(struct fc_ns_rft));
-       if (!fp) {
-               fc_lport_error(lport, fp);
+       switch (state) {
+       case LPORT_ST_RNN_ID:
+               cmd = FC_NS_RNN_ID;
+               size += sizeof(struct fc_ns_rn_id);
+               break;
+       case LPORT_ST_RSNN_NN:
+               len = strnlen(fc_host_symbolic_name(lport->host), 255);
+               /* if there is no symbolic name, skip to RFT_ID */
+               if (!len)
+                       return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+               cmd = FC_NS_RSNN_NN;
+               size += sizeof(struct fc_ns_rsnn) + len;
+               break;
+       case LPORT_ST_RSPN_ID:
+               len = strnlen(fc_host_symbolic_name(lport->host), 255);
+               /* if there is no symbolic name, skip to RFT_ID */
+               if (!len)
+                       return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
+               cmd = FC_NS_RSPN_ID;
+               size += sizeof(struct fc_ns_rspn) + len;
+               break;
+       case LPORT_ST_RFT_ID:
+               cmd = FC_NS_RFT_ID;
+               size += sizeof(struct fc_ns_rft);
+               break;
+       case LPORT_ST_RFF_ID:
+               cmd = FC_NS_RFF_ID;
+               size += sizeof(struct fc_ns_rff_id);
+               break;
+       default:
+               fc_lport_error(lport, NULL);
                return;
        }
 
-       if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID,
-                                 fc_lport_rft_id_resp,
-                                 lport, lport->e_d_tov))
-               fc_lport_error(lport, fp);
-}
-
-/**
- * fc_rport_enter_rft_id() - Register port name with the name server
- * @lport: Fibre Channel local port to register
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this routine.
- */
-static void fc_lport_enter_rpn_id(struct fc_lport *lport)
-{
-       struct fc_frame *fp;
-
-       FC_LPORT_DBG(lport, "Entered RPN_ID state from %s state\n",
-                    fc_lport_state(lport));
-
-       fc_lport_state_enter(lport, LPORT_ST_RPN_ID);
-
-       fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
-                           sizeof(struct fc_ns_rn_id));
+       fp = fc_frame_alloc(lport, size);
        if (!fp) {
                fc_lport_error(lport, fp);
                return;
        }
 
-       if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID,
-                                 fc_lport_rpn_id_resp,
-                                 lport, lport->e_d_tov))
+       if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, cmd,
+                                 fc_lport_ns_resp,
+                                 lport, 3 * lport->r_a_tov))
                fc_lport_error(lport, fp);
 }
 
@@ -1194,8 +1267,8 @@ static struct fc_rport_operations fc_lport_rport_ops = {
 };
 
 /**
- * fc_rport_enter_dns() - Create a rport to the name server
- * @lport: Fibre Channel local port requesting a rport for the name server
+ * fc_rport_enter_dns() - Create a fc_rport for the name server
+ * @lport: The local port requesting a remote port for the name server
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
@@ -1224,8 +1297,8 @@ err:
 }
 
 /**
- * fc_lport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_lport
+ * fc_lport_timeout() - Handler for the retry_work timer
+ * @work: The work struct of the local port
  */
 static void fc_lport_timeout(struct work_struct *work)
 {
@@ -1237,21 +1310,25 @@ static void fc_lport_timeout(struct work_struct *work)
 
        switch (lport->state) {
        case LPORT_ST_DISABLED:
+               WARN_ON(1);
+               break;
        case LPORT_ST_READY:
-       case LPORT_ST_RESET:
                WARN_ON(1);
                break;
+       case LPORT_ST_RESET:
+               break;
        case LPORT_ST_FLOGI:
                fc_lport_enter_flogi(lport);
                break;
        case LPORT_ST_DNS:
                fc_lport_enter_dns(lport);
                break;
-       case LPORT_ST_RPN_ID:
-               fc_lport_enter_rpn_id(lport);
-               break;
+       case LPORT_ST_RNN_ID:
+       case LPORT_ST_RSNN_NN:
+       case LPORT_ST_RSPN_ID:
        case LPORT_ST_RFT_ID:
-               fc_lport_enter_rft_id(lport);
+       case LPORT_ST_RFF_ID:
+               fc_lport_enter_ns(lport, lport->state);
                break;
        case LPORT_ST_SCR:
                fc_lport_enter_scr(lport);
@@ -1266,16 +1343,16 @@ static void fc_lport_timeout(struct work_struct *work)
 
 /**
  * fc_lport_logo_resp() - Handle response to LOGO request
- * @sp: current sequence in LOGO exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel lport port instance that sent the LOGO request
+ * @sp:            The sequence that the LOGO was on
+ * @fp:            The LOGO frame
+ * @lp_arg: The lport port that received the LOGO request
  *
  * Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
  * and then unlock the lport.
  */
-static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
-                              void *lp_arg)
+void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+                       void *lp_arg)
 {
        struct fc_lport *lport = lp_arg;
        u8 op;
@@ -1311,10 +1388,11 @@ out:
 err:
        mutex_unlock(&lport->lp_mutex);
 }
+EXPORT_SYMBOL(fc_lport_logo_resp);
 
 /**
  * fc_rport_enter_logo() - Logout of the fabric
- * @lport: Fibre Channel local port to be logged out
+ * @lport: The local port to be logged out
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this routine.
@@ -1328,6 +1406,7 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
                     fc_lport_state(lport));
 
        fc_lport_state_enter(lport, LPORT_ST_LOGO);
+       fc_vports_linkchange(lport);
 
        fp = fc_frame_alloc(lport, sizeof(*logo));
        if (!fp) {
@@ -1336,22 +1415,23 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
        }
 
        if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO,
-                                 fc_lport_logo_resp, lport, lport->e_d_tov))
-               fc_lport_error(lport, fp);
+                                 fc_lport_logo_resp, lport,
+                                 2 * lport->r_a_tov))
+               fc_lport_error(lport, NULL);
 }
 
 /**
  * fc_lport_flogi_resp() - Handle response to FLOGI request
- * @sp: current sequence in FLOGI exchange
- * @fp: response frame
- * @lp_arg: Fibre Channel lport port instance that sent the FLOGI request
+ * @sp:            The sequence that the FLOGI was on
+ * @fp:            The FLOGI response frame
+ * @lp_arg: The lport port that received the FLOGI response
  *
  * Locking Note: This function will be called without the lport lock
- * held, but it will lock, call an _enter_* function or fc_lport_error
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
  * and then unlock the lport.
  */
-static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
-                               void *lp_arg)
+void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+                        void *lp_arg)
 {
        struct fc_lport *lport = lp_arg;
        struct fc_frame_header *fh;
@@ -1385,11 +1465,6 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
        fh = fc_frame_header_get(fp);
        did = ntoh24(fh->fh_d_id);
        if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
-
-               printk(KERN_INFO "libfc: Assigned FID (%6x) in FLOGI response\n",
-                      did);
-               fc_host_port_id(lport->host) = did;
-
                flp = fc_frame_payload_get(fp, sizeof(*flp));
                if (flp) {
                        mfs = ntohs(flp->fl_csp.sp_bb_data) &
@@ -1402,12 +1477,18 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
                        e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov);
                        if (csp_flags & FC_SP_FT_EDTR)
                                e_d_tov /= 1000000;
+
+                       lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC);
+
                        if ((csp_flags & FC_SP_FT_FPORT) == 0) {
                                if (e_d_tov > lport->e_d_tov)
                                        lport->e_d_tov = e_d_tov;
                                lport->r_a_tov = 2 * e_d_tov;
-                               printk(KERN_INFO "libfc: Port (%6x) entered "
-                                      "point to point mode\n", did);
+                               fc_lport_set_port_id(lport, did, fp);
+                               printk(KERN_INFO "host%d: libfc: "
+                                      "Port (%6x) entered "
+                                      "point-to-point mode\n",
+                                      lport->host->host_no, did);
                                fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
                                                   get_unaligned_be64(
                                                           &flp->fl_wwpn),
@@ -1418,6 +1499,7 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
                                lport->r_a_tov = r_a_tov;
                                fc_host_fabric_name(lport->host) =
                                        get_unaligned_be64(&flp->fl_wwnn);
+                               fc_lport_set_port_id(lport, did, fp);
                                fc_lport_enter_dns(lport);
                        }
                }
@@ -1430,6 +1512,7 @@ out:
 err:
        mutex_unlock(&lport->lp_mutex);
 }
+EXPORT_SYMBOL(fc_lport_flogi_resp);
 
 /**
  * fc_rport_enter_flogi() - Send a FLOGI request to the fabric manager
@@ -1451,12 +1534,18 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
        if (!fp)
                return fc_lport_error(lport, fp);
 
-       if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI,
-                                 fc_lport_flogi_resp, lport, lport->e_d_tov))
-               fc_lport_error(lport, fp);
+       if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp,
+                                 lport->vport ? ELS_FDISC : ELS_FLOGI,
+                                 fc_lport_flogi_resp, lport,
+                                 lport->vport ? 2 * lport->r_a_tov :
+                                 lport->e_d_tov))
+               fc_lport_error(lport, NULL);
 }
 
-/* Configure a fc_lport */
+/**
+ * fc_lport_config() - Configure a fc_lport
+ * @lport: The local port to be configured
+ */
 int fc_lport_config(struct fc_lport *lport)
 {
        INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
@@ -1471,6 +1560,10 @@ int fc_lport_config(struct fc_lport *lport)
 }
 EXPORT_SYMBOL(fc_lport_config);
 
+/**
+ * fc_lport_init() - Initialize the lport layer for a local port
+ * @lport: The local port to initialize the exchange layer for
+ */
 int fc_lport_init(struct fc_lport *lport)
 {
        if (!lport->tt.lport_recv)
@@ -1500,7 +1593,253 @@ int fc_lport_init(struct fc_lport *lport)
        if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
                fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
 
-       INIT_LIST_HEAD(&lport->ema_list);
        return 0;
 }
 EXPORT_SYMBOL(fc_lport_init);
+
+/**
+ * fc_lport_bsg_resp() - The common response handler for FC Passthrough requests
+ * @sp:              The sequence for the FC Passthrough response
+ * @fp:              The response frame
+ * @info_arg: The BSG info that the response is for
+ */
+static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
+                             void *info_arg)
+{
+       struct fc_bsg_info *info = info_arg;
+       struct fc_bsg_job *job = info->job;
+       struct fc_lport *lport = info->lport;
+       struct fc_frame_header *fh;
+       size_t len;
+       void *buf;
+
+       if (IS_ERR(fp)) {
+               job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ?
+                       -ECONNABORTED : -ETIMEDOUT;
+               job->reply_len = sizeof(uint32_t);
+               job->state_flags |= FC_RQST_STATE_DONE;
+               job->job_done(job);
+               kfree(info);
+               return;
+       }
+
+       mutex_lock(&lport->lp_mutex);
+       fh = fc_frame_header_get(fp);
+       len = fr_len(fp) - sizeof(*fh);
+       buf = fc_frame_payload_get(fp, 0);
+
+       if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) {
+               /* Get the response code from the first frame payload */
+               unsigned short cmd = (info->rsp_code == FC_FS_ACC) ?
+                       ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) :
+                       (unsigned short)fc_frame_payload_op(fp);
+
+               /* Save the reply status of the job */
+               job->reply->reply_data.ctels_reply.status =
+                       (cmd == info->rsp_code) ?
+                       FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT;
+       }
+
+       job->reply->reply_payload_rcv_len +=
+               fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents,
+                                        &info->offset, KM_BIO_SRC_IRQ, NULL);
+
+       if (fr_eof(fp) == FC_EOF_T &&
+           (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
+           (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
+               if (job->reply->reply_payload_rcv_len >
+                   job->reply_payload.payload_len)
+                       job->reply->reply_payload_rcv_len =
+                               job->reply_payload.payload_len;
+               job->reply->result = 0;
+               job->state_flags |= FC_RQST_STATE_DONE;
+               job->job_done(job);
+               kfree(info);
+       }
+       fc_frame_free(fp);
+       mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * fc_lport_els_request() - Send ELS passthrough request
+ * @job:   The BSG Passthrough job
+ * @lport: The local port sending the request
+ * @did:   The destination port id
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static int fc_lport_els_request(struct fc_bsg_job *job,
+                               struct fc_lport *lport,
+                               u32 did, u32 tov)
+{
+       struct fc_bsg_info *info;
+       struct fc_frame *fp;
+       struct fc_frame_header *fh;
+       char *pp;
+       int len;
+
+       fp = fc_frame_alloc(lport, job->request_payload.payload_len);
+       if (!fp)
+               return -ENOMEM;
+
+       len = job->request_payload.payload_len;
+       pp = fc_frame_payload_get(fp, len);
+
+       sg_copy_to_buffer(job->request_payload.sg_list,
+                         job->request_payload.sg_cnt,
+                         pp, len);
+
+       fh = fc_frame_header_get(fp);
+       fh->fh_r_ctl = FC_RCTL_ELS_REQ;
+       hton24(fh->fh_d_id, did);
+       hton24(fh->fh_s_id, fc_host_port_id(lport->host));
+       fh->fh_type = FC_TYPE_ELS;
+       hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
+              FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+       fh->fh_cs_ctl = 0;
+       fh->fh_df_ctl = 0;
+       fh->fh_parm_offset = 0;
+
+       info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL);
+       if (!info) {
+               fc_frame_free(fp);
+               return -ENOMEM;
+       }
+
+       info->job = job;
+       info->lport = lport;
+       info->rsp_code = ELS_LS_ACC;
+       info->nents = job->reply_payload.sg_cnt;
+       info->sg = job->reply_payload.sg_list;
+
+       if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
+                                    NULL, info, tov))
+               return -ECOMM;
+       return 0;
+}
+
+/**
+ * fc_lport_ct_request() - Send CT Passthrough request
+ * @job:   The BSG Passthrough job
+ * @lport: The local port sending the request
+ * @did:   The destination FC-ID
+ * @tov:   The timeout period to wait for the response
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static int fc_lport_ct_request(struct fc_bsg_job *job,
+                              struct fc_lport *lport, u32 did, u32 tov)
+{
+       struct fc_bsg_info *info;
+       struct fc_frame *fp;
+       struct fc_frame_header *fh;
+       struct fc_ct_req *ct;
+       size_t len;
+
+       fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+                           job->request_payload.payload_len);
+       if (!fp)
+               return -ENOMEM;
+
+       len = job->request_payload.payload_len;
+       ct = fc_frame_payload_get(fp, len);
+
+       sg_copy_to_buffer(job->request_payload.sg_list,
+                         job->request_payload.sg_cnt,
+                         ct, len);
+
+       fh = fc_frame_header_get(fp);
+       fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL;
+       hton24(fh->fh_d_id, did);
+       hton24(fh->fh_s_id, fc_host_port_id(lport->host));
+       fh->fh_type = FC_TYPE_CT;
+       hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
+              FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+       fh->fh_cs_ctl = 0;
+       fh->fh_df_ctl = 0;
+       fh->fh_parm_offset = 0;
+
+       info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL);
+       if (!info) {
+               fc_frame_free(fp);
+               return -ENOMEM;
+       }
+
+       info->job = job;
+       info->lport = lport;
+       info->rsp_code = FC_FS_ACC;
+       info->nents = job->reply_payload.sg_cnt;
+       info->sg = job->reply_payload.sg_list;
+
+       if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp,
+                                    NULL, info, tov))
+               return -ECOMM;
+       return 0;
+}
+
+/**
+ * fc_lport_bsg_request() - The common entry point for sending
+ *                         FC Passthrough requests
+ * @job: The BSG passthrough job
+ */
+int fc_lport_bsg_request(struct fc_bsg_job *job)
+{
+       struct request *rsp = job->req->next_rq;
+       struct Scsi_Host *shost = job->shost;
+       struct fc_lport *lport = shost_priv(shost);
+       struct fc_rport *rport;
+       struct fc_rport_priv *rdata;
+       int rc = -EINVAL;
+       u32 did;
+
+       job->reply->reply_payload_rcv_len = 0;
+       rsp->resid_len = job->reply_payload.payload_len;
+
+       mutex_lock(&lport->lp_mutex);
+
+       switch (job->request->msgcode) {
+       case FC_BSG_RPT_ELS:
+               rport = job->rport;
+               if (!rport)
+                       break;
+
+               rdata = rport->dd_data;
+               rc = fc_lport_els_request(job, lport, rport->port_id,
+                                         rdata->e_d_tov);
+               break;
+
+       case FC_BSG_RPT_CT:
+               rport = job->rport;
+               if (!rport)
+                       break;
+
+               rdata = rport->dd_data;
+               rc = fc_lport_ct_request(job, lport, rport->port_id,
+                                        rdata->e_d_tov);
+               break;
+
+       case FC_BSG_HST_CT:
+               did = ntoh24(job->request->rqst_data.h_ct.port_id);
+               if (did == FC_FID_DIR_SERV)
+                       rdata = lport->dns_rdata;
+               else
+                       rdata = lport->tt.rport_lookup(lport, did);
+
+               if (!rdata)
+                       break;
+
+               rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov);
+               break;
+
+       case FC_BSG_HST_ELS_NOLOGIN:
+               did = ntoh24(job->request->rqst_data.h_els.port_id);
+               rc = fc_lport_els_request(job, lport, did, lport->e_d_tov);
+               break;
+       }
+
+       mutex_unlock(&lport->lp_mutex);
+       return rc;
+}
+EXPORT_SYMBOL(fc_lport_bsg_request);
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
new file mode 100644 (file)
index 0000000..c68f6c7
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+/*
+ * NPIV VN_Port helper functions for libfc
+ */
+
+#include <scsi/libfc.h>
+
+/**
+ * fc_vport_create() - Create a new NPIV vport instance
+ * @vport: fc_vport structure from scsi_transport_fc
+ * @privsize: driver private data size to allocate along with the Scsi_Host
+ */
+
+struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)
+{
+       struct Scsi_Host *shost = vport_to_shost(vport);
+       struct fc_lport *n_port = shost_priv(shost);
+       struct fc_lport *vn_port;
+
+       vn_port = libfc_host_alloc(shost->hostt, privsize);
+       if (!vn_port)
+               goto err_out;
+       if (fc_exch_mgr_list_clone(n_port, vn_port))
+               goto err_put;
+
+       vn_port->vport = vport;
+       vport->dd_data = vn_port;
+
+       mutex_lock(&n_port->lp_mutex);
+       list_add_tail(&vn_port->list, &n_port->vports);
+       mutex_unlock(&n_port->lp_mutex);
+
+       return vn_port;
+
+err_put:
+       scsi_host_put(vn_port->host);
+err_out:
+       return NULL;
+}
+EXPORT_SYMBOL(libfc_vport_create);
+
+/**
+ * fc_vport_id_lookup() - find NPIV lport that matches a given fabric ID
+ * @n_port: Top level N_Port which may have multiple NPIV VN_Ports
+ * @port_id: Fabric ID to find a match for
+ *
+ * Returns: matching lport pointer or NULL if there is no match
+ */
+struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
+{
+       struct fc_lport *lport = NULL;
+       struct fc_lport *vn_port;
+
+       if (fc_host_port_id(n_port->host) == port_id)
+               return n_port;
+
+       mutex_lock(&n_port->lp_mutex);
+       list_for_each_entry(vn_port, &n_port->vports, list) {
+               if (fc_host_port_id(vn_port->host) == port_id) {
+                       lport = vn_port;
+                       break;
+               }
+       }
+       mutex_unlock(&n_port->lp_mutex);
+
+       return lport;
+}
+
+/*
+ * When setting the link state of vports during an lport state change, it's
+ * necessary to hold the lp_mutex of both the N_Port and the VN_Port.
+ * This tells the lockdep engine to treat the nested locking of the VN_Port
+ * as a different lock class.
+ */
+enum libfc_lport_mutex_class {
+       LPORT_MUTEX_NORMAL = 0,
+       LPORT_MUTEX_VN_PORT = 1,
+};
+
+/**
+ * __fc_vport_setlink() - update link and status on a VN_Port
+ * @n_port: parent N_Port
+ * @vn_port: VN_Port to update
+ *
+ * Locking: must be called with both the N_Port and VN_Port lp_mutex held
+ */
+static void __fc_vport_setlink(struct fc_lport *n_port,
+                              struct fc_lport *vn_port)
+{
+       struct fc_vport *vport = vn_port->vport;
+
+       if (vn_port->state == LPORT_ST_DISABLED)
+               return;
+
+       if (n_port->state == LPORT_ST_READY) {
+               if (n_port->npiv_enabled) {
+                       fc_vport_set_state(vport, FC_VPORT_INITIALIZING);
+                       __fc_linkup(vn_port);
+               } else {
+                       fc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
+                       __fc_linkdown(vn_port);
+               }
+       } else {
+               fc_vport_set_state(vport, FC_VPORT_LINKDOWN);
+               __fc_linkdown(vn_port);
+       }
+}
+
+/**
+ * fc_vport_setlink() - update link and status on a VN_Port
+ * @vn_port: virtual port to update
+ */
+void fc_vport_setlink(struct fc_lport *vn_port)
+{
+       struct fc_vport *vport = vn_port->vport;
+       struct Scsi_Host *shost = vport_to_shost(vport);
+       struct fc_lport *n_port = shost_priv(shost);
+
+       mutex_lock(&n_port->lp_mutex);
+       mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
+       __fc_vport_setlink(n_port, vn_port);
+       mutex_unlock(&vn_port->lp_mutex);
+       mutex_unlock(&n_port->lp_mutex);
+}
+EXPORT_SYMBOL(fc_vport_setlink);
+
+/**
+ * fc_vports_linkchange() - change the link state of all vports
+ * @n_port: Parent N_Port that has changed state
+ *
+ * Locking: called with the n_port lp_mutex held
+ */
+void fc_vports_linkchange(struct fc_lport *n_port)
+{
+       struct fc_lport *vn_port;
+
+       list_for_each_entry(vn_port, &n_port->vports, list) {
+               mutex_lock_nested(&vn_port->lp_mutex, LPORT_MUTEX_VN_PORT);
+               __fc_vport_setlink(n_port, vn_port);
+               mutex_unlock(&vn_port->lp_mutex);
+       }
+}
+
index 03ea674..35ca0e7 100644 (file)
@@ -55,6 +55,8 @@
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
 
+#include "fc_libfc.h"
+
 struct workqueue_struct *rport_event_queue;
 
 static void fc_rport_enter_plogi(struct fc_rport_priv *);
@@ -86,12 +88,13 @@ static const char *fc_rport_state_names[] = {
        [RPORT_ST_LOGO] = "LOGO",
        [RPORT_ST_ADISC] = "ADISC",
        [RPORT_ST_DELETE] = "Delete",
+       [RPORT_ST_RESTART] = "Restart",
 };
 
 /**
- * fc_rport_lookup() - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
+ * fc_rport_lookup() - Lookup a remote port by port_id
+ * @lport:   The local port to lookup the remote port on
+ * @port_id: The remote port ID to look up
  */
 static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
                                             u32 port_id)
@@ -99,16 +102,17 @@ static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
        struct fc_rport_priv *rdata;
 
        list_for_each_entry(rdata, &lport->disc.rports, peers)
-               if (rdata->ids.port_id == port_id &&
-                   rdata->rp_state != RPORT_ST_DELETE)
+               if (rdata->ids.port_id == port_id)
                        return rdata;
        return NULL;
 }
 
 /**
  * fc_rport_create() - Create a new remote port
- * @lport:   The local port that the new remote port is for
- * @port_id: The port ID for the new remote port
+ * @lport: The local port this remote port will be associated with
+ * @ids:   The identifiers for the new remote port
+ *
+ * The remote port will start in the INIT state.
  *
  * Locking note:  must be called with the disc_mutex held.
  */
@@ -147,8 +151,8 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
 }
 
 /**
- * fc_rport_destroy() - free a remote port after last reference is released.
- * @kref: pointer to kref inside struct fc_rport_priv
+ * fc_rport_destroy() - Free a remote port after last reference is released
+ * @kref: The remote port's kref
  */
 static void fc_rport_destroy(struct kref *kref)
 {
@@ -159,8 +163,8 @@ static void fc_rport_destroy(struct kref *kref)
 }
 
 /**
- * fc_rport_state() - return a string for the state the rport is in
- * @rdata: remote port private data
+ * fc_rport_state() - Return a string identifying the remote port's state
+ * @rdata: The remote port
  */
 static const char *fc_rport_state(struct fc_rport_priv *rdata)
 {
@@ -173,9 +177,9 @@ static const char *fc_rport_state(struct fc_rport_priv *rdata)
 }
 
 /**
- * fc_set_rport_loss_tmo() - Set the remote port loss timeout in seconds.
- * @rport: Pointer to Fibre Channel remote port structure
- * @timeout: timeout in seconds
+ * fc_set_rport_loss_tmo() - Set the remote port loss timeout
+ * @rport:   The remote port that gets a new timeout value
+ * @timeout: The new timeout value (in seconds)
  */
 void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
 {
@@ -187,9 +191,11 @@ void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout)
 EXPORT_SYMBOL(fc_set_rport_loss_tmo);
 
 /**
- * fc_plogi_get_maxframe() - Get max payload from the common service parameters
- * @flp: FLOGI payload structure
- * @maxval: upper limit, may be less than what is in the service parameters
+ * fc_plogi_get_maxframe() - Get the maximum payload from the common service
+ *                          parameters in a FLOGI frame
+ * @flp:    The FLOGI payload
+ * @maxval: The maximum frame size upper limit; this may be less than what
+ *         is in the service parameters
  */
 static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
                                          unsigned int maxval)
@@ -210,9 +216,9 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
 }
 
 /**
- * fc_rport_state_enter() - Change the rport's state
- * @rdata: The rport whose state should change
- * @new: The new state of the rport
+ * fc_rport_state_enter() - Change the state of a remote port
+ * @rdata: The remote port whose state should change
+ * @new:   The new state
  *
  * Locking Note: Called with the rport lock held
  */
@@ -224,17 +230,22 @@ static void fc_rport_state_enter(struct fc_rport_priv *rdata,
        rdata->rp_state = new;
 }
 
+/**
+ * fc_rport_work() - Handler for remote port events in the rport_event_queue
+ * @work: Handle to the remote port being dequeued
+ */
 static void fc_rport_work(struct work_struct *work)
 {
        u32 port_id;
        struct fc_rport_priv *rdata =
                container_of(work, struct fc_rport_priv, event_work);
-       struct fc_rport_libfc_priv *rp;
+       struct fc_rport_libfc_priv *rpriv;
        enum fc_rport_event event;
        struct fc_lport *lport = rdata->local_port;
        struct fc_rport_operations *rport_ops;
        struct fc_rport_identifiers ids;
        struct fc_rport *rport;
+       int restart = 0;
 
        mutex_lock(&rdata->rp_mutex);
        event = rdata->event;
@@ -265,12 +276,12 @@ static void fc_rport_work(struct work_struct *work)
                rport->maxframe_size = rdata->maxframe_size;
                rport->supported_classes = rdata->supported_classes;
 
-               rp = rport->dd_data;
-               rp->local_port = lport;
-               rp->rp_state = rdata->rp_state;
-               rp->flags = rdata->flags;
-               rp->e_d_tov = rdata->e_d_tov;
-               rp->r_a_tov = rdata->r_a_tov;
+               rpriv = rport->dd_data;
+               rpriv->local_port = lport;
+               rpriv->rp_state = rdata->rp_state;
+               rpriv->flags = rdata->flags;
+               rpriv->e_d_tov = rdata->e_d_tov;
+               rpriv->r_a_tov = rdata->r_a_tov;
                mutex_unlock(&rdata->rp_mutex);
 
                if (rport_ops && rport_ops->event_callback) {
@@ -287,8 +298,19 @@ static void fc_rport_work(struct work_struct *work)
                mutex_unlock(&rdata->rp_mutex);
 
                if (port_id != FC_FID_DIR_SERV) {
+                       /*
+                        * We must drop rp_mutex before taking disc_mutex.
+                        * Re-evaluate state to allow for restart.
+                        * A transition to RESTART state must only happen
+                        * while disc_mutex is held and rdata is on the list.
+                        */
                        mutex_lock(&lport->disc.disc_mutex);
-                       list_del(&rdata->peers);
+                       mutex_lock(&rdata->rp_mutex);
+                       if (rdata->rp_state == RPORT_ST_RESTART)
+                               restart = 1;
+                       else
+                               list_del(&rdata->peers);
+                       mutex_unlock(&rdata->rp_mutex);
                        mutex_unlock(&lport->disc.disc_mutex);
                }
 
@@ -305,14 +327,20 @@ static void fc_rport_work(struct work_struct *work)
                lport->tt.exch_mgr_reset(lport, port_id, 0);
 
                if (rport) {
-                       rp = rport->dd_data;
-                       rp->rp_state = RPORT_ST_DELETE;
+                       rpriv = rport->dd_data;
+                       rpriv->rp_state = RPORT_ST_DELETE;
                        mutex_lock(&rdata->rp_mutex);
                        rdata->rport = NULL;
                        mutex_unlock(&rdata->rp_mutex);
                        fc_remote_port_delete(rport);
                }
-               kref_put(&rdata->kref, lport->tt.rport_destroy);
+               if (restart) {
+                       mutex_lock(&rdata->rp_mutex);
+                       FC_RPORT_DBG(rdata, "work restart\n");
+                       fc_rport_enter_plogi(rdata);
+                       mutex_unlock(&rdata->rp_mutex);
+               } else
+                       kref_put(&rdata->kref, lport->tt.rport_destroy);
                break;
 
        default:
@@ -323,7 +351,7 @@ static void fc_rport_work(struct work_struct *work)
 
 /**
  * fc_rport_login() - Start the remote port login state machine
- * @rdata: private remote port
+ * @rdata: The remote port to be logged in to
  *
  * Locking Note: Called without the rport lock held. This
  * function will hold the rport lock, call an _enter_*
@@ -342,6 +370,12 @@ int fc_rport_login(struct fc_rport_priv *rdata)
                FC_RPORT_DBG(rdata, "ADISC port\n");
                fc_rport_enter_adisc(rdata);
                break;
+       case RPORT_ST_RESTART:
+               break;
+       case RPORT_ST_DELETE:
+               FC_RPORT_DBG(rdata, "Restart deleted port\n");
+               fc_rport_state_enter(rdata, RPORT_ST_RESTART);
+               break;
        default:
                FC_RPORT_DBG(rdata, "Login to port\n");
                fc_rport_enter_plogi(rdata);
@@ -353,9 +387,9 @@ int fc_rport_login(struct fc_rport_priv *rdata)
 }
 
 /**
- * fc_rport_enter_delete() - schedule a remote port to be deleted.
- * @rdata: private remote port
- * @event: event to report as the reason for deletion
+ * fc_rport_enter_delete() - Schedule a remote port to be deleted
+ * @rdata: The remote port to be deleted
+ * @event: The event to report as the reason for deletion
  *
  * Locking Note: Called with the rport lock held.
  *
@@ -382,8 +416,8 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
 }
 
 /**
- * fc_rport_logoff() - Logoff and remove an rport
- * @rdata: private remote port
+ * fc_rport_logoff() - Logoff and remove a remote port
+ * @rdata: The remote port to be logged off of
  *
  * Locking Note: Called without the rport lock held. This
  * function will hold the rport lock, call an _enter_*
@@ -397,26 +431,27 @@ int fc_rport_logoff(struct fc_rport_priv *rdata)
 
        if (rdata->rp_state == RPORT_ST_DELETE) {
                FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
-               mutex_unlock(&rdata->rp_mutex);
                goto out;
        }
 
-       fc_rport_enter_logo(rdata);
+       if (rdata->rp_state == RPORT_ST_RESTART)
+               FC_RPORT_DBG(rdata, "Port in Restart state, deleting\n");
+       else
+               fc_rport_enter_logo(rdata);
 
        /*
         * Change the state to Delete so that we discard
         * the response.
         */
        fc_rport_enter_delete(rdata, RPORT_EV_STOP);
-       mutex_unlock(&rdata->rp_mutex);
-
 out:
+       mutex_unlock(&rdata->rp_mutex);
        return 0;
 }
 
 /**
- * fc_rport_enter_ready() - The rport is ready
- * @rdata: private remote port
+ * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state
+ * @rdata: The remote port that is ready
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this routine.
@@ -433,8 +468,8 @@ static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
 }
 
 /**
- * fc_rport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_rport_priv
+ * fc_rport_timeout() - Handler for the retry_work timer
+ * @work: Handle to the remote port that has timed out
  *
  * Locking Note: Called without the rport lock held. This
  * function will hold the rport lock, call an _enter_*
@@ -466,6 +501,7 @@ static void fc_rport_timeout(struct work_struct *work)
        case RPORT_ST_READY:
        case RPORT_ST_INIT:
        case RPORT_ST_DELETE:
+       case RPORT_ST_RESTART:
                break;
        }
 
@@ -474,8 +510,8 @@ static void fc_rport_timeout(struct work_struct *work)
 
 /**
  * fc_rport_error() - Error handler, called once retries have been exhausted
- * @rdata: private remote port
- * @fp: The frame pointer
+ * @rdata: The remote port the error is happened on
+ * @fp:           The error code encapsulated in a frame pointer
  *
  * Locking Note: The rport lock is expected to be held before
  * calling this routine
@@ -499,6 +535,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
                fc_rport_enter_logo(rdata);
                break;
        case RPORT_ST_DELETE:
+       case RPORT_ST_RESTART:
        case RPORT_ST_READY:
        case RPORT_ST_INIT:
                break;
@@ -506,9 +543,9 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
 }
 
 /**
- * fc_rport_error_retry() - Error handler when retries are desired
- * @rdata: private remote port data
- * @fp: The frame pointer
+ * fc_rport_error_retry() - Handler for remote port state retries
+ * @rdata: The remote port whose state is to be retried
+ * @fp:           The error code encapsulated in a frame pointer
  *
  * If the error was an exchange timeout retry immediately,
  * otherwise wait for E_D_TOV.
@@ -540,10 +577,10 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
 }
 
 /**
- * fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_plogi_recv_resp() - Handler for ELS PLOGI responses
+ * @sp:               The sequence the PLOGI is on
+ * @fp:               The PLOGI response frame
+ * @rdata_arg: The remote port that sent the PLOGI response
  *
  * Locking Note: This function will be called without the rport lock
  * held, but it will lock, call an _enter_* function or fc_rport_error
@@ -606,8 +643,8 @@ err:
 }
 
 /**
- * fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_plogi() - Send Port Login (PLOGI) request
+ * @rdata: The remote port to send a PLOGI to
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this routine.
@@ -631,17 +668,18 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
        rdata->e_d_tov = lport->e_d_tov;
 
        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
-                                 fc_rport_plogi_resp, rdata, lport->e_d_tov))
-               fc_rport_error_retry(rdata, fp);
+                                 fc_rport_plogi_resp, rdata,
+                                 2 * lport->r_a_tov))
+               fc_rport_error_retry(rdata, NULL);
        else
                kref_get(&rdata->kref);
 }
 
 /**
  * fc_rport_prli_resp() - Process Login (PRLI) response handler
- * @sp: current sequence in the PRLI exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * @sp:               The sequence the PRLI response was on
+ * @fp:               The PRLI response frame
+ * @rdata_arg: The remote port that sent the PRLI response
  *
  * Locking Note: This function will be called without the rport lock
  * held, but it will lock, call an _enter_* function or fc_rport_error
@@ -710,10 +748,10 @@ err:
 }
 
 /**
- * fc_rport_logo_resp() - Logout (LOGO) response handler
- * @sp: current sequence in the LOGO exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_logo_resp() - Handler for logout (LOGO) responses
+ * @sp:               The sequence the LOGO was on
+ * @fp:               The LOGO response frame
+ * @rdata_arg: The remote port that sent the LOGO response
  *
  * Locking Note: This function will be called without the rport lock
  * held, but it will lock, call an _enter_* function or fc_rport_error
@@ -756,8 +794,8 @@ err:
 }
 
 /**
- * fc_rport_enter_prli() - Send Process Login (PRLI) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_prli() - Send Process Login (PRLI) request
+ * @rdata: The remote port to send the PRLI request to
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this routine.
@@ -792,17 +830,18 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
        }
 
        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
-                                 fc_rport_prli_resp, rdata, lport->e_d_tov))
-               fc_rport_error_retry(rdata, fp);
+                                 fc_rport_prli_resp, rdata,
+                                 2 * lport->r_a_tov))
+               fc_rport_error_retry(rdata, NULL);
        else
                kref_get(&rdata->kref);
 }
 
 /**
- * fc_rport_els_rtv_resp() - Request Timeout Value response handler
- * @sp: current sequence in the RTV exchange
- * @fp: response frame
- * @rdata_arg: private remote port data
+ * fc_rport_els_rtv_resp() - Handler for Request Timeout Value (RTV) responses
+ * @sp:               The sequence the RTV was on
+ * @fp:               The RTV response frame
+ * @rdata_arg: The remote port that sent the RTV response
  *
  * Many targets don't seem to support this.
  *
@@ -865,8 +904,8 @@ err:
 }
 
 /**
- * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request
+ * @rdata: The remote port to send the RTV request to
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this routine.
@@ -888,15 +927,16 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
        }
 
        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
-                                    fc_rport_rtv_resp, rdata, lport->e_d_tov))
-               fc_rport_error_retry(rdata, fp);
+                                 fc_rport_rtv_resp, rdata,
+                                 2 * lport->r_a_tov))
+               fc_rport_error_retry(rdata, NULL);
        else
                kref_get(&rdata->kref);
 }
 
 /**
- * fc_rport_enter_logo() - Send Logout (LOGO) request to peer
- * @rdata: private remote port data
+ * fc_rport_enter_logo() - Send a logout (LOGO) request
+ * @rdata: The remote port to send the LOGO request to
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this routine.
@@ -918,24 +958,25 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
        }
 
        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
-                                 fc_rport_logo_resp, rdata, lport->e_d_tov))
-               fc_rport_error_retry(rdata, fp);
+                                 fc_rport_logo_resp, rdata,
+                                 2 * lport->r_a_tov))
+               fc_rport_error_retry(rdata, NULL);
        else
                kref_get(&rdata->kref);
 }
 
 /**
- * fc_rport_els_adisc_resp() - Address Discovery response handler
- * @sp: current sequence in the ADISC exchange
- * @fp: response frame
- * @rdata_arg: remote port private.
+ * fc_rport_els_adisc_resp() - Handler for Address Discovery (ADISC) responses
+ * @sp:               The sequence the ADISC response was on
+ * @fp:               The ADISC response frame
+ * @rdata_arg: The remote port that sent the ADISC response
  *
  * Locking Note: This function will be called without the rport lock
  * held, but it will lock, call an _enter_* function or fc_rport_error
  * and then unlock the rport.
  */
 static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
-                             void *rdata_arg)
+                               void *rdata_arg)
 {
        struct fc_rport_priv *rdata = rdata_arg;
        struct fc_els_adisc *adisc;
@@ -983,8 +1024,8 @@ err:
 }
 
 /**
- * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer
- * @rdata: remote port private data
+ * fc_rport_enter_adisc() - Send Address Discover (ADISC) request
+ * @rdata: The remote port to send the ADISC request to
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this routine.
@@ -1005,17 +1046,18 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
                return;
        }
        if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
-                                 fc_rport_adisc_resp, rdata, lport->e_d_tov))
-               fc_rport_error_retry(rdata, fp);
+                                 fc_rport_adisc_resp, rdata,
+                                 2 * lport->r_a_tov))
+               fc_rport_error_retry(rdata, NULL);
        else
                kref_get(&rdata->kref);
 }
 
 /**
- * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request
- * @rdata: remote port private
- * @sp: current sequence in the ADISC exchange
- * @in_fp: ADISC request frame
+ * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
+ * @rdata: The remote port that sent the ADISC request
+ * @sp:           The sequence the ADISC request was on
+ * @in_fp: The ADISC request frame
  *
  * Locking Note:  Called with the lport and rport locks held.
  */
@@ -1056,10 +1098,82 @@ drop:
 }
 
 /**
- * fc_rport_recv_els_req() - handle a validated ELS request.
- * @lport: Fibre Channel local port
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
+ * fc_rport_recv_rls_req() - Handle received Read Link Status request
+ * @rdata: The remote port that sent the RLS request
+ * @sp:        The sequence that the RLS was on
+ * @rx_fp: The PRLI request frame
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this function.
+ */
+static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
+                                 struct fc_seq *sp, struct fc_frame *rx_fp)
+
+{
+       struct fc_lport *lport = rdata->local_port;
+       struct fc_frame *fp;
+       struct fc_exch *ep = fc_seq_exch(sp);
+       struct fc_els_rls *rls;
+       struct fc_els_rls_resp *rsp;
+       struct fc_els_lesb *lesb;
+       struct fc_seq_els_data rjt_data;
+       struct fc_host_statistics *hst;
+       u32 f_ctl;
+
+       FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
+                    fc_rport_state(rdata));
+
+       rls = fc_frame_payload_get(rx_fp, sizeof(*rls));
+       if (!rls) {
+               rjt_data.reason = ELS_RJT_PROT;
+               rjt_data.explan = ELS_EXPL_INV_LEN;
+               goto out_rjt;
+       }
+
+       fp = fc_frame_alloc(lport, sizeof(*rsp));
+       if (!fp) {
+               rjt_data.reason = ELS_RJT_UNAB;
+               rjt_data.explan = ELS_EXPL_INSUF_RES;
+               goto out_rjt;
+       }
+
+       rsp = fc_frame_payload_get(fp, sizeof(*rsp));
+       memset(rsp, 0, sizeof(*rsp));
+       rsp->rls_cmd = ELS_LS_ACC;
+       lesb = &rsp->rls_lesb;
+       if (lport->tt.get_lesb) {
+               /* get LESB from LLD if it supports it */
+               lport->tt.get_lesb(lport, lesb);
+       } else {
+               fc_get_host_stats(lport->host);
+               hst = &lport->host_stats;
+               lesb->lesb_link_fail = htonl(hst->link_failure_count);
+               lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count);
+               lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count);
+               lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count);
+               lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count);
+               lesb->lesb_inv_crc = htonl(hst->invalid_crc_count);
+       }
+
+       sp = lport->tt.seq_start_next(sp);
+       f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
+       fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+                      FC_TYPE_ELS, f_ctl, 0);
+       lport->tt.seq_send(lport, sp, fp);
+       goto out;
+
+out_rjt:
+       rjt_data.fp = NULL;
+       lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+out:
+       fc_frame_free(rx_fp);
+}
+
+/**
+ * fc_rport_recv_els_req() - Handler for validated ELS requests
+ * @lport: The local port that received the ELS request
+ * @sp:           The sequence that the ELS request was on
+ * @fp:           The ELS request frame
  *
  * Handle incoming ELS requests that require port login.
  * The ELS opcode has already been validated by the caller.
@@ -1117,6 +1231,9 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
                els_data.fp = fp;
                lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
                break;
+       case ELS_RLS:
+               fc_rport_recv_rls_req(rdata, sp, fp);
+               break;
        default:
                fc_frame_free(fp);      /* can't happen */
                break;
@@ -1131,10 +1248,10 @@ reject:
 }
 
 /**
- * fc_rport_recv_req() - Handle a received ELS request from a rport
- * @sp: current sequence in the PLOGI exchange
- * @fp: response frame
- * @lport: Fibre Channel local port
+ * fc_rport_recv_req() - Handler for requests
+ * @sp:           The sequence the request was on
+ * @fp:           The request frame
+ * @lport: The local port that received the request
  *
  * Locking Note: Called with the lport lock held.
  */
@@ -1161,6 +1278,7 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
        case ELS_ADISC:
        case ELS_RRQ:
        case ELS_REC:
+       case ELS_RLS:
                fc_rport_recv_els_req(lport, sp, fp);
                break;
        default:
@@ -1174,10 +1292,10 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 }
 
 /**
- * fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
- * @lport: local port
- * @sp: current sequence in the PLOGI exchange
- * @fp: PLOGI request frame
+ * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
+ * @lport: The local port that received the PLOGI request
+ * @sp:           The sequence that the PLOGI request was on
+ * @rx_fp: The PLOGI request frame
  *
  * Locking Note: The rport lock is held before calling this function.
  */
@@ -1248,6 +1366,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
                }
                break;
        case RPORT_ST_PRLI:
+       case RPORT_ST_RTV:
        case RPORT_ST_READY:
        case RPORT_ST_ADISC:
                FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
@@ -1255,11 +1374,14 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
                /* XXX TBD - should reset */
                break;
        case RPORT_ST_DELETE:
-       default:
-               FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
-                            rdata->rp_state);
-               fc_frame_free(rx_fp);
-               goto out;
+       case RPORT_ST_LOGO:
+       case RPORT_ST_RESTART:
+               FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
+                            fc_rport_state(rdata));
+               mutex_unlock(&rdata->rp_mutex);
+               rjt_data.reason = ELS_RJT_BUSY;
+               rjt_data.explan = ELS_EXPL_NONE;
+               goto reject;
        }
 
        /*
@@ -1295,10 +1417,10 @@ reject:
 }
 
 /**
- * fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request
- * @rdata: private remote port data
- * @sp: current sequence in the PRLI exchange
- * @fp: PRLI request frame
+ * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
+ * @rdata: The remote port that sent the PRLI request
+ * @sp:           The sequence that the PRLI was on
+ * @rx_fp: The PRLI request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
@@ -1402,7 +1524,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
                                break;
                        case FC_TYPE_FCP:
                                fcp_parm = ntohl(rspp->spp_params);
-                               if (fcp_parm * FCP_SPPF_RETRY)
+                               if (fcp_parm & FCP_SPPF_RETRY)
                                        rdata->flags |= FC_RP_FLAGS_RETRY;
                                rdata->supported_classes = FC_COS_CLASS3;
                                if (fcp_parm & FCP_SPPF_INIT_FCN)
@@ -1452,10 +1574,10 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 }
 
 /**
- * fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request
- * @rdata: private remote port data
- * @sp: current sequence in the PRLO exchange
- * @fp: PRLO request frame
+ * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
+ * @rdata: The remote port that sent the PRLO request
+ * @sp:           The sequence that the PRLO was on
+ * @fp:           The PRLO request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
@@ -1482,10 +1604,10 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 }
 
 /**
- * fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request
- * @lport: local port.
- * @sp: current sequence in the LOGO exchange
- * @fp: LOGO request frame
+ * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests
+ * @lport: The local port that received the LOGO request
+ * @sp:           The sequence that the LOGO request was on
+ * @fp:           The LOGO request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
@@ -1510,14 +1632,14 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
                FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
                             fc_rport_state(rdata));
 
+               fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+
                /*
-                * If the remote port was created due to discovery,
-                * log back in.  It may have seen a stale RSCN about us.
+                * If the remote port was created due to discovery, set state
+                * to log back in.  It may have seen a stale RSCN about us.
                 */
-               if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id)
-                       fc_rport_enter_plogi(rdata);
-               else
-                       fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+               if (rdata->disc_id)
+                       fc_rport_state_enter(rdata, RPORT_ST_RESTART);
                mutex_unlock(&rdata->rp_mutex);
        } else
                FC_RPORT_ID_DBG(lport, sid,
@@ -1526,11 +1648,18 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
        fc_frame_free(fp);
 }
 
+/**
+ * fc_rport_flush_queue() - Flush the rport_event_queue
+ */
 static void fc_rport_flush_queue(void)
 {
        flush_workqueue(rport_event_queue);
 }
 
+/**
+ * fc_rport_init() - Initialize the remote port layer for a local port
+ * @lport: The local port to initialize the remote port layer for
+ */
 int fc_rport_init(struct fc_lport *lport)
 {
        if (!lport->tt.rport_lookup)
@@ -1558,25 +1687,33 @@ int fc_rport_init(struct fc_lport *lport)
 }
 EXPORT_SYMBOL(fc_rport_init);
 
-int fc_setup_rport(void)
+/**
+ * fc_setup_rport() - Initialize the rport_event_queue
+ */
+int fc_setup_rport()
 {
        rport_event_queue = create_singlethread_workqueue("fc_rport_eq");
        if (!rport_event_queue)
                return -ENOMEM;
        return 0;
 }
-EXPORT_SYMBOL(fc_setup_rport);
 
-void fc_destroy_rport(void)
+/**
+ * fc_destroy_rport() - Destroy the rport_event_queue
+ */
+void fc_destroy_rport()
 {
        destroy_workqueue(rport_event_queue);
 }
-EXPORT_SYMBOL(fc_destroy_rport);
 
+/**
+ * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port
+ * @rport: The remote port whose I/O should be terminated
+ */
 void fc_rport_terminate_io(struct fc_rport *rport)
 {
-       struct fc_rport_libfc_priv *rp = rport->dd_data;
-       struct fc_lport *lport = rp->local_port;
+       struct fc_rport_libfc_priv *rpriv = rport->dd_data;
+       struct fc_lport *lport = rpriv->local_port;
 
        lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
        lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
index f1a4246..b7689f3 100644 (file)
@@ -265,6 +265,88 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
        return 0;
 }
 
+/**
+ * iscsi_check_tmf_restrictions - check if a task is affected by TMF
+ * @task: iscsi task
+ * @opcode: opcode to check for
+ *
+ * During TMF a task has to be checked if it's affected.
+ * All unrelated I/O can be passed through, but I/O to the
+ * affected LUN should be restricted.
+ * If 'fast_abort' is set we won't be sending any I/O to the
+ * affected LUN.
+ * Otherwise the target is waiting for all TTTs to be completed,
+ * so we have to send all outstanding Data-Out PDUs to the target.
+ */
+static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode)
+{
+       struct iscsi_conn *conn = task->conn;
+       struct iscsi_tm *tmf = &conn->tmhdr;
+       unsigned int hdr_lun;
+
+       if (conn->tmf_state == TMF_INITIAL)
+               return 0;
+
+       if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC)
+               return 0;
+
+       switch (ISCSI_TM_FUNC_VALUE(tmf)) {
+       case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
+               /*
+                * Allow PDUs for unrelated LUNs
+                */
+               hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun);
+               if (hdr_lun != task->sc->device->lun)
+                       return 0;
+               /* fall through */
+       case ISCSI_TM_FUNC_TARGET_WARM_RESET:
+               /*
+                * Fail all SCSI cmd PDUs
+                */
+               if (opcode != ISCSI_OP_SCSI_DATA_OUT) {
+                       iscsi_conn_printk(KERN_INFO, conn,
+                                         "task [op %x/%x itt "
+                                         "0x%x/0x%x] "
+                                         "rejected.\n",
+                                         task->hdr->opcode, opcode,
+                                         task->itt, task->hdr_itt);
+                       return -EACCES;
+               }
+               /*
+                * And also all data-out PDUs in response to R2T
+                * if fast_abort is set.
+                */
+               if (conn->session->fast_abort) {
+                       iscsi_conn_printk(KERN_INFO, conn,
+                                         "task [op %x/%x itt "
+                                         "0x%x/0x%x] fast abort.\n",
+                                         task->hdr->opcode, opcode,
+                                         task->itt, task->hdr_itt);
+                       return -EACCES;
+               }
+               break;
+       case ISCSI_TM_FUNC_ABORT_TASK:
+               /*
+                * the caller has already checked if the task
+                * they want to abort was in the pending queue so if
+                * we are here the cmd pdu has gone out already, and
+                * we will only hit this for data-outs
+                */
+               if (opcode == ISCSI_OP_SCSI_DATA_OUT &&
+                   task->hdr_itt == tmf->rtt) {
+                       ISCSI_DBG_SESSION(conn->session,
+                                         "Preventing task %x/%x from sending "
+                                         "data-out due to abort task in "
+                                         "progress\n", task->itt,
+                                         task->hdr_itt);
+                       return -EACCES;
+               }
+               break;
+       }
+
+       return 0;
+}
+
 /**
  * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
  * @task: iscsi task
@@ -282,6 +364,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
        itt_t itt;
        int rc;
 
+       rc = iscsi_check_tmf_restrictions(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)
@@ -577,12 +663,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
        struct iscsi_session *session = conn->session;
        struct iscsi_hdr *hdr = task->hdr;
        struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+       uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
 
        if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
                return -ENOTCONN;
 
-       if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
-           hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+       if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT)
                nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
        /*
         * pre-format CmdSN for outgoing PDU.
@@ -590,9 +676,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
        nop->cmdsn = cpu_to_be32(session->cmdsn);
        if (hdr->itt != RESERVED_ITT) {
                /*
-                * TODO: We always use immediate, so we never hit this.
+                * TODO: We always use immediate for normal session pdus.
                 * If we start to send tmfs or nops as non-immediate then
                 * we should start checking the cmdsn numbers for mgmt tasks.
+                *
+                * During discovery sessions iscsid sends TEXT as non immediate,
+                * but we always only send one PDU at a time.
                 */
                if (conn->c_stage == ISCSI_CONN_STARTED &&
                    !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
@@ -620,22 +709,28 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 {
        struct iscsi_session *session = conn->session;
        struct iscsi_host *ihost = shost_priv(session->host);
+       uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK;
        struct iscsi_task *task;
        itt_t itt;
 
        if (session->state == ISCSI_STATE_TERMINATE)
                return NULL;
 
-       if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
-           hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+       if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) {
                /*
                 * Login and Text are sent serially, in
                 * request-followed-by-response sequence.
                 * Same task can be used. Same ITT must be used.
                 * Note that login_task is preallocated at conn_create().
                 */
+               if (conn->login_task->state != ISCSI_TASK_FREE) {
+                       iscsi_conn_printk(KERN_ERR, conn, "Login/Text in "
+                                         "progress. Cannot start new task.\n");
+                       return NULL;
+               }
+
                task = conn->login_task;
-       else {
+       else {
                if (session->state != ISCSI_STATE_LOGGED_IN)
                        return NULL;
 
@@ -1357,6 +1452,7 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_task);
  **/
 static int iscsi_data_xmit(struct iscsi_conn *conn)
 {
+       struct iscsi_task *task;
        int rc = 0;
 
        spin_lock_bh(&conn->session->lock);
@@ -1394,11 +1490,8 @@ check_mgmt:
 
        /* process pending command queue */
        while (!list_empty(&conn->cmdqueue)) {
-               if (conn->tmf_state == TMF_QUEUED)
-                       break;
-
-               conn->task = list_entry(conn->cmdqueue.next,
-                                        struct iscsi_task, running);
+               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_scsi_task(conn->task, DID_IMM_RETRY);
@@ -1406,7 +1499,7 @@ check_mgmt:
                }
                rc = iscsi_prep_scsi_cmd_pdu(conn->task);
                if (rc) {
-                       if (rc == -ENOMEM) {
+                       if (rc == -ENOMEM || rc == -EACCES) {
                                list_add_tail(&conn->task->running,
                                              &conn->cmdqueue);
                                conn->task = NULL;
@@ -1428,17 +1521,18 @@ check_mgmt:
        }
 
        while (!list_empty(&conn->requeue)) {
-               if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
-                       break;
-
                /*
                 * we always do fastlogout - conn stop code will clean up.
                 */
                if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
                        break;
 
-               conn->task = list_entry(conn->requeue.next,
-                                        struct iscsi_task, running);
+               task = list_entry(conn->requeue.next, struct iscsi_task,
+                                 running);
+               if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT))
+                       break;
+
+               conn->task = task;
                list_del_init(&conn->task->running);
                conn->task->state = ISCSI_TASK_RUNNING;
                rc = iscsi_xmit_task(conn);
@@ -1591,7 +1685,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
        if (!ihost->workq) {
                reason = iscsi_prep_scsi_cmd_pdu(task);
                if (reason) {
-                       if (reason == -ENOMEM) {
+                       if (reason == -ENOMEM ||  reason == -EACCES) {
                                reason = FAILURE_OOM;
                                goto prepd_reject;
                        } else {
@@ -1643,9 +1737,21 @@ fault:
 }
 EXPORT_SYMBOL_GPL(iscsi_queuecommand);
 
-int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
+int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
 {
-       scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+       switch (reason) {
+       case SCSI_QDEPTH_DEFAULT:
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+               break;
+       case SCSI_QDEPTH_QFULL:
+               scsi_track_queue_full(sdev, depth);
+               break;
+       case SCSI_QDEPTH_RAMP_UP:
+               scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
        return sdev->queue_depth;
 }
 EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
@@ -1660,72 +1766,6 @@ int iscsi_target_alloc(struct scsi_target *starget)
 }
 EXPORT_SYMBOL_GPL(iscsi_target_alloc);
 
-void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
-{
-       struct iscsi_session *session = cls_session->dd_data;
-
-       spin_lock_bh(&session->lock);
-       if (session->state != ISCSI_STATE_LOGGED_IN) {
-               session->state = ISCSI_STATE_RECOVERY_FAILED;
-               if (session->leadconn)
-                       wake_up(&session->leadconn->ehwait);
-       }
-       spin_unlock_bh(&session->lock);
-}
-EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
-
-int iscsi_eh_target_reset(struct scsi_cmnd *sc)
-{
-       struct iscsi_cls_session *cls_session;
-       struct iscsi_session *session;
-       struct iscsi_conn *conn;
-
-       cls_session = starget_to_session(scsi_target(sc->device));
-       session = cls_session->dd_data;
-       conn = session->leadconn;
-
-       mutex_lock(&session->eh_mutex);
-       spin_lock_bh(&session->lock);
-       if (session->state == ISCSI_STATE_TERMINATE) {
-failed:
-               ISCSI_DBG_EH(session,
-                            "failing target reset: Could not log back into "
-                            "target [age %d]\n",
-                            session->age);
-               spin_unlock_bh(&session->lock);
-               mutex_unlock(&session->eh_mutex);
-               return FAILED;
-       }
-
-       spin_unlock_bh(&session->lock);
-       mutex_unlock(&session->eh_mutex);
-       /*
-        * we drop the lock here but the leadconn cannot be destoyed while
-        * we are in the scsi eh
-        */
-       iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-
-       ISCSI_DBG_EH(session, "wait for relogin\n");
-       wait_event_interruptible(conn->ehwait,
-                                session->state == ISCSI_STATE_TERMINATE ||
-                                session->state == ISCSI_STATE_LOGGED_IN ||
-                                session->state == ISCSI_STATE_RECOVERY_FAILED);
-       if (signal_pending(current))
-               flush_signals(current);
-
-       mutex_lock(&session->eh_mutex);
-       spin_lock_bh(&session->lock);
-       if (session->state == ISCSI_STATE_LOGGED_IN) {
-               ISCSI_DBG_EH(session,
-                            "target reset succeeded\n");
-       } else
-               goto failed;
-       spin_unlock_bh(&session->lock);
-       mutex_unlock(&session->eh_mutex);
-       return SUCCESS;
-}
-EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
-
 static void iscsi_tmf_timedout(unsigned long data)
 {
        struct iscsi_conn *conn = (struct iscsi_conn *)data;
@@ -2108,6 +2148,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
                spin_lock_bh(&session->lock);
                fail_scsi_task(task, DID_ABORT);
                conn->tmf_state = TMF_INITIAL;
+               memset(hdr, 0, sizeof(*hdr));
                spin_unlock_bh(&session->lock);
                iscsi_start_tx(conn);
                goto success_unlocked;
@@ -2118,6 +2159,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
        case TMF_NOT_FOUND:
                if (!sc->SCp.ptr) {
                        conn->tmf_state = TMF_INITIAL;
+                       memset(hdr, 0, sizeof(*hdr));
                        /* task completed before tmf abort response */
                        ISCSI_DBG_EH(session, "sc completed while abort in "
                                              "progress\n");
@@ -2212,6 +2254,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
        iscsi_suspend_tx(conn);
 
        spin_lock_bh(&session->lock);
+       memset(hdr, 0, sizeof(*hdr));
        fail_scsi_tasks(conn, sc->device->lun, DID_ERROR);
        conn->tmf_state = TMF_INITIAL;
        spin_unlock_bh(&session->lock);
@@ -2229,6 +2272,172 @@ done:
 }
 EXPORT_SYMBOL_GPL(iscsi_eh_device_reset);
 
+void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
+{
+       struct iscsi_session *session = cls_session->dd_data;
+
+       spin_lock_bh(&session->lock);
+       if (session->state != ISCSI_STATE_LOGGED_IN) {
+               session->state = ISCSI_STATE_RECOVERY_FAILED;
+               if (session->leadconn)
+                       wake_up(&session->leadconn->ehwait);
+       }
+       spin_unlock_bh(&session->lock);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
+
+/**
+ * iscsi_eh_session_reset - drop session and attempt relogin
+ * @sc: scsi command
+ *
+ * This function will wait for a relogin, session termination from
+ * userspace, or a recovery/replacement timeout.
+ */
+static int iscsi_eh_session_reset(struct scsi_cmnd *sc)
+{
+       struct iscsi_cls_session *cls_session;
+       struct iscsi_session *session;
+       struct iscsi_conn *conn;
+
+       cls_session = starget_to_session(scsi_target(sc->device));
+       session = cls_session->dd_data;
+       conn = session->leadconn;
+
+       mutex_lock(&session->eh_mutex);
+       spin_lock_bh(&session->lock);
+       if (session->state == ISCSI_STATE_TERMINATE) {
+failed:
+               ISCSI_DBG_EH(session,
+                            "failing session reset: Could not log back into "
+                            "%s, %s [age %d]\n", session->targetname,
+                            conn->persistent_address, session->age);
+               spin_unlock_bh(&session->lock);
+               mutex_unlock(&session->eh_mutex);
+               return FAILED;
+       }
+
+       spin_unlock_bh(&session->lock);
+       mutex_unlock(&session->eh_mutex);
+       /*
+        * we drop the lock here but the leadconn cannot be destoyed while
+        * we are in the scsi eh
+        */
+       iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+
+       ISCSI_DBG_EH(session, "wait for relogin\n");
+       wait_event_interruptible(conn->ehwait,
+                                session->state == ISCSI_STATE_TERMINATE ||
+                                session->state == ISCSI_STATE_LOGGED_IN ||
+                                session->state == ISCSI_STATE_RECOVERY_FAILED);
+       if (signal_pending(current))
+               flush_signals(current);
+
+       mutex_lock(&session->eh_mutex);
+       spin_lock_bh(&session->lock);
+       if (session->state == ISCSI_STATE_LOGGED_IN) {
+               ISCSI_DBG_EH(session,
+                            "session reset succeeded for %s,%s\n",
+                            session->targetname, conn->persistent_address);
+       } else
+               goto failed;
+       spin_unlock_bh(&session->lock);
+       mutex_unlock(&session->eh_mutex);
+       return SUCCESS;
+}
+
+static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
+{
+       memset(hdr, 0, sizeof(*hdr));
+       hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+       hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK;
+       hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+       hdr->rtt = RESERVED_ITT;
+}
+
+/**
+ * iscsi_eh_target_reset - reset target
+ * @sc: scsi command
+ *
+ * This will attempt to send a warm target reset. If that fails
+ * then we will drop the session and attempt ERL0 recovery.
+ */
+int iscsi_eh_target_reset(struct scsi_cmnd *sc)
+{
+       struct iscsi_cls_session *cls_session;
+       struct iscsi_session *session;
+       struct iscsi_conn *conn;
+       struct iscsi_tm *hdr;
+       int rc = FAILED;
+
+       cls_session = starget_to_session(scsi_target(sc->device));
+       session = cls_session->dd_data;
+
+       ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc,
+                    session->targetname);
+
+       mutex_lock(&session->eh_mutex);
+       spin_lock_bh(&session->lock);
+       /*
+        * Just check if we are not logged in. We cannot check for
+        * the phase because the reset could come from a ioctl.
+        */
+       if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
+               goto unlock;
+       conn = session->leadconn;
+
+       /* only have one tmf outstanding at a time */
+       if (conn->tmf_state != TMF_INITIAL)
+               goto unlock;
+       conn->tmf_state = TMF_QUEUED;
+
+       hdr = &conn->tmhdr;
+       iscsi_prep_tgt_reset_pdu(sc, hdr);
+
+       if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+                                   session->tgt_reset_timeout)) {
+               rc = FAILED;
+               goto unlock;
+       }
+
+       switch (conn->tmf_state) {
+       case TMF_SUCCESS:
+               break;
+       case TMF_TIMEDOUT:
+               spin_unlock_bh(&session->lock);
+               iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+               goto done;
+       default:
+               conn->tmf_state = TMF_INITIAL;
+               goto unlock;
+       }
+
+       rc = SUCCESS;
+       spin_unlock_bh(&session->lock);
+
+       iscsi_suspend_tx(conn);
+
+       spin_lock_bh(&session->lock);
+       memset(hdr, 0, sizeof(*hdr));
+       fail_scsi_tasks(conn, -1, DID_ERROR);
+       conn->tmf_state = TMF_INITIAL;
+       spin_unlock_bh(&session->lock);
+
+       iscsi_start_tx(conn);
+       goto done;
+
+unlock:
+       spin_unlock_bh(&session->lock);
+done:
+       ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname,
+                    rc == SUCCESS ? "SUCCESS" : "FAILED");
+       mutex_unlock(&session->eh_mutex);
+
+       if (rc == FAILED)
+               rc = iscsi_eh_session_reset(sc);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
+
 /*
  * Pre-allocate a pool of @max items of @item_size. By default, the pool
  * should be accessed via kfifo_{get,put} on q->queue.
@@ -2495,6 +2704,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
        session->host = shost;
        session->state = ISCSI_STATE_FREE;
        session->fast_abort = 1;
+       session->tgt_reset_timeout = 30;
        session->lu_reset_timeout = 15;
        session->abort_timeout = 10;
        session->scsi_cmds_max = scsi_cmds;
@@ -2856,6 +3066,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
        spin_lock_bh(&session->lock);
        fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED);
        fail_mgmt_tasks(session, conn);
+       memset(&conn->tmhdr, 0, sizeof(conn->tmhdr));
        spin_unlock_bh(&session->lock);
        mutex_unlock(&session->eh_mutex);
 }
@@ -2932,6 +3143,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
        case ISCSI_PARAM_LU_RESET_TMO:
                sscanf(buf, "%d", &session->lu_reset_timeout);
                break;
+       case ISCSI_PARAM_TGT_RESET_TMO:
+               sscanf(buf, "%d", &session->tgt_reset_timeout);
+               break;
        case ISCSI_PARAM_PING_TMO:
                sscanf(buf, "%d", &conn->ping_timeout);
                break;
@@ -3031,6 +3245,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
        case ISCSI_PARAM_LU_RESET_TMO:
                len = sprintf(buf, "%d\n", session->lu_reset_timeout);
                break;
+       case ISCSI_PARAM_TGT_RESET_TMO:
+               len = sprintf(buf, "%d\n", session->tgt_reset_timeout);
+               break;
        case ISCSI_PARAM_INITIAL_R2T_EN:
                len = sprintf(buf, "%d\n", session->initial_r2t_en);
                break;
index 2e0746d..ca25ee5 100644 (file)
@@ -1004,7 +1004,7 @@ static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task)
  * iscsi_tcp_task_xmit - xmit normal PDU task
  * @task: iscsi command task
  *
- * We're expected to return 0 when everything was transmitted succesfully,
+ * We're expected to return 0 when everything was transmitted successfully,
  * -EAGAIN if there's still data in the queue, or != 0 for any other kind
  * of error.
  */
index 1c558d3..14b1319 100644 (file)
@@ -820,10 +820,14 @@ void sas_slave_destroy(struct scsi_device *scsi_dev)
                ata_port_disable(dev->sata_dev.ap);
 }
 
-int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth)
+int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth,
+                          int reason)
 {
        int res = min(new_depth, SAS_MAX_QD);
 
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (scsi_dev->tagged_supported)
                scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev),
                                        res);
index aa10f79..1cc23a6 100644 (file)
@@ -109,7 +109,8 @@ struct hbq_dmabuf {
        struct lpfc_dmabuf dbuf;
        uint32_t size;
        uint32_t tag;
-       struct lpfc_rcqe rcqe;
+       struct lpfc_cq_event cq_event;
+       unsigned long time_stamp;
 };
 
 /* Priority bit.  Set value to exceed low water mark in lpfc_mem. */
@@ -201,6 +202,7 @@ struct lpfc_stats {
        uint32_t elsRcvLIRR;
        uint32_t elsRcvRPS;
        uint32_t elsRcvRPL;
+       uint32_t elsRcvRRQ;
        uint32_t elsXmitFLOGI;
        uint32_t elsXmitFDISC;
        uint32_t elsXmitPLOGI;
@@ -289,8 +291,8 @@ struct lpfc_vport {
 
        uint16_t vpi;
        uint16_t vfi;
-       uint8_t vfi_state;
-#define LPFC_VFI_REGISTERED    0x1
+       uint8_t vpi_state;
+#define LPFC_VPI_REGISTERED    0x1
 
        uint32_t fc_flag;       /* FC flags */
 /* Several of these flags are HBA centric and should be moved to
@@ -405,6 +407,7 @@ struct lpfc_vport {
        uint8_t stat_data_enabled;
        uint8_t stat_data_blocked;
        struct list_head rcv_buffer_list;
+       unsigned long rcv_buffer_time_stamp;
        uint32_t vport_flag;
 #define STATIC_VPORT   1
 };
@@ -527,13 +530,16 @@ struct lpfc_hba {
 #define HBA_ERATT_HANDLED      0x1 /* This flag is set when eratt handled */
 #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_SP_QUEUE_EVT       0x8 /* Slow-path qevt 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
 #define LINK_DISABLED          0x100 /* Link disabled by user */
 #define FCF_DISC_INPROGRESS    0x200 /* FCF discovery in progress */
+#define HBA_FIP_SUPPORT                0x400 /* FIP support in HBA */
+#define HBA_AER_ENABLED                0x800 /* AER enabled with HBA */
+       uint32_t fcp_ring_in_use; /* When polling test if intr-hndlr active*/
        struct lpfc_dmabuf slim2p;
 
        MAILBOX_t *mbox;
@@ -551,6 +557,7 @@ struct lpfc_hba {
        uint8_t fc_linkspeed;   /* Link speed after last READ_LA */
 
        uint32_t fc_eventTag;   /* event tag for link attention */
+       uint32_t link_events;
 
        /* These fields used to be binfo */
        uint32_t fc_pref_DID;   /* preferred D_ID */
@@ -604,8 +611,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;
+       uint32_t cfg_aer_support;
 
        lpfc_vpd_t vpd;         /* vital product data */
 
index e1a30a1..91542f7 100644 (file)
 #include <linux/delay.h>
 #include <linux/pci.h>
 #include <linux/interrupt.h>
+#include <linux/aer.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_transport_fc.h>
+#include <scsi/fc/fc_fs.h>
 
 #include "lpfc_hw4.h"
 #include "lpfc_hw.h"
@@ -98,6 +100,28 @@ lpfc_drvr_version_show(struct device *dev, struct device_attribute *attr,
        return snprintf(buf, PAGE_SIZE, LPFC_MODULE_DESC "\n");
 }
 
+/**
+ * lpfc_enable_fip_show - Return the fip mode of the HBA
+ * @dev: class unused variable.
+ * @attr: device attribute, not used.
+ * @buf: on return contains the module description text.
+ *
+ * Returns: size of formatted string.
+ **/
+static ssize_t
+lpfc_enable_fip_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;
+       struct lpfc_hba   *phba = vport->phba;
+
+       if (phba->hba_flag & HBA_FIP_SUPPORT)
+               return snprintf(buf, PAGE_SIZE, "1\n");
+       else
+               return snprintf(buf, PAGE_SIZE, "0\n");
+}
+
 static ssize_t
 lpfc_bg_info_show(struct device *dev, struct device_attribute *attr,
                  char *buf)
@@ -654,7 +678,7 @@ lpfc_selective_reset(struct lpfc_hba *phba)
  * Notes:
  * Assumes any error from lpfc_selective_reset() will be negative.
  * If lpfc_selective_reset() returns zero then the length of the buffer
- * is returned which indicates succcess
+ * is returned which indicates success
  *
  * Returns:
  * -EINVAL if the buffer does not contain the string "selective"
@@ -762,9 +786,15 @@ lpfc_board_mode_store(struct device *dev, struct device_attribute *attr,
        } else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
                status = lpfc_do_offline(phba, LPFC_EVT_OFFLINE);
        else if (strncmp(buf, "warm", sizeof("warm") - 1) == 0)
-               status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
+               if (phba->sli_rev == LPFC_SLI_REV4)
+                       return -EINVAL;
+               else
+                       status = lpfc_do_offline(phba, LPFC_EVT_WARM_START);
        else if (strncmp(buf, "error", sizeof("error") - 1) == 0)
-               status = lpfc_do_offline(phba, LPFC_EVT_KILL);
+               if (phba->sli_rev == LPFC_SLI_REV4)
+                       return -EINVAL;
+               else
+                       status = lpfc_do_offline(phba, LPFC_EVT_KILL);
        else
                return -EINVAL;
 
@@ -1126,6 +1156,9 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr,
        if ((val & 0x3) != val)
                return -EINVAL;
 
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               val = 0;
+
        spin_lock_irq(&phba->hbalock);
 
        old_val = phba->cfg_poll;
@@ -1589,6 +1622,7 @@ static DEVICE_ATTR(num_discovered_ports, S_IRUGO,
 static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL);
 static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL);
 static DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL);
+static DEVICE_ATTR(lpfc_enable_fip, S_IRUGO, lpfc_enable_fip_show, NULL);
 static DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR,
                   lpfc_board_mode_show, lpfc_board_mode_store);
 static DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset);
@@ -2758,6 +2792,196 @@ lpfc_link_speed_init(struct lpfc_hba *phba, int val)
 static DEVICE_ATTR(lpfc_link_speed, S_IRUGO | S_IWUSR,
                lpfc_link_speed_show, lpfc_link_speed_store);
 
+/*
+# lpfc_aer_support: Support PCIe device Advanced Error Reporting (AER)
+#       0  = aer disabled or not supported
+#       1  = aer supported and enabled (default)
+# Value range is [0,1]. Default value is 1.
+*/
+
+/**
+ * lpfc_aer_support_store - Set the adapter for aer support
+ *
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string "selective".
+ * @count: unused variable.
+ *
+ * Description:
+ * If the val is 1 and currently the device's AER capability was not
+ * enabled, invoke the kernel's enable AER helper routine, trying to
+ * enable the device's AER capability. If the helper routine enabling
+ * AER returns success, update the device's cfg_aer_support flag to
+ * indicate AER is supported by the device; otherwise, if the device
+ * AER capability is already enabled to support AER, then do nothing.
+ *
+ * If the val is 0 and currently the device's AER support was enabled,
+ * invoke the kernel's disable AER helper routine. After that, update
+ * the device's cfg_aer_support flag to indicate AER is not supported
+ * by the device; otherwise, if the device AER capability is already
+ * disabled from supporting AER, then do nothing.
+ *
+ * Returns:
+ * length of the buf on success if val is in range the intended mode
+ * is supported.
+ * -EINVAL if val out of range or intended mode is not supported.
+ **/
+static ssize_t
+lpfc_aer_support_store(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       struct Scsi_Host *shost = class_to_shost(dev);
+       struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata;
+       struct lpfc_hba *phba = vport->phba;
+       int val = 0, rc = -EINVAL;
+
+       /* AER not supported on OC devices yet */
+       if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+               return -EPERM;
+       if (!isdigit(buf[0]))
+               return -EINVAL;
+       if (sscanf(buf, "%i", &val) != 1)
+               return -EINVAL;
+
+       switch (val) {
+       case 0:
+               if (phba->hba_flag & HBA_AER_ENABLED) {
+                       rc = pci_disable_pcie_error_reporting(phba->pcidev);
+                       if (!rc) {
+                               spin_lock_irq(&phba->hbalock);
+                               phba->hba_flag &= ~HBA_AER_ENABLED;
+                               spin_unlock_irq(&phba->hbalock);
+                               phba->cfg_aer_support = 0;
+                               rc = strlen(buf);
+                       } else
+                               rc = -EPERM;
+               } else {
+                       phba->cfg_aer_support = 0;
+                       rc = strlen(buf);
+               }
+               break;
+       case 1:
+               if (!(phba->hba_flag & HBA_AER_ENABLED)) {
+                       rc = pci_enable_pcie_error_reporting(phba->pcidev);
+                       if (!rc) {
+                               spin_lock_irq(&phba->hbalock);
+                               phba->hba_flag |= HBA_AER_ENABLED;
+                               spin_unlock_irq(&phba->hbalock);
+                               phba->cfg_aer_support = 1;
+                               rc = strlen(buf);
+                       } else
+                                rc = -EPERM;
+               } else {
+                       phba->cfg_aer_support = 1;
+                       rc = strlen(buf);
+               }
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+       return rc;
+}
+
+static int lpfc_aer_support = 1;
+module_param(lpfc_aer_support, int, 1);
+MODULE_PARM_DESC(lpfc_aer_support, "Enable PCIe device AER support");
+lpfc_param_show(aer_support)
+
+/**
+ * lpfc_aer_support_init - Set the initial adapters aer support flag
+ * @phba: lpfc_hba pointer.
+ * @val: link speed value.
+ *
+ * Description:
+ * If val is in a valid range [0,1], then set the adapter's initial
+ * cfg_aer_support field. It will be up to the driver's probe_one
+ * routine to determine whether the device's AER support can be set
+ * or not.
+ *
+ * Notes:
+ * If the value is not in range log a kernel error message, and
+ * choose the default value of setting AER support and return.
+ *
+ * Returns:
+ * zero if val saved.
+ * -EINVAL val out of range
+ **/
+static int
+lpfc_aer_support_init(struct lpfc_hba *phba, int val)
+{
+       /* AER not supported on OC devices yet */
+       if (phba->pci_dev_grp == LPFC_PCI_DEV_OC) {
+               phba->cfg_aer_support = 0;
+               return -EPERM;
+       }
+
+       if (val == 0 || val == 1) {
+               phba->cfg_aer_support = val;
+               return 0;
+       }
+       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2712 lpfc_aer_support attribute value %d out "
+                       "of range, allowed values are 0|1, setting it "
+                       "to default value of 1\n", val);
+       /* By default, try to enable AER on a device */
+       phba->cfg_aer_support = 1;
+       return -EINVAL;
+}
+
+static DEVICE_ATTR(lpfc_aer_support, S_IRUGO | S_IWUSR,
+                  lpfc_aer_support_show, lpfc_aer_support_store);
+
+/**
+ * lpfc_aer_cleanup_state - Clean up aer state to the aer enabled device
+ * @dev: class device that is converted into a Scsi_host.
+ * @attr: device attribute, not used.
+ * @buf: containing the string "selective".
+ * @count: unused variable.
+ *
+ * Description:
+ * If the @buf contains 1 and the device currently has the AER support
+ * enabled, then invokes the kernel AER helper routine
+ * pci_cleanup_aer_uncorrect_error_status to clean up the uncorrectable
+ * error status register.
+ *
+ * Notes:
+ *
+ * Returns:
+ * -EINVAL if the buf does not contain the 1 or the device is not currently
+ * enabled with the AER support.
+ **/
+static ssize_t
+lpfc_aer_cleanup_state(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       struct Scsi_Host  *shost = class_to_shost(dev);
+       struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
+       struct lpfc_hba   *phba = vport->phba;
+       int val, rc = -1;
+
+       /* AER not supported on OC devices yet */
+       if (phba->pci_dev_grp == LPFC_PCI_DEV_OC)
+               return -EPERM;
+       if (!isdigit(buf[0]))
+               return -EINVAL;
+       if (sscanf(buf, "%i", &val) != 1)
+               return -EINVAL;
+       if (val != 1)
+               return -EINVAL;
+
+       if (phba->hba_flag & HBA_AER_ENABLED)
+               rc = pci_cleanup_aer_uncorrect_error_status(phba->pcidev);
+
+       if (rc == 0)
+               return strlen(buf);
+       else
+               return -EPERM;
+}
+
+static DEVICE_ATTR(lpfc_aer_state_cleanup, S_IWUSR, NULL,
+                  lpfc_aer_cleanup_state);
+
 /*
 # lpfc_fcp_class:  Determines FC class to use for the FCP protocol.
 # Value range is [2,3]. Default value is 3.
@@ -2846,7 +3070,7 @@ LPFC_ATTR_R(multi_ring_support, 1, 1, 2, "Determines number of primary "
 # identifies what rctl value to configure the additional ring for.
 # Value range is [1,0xff]. Default value is 4 (Unsolicated Data).
 */
-LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1,
+LPFC_ATTR_R(multi_ring_rctl, FC_RCTL_DD_UNSOL_DATA, 1,
             255, "Identifies RCTL for additional ring configuration");
 
 /*
@@ -2854,7 +3078,7 @@ LPFC_ATTR_R(multi_ring_rctl, FC_UNSOL_DATA, 1,
 # identifies what type value to configure the additional ring for.
 # Value range is [1,0xff]. Default value is 5 (LLC/SNAP).
 */
-LPFC_ATTR_R(multi_ring_type, FC_LLC_SNAP, 1,
+LPFC_ATTR_R(multi_ring_type, FC_TYPE_IP, 1,
             255, "Identifies TYPE for additional ring configuration");
 
 /*
@@ -2946,15 +3170,6 @@ 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
 #      - Bit mask of host protection capabilities used to register with the
@@ -3013,6 +3228,7 @@ struct device_attribute *lpfc_hba_attrs[] = {
        &dev_attr_num_discovered_ports,
        &dev_attr_menlo_mgmt_mode,
        &dev_attr_lpfc_drvr_version,
+       &dev_attr_lpfc_enable_fip,
        &dev_attr_lpfc_temp_sensor,
        &dev_attr_lpfc_log_verbose,
        &dev_attr_lpfc_lun_queue_depth,
@@ -3020,7 +3236,6 @@ 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,
@@ -3061,6 +3276,8 @@ struct device_attribute *lpfc_hba_attrs[] = {
        &dev_attr_lpfc_max_scsicmpl_time,
        &dev_attr_lpfc_stat_data_ctrl,
        &dev_attr_lpfc_prot_sg_seg_cnt,
+       &dev_attr_lpfc_aer_support,
+       &dev_attr_lpfc_aer_state_cleanup,
        NULL,
 };
 
@@ -3073,7 +3290,6 @@ 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,
@@ -3147,7 +3363,7 @@ sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr,
  * sysfs_ctlreg_read - Read method for reading from ctlreg
  * @kobj: kernel kobject that contains the kernel class device.
  * @bin_attr: kernel attributes passed to us.
- * @buf: if succesful contains the data from the adapter IOREG space.
+ * @buf: if successful contains the data from the adapter IOREG space.
  * @off: offset into buffer to beginning of data.
  * @count: bytes to transfer.
  *
@@ -3815,7 +4031,11 @@ lpfc_get_stats(struct Scsi_Host *shost)
        hs->invalid_crc_count -= lso->invalid_crc_count;
        hs->error_frames -= lso->error_frames;
 
-       if (phba->fc_topology == TOPOLOGY_LOOP) {
+       if (phba->hba_flag & HBA_FCOE_SUPPORT) {
+               hs->lip_count = -1;
+               hs->nos_count = (phba->link_events >> 1);
+               hs->nos_count -= lso->link_events;
+       } else if (phba->fc_topology == TOPOLOGY_LOOP) {
                hs->lip_count = (phba->fc_eventTag >> 1);
                hs->lip_count -= lso->link_events;
                hs->nos_count = -1;
@@ -3906,7 +4126,10 @@ lpfc_reset_stats(struct Scsi_Host *shost)
        lso->invalid_tx_word_count = pmb->un.varRdLnk.invalidXmitWord;
        lso->invalid_crc_count = pmb->un.varRdLnk.crcCnt;
        lso->error_frames = pmb->un.varRdLnk.crcCnt;
-       lso->link_events = (phba->fc_eventTag >> 1);
+       if (phba->hba_flag & HBA_FCOE_SUPPORT)
+               lso->link_events = (phba->link_events >> 1);
+       else
+               lso->link_events = (phba->fc_eventTag >> 1);
 
        psli->stats_start = get_seconds();
 
@@ -4222,14 +4445,17 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
        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);
+       if (phba->sli_rev == LPFC_SLI_REV4)
+               phba->cfg_poll = 0;
+       else
        phba->cfg_poll = lpfc_poll;
        phba->cfg_soft_wwnn = 0L;
        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);
        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);
+       lpfc_aer_support_init(phba, lpfc_aer_support);
 
        return;
 }
index da6bf5a..a5d9048 100644 (file)
@@ -26,6 +26,7 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 #include <scsi/scsi_bsg_fc.h>
+#include <scsi/fc/fc_fs.h>
 
 #include "lpfc_hw4.h"
 #include "lpfc_hw.h"
@@ -148,8 +149,8 @@ lpfc_bsg_rport_ct(struct fc_bsg_job *job)
        cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
        cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
        cmd->un.genreq64.w5.hcsw.Dfctl = 0;
-       cmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
-       cmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+       cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+       cmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
        cmd->ulpBdeCount = 1;
        cmd->ulpLe = 1;
        cmd->ulpClass = CLASS3;
index 0830f37..650494d 100644 (file)
@@ -49,6 +49,8 @@ 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_rcv_buffers(struct lpfc_vport *);
+void lpfc_rcv_seq_check_edtov(struct lpfc_vport *);
 void lpfc_cleanup_rpis(struct lpfc_vport *, int);
 int lpfc_linkdown(struct lpfc_hba *);
 void lpfc_linkdown_port(struct lpfc_vport *);
@@ -144,6 +146,8 @@ void lpfc_hb_timeout_handler(struct lpfc_hba *);
 
 void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
                         struct lpfc_iocbq *);
+void lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+                                   struct lpfc_iocbq *);
 int lpfc_ns_cmd(struct lpfc_vport *, int, uint8_t, uint32_t);
 int lpfc_fdmi_cmd(struct lpfc_vport *, struct lpfc_nodelist *, int);
 void lpfc_fdmi_tmo(unsigned long);
@@ -188,7 +192,7 @@ 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 lpfc_hba *, struct lpfcMboxq *, uint16_t);
-void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t);
+void lpfc_unreg_vfi(struct lpfcMboxq *, struct lpfc_vport *);
 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 *);
@@ -212,7 +216,10 @@ void lpfc_stop_vport_timers(struct lpfc_vport *);
 void lpfc_poll_timeout(unsigned long ptr);
 void lpfc_poll_start_timer(struct lpfc_hba *);
 void lpfc_poll_eratt(unsigned long);
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *);
+int
+lpfc_sli_handle_fast_ring_event(struct lpfc_hba *,
+                       struct lpfc_sli_ring *, uint32_t);
+
 struct lpfc_iocbq * lpfc_sli_get_iocbq(struct lpfc_hba *);
 void lpfc_sli_release_iocbq(struct lpfc_hba *, struct lpfc_iocbq *);
 uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *);
@@ -235,7 +242,7 @@ void lpfc_sli_mbox_sys_shutdown(struct lpfc_hba *);
 int lpfc_sli_check_eratt(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_sli4_handle_received_buffer(struct lpfc_hba *, struct hbq_dmabuf *);
 void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
 int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
                        struct lpfc_iocbq *, uint32_t);
@@ -361,6 +368,7 @@ 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);
+struct lpfc_vport *lpfc_find_vport_by_vpid(struct lpfc_hba *, uint16_t);
 
 #define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
 #define HBA_EVENT_RSCN                   5
index 9a1bd95..0ebcd9b 100644 (file)
@@ -31,6 +31,7 @@
 #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"
@@ -87,7 +88,6 @@ void
 lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                    struct lpfc_iocbq *piocbq)
 {
-
        struct lpfc_dmabuf *mp = NULL;
        IOCB_t *icmd = &piocbq->iocb;
        int i;
@@ -160,6 +160,39 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        }
 }
 
+/**
+ * lpfc_sli4_ct_abort_unsol_event - Default handle for sli4 unsol abort
+ * @phba: Pointer to HBA context object.
+ * @pring: Pointer to the driver internal I/O ring.
+ * @piocbq: Pointer to the IOCBQ.
+ *
+ * This function serves as the default handler for the sli4 unsolicited
+ * abort event. It shall be invoked when there is no application interface
+ * registered unsolicited abort handler. This handler does nothing but
+ * just simply releases the dma buffer used by the unsol abort event.
+ **/
+void
+lpfc_sli4_ct_abort_unsol_event(struct lpfc_hba *phba,
+                              struct lpfc_sli_ring *pring,
+                              struct lpfc_iocbq *piocbq)
+{
+       IOCB_t *icmd = &piocbq->iocb;
+       struct lpfc_dmabuf *bdeBuf;
+       uint32_t size;
+
+       /* Forward abort event to any process registered to receive ct event */
+       lpfc_bsg_ct_unsol_event(phba, pring, piocbq);
+
+       /* If there is no BDE associated with IOCB, there is nothing to do */
+       if (icmd->ulpBdeCount == 0)
+               return;
+       bdeBuf = piocbq->context2;
+       piocbq->context2 = NULL;
+       size  = icmd->un.cont64[0].tus.f.bdeSize;
+       lpfc_ct_unsol_buffer(phba, piocbq, bdeBuf, size);
+       lpfc_in_buf_free(phba, bdeBuf);
+}
+
 static void
 lpfc_free_ct_rsp(struct lpfc_hba *phba, struct lpfc_dmabuf *mlist)
 {
@@ -304,8 +337,8 @@ lpfc_gen_req(struct lpfc_vport *vport, struct lpfc_dmabuf *bmp,
        /* Fill in rest of iocb */
        icmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
        icmd->un.genreq64.w5.hcsw.Dfctl = 0;
-       icmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
-       icmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+       icmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CTL;
+       icmd->un.genreq64.w5.hcsw.Type = FC_TYPE_CT;
 
        if (!tmo) {
                 /* FC spec states we need 3 * ratov for CT requests */
@@ -363,9 +396,14 @@ lpfc_ct_cmd(struct lpfc_vport *vport, struct lpfc_dmabuf *inmp,
        outmp = lpfc_alloc_ct_rsp(phba, cmdcode, bpl, rsp_size, &cnt);
        if (!outmp)
                return -ENOMEM;
-
+       /*
+        * Form the CT IOCB.  The total number of BDEs in this IOCB
+        * is the single command plus response count from
+        * lpfc_alloc_ct_rsp.
+        */
+       cnt += 1;
        status = lpfc_gen_req(vport, bmp, inmp, outmp, cmpl, ndlp, 0,
-                             cnt+1, 0, retry);
+                             cnt, 0, retry);
        if (status) {
                lpfc_free_ct_rsp(phba, outmp);
                return -ENOMEM;
@@ -501,6 +539,9 @@ lpfc_ns_rsp(struct lpfc_vport *vport, struct lpfc_dmabuf *mp, uint32_t Size)
                                                        SLI_CTNS_GFF_ID,
                                                        0, Did) == 0)
                                                        vport->num_disc_nodes++;
+                                               else
+                                                       lpfc_setup_disc_node
+                                                               (vport, Did);
                                        }
                                        else {
                                                lpfc_debugfs_disc_trc(vport,
@@ -1209,7 +1250,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode,
                    be16_to_cpu(SLI_CTNS_RFF_ID);
                CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID);
                CtReq->un.rff.fbits = FC4_FEATURE_INIT;
-               CtReq->un.rff.type_code = FC_FCP_DATA;
+               CtReq->un.rff.type_code = FC_TYPE_FCP;
                cmpl = lpfc_cmpl_ct_cmd_rff_id;
                break;
        }
index 8d0f0de..3915841 100644 (file)
@@ -926,7 +926,7 @@ lpfc_debugfs_dumpData_open(struct inode *inode, struct file *file)
                goto out;
 
        /* Round to page boundry */
-       printk(KERN_ERR "BLKGRD %s: _dump_buf_data=0x%p\n",
+       printk(KERN_ERR "9059 BLKGRD:  %s: _dump_buf_data=0x%p\n",
                        __func__, _dump_buf_data);
        debug->buffer = _dump_buf_data;
        if (!debug->buffer) {
@@ -956,8 +956,8 @@ lpfc_debugfs_dumpDif_open(struct inode *inode, struct file *file)
                goto out;
 
        /* Round to page boundry */
-       printk(KERN_ERR "BLKGRD %s: _dump_buf_dif=0x%p file=%s\n", __func__,
-              _dump_buf_dif, file->f_dentry->d_name.name);
+       printk(KERN_ERR "9060 BLKGRD: %s: _dump_buf_dif=0x%p file=%s\n",
+               __func__, _dump_buf_dif, file->f_dentry->d_name.name);
        debug->buffer = _dump_buf_dif;
        if (!debug->buffer) {
                kfree(debug);
@@ -1377,7 +1377,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
                        debugfs_create_dir(name, phba->hba_debugfs_root);
                if (!vport->vport_debugfs_root) {
                        lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
-                                        "0417 Cant create debugfs");
+                                        "0417 Cant create debugfs\n");
                        goto debug_failed;
                }
                atomic_inc(&phba->debugfs_vport_count);
@@ -1430,7 +1430,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
                                 vport, &lpfc_debugfs_op_nodelist);
        if (!vport->debug_nodelist) {
                lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
-                                "0409 Cant create debugfs nodelist");
+                                "0409 Cant create debugfs nodelist\n");
                goto debug_failed;
        }
 debug_failed:
index 1142070..2851d75 100644 (file)
@@ -19,7 +19,7 @@
  *******************************************************************/
 
 #define FC_MAX_HOLD_RSCN     32              /* max number of deferred RSCNs */
-#define FC_MAX_NS_RSP        65536    /* max size NameServer rsp */
+#define FC_MAX_NS_RSP        64512    /* max size NameServer rsp */
 #define FC_MAXLOOP           126      /* max devices supported on a fc loop */
 #define LPFC_DISC_FLOGI_TMO  10              /* Discovery FLOGI ratov */
 
@@ -105,8 +105,6 @@ struct lpfc_nodelist {
        struct lpfc_vport *vport;
        struct lpfc_work_evt els_retry_evt;
        struct lpfc_work_evt dev_loss_evt;
-       unsigned long last_ramp_up_time;        /* jiffy of last ramp up */
-       unsigned long last_q_full_time;         /* jiffy of last queue full */
        struct kref     kref;
        atomic_t cmd_pending;
        uint32_t cmd_qdepth;
index 45337cd..ce52270 100644 (file)
@@ -173,13 +173,26 @@ lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
         * in FIP mode send FLOGI, FDISC and LOGO as FIP frames.
         */
        if ((did == Fabric_DID) &&
-               bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags) &&
+               (phba->hba_flag & HBA_FIP_SUPPORT) &&
                ((elscmd == ELS_CMD_FLOGI) ||
                 (elscmd == ELS_CMD_FDISC) ||
                 (elscmd == ELS_CMD_LOGO)))
-               elsiocb->iocb_flag |= LPFC_FIP_ELS;
+               switch (elscmd) {
+               case ELS_CMD_FLOGI:
+               elsiocb->iocb_flag |= ((ELS_ID_FLOGI << LPFC_FIP_ELS_ID_SHIFT)
+                                       & LPFC_FIP_ELS_ID_MASK);
+               break;
+               case ELS_CMD_FDISC:
+               elsiocb->iocb_flag |= ((ELS_ID_FDISC << LPFC_FIP_ELS_ID_SHIFT)
+                                       & LPFC_FIP_ELS_ID_MASK);
+               break;
+               case ELS_CMD_LOGO:
+               elsiocb->iocb_flag |= ((ELS_ID_LOGO << LPFC_FIP_ELS_ID_SHIFT)
+                                       & LPFC_FIP_ELS_ID_MASK);
+               break;
+               }
        else
-               elsiocb->iocb_flag &= ~LPFC_FIP_ELS;
+               elsiocb->iocb_flag &= ~LPFC_FIP_ELS_ID_MASK;
 
        icmd = &elsiocb->iocb;
 
@@ -591,7 +604,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        } else {
                ndlp->nlp_type |= NLP_FABRIC;
                lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
-               if (vport->vfi_state & LPFC_VFI_REGISTERED) {
+               if (vport->vpi_state & LPFC_VPI_REGISTERED) {
                        lpfc_start_fdiscs(phba);
                        lpfc_do_scr_ns_plogi(phba, vport);
                } else
@@ -802,7 +815,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
 
        /* FLOGI completes successfully */
        lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS,
-                        "0101 FLOGI completes sucessfully "
+                        "0101 FLOGI completes successfully "
                         "Data: x%x x%x x%x x%x\n",
                         irsp->un.ulpWord[4], sp->cmn.e_d_tov,
                         sp->cmn.w2.r_a_tov, sp->cmn.edtovResolution);
@@ -2452,6 +2465,7 @@ lpfc_els_retry_delay_handler(struct lpfc_nodelist *ndlp)
         */
        del_timer_sync(&ndlp->nlp_delayfunc);
        retry = ndlp->nlp_retry;
+       ndlp->nlp_retry = 0;
 
        switch (cmd) {
        case ELS_CMD_FLOGI:
@@ -2711,12 +2725,16 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
            !lpfc_error_lost_link(irsp)) {
                /* FLOGI retry policy */
                retry = 1;
-               maxretry = 48;
-               if (cmdiocb->retry >= 32)
+               /* retry forever */
+               maxretry = 0;
+               if (cmdiocb->retry >= 100)
+                       delay = 5000;
+               else if (cmdiocb->retry >= 32)
                        delay = 1000;
        }
 
-       if ((++cmdiocb->retry) >= maxretry) {
+       cmdiocb->retry++;
+       if (maxretry && (cmdiocb->retry >= maxretry)) {
                phba->fc_stat.elsRetryExceeded++;
                retry = 0;
        }
@@ -4133,7 +4151,7 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
        /* Indicate we are walking fc_rscn_id_list on this vport */
        vport->fc_rscn_flush = 1;
        spin_unlock_irq(shost->host_lock);
-       /* Get the array count after sucessfully have the token */
+       /* Get the array count after successfully have the token */
        rscn_cnt = vport->fc_rscn_id_cnt;
        /* If we are already processing an RSCN, save the received
         * RSCN payload buffer, cmdiocb->context2 to process later.
@@ -4502,6 +4520,29 @@ lpfc_els_rcv_lirr(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
        return 0;
 }
 
+/**
+ * lpfc_els_rcv_rrq - Process an unsolicited rrq iocb
+ * @vport: pointer to a host virtual N_Port data structure.
+ * @cmdiocb: pointer to lpfc command iocb data structure.
+ * @ndlp: pointer to a node-list data structure.
+ *
+ * This routine processes a Reinstate Recovery Qualifier (RRQ) IOCB
+ * received as an ELS unsolicited event. A request to RRQ shall only
+ * be accepted if the Originator Nx_Port N_Port_ID or the Responder
+ * Nx_Port N_Port_ID of the target Exchange is the same as the
+ * N_Port_ID of the Nx_Port that makes the request. If the RRQ is
+ * not accepted, an LS_RJT with reason code "Unable to perform
+ * command request" and reason code explanation "Invalid Originator
+ * S_ID" shall be returned. For now, we just unconditionally accept
+ * RRQ from the target.
+ **/
+static void
+lpfc_els_rcv_rrq(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
+                struct lpfc_nodelist *ndlp)
+{
+       lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL);
+}
+
 /**
  * lpfc_els_rsp_rps_acc - Completion callbk func for MBX_READ_LNK_STAT mbox cmd
  * @phba: pointer to lpfc hba data structure.
@@ -5396,7 +5437,7 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        if (lpfc_els_chk_latt(vport))
                goto dropit;
 
-       /* Ignore traffic recevied during vport shutdown. */
+       /* Ignore traffic received during vport shutdown. */
        if (vport->load_flag & FC_UNLOADING)
                goto dropit;
 
@@ -5618,6 +5659,16 @@ lpfc_els_unsol_buffer(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                if (newnode)
                        lpfc_nlp_put(ndlp);
                break;
+       case ELS_CMD_RRQ:
+               lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
+                       "RCV RRQ:         did:x%x/ste:x%x flg:x%x",
+                       did, vport->port_state, ndlp->nlp_flag);
+
+               phba->fc_stat.elsRcvRRQ++;
+               lpfc_els_rcv_rrq(vport, elsiocb, ndlp);
+               if (newnode)
+                       lpfc_nlp_put(ndlp);
+               break;
        default:
                lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_ELS_UNSOL,
                        "RCV ELS cmd:     cmd:x%x did:x%x/ste:x%x",
@@ -5670,7 +5721,7 @@ dropit:
  *    NULL - No vport with the matching @vpi found
  *    Otherwise - Address to the vport with the matching @vpi.
  **/
-static struct lpfc_vport *
+struct lpfc_vport *
 lpfc_find_vport_by_vpid(struct lpfc_hba *phba, uint16_t vpi)
 {
        struct lpfc_vport *vport;
@@ -6024,11 +6075,6 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                                 irsp->ulpStatus, irsp->un.ulpWord[4]);
                goto fdisc_failed;
        }
-               if (vport->fc_vport->vport_state == FC_VPORT_INITIALIZING)
-                       lpfc_vport_set_state(vport, FC_VPORT_FAILED);
-               lpfc_nlp_put(ndlp);
-               /* giving up on FDISC. Cancel discovery timer */
-               lpfc_can_disctmo(vport);
        spin_lock_irq(shost->host_lock);
        vport->fc_flag |= FC_FABRIC;
        if (vport->phba->fc_topology == TOPOLOGY_LOOP)
@@ -6107,6 +6153,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
        int did = ndlp->nlp_DID;
        int rc;
 
+       vport->port_state = LPFC_FDISC;
        cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
        elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did,
                                     ELS_CMD_FDISC);
@@ -6172,7 +6219,6 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
                return 1;
        }
        lpfc_vport_set_state(vport, FC_VPORT_INITIALIZING);
-       vport->port_state = LPFC_FDISC;
        return 0;
 }
 
old mode 100644 (file)
new mode 100755 (executable)
index e6a47e2..3b94244
@@ -525,8 +525,6 @@ lpfc_work_done(struct lpfc_hba *phba)
                        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);
@@ -568,8 +566,9 @@ lpfc_work_done(struct lpfc_hba *phba)
        pring = &phba->sli.ring[LPFC_ELS_RING];
        status = (ha_copy & (HA_RXMASK  << (4*LPFC_ELS_RING)));
        status >>= (4*LPFC_ELS_RING);
-       if ((status & HA_RXMASK)
-               || (pring->flag & LPFC_DEFERRED_RING_EVENT)) {
+       if ((status & HA_RXMASK) ||
+           (pring->flag & LPFC_DEFERRED_RING_EVENT) ||
+           (phba->hba_flag & HBA_SP_QUEUE_EVT)) {
                if (pring->flag & LPFC_STOP_IOCB_EVENT) {
                        pring->flag |= LPFC_DEFERRED_RING_EVENT;
                        /* Set the lpfc data pending flag */
@@ -688,7 +687,8 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
                        lpfc_unreg_rpi(vport, ndlp);
 
                /* Leave Fabric nodes alone on link down */
-               if (!remove && ndlp->nlp_type & NLP_FABRIC)
+               if ((phba->sli_rev < LPFC_SLI_REV4) &&
+                   (!remove && ndlp->nlp_type & NLP_FABRIC))
                        continue;
                rc = lpfc_disc_state_machine(vport, ndlp, NULL,
                                             remove
@@ -706,6 +706,9 @@ lpfc_cleanup_rpis(struct lpfc_vport *vport, int remove)
 void
 lpfc_port_link_failure(struct lpfc_vport *vport)
 {
+       /* Cleanup any outstanding received buffers */
+       lpfc_cleanup_rcv_buffers(vport);
+
        /* Cleanup any outstanding RSCN activity */
        lpfc_els_flush_rscn(vport);
 
@@ -1015,13 +1018,12 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
                mempool_free(mboxq, phba->mbox_mem_pool);
                return;
        }
-       if (vport->port_state != LPFC_FLOGI) {
-               spin_lock_irqsave(&phba->hbalock, flags);
-               phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
-               phba->hba_flag &= ~FCF_DISC_INPROGRESS;
-               spin_unlock_irqrestore(&phba->hbalock, flags);
+       spin_lock_irqsave(&phba->hbalock, flags);
+       phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+       phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+       spin_unlock_irqrestore(&phba->hbalock, flags);
+       if (vport->port_state != LPFC_FLOGI)
                lpfc_initial_flogi(vport);
-       }
 
        mempool_free(mboxq, phba->mbox_mem_pool);
        return;
@@ -1199,6 +1201,7 @@ lpfc_register_fcf(struct lpfc_hba *phba)
 
        /* If the FCF is not availabe do nothing. */
        if (!(phba->fcf.fcf_flag & FCF_AVAILABLE)) {
+               phba->hba_flag &= ~FCF_DISC_INPROGRESS;
                spin_unlock_irqrestore(&phba->hbalock, flags);
                return;
        }
@@ -1216,15 +1219,23 @@ lpfc_register_fcf(struct lpfc_hba *phba)
 
        fcf_mbxq = mempool_alloc(phba->mbox_mem_pool,
                GFP_KERNEL);
-       if (!fcf_mbxq)
+       if (!fcf_mbxq) {
+               spin_lock_irqsave(&phba->hbalock, flags);
+               phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+               spin_unlock_irqrestore(&phba->hbalock, flags);
                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)
+       if (rc == MBX_NOT_FINISHED) {
+               spin_lock_irqsave(&phba->hbalock, flags);
+               phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+               spin_unlock_irqrestore(&phba->hbalock, flags);
                mempool_free(fcf_mbxq, phba->mbox_mem_pool);
+       }
 
        return;
 }
@@ -1253,13 +1264,27 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
                        uint16_t *vlan_id)
 {
        struct lpfc_fcf_conn_entry *conn_entry;
+       int i, j, fcf_vlan_id = 0;
+
+       /* Find the lowest VLAN id in the FCF record */
+       for (i = 0; i < 512; i++) {
+               if (new_fcf_record->vlan_bitmap[i]) {
+                       fcf_vlan_id = i * 8;
+                       j = 0;
+                       while (!((new_fcf_record->vlan_bitmap[i] >> j) & 1)) {
+                               j++;
+                               fcf_vlan_id++;
+                       }
+                       break;
+               }
+       }
 
        /* If FCF not available return 0 */
        if (!bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record) ||
                !bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record))
                return 0;
 
-       if (!phba->cfg_enable_fip) {
+       if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
                *boot_flag = 0;
                *addr_mode = bf_get(lpfc_fcf_record_mac_addr_prov,
                                new_fcf_record);
@@ -1286,7 +1311,11 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
                if (*addr_mode & LPFC_FCF_FPMA)
                        *addr_mode = LPFC_FCF_FPMA;
 
-               *vlan_id = 0xFFFF;
+               /* If FCF record report a vlan id use that vlan id */
+               if (fcf_vlan_id)
+                       *vlan_id = fcf_vlan_id;
+               else
+                       *vlan_id = 0xFFFF;
                return 1;
        }
 
@@ -1384,8 +1413,15 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
                        (*addr_mode & LPFC_FCF_FPMA))
                                *addr_mode = LPFC_FCF_FPMA;
 
+               /* If matching connect list has a vlan id, use it */
                if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID)
                        *vlan_id = conn_entry->conn_rec.vlan_tag;
+               /*
+                * If no vlan id is specified in connect list, use the vlan id
+                * in the FCF record
+                */
+               else if (fcf_vlan_id)
+                       *vlan_id = fcf_vlan_id;
                else
                        *vlan_id = 0xFFFF;
 
@@ -1423,6 +1459,15 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
 
        if (phba->link_state >= LPFC_LINK_UP)
                lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+       else {
+               /*
+                * Do not continue FCF discovery and clear FCF_DISC_INPROGRESS
+                * flag
+                */
+               spin_lock_irq(&phba->hbalock);
+               phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+               spin_unlock_irq(&phba->hbalock);
+       }
 
        if (unreg_fcf) {
                spin_lock_irq(&phba->hbalock);
@@ -1659,9 +1704,8 @@ lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
                lpfc_initial_fdisc(vport);
        else {
                lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
-               lpfc_printf_vlog(vport, KERN_ERR,
-                       LOG_ELS,
-                       "2606 No NPIV Fabric support\n");
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
+                                "2606 No NPIV Fabric support\n");
        }
        return;
 }
@@ -1756,8 +1800,8 @@ lpfc_mbx_cmpl_reg_vfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
                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;
+       /* The VPI is implicitly registered when the VFI is registered */
+       vport->vpi_state |= LPFC_VPI_REGISTERED;
 
        if (vport->port_state == LPFC_FABRIC_CFG_LINK) {
                lpfc_start_fdiscs(phba);
@@ -1861,7 +1905,10 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
        if (phba->fc_topology == TOPOLOGY_LOOP) {
                phba->sli3_options &= ~LPFC_SLI3_NPIV_ENABLED;
 
-               if (phba->cfg_enable_npiv)
+               /* if npiv is enabled and this adapter supports npiv log
+                * a message that npiv is not supported in this topology
+                */
+               if (phba->cfg_enable_npiv && phba->max_vpi)
                        lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT,
                                "1309 Link Up Event npiv not supported in loop "
                                "topology\n");
@@ -1955,7 +2002,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
                 * is phase 1 implementation that support FCF index 0 and driver
                 * defaults.
                 */
-               if (phba->cfg_enable_fip == 0) {
+               if (!(phba->hba_flag & HBA_FIP_SUPPORT)) {
                        fcf_record = kzalloc(sizeof(struct fcf_record),
                                        GFP_KERNEL);
                        if (unlikely(!fcf_record)) {
@@ -2085,6 +2132,7 @@ lpfc_mbx_cmpl_read_la(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
        else
                phba->sli.sli_flag &= ~LPFC_MENLO_MAINT;
 
+       phba->link_events++;
        if (la->attType == AT_LINK_UP && (!la->mm)) {
                phba->fc_stat.LinkUp++;
                if (phba->link_flag & LS_LOOPBACK_MODE) {
@@ -2211,13 +2259,14 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                                 mb->mbxStatus);
                break;
        }
+       vport->vpi_state &= ~LPFC_VPI_REGISTERED;
        vport->unreg_vpi_cmpl = VPORT_OK;
        mempool_free(pmb, phba->mbox_mem_pool);
        /*
         * This shost reference might have been taken at the beginning of
         * lpfc_vport_delete()
         */
-       if (vport->load_flag & FC_UNLOADING)
+       if ((vport->load_flag & FC_UNLOADING) && (vport != phba->pport))
                scsi_host_put(shost);
 }
 
@@ -2268,6 +2317,7 @@ lpfc_mbx_cmpl_reg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
                goto out;
        }
 
+       vport->vpi_state |= LPFC_VPI_REGISTERED;
        vport->num_disc_nodes = 0;
        /* go thru NPR list and issue ELS PLOGIs */
        if (vport->fc_npr_cnt)
@@ -3077,7 +3127,7 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
        struct lpfc_sli *psli;
        struct lpfc_sli_ring *pring;
        struct lpfc_iocbq *iocb, *next_iocb;
-       uint32_t rpi, i;
+       uint32_t i;
 
        lpfc_fabric_abort_nport(ndlp);
 
@@ -3086,7 +3136,6 @@ lpfc_no_rpi(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
         * by firmware with a no rpi error.
         */
        psli = &phba->sli;
-       rpi = ndlp->nlp_rpi;
        if (ndlp->nlp_flag & NLP_RPI_VALID) {
                /* Now process each ring */
                for (i = 0; i < psli->num_rings; i++) {
@@ -4322,6 +4371,14 @@ lpfc_fcf_inuse(struct lpfc_hba *phba)
                                ret = 1;
                                spin_unlock_irq(shost->host_lock);
                                goto out;
+                       } else {
+                               lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                                       "2624 RPI %x DID %x flg %x still "
+                                       "logged in\n",
+                                       ndlp->nlp_rpi, ndlp->nlp_DID,
+                                       ndlp->nlp_flag);
+                               if (ndlp->nlp_flag & NLP_RPI_VALID)
+                                       ret = 1;
                        }
                }
                spin_unlock_irq(shost->host_lock);
@@ -4400,7 +4457,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
         */
        if (!(phba->hba_flag & HBA_FCOE_SUPPORT) ||
                !(phba->fcf.fcf_flag & FCF_REGISTERED) ||
-               (phba->cfg_enable_fip == 0)) {
+               (!(phba->hba_flag & HBA_FIP_SUPPORT))) {
                spin_unlock_irq(&phba->hbalock);
                return;
        }
@@ -4409,6 +4466,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
        if (lpfc_fcf_inuse(phba))
                return;
 
+       /* At this point, all discovery is aborted */
+       phba->pport->port_state = LPFC_VPORT_UNKNOWN;
 
        /* Unregister VPIs */
        vports = lpfc_create_vport_work_array(phba);
@@ -4416,8 +4475,8 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
                (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;
+                       vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+                       vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
                }
        lpfc_destroy_vport_work_array(phba, vports);
 
@@ -4431,7 +4490,7 @@ lpfc_unregister_unused_fcf(struct lpfc_hba *phba)
                return;
        }
 
-       lpfc_unreg_vfi(mbox, phba->pport->vfi);
+       lpfc_unreg_vfi(mbox, phba->pport);
        mbox->vport = phba->pport;
        mbox->mbox_cmpl = lpfc_unregister_vfi_cmpl;
 
@@ -4512,8 +4571,10 @@ lpfc_read_fcf_conn_tbl(struct lpfc_hba *phba,
 
        /* Free the current connect table */
        list_for_each_entry_safe(conn_entry, next_conn_entry,
-               &phba->fcf_conn_rec_list, list)
+               &phba->fcf_conn_rec_list, list) {
+               list_del_init(&conn_entry->list);
                kfree(conn_entry);
+       }
 
        conn_hdr = (struct lpfc_fcf_conn_hdr *) buff;
        record_count = conn_hdr->length * sizeof(uint32_t)/
@@ -4569,14 +4630,6 @@ lpfc_read_fcoe_param(struct lpfc_hba *phba,
                (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) &
index ccb2672..c9faa1d 100644 (file)
@@ -1124,21 +1124,6 @@ typedef struct {
 /* Number of 4-byte words in an IOCB. */
 #define IOCB_WORD_SZ    8
 
-/* defines for type field in fc header */
-#define FC_ELS_DATA     0x1
-#define FC_LLC_SNAP     0x5
-#define FC_FCP_DATA     0x8
-#define FC_COMMON_TRANSPORT_ULP 0x20
-
-/* defines for rctl field in fc header */
-#define FC_DEV_DATA     0x0
-#define FC_UNSOL_CTL    0x2
-#define FC_SOL_CTL      0x3
-#define FC_UNSOL_DATA   0x4
-#define FC_FCP_CMND     0x6
-#define FC_ELS_REQ      0x22
-#define FC_ELS_RSP      0x23
-
 /* network headers for Dfctl field */
 #define FC_NET_HDR      0x20
 
@@ -1183,6 +1168,8 @@ typedef struct {
 #define PCI_DEVICE_ID_ZEPHYR_DCSP   0xfe12
 #define PCI_VENDOR_ID_SERVERENGINE  0x19a2
 #define PCI_DEVICE_ID_TIGERSHARK    0x0704
+#define PCI_DEVICE_ID_TOMCAT        0x0714
+#define PCI_DEVICE_ID_FALCON        0xf180
 
 #define JEDEC_ID_ADDRESS            0x0080001c
 #define FIREFLY_JEDEC_ID            0x1ACC
@@ -1444,6 +1431,7 @@ typedef struct {          /* FireFly BIU registers */
 #define CMD_ABORT_MXRI64_CN     0x8C
 #define CMD_RCV_ELS_REQ64_CX    0x8D
 #define CMD_XMIT_ELS_RSP64_CX   0x95
+#define CMD_XMIT_BLS_RSP64_CX   0x97
 #define CMD_FCP_IWRITE64_CR     0x98
 #define CMD_FCP_IWRITE64_CX     0x99
 #define CMD_FCP_IREAD64_CR      0x9A
@@ -2306,8 +2294,7 @@ typedef struct {
        uint32_t rsvd1;
        uint32_t rsvd2:8;
        uint32_t sid:24;
-       uint32_t rsvd3;
-       uint32_t rsvd4;
+       uint32_t wwn[2];
        uint32_t rsvd5;
        uint16_t vfi;
        uint16_t vpi;
@@ -2315,8 +2302,7 @@ typedef struct {
        uint32_t rsvd1;
        uint32_t sid:24;
        uint32_t rsvd2:8;
-       uint32_t rsvd3;
-       uint32_t rsvd4;
+       uint32_t wwn[2];
        uint32_t rsvd5;
        uint16_t vpi;
        uint16_t vfi;
@@ -2326,7 +2312,13 @@ typedef struct {
 /* Structure for MB Command UNREG_VPI (0x97) */
 typedef struct {
        uint32_t rsvd1;
-       uint32_t rsvd2;
+#ifdef __BIG_ENDIAN_BITFIELD
+       uint16_t rsvd2;
+       uint16_t sli4_vpi;
+#else  /*  __LITTLE_ENDIAN */
+       uint16_t sli4_vpi;
+       uint16_t rsvd2;
+#endif
        uint32_t rsvd3;
        uint32_t rsvd4;
        uint32_t rsvd5;
@@ -3547,7 +3539,7 @@ typedef struct _IOCB {    /* IOCB structure */
                ASYNCSTAT_FIELDS asyncstat; /* async_status iocb */
                QUE_XRI64_CX_FIELDS quexri64cx; /* que_xri64_cx fields */
                struct rcv_seq64 rcvseq64;      /* RCV_SEQ64 and RCV_CONT64 */
-
+               struct sli4_bls_acc bls_acc; /* UNSOL ABTS BLS_ACC params */
                uint32_t ulpWord[IOCB_WORD_SZ - 2];     /* generic 6 'words' */
        } un;
        union {
index 3689eee..1585148 100644 (file)
@@ -194,6 +194,26 @@ struct lpfc_sli4_flags {
 #define lpfc_fip_flag_WORD word0
 };
 
+struct sli4_bls_acc {
+       uint32_t word0_rsvd;      /* Word0 must be reserved */
+       uint32_t word1;
+#define lpfc_abts_orig_SHIFT      0
+#define lpfc_abts_orig_MASK       0x00000001
+#define lpfc_abts_orig_WORD       word1
+#define LPFC_ABTS_UNSOL_RSP       1
+#define LPFC_ABTS_UNSOL_INT       0
+       uint32_t word2;
+#define lpfc_abts_rxid_SHIFT      0
+#define lpfc_abts_rxid_MASK       0x0000FFFF
+#define lpfc_abts_rxid_WORD       word2
+#define lpfc_abts_oxid_SHIFT      16
+#define lpfc_abts_oxid_MASK       0x0000FFFF
+#define lpfc_abts_oxid_WORD       word2
+       uint32_t word3;
+       uint32_t word4;
+       uint32_t word5_rsvd;    /* Word5 must be reserved */
+};
+
 /* event queue entry structure */
 struct lpfc_eqe {
        uint32_t word0;
@@ -425,7 +445,7 @@ struct lpfc_wqe_generic{
 #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_MASK           0x00000003
 #define lpfc_wqe_gen_ct_WORD           word7
        uint32_t abort_tag;
        uint32_t word9;
@@ -453,6 +473,13 @@ struct lpfc_wqe_generic{
 #define lpfc_wqe_gen_wqec_SHIFT                7
 #define lpfc_wqe_gen_wqec_MASK         0x00000001
 #define lpfc_wqe_gen_wqec_WORD         word11
+#define ELS_ID_FLOGI 3
+#define ELS_ID_FDISC 2
+#define ELS_ID_LOGO  1
+#define ELS_ID_DEFAULT 0
+#define lpfc_wqe_gen_els_id_SHIFT      4
+#define lpfc_wqe_gen_els_id_MASK       0x00000003
+#define lpfc_wqe_gen_els_id_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
@@ -487,8 +514,8 @@ struct lpfc_register {
 
 #define LPFC_UERR_STATUS_HI            0x00A4
 #define LPFC_UERR_STATUS_LO            0x00A0
-#define LPFC_ONLINE0                   0x00B0
-#define LPFC_ONLINE1                   0x00B4
+#define LPFC_UE_MASK_HI                        0x00AC
+#define LPFC_UE_MASK_LO                        0x00A8
 #define LPFC_SCRATCHPAD                        0x0058
 
 /* BAR0 Registers */
@@ -760,6 +787,7 @@ struct mbox_header {
 #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_QUERY_FW_CFG          0x3A
 #define LPFC_MBOX_OPCODE_FUNCTION_RESET                0x3D
 
 /* FCoE Opcodes */
@@ -1273,6 +1301,51 @@ struct lpfc_mbx_del_fcf_tbl_entry {
 #define lpfc_mbx_del_fcf_tbl_index_WORD                word10
 };
 
+struct lpfc_mbx_query_fw_cfg {
+       struct mbox_header header;
+       uint32_t config_number;
+       uint32_t asic_rev;
+       uint32_t phys_port;
+       uint32_t function_mode;
+/* firmware Function Mode */
+#define lpfc_function_mode_toe_SHIFT           0
+#define lpfc_function_mode_toe_MASK            0x00000001
+#define lpfc_function_mode_toe_WORD            function_mode
+#define lpfc_function_mode_nic_SHIFT           1
+#define lpfc_function_mode_nic_MASK            0x00000001
+#define lpfc_function_mode_nic_WORD            function_mode
+#define lpfc_function_mode_rdma_SHIFT          2
+#define lpfc_function_mode_rdma_MASK           0x00000001
+#define lpfc_function_mode_rdma_WORD           function_mode
+#define lpfc_function_mode_vm_SHIFT            3
+#define lpfc_function_mode_vm_MASK             0x00000001
+#define lpfc_function_mode_vm_WORD             function_mode
+#define lpfc_function_mode_iscsi_i_SHIFT       4
+#define lpfc_function_mode_iscsi_i_MASK                0x00000001
+#define lpfc_function_mode_iscsi_i_WORD                function_mode
+#define lpfc_function_mode_iscsi_t_SHIFT       5
+#define lpfc_function_mode_iscsi_t_MASK                0x00000001
+#define lpfc_function_mode_iscsi_t_WORD                function_mode
+#define lpfc_function_mode_fcoe_i_SHIFT                6
+#define lpfc_function_mode_fcoe_i_MASK         0x00000001
+#define lpfc_function_mode_fcoe_i_WORD         function_mode
+#define lpfc_function_mode_fcoe_t_SHIFT                7
+#define lpfc_function_mode_fcoe_t_MASK         0x00000001
+#define lpfc_function_mode_fcoe_t_WORD         function_mode
+#define lpfc_function_mode_dal_SHIFT           8
+#define lpfc_function_mode_dal_MASK            0x00000001
+#define lpfc_function_mode_dal_WORD            function_mode
+#define lpfc_function_mode_lro_SHIFT           9
+#define lpfc_function_mode_lro_MASK            0x00000001
+#define lpfc_function_mode_lro_WORD            function_mode9
+#define lpfc_function_mode_flex10_SHIFT                10
+#define lpfc_function_mode_flex10_MASK         0x00000001
+#define lpfc_function_mode_flex10_WORD         function_mode
+#define lpfc_function_mode_ncsi_SHIFT          11
+#define lpfc_function_mode_ncsi_MASK           0x00000001
+#define lpfc_function_mode_ncsi_WORD           function_mode
+};
+
 /* Status field for embedded SLI_CONFIG mailbox command */
 #define STATUS_SUCCESS                                 0x0
 #define STATUS_FAILED                                  0x1
@@ -1349,8 +1422,7 @@ struct lpfc_mbx_reg_vfi {
 #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;
+       uint32_t wwn[2];
        struct ulp_bde64 bde;
        uint32_t word8_rsvd;
        uint32_t word9_rsvd;
@@ -1555,6 +1627,11 @@ struct lpfc_mbx_read_rev {
 #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_cee_ver_SHIFT          21
+#define lpfc_mbx_rd_rev_cee_ver_MASK           0x00000003
+#define lpfc_mbx_rd_rev_cee_ver_WORD           word1
+#define LPFC_PREDCBX_CEE_MODE  0
+#define LPFC_DCBX_CEE_MODE     1
 #define lpfc_mbx_rd_rev_vpd_SHIFT              29
 #define lpfc_mbx_rd_rev_vpd_MASK               0x00000001
 #define lpfc_mbx_rd_rev_vpd_WORD               word1
@@ -1804,6 +1881,7 @@ struct lpfc_mqe {
                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_query_fw_cfg query_fw_cfg;
                struct lpfc_mbx_nop nop;
        } un;
 };
@@ -1885,7 +1963,7 @@ struct lpfc_acqe_link {
 };
 
 struct lpfc_acqe_fcoe {
-       uint32_t fcf_index;
+       uint32_t index;
        uint32_t word1;
 #define lpfc_acqe_fcoe_fcf_count_SHIFT         0
 #define lpfc_acqe_fcoe_fcf_count_MASK          0x0000FFFF
@@ -1896,6 +1974,7 @@ struct lpfc_acqe_fcoe {
 #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
+#define LPFC_FCOE_EVENT_TYPE_CVL               0x4
        uint32_t event_tag;
        uint32_t trailer;
 };
@@ -1921,12 +2000,13 @@ struct lpfc_bmbx_create {
 #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)
+#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_xri_tag_SHIFT     0
+#define wqe_xri_tag_MASK      0x0000FFFF
+#define wqe_xri_tag_WORD      word6
 #define wqe_ctxt_tag_SHIFT    16
 #define wqe_ctxt_tag_MASK     0x0000FFFF
 #define wqe_ctxt_tag_WORD     word6
@@ -1987,7 +2067,7 @@ struct wqe_common {
 #define wqe_wqec_MASK       0x00000001
 #define wqe_wqec_WORD       word11
 #define wqe_cqid_SHIFT      16
-#define wqe_cqid_MASK       0x000003ff
+#define wqe_cqid_MASK       0x0000ffff
 #define wqe_cqid_WORD       word11
 };
 
@@ -1996,6 +2076,9 @@ struct wqe_did {
 #define wqe_els_did_SHIFT         0
 #define wqe_els_did_MASK          0x00FFFFFF
 #define wqe_els_did_WORD          word5
+#define wqe_xmit_bls_pt_SHIFT         28
+#define wqe_xmit_bls_pt_MASK          0x00000003
+#define wqe_xmit_bls_pt_WORD          word5
 #define wqe_xmit_bls_ar_SHIFT         30
 #define wqe_xmit_bls_ar_MASK          0x00000001
 #define wqe_xmit_bls_ar_WORD          word5
@@ -2044,6 +2127,23 @@ struct xmit_els_rsp64_wqe {
 
 struct xmit_bls_rsp64_wqe {
        uint32_t payload0;
+/* Payload0 for BA_ACC */
+#define xmit_bls_rsp64_acc_seq_id_SHIFT        16
+#define xmit_bls_rsp64_acc_seq_id_MASK         0x000000ff
+#define xmit_bls_rsp64_acc_seq_id_WORD         payload0
+#define xmit_bls_rsp64_acc_seq_id_vald_SHIFT   24
+#define xmit_bls_rsp64_acc_seq_id_vald_MASK    0x000000ff
+#define xmit_bls_rsp64_acc_seq_id_vald_WORD    payload0
+/* Payload0 for BA_RJT */
+#define xmit_bls_rsp64_rjt_vspec_SHIFT   0
+#define xmit_bls_rsp64_rjt_vspec_MASK    0x000000ff
+#define xmit_bls_rsp64_rjt_vspec_WORD    payload0
+#define xmit_bls_rsp64_rjt_expc_SHIFT    8
+#define xmit_bls_rsp64_rjt_expc_MASK     0x000000ff
+#define xmit_bls_rsp64_rjt_expc_WORD     payload0
+#define xmit_bls_rsp64_rjt_rsnc_SHIFT    16
+#define xmit_bls_rsp64_rjt_rsnc_MASK     0x000000ff
+#define xmit_bls_rsp64_rjt_rsnc_WORD     payload0
        uint32_t word1;
 #define xmit_bls_rsp64_rxid_SHIFT  0
 #define xmit_bls_rsp64_rxid_MASK   0x0000ffff
@@ -2052,18 +2152,19 @@ struct xmit_bls_rsp64_wqe {
 #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_SHIFT  0
 #define xmit_bls_rsp64_seqcnthi_MASK   0x0000ffff
 #define xmit_bls_rsp64_seqcnthi_WORD   word2
+#define xmit_bls_rsp64_seqcntlo_SHIFT  16
+#define xmit_bls_rsp64_seqcntlo_MASK   0x0000ffff
+#define xmit_bls_rsp64_seqcntlo_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
index 562d8ce..226920d 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/pci.h>
 #include <linux/spinlock.h>
 #include <linux/ctype.h>
+#include <linux/aer.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_device.h>
@@ -645,7 +646,7 @@ lpfc_hba_down_prep(struct lpfc_hba *phba)
  * down the SLI Layer.
  *
  * Return codes
- *   0 - sucess.
+ *   0 - success.
  *   Any other value - error.
  **/
 static int
@@ -700,7 +701,7 @@ lpfc_hba_down_post_s3(struct lpfc_hba *phba)
  * down the SLI Layer.
  *
  * Return codes
- *   0 - sucess.
+ *   0 - success.
  *   Any other value - error.
  **/
 static int
@@ -755,7 +756,7 @@ lpfc_hba_down_post_s4(struct lpfc_hba *phba)
  * uninitialization after the HBA is reset when bring down the SLI Layer.
  *
  * Return codes
- *   0 - sucess.
+ *   0 - success.
  *   Any other value - error.
  **/
 int
@@ -852,12 +853,19 @@ lpfc_hb_mbox_cmpl(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq)
 void
 lpfc_hb_timeout_handler(struct lpfc_hba *phba)
 {
+       struct lpfc_vport **vports;
        LPFC_MBOXQ_t *pmboxq;
        struct lpfc_dmabuf *buf_ptr;
-       int retval;
+       int retval, i;
        struct lpfc_sli *psli = &phba->sli;
        LIST_HEAD(completions);
 
+       vports = lpfc_create_vport_work_array(phba);
+       if (vports != NULL)
+               for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++)
+                       lpfc_rcv_seq_check_edtov(vports[i]);
+       lpfc_destroy_vport_work_array(phba, vports);
+
        if ((phba->link_state == LPFC_HBA_ERROR) ||
                (phba->pport->load_flag & FC_UNLOADING) ||
                (phba->pport->fc_flag & FC_OFFLINE_MODE))
@@ -1254,7 +1262,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba)
  * routine from the API jump table function pointer from the lpfc_hba struct.
  *
  * Return codes
- *   0 - sucess.
+ *   0 - success.
  *   Any other value - error.
  **/
 void
@@ -1521,10 +1529,10 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
        int GE = 0;
        int oneConnect = 0; /* default is not a oneConnect */
        struct {
-               char * name;
-               int    max_speed;
-               char * bus;
-       } m = {"<Unknown>", 0, ""};
+               char *name;
+               char *bus;
+               char *function;
+       } m = {"<Unknown>", "", ""};
 
        if (mdp && mdp[0] != '\0'
                && descp && descp[0] != '\0')
@@ -1545,132 +1553,155 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
 
        switch (dev_id) {
        case PCI_DEVICE_ID_FIREFLY:
-               m = (typeof(m)){"LP6000", max_speed, "PCI"};
+               m = (typeof(m)){"LP6000", "PCI", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SUPERFLY:
                if (vp->rev.biuRev >= 1 && vp->rev.biuRev <= 3)
-                       m = (typeof(m)){"LP7000", max_speed,  "PCI"};
+                       m = (typeof(m)){"LP7000", "PCI",
+                                       "Fibre Channel Adapter"};
                else
-                       m = (typeof(m)){"LP7000E", max_speed, "PCI"};
+                       m = (typeof(m)){"LP7000E", "PCI",
+                                       "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_DRAGONFLY:
-               m = (typeof(m)){"LP8000", max_speed, "PCI"};
+               m = (typeof(m)){"LP8000", "PCI",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_CENTAUR:
                if (FC_JEDEC_ID(vp->rev.biuRev) == CENTAUR_2G_JEDEC_ID)
-                       m = (typeof(m)){"LP9002", max_speed, "PCI"};
+                       m = (typeof(m)){"LP9002", "PCI",
+                                       "Fibre Channel Adapter"};
                else
-                       m = (typeof(m)){"LP9000", max_speed, "PCI"};
+                       m = (typeof(m)){"LP9000", "PCI",
+                                       "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_RFLY:
-               m = (typeof(m)){"LP952", max_speed, "PCI"};
+               m = (typeof(m)){"LP952", "PCI",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PEGASUS:
-               m = (typeof(m)){"LP9802", max_speed, "PCI-X"};
+               m = (typeof(m)){"LP9802", "PCI-X",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_THOR:
-               m = (typeof(m)){"LP10000", max_speed, "PCI-X"};
+               m = (typeof(m)){"LP10000", "PCI-X",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_VIPER:
-               m = (typeof(m)){"LPX1000", max_speed,  "PCI-X"};
+               m = (typeof(m)){"LPX1000",  "PCI-X",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PFLY:
-               m = (typeof(m)){"LP982", max_speed, "PCI-X"};
+               m = (typeof(m)){"LP982", "PCI-X",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_TFLY:
-               m = (typeof(m)){"LP1050", max_speed, "PCI-X"};
+               m = (typeof(m)){"LP1050", "PCI-X",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HELIOS:
-               m = (typeof(m)){"LP11000", max_speed, "PCI-X2"};
+               m = (typeof(m)){"LP11000", "PCI-X2",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HELIOS_SCSP:
-               m = (typeof(m)){"LP11000-SP", max_speed, "PCI-X2"};
+               m = (typeof(m)){"LP11000-SP", "PCI-X2",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HELIOS_DCSP:
-               m = (typeof(m)){"LP11002-SP", max_speed, "PCI-X2"};
+               m = (typeof(m)){"LP11002-SP",  "PCI-X2",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_NEPTUNE:
-               m = (typeof(m)){"LPe1000", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe1000", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_NEPTUNE_SCSP:
-               m = (typeof(m)){"LPe1000-SP", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe1000-SP", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_NEPTUNE_DCSP:
-               m = (typeof(m)){"LPe1002-SP", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe1002-SP", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_BMID:
-               m = (typeof(m)){"LP1150", max_speed, "PCI-X2"};
+               m = (typeof(m)){"LP1150", "PCI-X2", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_BSMB:
-               m = (typeof(m)){"LP111", max_speed, "PCI-X2"};
+               m = (typeof(m)){"LP111", "PCI-X2", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_ZEPHYR:
-               m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_ZEPHYR_SCSP:
-               m = (typeof(m)){"LPe11000", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe11000", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_ZEPHYR_DCSP:
-               m = (typeof(m)){"LP2105", max_speed, "PCIe"};
+               m = (typeof(m)){"LP2105", "PCIe", "FCoE Adapter"};
                GE = 1;
                break;
        case PCI_DEVICE_ID_ZMID:
-               m = (typeof(m)){"LPe1150", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe1150", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_ZSMB:
-               m = (typeof(m)){"LPe111", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe111", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LP101:
-               m = (typeof(m)){"LP101", max_speed, "PCI-X"};
+               m = (typeof(m)){"LP101", "PCI-X", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LP10000S:
-               m = (typeof(m)){"LP10000-S", max_speed, "PCI"};
+               m = (typeof(m)){"LP10000-S", "PCI", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LP11000S:
-               m = (typeof(m)){"LP11000-S", max_speed,
-                       "PCI-X2"};
+               m = (typeof(m)){"LP11000-S", "PCI-X2", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_LPE11000S:
-               m = (typeof(m)){"LPe11000-S", max_speed,
-                       "PCIe"};
+               m = (typeof(m)){"LPe11000-S", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT:
-               m = (typeof(m)){"LPe12000", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe12000", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT_MID:
-               m = (typeof(m)){"LPe1250", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe1250", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT_SMB:
-               m = (typeof(m)){"LPe121", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe121", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT_DCSP:
-               m = (typeof(m)){"LPe12002-SP", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe12002-SP", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT_SCSP:
-               m = (typeof(m)){"LPe12000-SP", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe12000-SP", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_SAT_S:
-               m = (typeof(m)){"LPe12000-S", max_speed, "PCIe"};
+               m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_HORNET:
-               m = (typeof(m)){"LP21000", max_speed, "PCIe"};
+               m = (typeof(m)){"LP21000", "PCIe", "FCoE Adapter"};
                GE = 1;
                break;
        case PCI_DEVICE_ID_PROTEUS_VF:
-               m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+               m = (typeof(m)){"LPev12000", "PCIe IOV",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PROTEUS_PF:
-               m = (typeof(m)) {"LPev12000", max_speed, "PCIe IOV"};
+               m = (typeof(m)){"LPev12000", "PCIe IOV",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_PROTEUS_S:
-               m = (typeof(m)) {"LPemv12002-S", max_speed, "PCIe IOV"};
+               m = (typeof(m)){"LPemv12002-S", "PCIe IOV",
+                               "Fibre Channel Adapter"};
                break;
        case PCI_DEVICE_ID_TIGERSHARK:
                oneConnect = 1;
-               m = (typeof(m)) {"OCe10100-F", max_speed, "PCIe"};
+               m = (typeof(m)){"OCe10100", "PCIe", "FCoE"};
+               break;
+       case PCI_DEVICE_ID_TOMCAT:
+               oneConnect = 1;
+               m = (typeof(m)){"OCe11100", "PCIe", "FCoE"};
+               break;
+       case PCI_DEVICE_ID_FALCON:
+               m = (typeof(m)){"LPSe12002-ML1-E", "PCIe",
+                               "EmulexSecure Fibre"};
                break;
        default:
-               m = (typeof(m)){ NULL };
+               m = (typeof(m)){"Unknown", "", ""};
                break;
        }
 
@@ -1682,17 +1713,14 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp)
        if (descp && descp[0] == '\0') {
                if (oneConnect)
                        snprintf(descp, 255,
-                               "Emulex OneConnect %s, FCoE Initiator, Port %s",
-                               m.name,
+                               "Emulex OneConnect %s, %s Initiator, Port %s",
+                               m.name, m.function,
                                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");
+                               m.name, max_speed, (GE) ? "GE" : "Gb",
+                               m.bus, m.function);
        }
 }
 
@@ -2217,7 +2245,7 @@ lpfc_offline_prep(struct lpfc_hba * phba)
 
                        if (vports[i]->load_flag & FC_UNLOADING)
                                continue;
-                       vports[i]->vfi_state &= ~LPFC_VFI_REGISTERED;
+                       vports[i]->vpi_state &= ~LPFC_VPI_REGISTERED;
                        shost = lpfc_shost_from_vport(vports[i]);
                        list_for_each_entry_safe(ndlp, next_ndlp,
                                                 &vports[i]->fc_nodes,
@@ -2308,6 +2336,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
 
        spin_lock_irq(&phba->hbalock);
        /* Release all the lpfc_scsi_bufs maintained by this host. */
+       spin_lock(&phba->scsi_buf_list_lock);
        list_for_each_entry_safe(sb, sb_next, &phba->lpfc_scsi_buf_list, list) {
                list_del(&sb->list);
                pci_pool_free(phba->lpfc_scsi_dma_buf_pool, sb->data,
@@ -2315,6 +2344,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
                kfree(sb);
                phba->total_scsi_bufs--;
        }
+       spin_unlock(&phba->scsi_buf_list_lock);
 
        /* Release all the lpfc_iocbq entries maintained by this host. */
        list_for_each_entry_safe(io, io_next, &phba->lpfc_iocb_list, list) {
@@ -2322,9 +2352,7 @@ lpfc_scsi_free(struct lpfc_hba *phba)
                kfree(io);
                phba->total_iocbq_bufs--;
        }
-
        spin_unlock_irq(&phba->hbalock);
-
        return 0;
 }
 
@@ -2408,7 +2436,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev)
        vport->els_tmofunc.function = lpfc_els_timeout;
        vport->els_tmofunc.data = (unsigned long)vport;
 
-       error = scsi_add_host(shost, dev);
+       error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev);
        if (error)
                goto out_put_shost;
 
@@ -2698,6 +2726,63 @@ lpfc_sli_remove_dflt_fcf(struct lpfc_hba *phba)
                mempool_free(mboxq, phba->mbox_mem_pool);
 }
 
+/**
+ * lpfc_sli4_fw_cfg_check - Read the firmware config and verify FCoE support
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function uses the QUERY_FW_CFG mailbox command to determine if the
+ * firmware loaded supports FCoE. A return of zero indicates that the mailbox
+ * was successful and the firmware supports FCoE. Any other return indicates
+ * a error. It is assumed that this function will be called before interrupts
+ * are enabled.
+ **/
+static int
+lpfc_sli4_fw_cfg_check(struct lpfc_hba *phba)
+{
+       int rc = 0;
+       LPFC_MBOXQ_t *mboxq;
+       struct lpfc_mbx_query_fw_cfg *query_fw_cfg;
+       uint32_t length;
+       uint32_t shdr_status, shdr_add_status;
+
+       mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+       if (!mboxq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "2621 Failed to allocate mbox for "
+                               "query firmware config cmd\n");
+               return -ENOMEM;
+       }
+       query_fw_cfg = &mboxq->u.mqe.un.query_fw_cfg;
+       length = (sizeof(struct lpfc_mbx_query_fw_cfg) -
+                 sizeof(struct lpfc_sli4_cfg_mhdr));
+       lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_COMMON,
+                        LPFC_MBOX_OPCODE_QUERY_FW_CFG,
+                        length, LPFC_SLI4_MBX_EMBED);
+       rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
+       /* The IOCTL status is embedded in the mailbox subheader. */
+       shdr_status = bf_get(lpfc_mbox_hdr_status,
+                            &query_fw_cfg->header.cfg_shdr.response);
+       shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+                                &query_fw_cfg->header.cfg_shdr.response);
+       if (shdr_status || shdr_add_status || rc != MBX_SUCCESS) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2622 Query Firmware Config failed "
+                               "mbx status x%x, status x%x add_status x%x\n",
+                               rc, shdr_status, shdr_add_status);
+               return -EINVAL;
+       }
+       if (!bf_get(lpfc_function_mode_fcoe_i, query_fw_cfg)) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2623 FCoE Function not supported by firmware. "
+                               "Function mode = %08x\n",
+                               query_fw_cfg->function_mode);
+               return -EINVAL;
+       }
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+       return 0;
+}
+
 /**
  * lpfc_sli4_parse_latt_fault - Parse sli4 link-attention link fault code
  * @phba: pointer to lpfc hba data structure.
@@ -2918,13 +3003,17 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
 {
        uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe);
        int rc;
+       struct lpfc_vport *vport;
+       struct lpfc_nodelist *ndlp;
+       struct Scsi_Host  *shost;
 
+       phba->fc_eventTag = acqe_fcoe->event_tag;
        phba->fcoe_eventtag = acqe_fcoe->event_tag;
        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->index,
                        acqe_fcoe->event_tag);
                /*
                 * If the current FCF is in discovered state, or
@@ -2939,12 +3028,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
                spin_unlock_irq(&phba->hbalock);
 
                /* Read the FCF table and re-discover SAN. */
-               rc = lpfc_sli4_read_fcf_record(phba,
-                       LPFC_FCOE_FCF_GET_FIRST);
+               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);
+                                       "2547 Read FCF record failed 0x%x\n",
+                                       rc);
                break;
 
        case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL:
@@ -2956,11 +3044,11 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
 
        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,
+                       "2549 FCF disconnected from network index 0x%x"
+                       " tag 0x%x\n", acqe_fcoe->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)
+               if (phba->fcf.fcf_indx != acqe_fcoe->index)
                        break;
                /*
                 * Currently, driver support only one FCF - so treat this as
@@ -2970,7 +3058,28 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
                /* Unregister FCF if no devices connected to it */
                lpfc_unregister_unused_fcf(phba);
                break;
-
+       case LPFC_FCOE_EVENT_TYPE_CVL:
+               lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
+                       "2718 Clear Virtual Link Received for VPI 0x%x"
+                       " tag 0x%x\n", acqe_fcoe->index, acqe_fcoe->event_tag);
+               vport = lpfc_find_vport_by_vpid(phba,
+                               acqe_fcoe->index - phba->vpi_base);
+               if (!vport)
+                       break;
+               ndlp = lpfc_findnode_did(vport, Fabric_DID);
+               if (!ndlp)
+                       break;
+               shost = lpfc_shost_from_vport(vport);
+               lpfc_linkdown_port(vport);
+               if (vport->port_type != LPFC_NPIV_PORT) {
+                       mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ);
+                       spin_lock_irq(shost->host_lock);
+                       ndlp->nlp_flag |= NLP_DELAY_TMO;
+                       spin_unlock_irq(shost->host_lock);
+                       ndlp->nlp_last_elscmd = ELS_CMD_FLOGI;
+                       vport->port_state = LPFC_FLOGI;
+               }
+               break;
        default:
                lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
                        "0288 Unknown FCoE event type 0x%x event tag "
@@ -2990,6 +3099,7 @@ static void
 lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba,
                         struct lpfc_acqe_dcbx *acqe_dcbx)
 {
+       phba->fc_eventTag = acqe_dcbx->event_tag;
        lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
                        "0290 The SLI4 DCBX asynchronous event is not "
                        "handled yet\n");
@@ -3124,7 +3234,7 @@ static void lpfc_log_intr_mode(struct lpfc_hba *phba, uint32_t intr_mode)
  * PCI devices.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3220,7 +3330,7 @@ lpfc_reset_hba(struct lpfc_hba *phba)
  * support the SLI-3 HBA device it attached to.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3321,7 +3431,7 @@ lpfc_sli_driver_resource_unset(struct lpfc_hba *phba)
  * support the SLI-4 HBA device it attached to.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3432,7 +3542,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
        /* 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);
+       INIT_LIST_HEAD(&phba->sli4_hba.sp_queue_event);
        /* 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 */
@@ -3461,6 +3571,10 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
        if (unlikely(rc))
                goto out_free_bsmbx;
 
+       rc = lpfc_sli4_fw_cfg_check(phba);
+       if (unlikely(rc))
+               goto out_free_bsmbx;
+
        /* Set up the hba's configuration parameters. */
        rc = lpfc_sli4_read_config(phba);
        if (unlikely(rc))
@@ -3594,8 +3708,10 @@ lpfc_sli4_driver_resource_unset(struct lpfc_hba *phba)
 
        /* Free the current connect table */
        list_for_each_entry_safe(conn_entry, next_conn_entry,
-               &phba->fcf_conn_rec_list, list)
+               &phba->fcf_conn_rec_list, list) {
+               list_del_init(&conn_entry->list);
                kfree(conn_entry);
+       }
 
        return;
 }
@@ -3642,7 +3758,7 @@ lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
  * device specific resource setup to support the HBA device it attached to.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3688,7 +3804,7 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba)
  * device specific resource setup to support the HBA device it attached to.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3753,7 +3869,7 @@ lpfc_free_iocb_list(struct lpfc_hba *phba)
  * list and set up the IOCB tag array accordingly.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3824,7 +3940,7 @@ lpfc_free_sgl_list(struct lpfc_hba *phba)
        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);
+                       "2005 Unable to deregister pages from HBA: %x\n", rc);
        }
        kfree(phba->sli4_hba.lpfc_els_sgl_array);
 }
@@ -3872,7 +3988,7 @@ lpfc_free_active_sgl(struct lpfc_hba *phba)
  * list and set up the sgl xritag tag array accordingly.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -3986,7 +4102,7 @@ out_free_mem:
  * enabled and the driver is reinitializing the device.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -4146,7 +4262,7 @@ lpfc_sli4_remove_rpi_hdrs(struct lpfc_hba *phba)
  * PCI device data structure is set.
  *
  * Return codes
- *      pointer to @phba - sucessful
+ *      pointer to @phba - successful
  *      NULL - error
  **/
 static struct lpfc_hba *
@@ -4202,7 +4318,7 @@ lpfc_hba_free(struct lpfc_hba *phba)
  * host with it.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      other values - error
  **/
 static int
@@ -4273,7 +4389,8 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
                        _dump_buf_data =
                                (char *) __get_free_pages(GFP_KERNEL, pagecnt);
                        if (_dump_buf_data) {
-                               printk(KERN_ERR "BLKGRD allocated %d pages for "
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9043 BLKGRD: allocated %d pages for "
                                       "_dump_buf_data at 0x%p\n",
                                       (1 << pagecnt), _dump_buf_data);
                                _dump_buf_data_order = pagecnt;
@@ -4284,17 +4401,20 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
                                --pagecnt;
                }
                if (!_dump_buf_data_order)
-                       printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9044 BLKGRD: ERROR unable to allocate "
                               "memory for hexdump\n");
        } else
-               printk(KERN_ERR "BLKGRD already allocated _dump_buf_data=0x%p"
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9045 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 "
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9046 BLKGRD: allocated %d pages for "
                                       "_dump_buf_dif at 0x%p\n",
                                       (1 << pagecnt), _dump_buf_dif);
                                _dump_buf_dif_order = pagecnt;
@@ -4305,10 +4425,12 @@ lpfc_setup_bg(struct lpfc_hba *phba, struct Scsi_Host *shost)
                                --pagecnt;
                }
                if (!_dump_buf_dif_order)
-                       printk(KERN_ERR "BLKGRD ERROR unable to allocate "
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9047 BLKGRD: ERROR unable to allocate "
                               "memory for hexdump\n");
        } else
-               printk(KERN_ERR "BLKGRD already allocated _dump_buf_dif=0x%p\n",
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9048 BLKGRD: already allocated _dump_buf_dif=0x%p\n",
                       _dump_buf_dif);
 }
 
@@ -4365,7 +4487,7 @@ lpfc_post_init_setup(struct lpfc_hba *phba)
  * with SLI-3 interface spec.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -4512,7 +4634,6 @@ 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)
@@ -4556,21 +4677,20 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
                        bf_get(lpfc_scratchpad_slirev, &scratchpad),
                        bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
                        bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
-
+       phba->sli4_hba.ue_mask_lo = readl(phba->sli4_hba.UEMASKLOregaddr);
+       phba->sli4_hba.ue_mask_hi = readl(phba->sli4_hba.UEMASKHIregaddr);
        /* 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);
-               }
+       uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
+       uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
+       if ((~phba->sli4_hba.ue_mask_lo & uerrlo_reg.word0) ||
+           (~phba->sli4_hba.ue_mask_hi & 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, "
+                               "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+                               uerrlo_reg.word0, uerrhi_reg.word0,
+                               phba->sli4_hba.ue_mask_lo,
+                               phba->sli4_hba.ue_mask_hi);
                return -ENODEV;
        }
 
@@ -4591,10 +4711,10 @@ lpfc_sli4_bar0_register_memmap(struct lpfc_hba *phba)
                                        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.UEMASKLOregaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_UE_MASK_LO;
+       phba->sli4_hba.UEMASKHIregaddr = phba->sli4_hba.conf_regs_memmap_p +
+                                       LPFC_UE_MASK_HI;
        phba->sli4_hba.SCRATCHPADregaddr = phba->sli4_hba.conf_regs_memmap_p +
                                        LPFC_SCRATCHPAD;
 }
@@ -4662,7 +4782,7 @@ lpfc_sli4_bar2_register_memmap(struct lpfc_hba *phba, uint32_t vf)
  * this routine.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     ENOMEM - could not allocated memory.
  **/
 static int
@@ -4761,7 +4881,7 @@ lpfc_destroy_bootstrap_mbox(struct lpfc_hba *phba)
  * allocation for the port.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -4825,7 +4945,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
                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_vpi = (phba->sli4_hba.max_cfg_param.max_vpi > 0) ?
+                               (phba->sli4_hba.max_cfg_param.max_vpi - 1) : 0;
                phba->max_vports = phba->max_vpi;
                lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
                                "2003 cfg params XRI(B:%d M:%d), "
@@ -4861,7 +4982,7 @@ lpfc_sli4_read_config(struct lpfc_hba *phba)
  * HBA consistent with the SLI-4 interface spec.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -4910,7 +5031,7 @@ lpfc_setup_endian_order(struct lpfc_hba *phba)
  * we just use some constant number as place holder.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -4979,10 +5100,9 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
        /* 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,
+                               "2593 The FCP EQ count(%d) cannot be greater "
+                               "than the FCP WQ count(%d), limiting the "
+                               "FCP EQ count to %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;
@@ -5058,15 +5178,6 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
        }
        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 *) *
@@ -5075,7 +5186,7 @@ lpfc_sli4_queue_create(struct lpfc_hba *phba)
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                                "2577 Failed allocate memory for fast-path "
                                "CQ record array\n");
-               goto out_free_rxq_cq;
+               goto out_free_els_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,
@@ -5188,9 +5299,6 @@ out_free_fcp_cq:
                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;
@@ -5218,7 +5326,7 @@ out_error:
  * operation.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -5247,10 +5355,6 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
        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;
@@ -5286,7 +5390,7 @@ lpfc_sli4_queue_destroy(struct lpfc_hba *phba)
  * operation.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -5383,25 +5487,6 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
                        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]) {
@@ -5507,7 +5592,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
                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);
+                           phba->sli4_hba.els_cq, LPFC_USOL);
        if (rc) {
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                                "0541 Failed setup of Receive Queue: "
@@ -5519,7 +5604,7 @@ lpfc_sli4_queue_setup(struct lpfc_hba *phba)
                        "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);
+                       phba->sli4_hba.els_cq->queue_id);
        return 0;
 
 out_destroy_fcp_wq:
@@ -5531,8 +5616,6 @@ out_destroy_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);
@@ -5552,7 +5635,7 @@ out_error:
  * operation.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -5574,8 +5657,6 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
        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]);
@@ -5599,7 +5680,7 @@ lpfc_sli4_queue_unset(struct lpfc_hba *phba)
  * Later, this can be used for all the slow-path events.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      -ENOMEM - No availble memory
  **/
 static int
@@ -5760,7 +5841,7 @@ lpfc_sli4_cq_event_release_all(struct lpfc_hba *phba)
  * all resources assigned to the PCI function which originates this request.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      ENOMEM - No availble memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -5923,7 +6004,7 @@ lpfc_sli4_fcfi_unreg(struct lpfc_hba *phba, uint16_t fcfi)
  * with SLI-4 interface spec.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -6052,7 +6133,7 @@ lpfc_sli4_pci_mem_unset(struct lpfc_hba *phba)
  * will be left with MSI-X enabled and leaks its vectors.
  *
  * Return codes
- *   0 - sucessful
+ *   0 - successful
  *   other values - error
  **/
 static int
@@ -6184,7 +6265,7 @@ lpfc_sli_disable_msix(struct lpfc_hba *phba)
  * is done in this function.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  */
 static int
@@ -6243,7 +6324,7 @@ lpfc_sli_disable_msi(struct lpfc_hba *phba)
  * MSI-X -> MSI -> IRQ.
  *
  * Return codes
- *   0 - sucessful
+ *   0 - successful
  *   other values - error
  **/
 static uint32_t
@@ -6333,7 +6414,7 @@ lpfc_sli_disable_intr(struct lpfc_hba *phba)
  * enabled and leaks its vectors.
  *
  * Return codes
- * 0 - sucessful
+ * 0 - successful
  * other values - error
  **/
 static int
@@ -6443,7 +6524,7 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba)
  * which is done in this function.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static int
@@ -6508,7 +6589,7 @@ lpfc_sli4_disable_msi(struct lpfc_hba *phba)
  * MSI-X -> MSI -> IRQ.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     other values - error
  **/
 static uint32_t
@@ -6722,6 +6803,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
 {
        struct lpfc_hba   *phba;
        struct lpfc_vport *vport = NULL;
+       struct Scsi_Host  *shost = NULL;
        int error;
        uint32_t cfg_mode, intr_mode;
 
@@ -6800,6 +6882,7 @@ lpfc_pci_probe_one_s3(struct pci_dev *pdev, const struct pci_device_id *pid)
                goto out_destroy_shost;
        }
 
+       shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
        /* Now, trying to enable interrupt and bring up the device */
        cfg_mode = phba->cfg_use_msi;
        while (true) {
@@ -6866,6 +6949,8 @@ out_unset_pci_mem_s3:
        lpfc_sli_pci_mem_unset(phba);
 out_disable_pci_dev:
        lpfc_disable_pci_dev(phba);
+       if (shost)
+               scsi_host_put(shost);
 out_free_phba:
        lpfc_hba_free(phba);
        return error;
@@ -7036,6 +7121,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
        /* 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);
 
@@ -7069,6 +7155,75 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
        return 0;
 }
 
+/**
+ * lpfc_sli_prep_dev_for_recover - Prepare SLI3 device for pci slot recover
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot recover. It
+ * aborts and stops all the on-going I/Os on the pci device.
+ **/
+static void
+lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba)
+{
+       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2723 PCI channel I/O abort preparing for recovery\n");
+       /* Prepare for bringing HBA offline */
+       lpfc_offline_prep(phba);
+       /* Clear sli active flag to prevent sysfs access to HBA */
+       spin_lock_irq(&phba->hbalock);
+       phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE;
+       spin_unlock_irq(&phba->hbalock);
+       /* Stop and flush all I/Os and bring HBA offline */
+       lpfc_offline(phba);
+}
+
+/**
+ * lpfc_sli_prep_dev_for_reset - Prepare SLI3 device for pci slot reset
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot reset. It
+ * disables the device interrupt and pci device, and aborts the internal FCP
+ * pending I/Os.
+ **/
+static void
+lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba)
+{
+       struct lpfc_sli *psli = &phba->sli;
+       struct lpfc_sli_ring  *pring;
+
+       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2710 PCI channel disable preparing for reset\n");
+       /* Disable interrupt and pci device */
+       lpfc_sli_disable_intr(phba);
+       pci_disable_device(phba->pcidev);
+       /*
+        * 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_sli_prep_dev_for_perm_failure - Prepare SLI3 dev for pci slot disable
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is called to prepare the SLI3 device for PCI slot permanently
+ * disabling. It blocks the SCSI transport layer traffic and flushes the FCP
+ * pending I/Os.
+ **/
+static void
+lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba)
+{
+       lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                       "2711 PCI channel permanent disable for 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);
+}
+
 /**
  * lpfc_io_error_detected_s3 - Method for handling SLI-3 device PCI I/O error
  * @pdev: pointer to PCI device.
@@ -7083,6 +7238,7 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev)
  * as desired.
  *
  * Return codes
+ *     PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link
  *     PCI_ERS_RESULT_NEED_RESET - need to reset before recovery
  *     PCI_ERS_RESULT_DISCONNECT - device could not be recovered
  **/
@@ -7091,33 +7247,27 @@ 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);
+       switch (state) {
+       case pci_channel_io_normal:
+               /* Non-fatal error, prepare for recovery */
+               lpfc_sli_prep_dev_for_recover(phba);
+               return PCI_ERS_RESULT_CAN_RECOVER;
+       case pci_channel_io_frozen:
+               /* Fatal error, prepare for slot reset */
+               lpfc_sli_prep_dev_for_reset(phba);
+               return PCI_ERS_RESULT_NEED_RESET;
+       case pci_channel_io_perm_failure:
+               /* Permanent failure, prepare for device down */
+               lpfc_prep_dev_for_perm_failure(phba);
                return PCI_ERS_RESULT_DISCONNECT;
+       default:
+               /* Unknown state, prepare and request slot reset */
+               lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+                               "0472 Unknown PCI error state: x%x\n", state);
+               lpfc_sli_prep_dev_for_reset(phba);
+               return PCI_ERS_RESULT_NEED_RESET;
        }
-
-       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;
 }
 
 /**
@@ -7197,7 +7347,12 @@ 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;
 
+       /* Bring the device online */
        lpfc_online(phba);
+
+       /* Clean up Advanced Error Reporting (AER) if needed */
+       if (phba->hba_flag & HBA_AER_ENABLED)
+               pci_cleanup_aer_uncorrect_error_status(pdev);
 }
 
 /**
@@ -7213,15 +7368,15 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
 
        if (phba->sli_rev == LPFC_SLI_REV4) {
                if (max_xri <= 100)
-                       return 4;
+                       return 10;
                else if (max_xri <= 256)
-                       return 8;
+                       return 25;
                else if (max_xri <= 512)
-                       return 16;
+                       return 50;
                else if (max_xri <= 1024)
-                       return 32;
+                       return 100;
                else
-                       return 48;
+                       return 150;
        } else
                return 0;
 }
@@ -7249,6 +7404,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
 {
        struct lpfc_hba   *phba;
        struct lpfc_vport *vport = NULL;
+       struct Scsi_Host  *shost = NULL;
        int error;
        uint32_t cfg_mode, intr_mode;
        int mcnt;
@@ -7329,6 +7485,7 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
                goto out_destroy_shost;
        }
 
+       shost = lpfc_shost_from_vport(vport); /* save shost for error cleanup */
        /* Now, trying to enable interrupt and bring up the device */
        cfg_mode = phba->cfg_use_msi;
        while (true) {
@@ -7397,6 +7554,8 @@ out_unset_pci_mem_s4:
        lpfc_sli4_pci_mem_unset(phba);
 out_disable_pci_dev:
        lpfc_disable_pci_dev(phba);
+       if (shost)
+               scsi_host_put(shost);
 out_free_phba:
        lpfc_hba_free(phba);
        return error;
@@ -7971,6 +8130,10 @@ static struct pci_device_id lpfc_id_table[] = {
                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_TOMCAT,
+               PCI_ANY_ID, PCI_ANY_ID, },
+       {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_FALCON,
+               PCI_ANY_ID, PCI_ANY_ID, },
        { 0 }
 };
 
@@ -8053,15 +8216,15 @@ lpfc_exit(void)
        if (lpfc_enable_npiv)
                fc_release_transport(lpfc_vport_transport_template);
        if (_dump_buf_data) {
-               printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_data "
-                               "at 0x%p\n",
+               printk(KERN_ERR "9062 BLKGRD: freeing %lu pages for "
+                               "_dump_buf_data at 0x%p\n",
                                (1L << _dump_buf_data_order), _dump_buf_data);
                free_pages((unsigned long)_dump_buf_data, _dump_buf_data_order);
        }
 
        if (_dump_buf_dif) {
-               printk(KERN_ERR "BLKGRD freeing %lu pages for _dump_buf_dif "
-                               "at 0x%p\n",
+               printk(KERN_ERR "9049 BLKGRD: freeing %lu pages for "
+                               "_dump_buf_dif at 0x%p\n",
                                (1L << _dump_buf_dif_order), _dump_buf_dif);
                free_pages((unsigned long)_dump_buf_dif, _dump_buf_dif_order);
        }
index 1ab4059..a9afd8b 100644 (file)
@@ -25,8 +25,8 @@
 
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_transport_fc.h>
-
 #include <scsi/scsi.h>
+#include <scsi/fc/fc_fs.h>
 
 #include "lpfc_hw4.h"
 #include "lpfc_hw.h"
@@ -820,6 +820,10 @@ lpfc_reg_vpi(struct lpfc_vport *vport, LPFC_MBOXQ_t *pmb)
        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;
+       memcpy(mb->un.varRegVpi.wwn, &vport->fc_portname,
+              sizeof(struct lpfc_name));
+       mb->un.varRegVpi.wwn[0] = cpu_to_le32(mb->un.varRegVpi.wwn[0]);
+       mb->un.varRegVpi.wwn[1] = cpu_to_le32(mb->un.varRegVpi.wwn[1]);
 
        mb->mbxCommand = MBX_REG_VPI;
        mb->mbxOwner = OWN_HOST;
@@ -849,7 +853,10 @@ lpfc_unreg_vpi(struct lpfc_hba *phba, uint16_t vpi, LPFC_MBOXQ_t *pmb)
        MAILBOX_t *mb = &pmb->u.mb;
        memset(pmb, 0, sizeof (LPFC_MBOXQ_t));
 
-       mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
+       if (phba->sli_rev < LPFC_SLI_REV4)
+               mb->un.varUnregVpi.vpi = vpi + phba->vpi_base;
+       else
+               mb->un.varUnregVpi.sli4_vpi = vpi + phba->vpi_base;
 
        mb->mbxCommand = MBX_UNREG_VPI;
        mb->mbxOwner = OWN_HOST;
@@ -1132,7 +1139,7 @@ lpfc_config_ring(struct lpfc_hba * phba, int ring, LPFC_MBOXQ_t * pmb)
        /* Otherwise we setup specific rctl / type masks for this ring */
        for (i = 0; i < pring->num_mask; i++) {
                mb->un.varCfgRing.rrRegs[i].rval = pring->prt[i].rctl;
-               if (mb->un.varCfgRing.rrRegs[i].rval != FC_ELS_REQ)
+               if (mb->un.varCfgRing.rrRegs[i].rval != FC_RCTL_ELS_REQ)
                        mb->un.varCfgRing.rrRegs[i].rmask = 0xff;
                else
                        mb->un.varCfgRing.rrRegs[i].rmask = 0xfe;
@@ -1654,9 +1661,12 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox,
        /* Allocate record for keeping SGE virtual addresses */
        mbox->sge_array = kmalloc(sizeof(struct lpfc_mbx_nembed_sge_virt),
                                  GFP_KERNEL);
-       if (!mbox->sge_array)
+       if (!mbox->sge_array) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+                               "2527 Failed to allocate non-embedded SGE "
+                               "array.\n");
                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
@@ -1753,11 +1763,6 @@ lpfc_request_features(struct lpfc_hba *phba, struct lpfcMboxq *mboxq)
        /* Set up host requested features. */
        bf_set(lpfc_mbx_rq_ftr_rq_fcpi, &mboxq->u.mqe.un.req_ftrs, 1);
 
-       if (phba->cfg_enable_fip)
-               bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 0);
-       else
-               bf_set(lpfc_mbx_rq_ftr_rq_ifip, &mboxq->u.mqe.un.req_ftrs, 1);
-
        /* 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);
@@ -1817,6 +1822,9 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
        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);
+       memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name));
+       reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]);
+       reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]);
        reg_vfi->bde.addrHigh = putPaddrHigh(phys);
        reg_vfi->bde.addrLow = putPaddrLow(phys);
        reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam);
@@ -1850,7 +1858,7 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
 /**
  * lpfc_unreg_vfi - Initialize the UNREG_VFI mailbox command
  * @mbox: pointer to lpfc mbox command to initialize.
- * @vfi: VFI to be unregistered.
+ * @vport: vport associated with the VF.
  *
  * 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
@@ -1859,11 +1867,12 @@ lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
  * fabric inactive.
  **/
 void
-lpfc_unreg_vfi(struct lpfcMboxq *mbox, uint16_t vfi)
+lpfc_unreg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport)
 {
        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);
+       bf_set(lpfc_unreg_vfi_vfi, &mbox->u.mqe.un.unreg_vfi,
+              vport->vfi + vport->phba->vfi_base);
 }
 
 /**
index 3e74136..2ed6af1 100644 (file)
@@ -1223,6 +1223,12 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
        list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
                if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) &&
                   (ndlp == (struct lpfc_nodelist *) mb->context2)) {
+                       if (phba->sli_rev == LPFC_SLI_REV4) {
+                               spin_unlock_irq(&phba->hbalock);
+                               lpfc_sli4_free_rpi(phba,
+                                       mb->u.mb.un.varRegLogin.rpi);
+                               spin_lock_irq(&phba->hbalock);
+                       }
                        mp = (struct lpfc_dmabuf *) (mb->context1);
                        if (mp) {
                                __lpfc_mbuf_free(phba, mp->virt, mp->phys);
@@ -1230,6 +1236,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport,
                        }
                        lpfc_nlp_put(ndlp);
                        list_del(&mb->list);
+                       phba->sli.mboxq_cnt--;
                        mempool_free(mb, phba->mbox_mem_pool);
                }
        }
index c88f59f..a246410 100644 (file)
@@ -59,22 +59,26 @@ static char *dif_op_str[] = {
 };
 static void
 lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
+static void
+lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb);
 
 static void
-lpfc_debug_save_data(struct scsi_cmnd *cmnd)
+lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
 {
        void *src, *dst;
        struct scatterlist *sgde = scsi_sglist(cmnd);
 
        if (!_dump_buf_data) {
-               printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9050 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
                                __func__);
                return;
        }
 
 
        if (!sgde) {
-               printk(KERN_ERR "BLKGRD ERROR: data scatterlist is null\n");
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9051 BLKGRD: ERROR: data scatterlist is null\n");
                return;
        }
 
@@ -88,19 +92,21 @@ lpfc_debug_save_data(struct scsi_cmnd *cmnd)
 }
 
 static void
-lpfc_debug_save_dif(struct scsi_cmnd *cmnd)
+lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd)
 {
        void *src, *dst;
        struct scatterlist *sgde = scsi_prot_sglist(cmnd);
 
        if (!_dump_buf_dif) {
-               printk(KERN_ERR "BLKGRD ERROR %s _dump_buf_data is NULL\n",
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9052 BLKGRD: ERROR %s _dump_buf_data is NULL\n",
                                __func__);
                return;
        }
 
        if (!sgde) {
-               printk(KERN_ERR "BLKGRD ERROR: prot scatterlist is null\n");
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9053 BLKGRD: ERROR: prot scatterlist is null\n");
                return;
        }
 
@@ -241,6 +247,36 @@ lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
        return;
 }
 
+/**
+ * lpfc_change_queue_depth - Alter scsi device queue depth
+ * @sdev: Pointer the scsi device on which to change the queue depth.
+ * @qdepth: New queue depth to set the sdev to.
+ * @reason: The reason for the queue depth change.
+ *
+ * This function is called by the midlayer and the LLD to alter the queue
+ * depth for a scsi device. This function sets the queue depth to the new
+ * value and sends an event out to log the queue depth change.
+ **/
+int
+lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+{
+       struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
+       struct lpfc_hba   *phba = vport->phba;
+       struct lpfc_rport_data *rdata;
+       unsigned long new_queue_depth, old_queue_depth;
+
+       old_queue_depth = sdev->queue_depth;
+       scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+       new_queue_depth = sdev->queue_depth;
+       rdata = sdev->hostdata;
+       if (rdata)
+               lpfc_send_sdev_queuedepth_change_event(phba, vport,
+                                                      rdata->pnode, sdev->lun,
+                                                      old_queue_depth,
+                                                      new_queue_depth);
+       return sdev->queue_depth;
+}
+
 /**
  * lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread
  * @phba: The Hba for which this call is being executed.
@@ -305,8 +341,10 @@ lpfc_rampup_queue_depth(struct lpfc_vport  *vport,
        if (vport->cfg_lun_queue_depth <= queue_depth)
                return;
        spin_lock_irqsave(&phba->hbalock, flags);
-       if (((phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) > jiffies) ||
-        ((phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL ) > jiffies)) {
+       if (time_before(jiffies,
+                       phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) ||
+           time_before(jiffies,
+                       phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) {
                spin_unlock_irqrestore(&phba->hbalock, flags);
                return;
        }
@@ -338,10 +376,9 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
        struct lpfc_vport **vports;
        struct Scsi_Host  *shost;
        struct scsi_device *sdev;
-       unsigned long new_queue_depth, old_queue_depth;
+       unsigned long new_queue_depth;
        unsigned long num_rsrc_err, num_cmd_success;
        int i;
-       struct lpfc_rport_data *rdata;
 
        num_rsrc_err = atomic_read(&phba->num_rsrc_err);
        num_cmd_success = atomic_read(&phba->num_cmd_success);
@@ -359,22 +396,8 @@ lpfc_ramp_down_queue_handler(struct lpfc_hba *phba)
                                else
                                        new_queue_depth = sdev->queue_depth -
                                                                new_queue_depth;
-                               old_queue_depth = sdev->queue_depth;
-                               if (sdev->ordered_tags)
-                                       scsi_adjust_queue_depth(sdev,
-                                                       MSG_ORDERED_TAG,
-                                                       new_queue_depth);
-                               else
-                                       scsi_adjust_queue_depth(sdev,
-                                                       MSG_SIMPLE_TAG,
-                                                       new_queue_depth);
-                               rdata = sdev->hostdata;
-                               if (rdata)
-                                       lpfc_send_sdev_queuedepth_change_event(
-                                               phba, vports[i],
-                                               rdata->pnode,
-                                               sdev->lun, old_queue_depth,
-                                               new_queue_depth);
+                               lpfc_change_queue_depth(sdev, new_queue_depth,
+                                                       SCSI_QDEPTH_DEFAULT);
                        }
                }
        lpfc_destroy_vport_work_array(phba, vports);
@@ -398,7 +421,6 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
        struct Scsi_Host  *shost;
        struct scsi_device *sdev;
        int i;
-       struct lpfc_rport_data *rdata;
 
        vports = lpfc_create_vport_work_array(phba);
        if (vports != NULL)
@@ -408,22 +430,9 @@ lpfc_ramp_up_queue_handler(struct lpfc_hba *phba)
                                if (vports[i]->cfg_lun_queue_depth <=
                                    sdev->queue_depth)
                                        continue;
-                               if (sdev->ordered_tags)
-                                       scsi_adjust_queue_depth(sdev,
-                                                       MSG_ORDERED_TAG,
-                                                       sdev->queue_depth+1);
-                               else
-                                       scsi_adjust_queue_depth(sdev,
-                                                       MSG_SIMPLE_TAG,
-                                                       sdev->queue_depth+1);
-                               rdata = sdev->hostdata;
-                               if (rdata)
-                                       lpfc_send_sdev_queuedepth_change_event(
-                                               phba, vports[i],
-                                               rdata->pnode,
-                                               sdev->lun,
-                                               sdev->queue_depth - 1,
-                                               sdev->queue_depth);
+                               lpfc_change_queue_depth(sdev,
+                                                       sdev->queue_depth+1,
+                                                       SCSI_QDEPTH_RAMP_UP);
                        }
                }
        lpfc_destroy_vport_work_array(phba, vports);
@@ -589,7 +598,7 @@ lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc)
                iocb->ulpClass = CLASS3;
                psb->status = IOSTAT_SUCCESS;
                /* Put it back into the SCSI buffer list */
-               lpfc_release_scsi_buf_s4(phba, psb);
+               lpfc_release_scsi_buf_s3(phba, psb);
 
        }
 
@@ -1024,7 +1033,8 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
 
                lpfc_cmd->seg_cnt = nseg;
                if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
-                       printk(KERN_ERR "%s: Too many sg segments from "
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9064 BLKGRD: %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);
@@ -1112,7 +1122,7 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
  * with the cmd
  */
 static int
-lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
+lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc)
 {
        uint8_t guard_type = scsi_host_get_guard(sc->device->host);
        uint8_t ret_prof = LPFC_PROF_INVALID;
@@ -1136,7 +1146,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
 
                case SCSI_PROT_NORMAL:
                default:
-                       printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9063 BLKGRD:Bad op/guard:%d/%d combination\n",
                                        scsi_get_prot_op(sc), guard_type);
                        break;
 
@@ -1157,7 +1168,8 @@ lpfc_sc_to_sli_prof(struct scsi_cmnd *sc)
                case SCSI_PROT_WRITE_STRIP:
                case SCSI_PROT_NORMAL:
                default:
-                       printk(KERN_ERR "Bad op/guard:%d/%d combination\n",
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9075 BLKGRD: Bad op/guard:%d/%d combination\n",
                                        scsi_get_prot_op(sc), guard_type);
                        break;
                }
@@ -1259,7 +1271,7 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
        uint16_t apptagmask, apptagval;
 
        pde1 = (struct lpfc_pde *) bpl;
-       prof = lpfc_sc_to_sli_prof(sc);
+       prof = lpfc_sc_to_sli_prof(phba, sc);
 
        if (prof == LPFC_PROF_INVALID)
                goto out;
@@ -1359,7 +1371,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                return 0;
        }
 
-       prof = lpfc_sc_to_sli_prof(sc);
+       prof = lpfc_sc_to_sli_prof(phba, sc);
        if (prof == LPFC_PROF_INVALID)
                goto out;
 
@@ -1408,7 +1420,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                subtotal = 0; /* total bytes processed for current prot grp */
                while (!pgdone) {
                        if (!sgde) {
-                               printk(KERN_ERR "%s Invalid data segment\n",
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9065 BLKGRD:%s Invalid data segment\n",
                                                __func__);
                                return 0;
                        }
@@ -1462,7 +1475,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
                        reftag += protgrp_blks;
                } else {
                        /* if we're here, we have a bug */
-                       printk(KERN_ERR "BLKGRD: bug in %s\n", __func__);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9054 BLKGRD: bug in %s\n", __func__);
                }
 
        } while (!alldone);
@@ -1544,8 +1558,10 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
 
                lpfc_cmd->seg_cnt = datasegcnt;
                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",
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9067 BLKGRD: %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);
@@ -1579,8 +1595,9 @@ lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
                        lpfc_cmd->prot_seg_cnt = protsegcnt;
                        if (lpfc_cmd->prot_seg_cnt
                            > phba->cfg_prot_sg_seg_cnt) {
-                               printk(KERN_ERR "%s: Too many prot sg segments "
-                                               "from dma_map_sg.  Config %d,"
+                               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                                       "9068 BLKGRD: %s: Too many prot sg "
+                                       "segments from dma_map_sg.  Config %d,"
                                                "prot_seg_cnt %d\n", __func__,
                                                phba->cfg_prot_sg_seg_cnt,
                                                lpfc_cmd->prot_seg_cnt);
@@ -1671,23 +1688,26 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
        uint32_t bgstat = bgf->bgstat;
        uint64_t failing_sector = 0;
 
-       printk(KERN_ERR "BG ERROR in cmd 0x%x lba 0x%llx blk cnt 0x%x "
+       lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9069 BLKGRD: BG ERROR in cmd"
+                       " 0x%x lba 0x%llx blk cnt 0x%x "
                        "bgstat=0x%x bghm=0x%x\n",
                        cmd->cmnd[0], (unsigned long long)scsi_get_lba(cmd),
                        blk_rq_sectors(cmd->request), bgstat, bghm);
 
        spin_lock(&_dump_buf_lock);
        if (!_dump_buf_done) {
-               printk(KERN_ERR "Saving Data for %u blocks to debugfs\n",
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,  "9070 BLKGRD: Saving"
+                       " Data for %u blocks to debugfs\n",
                                (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
-               lpfc_debug_save_data(cmd);
+               lpfc_debug_save_data(phba, cmd);
 
                /* If we have a prot sgl, save the DIF buffer */
                if (lpfc_prot_group_type(phba, cmd) ==
                                LPFC_PG_TYPE_DIF_BUF) {
-                       printk(KERN_ERR "Saving DIF for %u blocks to debugfs\n",
-                                       (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
-                       lpfc_debug_save_dif(cmd);
+                       lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9071 BLKGRD: "
+                               "Saving DIF for %u blocks to debugfs\n",
+                               (cmd->cmnd[7] << 8 | cmd->cmnd[8]));
+                       lpfc_debug_save_dif(phba, cmd);
                }
 
                _dump_buf_done = 1;
@@ -1696,15 +1716,17 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
 
        if (lpfc_bgs_get_invalid_prof(bgstat)) {
                cmd->result = ScsiResult(DID_ERROR, 0);
-               printk(KERN_ERR "Invalid BlockGuard profile. bgstat:0x%x\n",
-                               bgstat);
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9072 BLKGRD: Invalid"
+                       " BlockGuard profile. bgstat:0x%x\n",
+                       bgstat);
                ret = (-1);
                goto out;
        }
 
        if (lpfc_bgs_get_uninit_dif_block(bgstat)) {
                cmd->result = ScsiResult(DID_ERROR, 0);
-               printk(KERN_ERR "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9073 BLKGRD: "
+                               "Invalid BlockGuard DIF Block. bgstat:0x%x\n",
                                bgstat);
                ret = (-1);
                goto out;
@@ -1718,7 +1740,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
                cmd->result = DRIVER_SENSE << 24
                        | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
                phba->bg_guard_err_cnt++;
-               printk(KERN_ERR "BLKGRD: guard_tag error\n");
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9055 BLKGRD: guard_tag error\n");
        }
 
        if (lpfc_bgs_get_reftag_err(bgstat)) {
@@ -1730,7 +1753,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
                        | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
 
                phba->bg_reftag_err_cnt++;
-               printk(KERN_ERR "BLKGRD: ref_tag error\n");
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9056 BLKGRD: ref_tag error\n");
        }
 
        if (lpfc_bgs_get_apptag_err(bgstat)) {
@@ -1742,7 +1766,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
                        | ScsiResult(DID_ABORT, SAM_STAT_CHECK_CONDITION);
 
                phba->bg_apptag_err_cnt++;
-               printk(KERN_ERR "BLKGRD: app_tag error\n");
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9061 BLKGRD: app_tag error\n");
        }
 
        if (lpfc_bgs_get_hi_water_mark_present(bgstat)) {
@@ -1763,7 +1788,8 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
        if (!ret) {
                /* No error was reported - problem in FW? */
                cmd->result = ScsiResult(DID_ERROR, 0);
-               printk(KERN_ERR "BLKGRD: no errors reported!\n");
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                       "9057 BLKGRD: no errors reported!\n");
        }
 
 out:
@@ -1822,9 +1848,10 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
 
                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_printf_log(phba, KERN_ERR, LOG_BG, "9074 BLKGRD:"
+                               " %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;
@@ -2050,6 +2077,21 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
                goto out;
        }
 
+       if (resp_info & RSP_LEN_VALID) {
+               rsplen = be32_to_cpu(fcprsp->rspRspLen);
+               if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
+                   (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
+                       lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+                                "2719 Invalid response length: "
+                                "tgt x%x lun x%x cmnd x%x rsplen x%x\n",
+                                cmnd->device->id,
+                                cmnd->device->lun, cmnd->cmnd[0],
+                                rsplen);
+                       host_status = DID_ERROR;
+                       goto out;
+               }
+       }
+
        if ((resp_info & SNS_LEN_VALID) && fcprsp->rspSnsLen) {
                uint32_t snslen = be32_to_cpu(fcprsp->rspSnsLen);
                if (snslen > SCSI_SENSE_BUFFERSIZE)
@@ -2074,15 +2116,6 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
                         be32_to_cpu(fcprsp->rspRspLen),
                         fcprsp->rspInfo3);
 
-       if (resp_info & RSP_LEN_VALID) {
-               rsplen = be32_to_cpu(fcprsp->rspRspLen);
-               if ((rsplen != 0 && rsplen != 4 && rsplen != 8) ||
-                   (fcprsp->rspInfo3 != RSP_NO_FAILURE)) {
-                       host_status = DID_ERROR;
-                       goto out;
-               }
-       }
-
        scsi_set_resid(cmnd, 0);
        if (resp_info & RESID_UNDER) {
                scsi_set_resid(cmnd, be32_to_cpu(fcprsp->rspResId));
@@ -2180,7 +2213,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
        struct scsi_cmnd *cmd = lpfc_cmd->pCmd;
        int result;
        struct scsi_device *tmp_sdev;
-       int depth = 0;
+       int depth;
        unsigned long flags;
        struct lpfc_fast_path_event *fast_path_evt;
        struct Scsi_Host *shost = cmd->device->host;
@@ -2264,7 +2297,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
                                        lpfc_printf_vlog(vport, KERN_WARNING,
                                                        LOG_BG,
                                                        "9031 non-zero BGSTAT "
-                                                       "on unprotected cmd");
+                                                       "on unprotected cmd\n");
                                }
                        }
 
@@ -2347,67 +2380,29 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
                return;
        }
 
-
        if (!result)
                lpfc_rampup_queue_depth(vport, queue_depth);
 
-       if (!result && pnode && NLP_CHK_NODE_ACT(pnode) &&
-          ((jiffies - pnode->last_ramp_up_time) >
-               LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
-          ((jiffies - pnode->last_q_full_time) >
-               LPFC_Q_RAMP_UP_INTERVAL * HZ) &&
-          (vport->cfg_lun_queue_depth > queue_depth)) {
-               shost_for_each_device(tmp_sdev, shost) {
-                       if (vport->cfg_lun_queue_depth > tmp_sdev->queue_depth){
-                               if (tmp_sdev->id != scsi_id)
-                                       continue;
-                               if (tmp_sdev->ordered_tags)
-                                       scsi_adjust_queue_depth(tmp_sdev,
-                                               MSG_ORDERED_TAG,
-                                               tmp_sdev->queue_depth+1);
-                               else
-                                       scsi_adjust_queue_depth(tmp_sdev,
-                                               MSG_SIMPLE_TAG,
-                                               tmp_sdev->queue_depth+1);
-
-                               pnode->last_ramp_up_time = jiffies;
-                       }
-               }
-               lpfc_send_sdev_queuedepth_change_event(phba, vport, pnode,
-                       0xFFFFFFFF,
-                       queue_depth , queue_depth + 1);
-       }
-
        /*
         * Check for queue full.  If the lun is reporting queue full, then
         * back off the lun queue depth to prevent target overloads.
         */
        if (result == SAM_STAT_TASK_SET_FULL && pnode &&
            NLP_CHK_NODE_ACT(pnode)) {
-               pnode->last_q_full_time = jiffies;
-
                shost_for_each_device(tmp_sdev, shost) {
                        if (tmp_sdev->id != scsi_id)
                                continue;
                        depth = scsi_track_queue_full(tmp_sdev,
-                                       tmp_sdev->queue_depth - 1);
-               }
-               /*
-                * The queue depth cannot be lowered any more.
-                * Modify the returned error code to store
-                * the final depth value set by
-                * scsi_track_queue_full.
-                */
-               if (depth == -1)
-                       depth = shost->cmd_per_lun;
-
-               if (depth) {
+                                                     tmp_sdev->queue_depth-1);
+                       if (depth <= 0)
+                               continue;
                        lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
                                         "0711 detected queue full - lun queue "
                                         "depth adjusted to %d.\n", depth);
                        lpfc_send_sdev_queuedepth_change_event(phba, vport,
-                               pnode, 0xFFFFFFFF,
-                               depth+1, depth);
+                                                              pnode,
+                                                              tmp_sdev->lun,
+                                                              depth+1, depth);
                }
        }
 
@@ -2745,7 +2740,9 @@ void lpfc_poll_timeout(unsigned long ptr)
        struct lpfc_hba *phba = (struct lpfc_hba *) ptr;
 
        if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
-               lpfc_sli_poll_fcp_ring (phba);
+               lpfc_sli_handle_fast_ring_event(phba,
+                       &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+
                if (phba->cfg_poll & DISABLE_FCP_RING_INT)
                        lpfc_poll_rearm_timer(phba);
        }
@@ -2771,7 +2768,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_hba   *phba = vport->phba;
        struct lpfc_rport_data *rdata = cmnd->device->hostdata;
-       struct lpfc_nodelist *ndlp = rdata->pnode;
+       struct lpfc_nodelist *ndlp;
        struct lpfc_scsi_buf *lpfc_cmd;
        struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
        int err;
@@ -2781,13 +2778,15 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
                cmnd->result = err;
                goto out_fail_command;
        }
+       ndlp = rdata->pnode;
 
        if (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) &&
                scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
 
-               printk(KERN_ERR "BLKGRD ERROR: rcvd protected cmd:%02x op:%02x "
-                               "str=%s without registering for BlockGuard - "
-                               "Rejecting command\n",
+               lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+                               "9058 BLKGRD: ERROR: rcvd protected cmd:%02x"
+                               " op:%02x str=%s without registering for"
+                               " BlockGuard - Rejecting command\n",
                                cmnd->cmnd[0], scsi_get_prot_op(cmnd),
                                dif_op_str[scsi_get_prot_op(cmnd)]);
                goto out_fail_command;
@@ -2827,61 +2826,66 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        cmnd->scsi_done = done;
 
        if (scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) {
-               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+               if (vport->phba->cfg_enable_bg) {
+                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                "9033 BLKGRD: rcvd protected cmd:%02x op:%02x "
                                "str=%s\n",
                                cmnd->cmnd[0], scsi_get_prot_op(cmnd),
                                dif_op_str[scsi_get_prot_op(cmnd)]);
-               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                "9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
                                "%02x %02x %02x %02x %02x\n",
                                cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
                                cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
                                cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
                                cmnd->cmnd[9]);
-               if (cmnd->cmnd[0] == READ_10)
-                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                       if (cmnd->cmnd[0] == READ_10)
+                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                        "9035 BLKGRD: READ @ sector %llu, "
                                        "count %u\n",
                                        (unsigned long long)scsi_get_lba(cmnd),
                                        blk_rq_sectors(cmnd->request));
-               else if (cmnd->cmnd[0] == WRITE_10)
-                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                       else if (cmnd->cmnd[0] == WRITE_10)
+                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                        "9036 BLKGRD: WRITE @ sector %llu, "
                                        "count %u cmd=%p\n",
                                        (unsigned long long)scsi_get_lba(cmnd),
                                        blk_rq_sectors(cmnd->request),
                                        cmnd);
+               }
 
                err = lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
        } else {
-               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                               "9038 BLKGRD: rcvd unprotected cmd:%02x op:%02x"
-                               " str=%s\n",
-                               cmnd->cmnd[0], scsi_get_prot_op(cmnd),
-                               dif_op_str[scsi_get_prot_op(cmnd)]);
-               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                                "9039 BLKGRD: CDB: %02x %02x %02x %02x %02x "
-                                "%02x %02x %02x %02x %02x\n",
-                                cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
-                                cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
-                                cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
-                                cmnd->cmnd[9]);
-               if (cmnd->cmnd[0] == READ_10)
+               if (vport->phba->cfg_enable_bg) {
                        lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
-                                        "9040 dbg: READ @ sector %llu, "
-                                        "count %u\n",
-                                        (unsigned long long)scsi_get_lba(cmnd),
+                                       "9038 BLKGRD: rcvd unprotected cmd:"
+                                       "%02x op:%02x str=%s\n",
+                                       cmnd->cmnd[0], scsi_get_prot_op(cmnd),
+                                       dif_op_str[scsi_get_prot_op(cmnd)]);
+                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                                       "9039 BLKGRD: CDB: %02x %02x %02x "
+                                       "%02x %02x %02x %02x %02x %02x %02x\n",
+                                       cmnd->cmnd[0], cmnd->cmnd[1],
+                                       cmnd->cmnd[2], cmnd->cmnd[3],
+                                       cmnd->cmnd[4], cmnd->cmnd[5],
+                                       cmnd->cmnd[6], cmnd->cmnd[7],
+                                       cmnd->cmnd[8], cmnd->cmnd[9]);
+                       if (cmnd->cmnd[0] == READ_10)
+                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                                       "9040 dbg: READ @ sector %llu, "
+                                       "count %u\n",
+                                       (unsigned long long)scsi_get_lba(cmnd),
                                         blk_rq_sectors(cmnd->request));
-               else if (cmnd->cmnd[0] == WRITE_10)
-                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                       else if (cmnd->cmnd[0] == WRITE_10)
+                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                         "9041 dbg: WRITE @ sector %llu, "
                                         "count %u cmd=%p\n",
                                         (unsigned long long)scsi_get_lba(cmnd),
                                         blk_rq_sectors(cmnd->request), cmnd);
-               else
-                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
+                       else
+                               lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
                                         "9042 dbg: parser not implemented\n");
+               }
                err = lpfc_scsi_prep_dma_buf(phba, lpfc_cmd);
        }
 
@@ -2898,7 +2902,11 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
                goto out_host_busy_free_buf;
        }
        if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
-               lpfc_sli_poll_fcp_ring(phba);
+               spin_unlock(shost->host_lock);
+               lpfc_sli_handle_fast_ring_event(phba,
+                       &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
+
+               spin_lock(shost->host_lock);
                if (phba->cfg_poll & DISABLE_FCP_RING_INT)
                        lpfc_poll_rearm_timer(phba);
        }
@@ -2916,28 +2924,6 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
        return 0;
 }
 
-/**
- * lpfc_block_error_handler - Routine to block error  handler
- * @cmnd: Pointer to scsi_cmnd data structure.
- *
- *  This routine blocks execution till fc_rport state is not FC_PORSTAT_BLCOEKD.
- **/
-static void
-lpfc_block_error_handler(struct scsi_cmnd *cmnd)
-{
-       struct Scsi_Host *shost = cmnd->device->host;
-       struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
-
-       spin_lock_irq(shost->host_lock);
-       while (rport->port_state == FC_PORTSTATE_BLOCKED) {
-               spin_unlock_irq(shost->host_lock);
-               msleep(1000);
-               spin_lock_irq(shost->host_lock);
-       }
-       spin_unlock_irq(shost->host_lock);
-       return;
-}
-
 /**
  * lpfc_abort_handler - scsi_host_template eh_abort_handler entry point
  * @cmnd: Pointer to scsi_cmnd data structure.
@@ -2961,7 +2947,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
        int ret = SUCCESS;
        DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq);
 
-       lpfc_block_error_handler(cmnd);
+       fc_block_scsi_eh(cmnd);
        lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble;
        BUG_ON(!lpfc_cmd);
 
@@ -3001,6 +2987,10 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
 
        icmd->ulpLe = 1;
        icmd->ulpClass = cmd->ulpClass;
+
+       /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+       abtsiocb->fcp_wqidx = iocb->fcp_wqidx;
+
        if (lpfc_is_link_up(phba))
                icmd->ulpCommand = CMD_ABORT_XRI_CN;
        else
@@ -3016,7 +3006,8 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
        }
 
        if (phba->cfg_poll & DISABLE_FCP_RING_INT)
-               lpfc_sli_poll_fcp_ring (phba);
+               lpfc_sli_handle_fast_ring_event(phba,
+                       &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
 
        lpfc_cmd->waitq = &waitq;
        /* Wait for abort to complete */
@@ -3166,9 +3157,15 @@ static int
 lpfc_chk_tgt_mapped(struct lpfc_vport *vport, struct scsi_cmnd *cmnd)
 {
        struct lpfc_rport_data *rdata = cmnd->device->hostdata;
-       struct lpfc_nodelist *pnode = rdata->pnode;
+       struct lpfc_nodelist *pnode;
        unsigned long later;
 
+       if (!rdata) {
+               lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP,
+                       "0797 Tgt Map rport failure: rdata x%p\n", rdata);
+               return FAILED;
+       }
+       pnode = rdata->pnode;
        /*
         * If target is not in a MAPPED state, delay until
         * target is rediscovered or devloss timeout expires.
@@ -3253,13 +3250,19 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd)
        struct Scsi_Host  *shost = cmnd->device->host;
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_rport_data *rdata = cmnd->device->hostdata;
-       struct lpfc_nodelist *pnode = rdata->pnode;
+       struct lpfc_nodelist *pnode;
        unsigned tgt_id = cmnd->device->id;
        unsigned int lun_id = cmnd->device->lun;
        struct lpfc_scsi_event_header scsi_event;
        int status;
 
-       lpfc_block_error_handler(cmnd);
+       if (!rdata) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+                       "0798 Device Reset rport failure: rdata x%p\n", rdata);
+               return FAILED;
+       }
+       pnode = rdata->pnode;
+       fc_block_scsi_eh(cmnd);
 
        status = lpfc_chk_tgt_mapped(vport, cmnd);
        if (status == FAILED) {
@@ -3312,13 +3315,19 @@ lpfc_target_reset_handler(struct scsi_cmnd *cmnd)
        struct Scsi_Host  *shost = cmnd->device->host;
        struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata;
        struct lpfc_rport_data *rdata = cmnd->device->hostdata;
-       struct lpfc_nodelist *pnode = rdata->pnode;
+       struct lpfc_nodelist *pnode;
        unsigned tgt_id = cmnd->device->id;
        unsigned int lun_id = cmnd->device->lun;
        struct lpfc_scsi_event_header scsi_event;
        int status;
 
-       lpfc_block_error_handler(cmnd);
+       if (!rdata) {
+               lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP,
+                       "0799 Target Reset rport failure: rdata x%p\n", rdata);
+               return FAILED;
+       }
+       pnode = rdata->pnode;
+       fc_block_scsi_eh(cmnd);
 
        status = lpfc_chk_tgt_mapped(vport, cmnd);
        if (status == FAILED) {
@@ -3384,7 +3393,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd)
        fc_host_post_vendor_event(shost, fc_get_event_number(),
                sizeof(scsi_event), (char *)&scsi_event, LPFC_NL_VENDOR_ID);
 
-       lpfc_block_error_handler(cmnd);
+       fc_block_scsi_eh(cmnd);
 
        /*
         * Since the driver manages a single bus device, reset all
@@ -3498,6 +3507,8 @@ lpfc_slave_alloc(struct scsi_device *sdev)
                                 "Allocated %d buffers.\n",
                                 num_to_alloc, num_allocated);
        }
+       if (num_allocated > 0)
+               phba->total_scsi_bufs += num_allocated;
        return 0;
 }
 
@@ -3534,7 +3545,8 @@ lpfc_slave_configure(struct scsi_device *sdev)
        rport->dev_loss_tmo = vport->cfg_devloss_tmo;
 
        if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
-               lpfc_sli_poll_fcp_ring(phba);
+               lpfc_sli_handle_fast_ring_event(phba,
+                       &phba->sli.ring[LPFC_FCP_RING], HA_R0RE_REQ);
                if (phba->cfg_poll & DISABLE_FCP_RING_INT)
                        lpfc_poll_rearm_timer(phba);
        }
@@ -3576,6 +3588,7 @@ struct scsi_host_template lpfc_template = {
        .shost_attrs            = lpfc_hba_attrs,
        .max_sectors            = 0xFFFF,
        .vendor_id              = LPFC_NL_VENDOR_ID,
+       .change_queue_depth     = lpfc_change_queue_depth,
 };
 
 struct scsi_host_template lpfc_vport_template = {
@@ -3597,4 +3610,5 @@ struct scsi_host_template lpfc_vport_template = {
        .use_clustering         = ENABLE_CLUSTERING,
        .shost_attrs            = lpfc_vport_attrs,
        .max_sectors            = 0xFFFF,
+       .change_queue_depth     = lpfc_change_queue_depth,
 };
index 43cbe33..7935667 100644 (file)
@@ -30,6 +30,7 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport_fc.h>
 #include <scsi/fc/fc_fs.h>
+#include <linux/aer.h>
 
 #include "lpfc_hw4.h"
 #include "lpfc_hw.h"
@@ -58,8 +59,11 @@ typedef enum _lpfc_iocb_type {
 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 *);
-
+                             uint8_t *, uint32_t *);
+static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *,
+                                                        struct lpfc_iocbq *);
+static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
+                                     struct hbq_dmabuf *);
 static IOCB_t *
 lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq)
 {
@@ -259,6 +263,9 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
        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);
+       /* PCI read to flush PCI pipeline on re-arming for INTx mode */
+       if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
+               readl(q->phba->sli4_hba.EQCQDBregaddr);
        return released;
 }
 
@@ -515,6 +522,8 @@ __lpfc_sli_get_sglq(struct lpfc_hba *phba)
        struct lpfc_sglq *sglq = NULL;
        uint16_t adj_xri;
        list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
+       if (!sglq)
+               return NULL;
        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;
@@ -572,9 +581,9 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
                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.ulpStatus == IOSTAT_LOCAL_REJECT)
                        && (iocbq->iocb.un.ulpWord[4]
-                               == IOERR_SLI_ABORTED))) {
+                               == IOERR_ABORT_REQUESTED))) {
                        spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
                                        iflag);
                        list_add(&sglq->list,
@@ -767,6 +776,7 @@ lpfc_sli_iocb_cmd_type(uint8_t iocb_cmnd)
        case CMD_CLOSE_XRI_CX:
        case CMD_XRI_ABORTED_CX:
        case CMD_ABORT_MXRI64_CN:
+       case CMD_XMIT_BLS_RSP64_CX:
                type = LPFC_ABORT_IOCB;
                break;
        case CMD_RCV_SEQUENCE_CX:
@@ -1794,7 +1804,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba *phba)
                 */
                if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) ==
                    MBX_SHUTDOWN) {
-                       /* Unknow mailbox command compl */
+                       /* Unknown mailbox command compl */
                        lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
                                        "(%d):0323 Unknown Mailbox command "
                                        "x%x (x%x) Cmpl\n",
@@ -2068,8 +2078,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
        if ((irsp->ulpCommand == CMD_RCV_ELS_REQ64_CX) ||
            (irsp->ulpCommand == CMD_RCV_ELS_REQ_CX) ||
            (irsp->ulpCommand == CMD_IOCB_RCV_ELS64_CX)) {
-               Rctl = FC_ELS_REQ;
-               Type = FC_ELS_DATA;
+               Rctl = FC_RCTL_ELS_REQ;
+               Type = FC_TYPE_ELS;
        } else {
                w5p = (WORD5 *)&(saveq->iocb.un.ulpWord[5]);
                Rctl = w5p->hcsw.Rctl;
@@ -2079,8 +2089,8 @@ lpfc_sli_process_unsol_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
                if ((Rctl == 0) && (pring->ringno == LPFC_ELS_RING) &&
                        (irsp->ulpCommand == CMD_RCV_SEQUENCE64_CX ||
                         irsp->ulpCommand == CMD_IOCB_RCV_SEQ64_CX)) {
-                       Rctl = FC_ELS_REQ;
-                       Type = FC_ELS_DATA;
+                       Rctl = FC_RCTL_ELS_REQ;
+                       Type = FC_TYPE_ELS;
                        w5p->hcsw.Rctl = Rctl;
                        w5p->hcsw.Type = Type;
                }
@@ -2324,168 +2334,6 @@ void lpfc_poll_eratt(unsigned long ptr)
        return;
 }
 
-/**
- * lpfc_sli_poll_fcp_ring - Handle FCP ring completion in polling mode
- * @phba: Pointer to HBA context object.
- *
- * This function is called from lpfc_queuecommand, lpfc_poll_timeout,
- * lpfc_abort_handler and lpfc_slave_configure when FCP_RING_POLLING
- * is enabled.
- *
- * 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 the completion handler of the command iocb
- * if the response iocb indicates a completion for a command iocb or it is
- * an abort completion.
- **/
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
-{
-       struct lpfc_sli      *psli  = &phba->sli;
-       struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING];
-       IOCB_t *irsp = NULL;
-       IOCB_t *entry = NULL;
-       struct lpfc_iocbq *cmdiocbq = NULL;
-       struct lpfc_iocbq rspiocbq;
-       struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno];
-       uint32_t status;
-       uint32_t portRspPut, portRspMax;
-       int type;
-       uint32_t rsp_cmpl = 0;
-       uint32_t ha_copy;
-       unsigned long iflags;
-
-       pring->stats.iocb_event++;
-
-       /*
-        * The next available response entry should never exceed the maximum
-        * entries.  If it does, treat it as an adapter hardware error.
-        */
-       portRspMax = pring->numRiocb;
-       portRspPut = le32_to_cpu(pgp->rspPutInx);
-       if (unlikely(portRspPut >= portRspMax)) {
-               lpfc_sli_rsp_pointers_error(phba, pring);
-               return;
-       }
-
-       rmb();
-       while (pring->rspidx != portRspPut) {
-               entry = lpfc_resp_iocb(phba, pring);
-               if (++pring->rspidx >= portRspMax)
-                       pring->rspidx = 0;
-
-               lpfc_sli_pcimem_bcopy((uint32_t *) entry,
-                                     (uint32_t *) &rspiocbq.iocb,
-                                     phba->iocb_rsp_size);
-               irsp = &rspiocbq.iocb;
-               type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK);
-               pring->stats.iocb_rsp++;
-               rsp_cmpl++;
-
-               if (unlikely(irsp->ulpStatus)) {
-                       /* Rsp ring <ringno> error: IOCB */
-                       lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
-                                       "0326 Rsp Ring %d error: IOCB Data: "
-                                       "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->un1,
-                                       *((uint32_t *)&irsp->un1 + 1));
-               }
-
-               switch (type) {
-               case LPFC_ABORT_IOCB:
-               case LPFC_SOL_IOCB:
-                       /*
-                        * Idle exchange closed via ABTS from port.  No iocb
-                        * resources need to be recovered.
-                        */
-                       if (unlikely(irsp->ulpCommand == CMD_XRI_ABORTED_CX)) {
-                               lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
-                                               "0314 IOCB cmd 0x%x "
-                                               "processed. Skipping "
-                                               "completion",
-                                               irsp->ulpCommand);
-                               break;
-                       }
-
-                       spin_lock_irqsave(&phba->hbalock, iflags);
-                       cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
-                                                        &rspiocbq);
-                       spin_unlock_irqrestore(&phba->hbalock, iflags);
-                       if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
-                               (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
-                                                     &rspiocbq);
-                       }
-                       break;
-               default:
-                       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,
-                                               "0321 Unknown IOCB command "
-                                               "Data: x%x, x%x x%x x%x x%x\n",
-                                               type, irsp->ulpCommand,
-                                               irsp->ulpStatus,
-                                               irsp->ulpIoTag,
-                                               irsp->ulpContext);
-                       }
-                       break;
-               }
-
-               /*
-                * The response IOCB has been processed.  Update the ring
-                * pointer in SLIM.  If the port response put pointer has not
-                * been updated, sync the pgp->rspPutInx and fetch the new port
-                * response put pointer.
-                */
-               writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
-
-               if (pring->rspidx == portRspPut)
-                       portRspPut = le32_to_cpu(pgp->rspPutInx);
-       }
-
-       ha_copy = readl(phba->HAregaddr);
-       ha_copy >>= (LPFC_FCP_RING * 4);
-
-       if ((rsp_cmpl > 0) && (ha_copy & HA_R0RE_REQ)) {
-               spin_lock_irqsave(&phba->hbalock, iflags);
-               pring->stats.iocb_rsp_full++;
-               status = ((CA_R0ATT | CA_R0RE_RSP) << (LPFC_FCP_RING * 4));
-               writel(status, phba->CAregaddr);
-               readl(phba->CAregaddr);
-               spin_unlock_irqrestore(&phba->hbalock, iflags);
-       }
-       if ((ha_copy & HA_R0CE_RSP) &&
-           (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
-               spin_lock_irqsave(&phba->hbalock, iflags);
-               pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
-               pring->stats.iocb_cmd_empty++;
-
-               /* Force update of the local copy of cmdGetInx */
-               pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
-               lpfc_sli_resume_iocb(phba, pring);
-
-               if ((pring->lpfc_sli_cmd_available))
-                       (pring->lpfc_sli_cmd_available) (phba, pring);
-
-               spin_unlock_irqrestore(&phba->hbalock, iflags);
-       }
-
-       return;
-}
 
 /**
  * lpfc_sli_handle_fast_ring_event - Handle ring events on FCP ring
@@ -2502,9 +2350,9 @@ void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
  * an abort completion. The function will call lpfc_sli_process_unsol_iocb
  * function if this is an unsolicited iocb.
  * This routine presumes LPFC_FCP_RING handling and doesn't bother
- * to check it explicitly. This function always returns 1.
- **/
-static int
+ * to check it explicitly.
+ */
+int
 lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
                                struct lpfc_sli_ring *pring, uint32_t mask)
 {
@@ -2534,6 +2382,11 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
                spin_unlock_irqrestore(&phba->hbalock, iflag);
                return 1;
        }
+       if (phba->fcp_ring_in_use) {
+               spin_unlock_irqrestore(&phba->hbalock, iflag);
+               return 1;
+       } else
+               phba->fcp_ring_in_use = 1;
 
        rmb();
        while (pring->rspidx != portRspPut) {
@@ -2604,10 +2457,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
                        cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
                                                         &rspiocbq);
                        if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
-                               if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
-                                       (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
-                                                             &rspiocbq);
-                               } else {
                                        spin_unlock_irqrestore(&phba->hbalock,
                                                               iflag);
                                        (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
@@ -2615,7 +2464,6 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
                                        spin_lock_irqsave(&phba->hbalock,
                                                          iflag);
                                }
-                       }
                        break;
                case LPFC_UNSOL_IOCB:
                        spin_unlock_irqrestore(&phba->hbalock, iflag);
@@ -2675,6 +2523,7 @@ lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
 
        }
 
+       phba->fcp_ring_in_use = 0;
        spin_unlock_irqrestore(&phba->hbalock, iflag);
        return rc;
 }
@@ -3018,16 +2867,39 @@ lpfc_sli_handle_slow_ring_event_s4(struct lpfc_hba *phba,
                                   struct lpfc_sli_ring *pring, uint32_t mask)
 {
        struct lpfc_iocbq *irspiocbq;
+       struct hbq_dmabuf *dmabuf;
+       struct lpfc_cq_event *cq_event;
        unsigned long iflag;
 
-       while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) {
+       spin_lock_irqsave(&phba->hbalock, iflag);
+       phba->hba_flag &= ~HBA_SP_QUEUE_EVT;
+       spin_unlock_irqrestore(&phba->hbalock, iflag);
+       while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
                /* 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);
+               list_remove_head(&phba->sli4_hba.sp_queue_event,
+                                cq_event, struct lpfc_cq_event, list);
                spin_unlock_irqrestore(&phba->hbalock, iflag);
-               /* Process the response iocb */
-               lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq);
+
+               switch (bf_get(lpfc_wcqe_c_code, &cq_event->cqe.wcqe_cmpl)) {
+               case CQE_CODE_COMPL_WQE:
+                       irspiocbq = container_of(cq_event, struct lpfc_iocbq,
+                                                cq_event);
+                       /* Translate ELS WCQE to response IOCBQ */
+                       irspiocbq = lpfc_sli4_els_wcqe_to_rspiocbq(phba,
+                                                                  irspiocbq);
+                       if (irspiocbq)
+                               lpfc_sli_sp_handle_rspiocb(phba, pring,
+                                                          irspiocbq);
+                       break;
+               case CQE_CODE_RECEIVE:
+                       dmabuf = container_of(cq_event, struct hbq_dmabuf,
+                                             cq_event);
+                       lpfc_sli4_handle_received_buffer(phba, dmabuf);
+                       break;
+               default:
+                       break;
+               }
        }
 }
 
@@ -3416,6 +3288,7 @@ lpfc_sli_brdreset(struct lpfc_hba *phba)
 
        /* perform board reset */
        phba->fc_eventTag = 0;
+       phba->link_events = 0;
        phba->pport->fc_myDID = 0;
        phba->pport->fc_prevDID = 0;
 
@@ -3476,6 +3349,7 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
 
        /* perform board reset */
        phba->fc_eventTag = 0;
+       phba->link_events = 0;
        phba->pport->fc_myDID = 0;
        phba->pport->fc_prevDID = 0;
 
@@ -3495,7 +3369,6 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba)
        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++)
@@ -3531,9 +3404,13 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
        struct lpfc_sli *psli;
        volatile uint32_t word0;
        void __iomem *to_slim;
+       uint32_t hba_aer_enabled;
 
        spin_lock_irq(&phba->hbalock);
 
+       /* Take PCIe device Advanced Error Reporting (AER) state */
+       hba_aer_enabled = phba->hba_flag & HBA_AER_ENABLED;
+
        psli = &phba->sli;
 
        /* Restart HBA */
@@ -3573,6 +3450,10 @@ lpfc_sli_brdrestart_s3(struct lpfc_hba *phba)
        /* Give the INITFF and Post time to settle. */
        mdelay(100);
 
+       /* Reset HBA AER if it was enabled, note hba_flag was reset above */
+       if (hba_aer_enabled)
+               pci_disable_pcie_error_reporting(phba->pcidev);
+
        lpfc_hba_down_post(phba);
 
        return 0;
@@ -4042,6 +3923,24 @@ lpfc_sli_hba_setup(struct lpfc_hba *phba)
        if (rc)
                goto lpfc_sli_hba_setup_error;
 
+       /* Enable PCIe device Advanced Error Reporting (AER) if configured */
+       if (phba->cfg_aer_support == 1 && !(phba->hba_flag & HBA_AER_ENABLED)) {
+               rc = pci_enable_pcie_error_reporting(phba->pcidev);
+               if (!rc) {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                                       "2709 This device supports "
+                                       "Advanced Error Reporting (AER)\n");
+                       spin_lock_irq(&phba->hbalock);
+                       phba->hba_flag |= HBA_AER_ENABLED;
+                       spin_unlock_irq(&phba->hbalock);
+               } else {
+                       lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+                                       "2708 This device does not support "
+                                       "Advanced Error Reporting (AER)\n");
+                       phba->cfg_aer_support = 0;
+               }
+       }
+
        if (phba->sli_rev == 3) {
                phba->iocb_cmd_size = SLI3_IOCB_CMD_SIZE;
                phba->iocb_rsp_size = SLI3_IOCB_RSP_SIZE;
@@ -4163,7 +4062,7 @@ lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
  * addition, this routine gets the port vpd data.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     ENOMEM - could not allocated memory.
  **/
 static int
@@ -4243,7 +4142,6 @@ lpfc_sli4_arm_cqeq_intr(struct lpfc_hba *phba)
 
        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);
@@ -4322,6 +4220,13 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
        phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev);
        if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev))
                phba->hba_flag |= HBA_FCOE_SUPPORT;
+
+       if (bf_get(lpfc_mbx_rd_rev_cee_ver, &mqe->un.read_rev) ==
+               LPFC_DCBX_CEE_MODE)
+               phba->hba_flag |= HBA_FIP_SUPPORT;
+       else
+               phba->hba_flag &= ~HBA_FIP_SUPPORT;
+
        if (phba->sli_rev != LPFC_SLI_REV4 ||
            !(phba->hba_flag & HBA_FCOE_SUPPORT)) {
                lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
@@ -4468,7 +4373,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
        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);
+                               "0582 Error %d during sgl post operation\n",
+                                       rc);
                rc = -ENODEV;
                goto out_free_vpd;
        }
@@ -4477,8 +4383,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
        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);
+                               "0383 Error %d during scsi sgl post "
+                               "operation\n", rc);
                /* Some Scsi buffers were moved to the abort scsi list */
                /* A pci function reset will repost them */
                rc = -ENODEV;
@@ -4494,10 +4400,6 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
                rc = -ENODEV;
                goto out_free_vpd;
        }
-       if (phba->cfg_enable_fip)
-               bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 1);
-       else
-               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);
@@ -5669,7 +5571,7 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number,
                case CMD_GEN_REQUEST64_CX:
                        if (!(phba->sli.sli_flag & LPFC_MENLO_MAINT) ||
                                (piocb->iocb.un.genreq64.w5.hcsw.Rctl !=
-                                       FC_FCP_CMND) ||
+                                       FC_RCTL_DD_UNSOL_CMD) ||
                                (piocb->iocb.un.genreq64.w5.hcsw.Type !=
                                        MENLO_TRANSPORT_TYPE))
 
@@ -5849,7 +5751,7 @@ static int
 lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                union lpfc_wqe *wqe)
 {
-       uint32_t payload_len = 0;
+       uint32_t xmit_len = 0, total_len = 0;
        uint8_t ct = 0;
        uint32_t fip;
        uint32_t abort_tag;
@@ -5857,12 +5759,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
        uint8_t cmnd;
        uint16_t xritag;
        struct ulp_bde64 *bpl = NULL;
+       uint32_t els_id = ELS_ID_DEFAULT;
+       int numBdes, i;
+       struct ulp_bde64 bde;
 
-       fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags);
+       fip = phba->hba_flag & HBA_FIP_SUPPORT;
        /* The fcp commands will set command type */
        if (iocbq->iocb_flag &  LPFC_IO_FCP)
                command_type = FCP_COMMAND;
-       else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS))
+       else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK))
                command_type = ELS_COMMAND_FIP;
        else
                command_type = ELS_COMMAND_NON_FIP;
@@ -5874,6 +5779,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
        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) {
+               numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
+                               sizeof(struct ulp_bde64);
                bpl  = (struct ulp_bde64 *)
                        ((struct lpfc_dmabuf *)iocbq->context3)->virt;
                if (!bpl)
@@ -5886,9 +5793,14 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                 * 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;
+               xmit_len = wqe->generic.bde.tus.f.bdeSize;
+               total_len = 0;
+               for (i = 0; i < numBdes; i++) {
+                       bde.tus.w  = le32_to_cpu(bpl[i].tus.w);
+                       total_len += bde.tus.f.bdeSize;
+               }
        } else
-               payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
+               xmit_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
 
        iocbq->iocb.ulpIoTag = iocbq->iotag;
        cmnd = iocbq->iocb.ulpCommand;
@@ -5902,7 +5814,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                                iocbq->iocb.ulpCommand);
                        return IOCB_ERROR;
                }
-               wqe->els_req.payload_len = payload_len;
+               wqe->els_req.payload_len = xmit_len;
                /* Els_reguest64 has a TMO */
                bf_set(wqe_tmo, &wqe->els_req.wqe_com,
                        iocbq->iocb.ulpTimeout);
@@ -5923,7 +5835,23 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                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 */
+
+               if (command_type == ELS_COMMAND_FIP) {
+                       els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)
+                                       >> LPFC_FIP_ELS_ID_SHIFT);
+               }
+               bf_set(lpfc_wqe_gen_els_id, &wqe->generic, els_id);
+
        break;
+       case CMD_XMIT_SEQUENCE64_CX:
+               bf_set(lpfc_wqe_gen_context, &wqe->generic,
+                                       iocbq->iocb.un.ulpWord[3]);
+               wqe->generic.word3 = 0;
+               bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext);
+               bf_set(wqe_xc, &wqe->generic, 1);
+               /* The entire sequence is transmitted for this IOCB */
+               xmit_len = total_len;
+               cmnd = CMD_XMIT_SEQUENCE64_CR;
        case CMD_XMIT_SEQUENCE64_CR:
                /* word3 iocb=io_tag32 wqe=payload_offset */
                /* payload offset used for multilpe outstanding
@@ -5933,7 +5861,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                /* 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;
+               wqe->xmit_sequence.xmit_len = xmit_len;
+               command_type = OTHER_COMMAND;
        break;
        case CMD_XMIT_BCAST64_CN:
                /* word3 iocb=iotag32 wqe=payload_len */
@@ -5962,7 +5891,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
        case CMD_FCP_IREAD64_CR:
                /* FCP_CMD is always the 1st sgl entry */
                wqe->fcp_iread.payload_len =
-                       payload_len + sizeof(struct fcp_rsp);
+                       xmit_len + sizeof(struct fcp_rsp);
 
                /* word 4 (xfer length) should have been set on the memcpy */
 
@@ -5999,7 +5928,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                 * sgl[1] = rsp.
                 *
                 */
-               wqe->gen_req.command_len = payload_len;
+               wqe->gen_req.command_len = xmit_len;
                /* Word4 parameter  copied in the memcpy */
                /* Word5 [rctl, type, df_ctl, la] copied in memcpy */
                /* word6 context tag copied in memcpy */
@@ -6066,6 +5995,38 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
                command_type = OTHER_COMMAND;
                xritag = 0;
        break;
+       case CMD_XMIT_BLS_RSP64_CX:
+               /* As BLS ABTS-ACC WQE is very different from other WQEs,
+                * we re-construct this WQE here based on information in
+                * iocbq from scratch.
+                */
+               memset(wqe, 0, sizeof(union lpfc_wqe));
+               /* OX_ID is invariable to who sent ABTS to CT exchange */
+               bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp,
+                      bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_acc));
+               if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_acc) ==
+                   LPFC_ABTS_UNSOL_INT) {
+                       /* ABTS sent by initiator to CT exchange, the
+                        * RX_ID field will be filled with the newly
+                        * allocated responder XRI.
+                        */
+                       bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+                              iocbq->sli4_xritag);
+               } else {
+                       /* ABTS sent by responder to CT exchange, the
+                        * RX_ID field will be filled with the responder
+                        * RX_ID from ABTS.
+                        */
+                       bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+                              bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_acc));
+               }
+               bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
+               bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
+               bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
+                      iocbq->iocb.ulpContext);
+               /* Overwrite the pre-set comnd type with OTHER_COMMAND */
+               command_type = OTHER_COMMAND;
+       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 */
@@ -6120,11 +6081,10 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
        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)
+                   piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
                        sglq = NULL;
                else {
                        sglq = __lpfc_sli_get_sglq(phba);
@@ -6155,8 +6115,17 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
                return IOCB_ERROR;
 
        if (piocb->iocb_flag &  LPFC_IO_FCP) {
-               fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
-               if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
+               /*
+                * For FCP command IOCB, get a new WQ index to distribute
+                * WQE across the WQsr. On the other hand, for abort IOCB,
+                * it carries the same WQ index to the original command
+                * IOCB.
+                */
+               if ((piocb->iocb.ulpCommand != CMD_ABORT_XRI_CN) &&
+                   (piocb->iocb.ulpCommand != CMD_CLOSE_XRI_CN))
+                       piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
+               if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
+                                    &wqe))
                        return IOCB_ERROR;
        } else {
                if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
@@ -6449,31 +6418,37 @@ lpfc_sli_setup(struct lpfc_hba *phba)
                        pring->iotag_max = 4096;
                        pring->lpfc_sli_rcv_async_status =
                                lpfc_sli_async_event_handler;
-                       pring->num_mask = 4;
+                       pring->num_mask = LPFC_MAX_RING_MASK;
                        pring->prt[0].profile = 0;      /* Mask 0 */
-                       pring->prt[0].rctl = FC_ELS_REQ;
-                       pring->prt[0].type = FC_ELS_DATA;
+                       pring->prt[0].rctl = FC_RCTL_ELS_REQ;
+                       pring->prt[0].type = FC_TYPE_ELS;
                        pring->prt[0].lpfc_sli_rcv_unsol_event =
                            lpfc_els_unsol_event;
                        pring->prt[1].profile = 0;      /* Mask 1 */
-                       pring->prt[1].rctl = FC_ELS_RSP;
-                       pring->prt[1].type = FC_ELS_DATA;
+                       pring->prt[1].rctl = FC_RCTL_ELS_REP;
+                       pring->prt[1].type = FC_TYPE_ELS;
                        pring->prt[1].lpfc_sli_rcv_unsol_event =
                            lpfc_els_unsol_event;
                        pring->prt[2].profile = 0;      /* Mask 2 */
                        /* NameServer Inquiry */
-                       pring->prt[2].rctl = FC_UNSOL_CTL;
+                       pring->prt[2].rctl = FC_RCTL_DD_UNSOL_CTL;
                        /* NameServer */
-                       pring->prt[2].type = FC_COMMON_TRANSPORT_ULP;
+                       pring->prt[2].type = FC_TYPE_CT;
                        pring->prt[2].lpfc_sli_rcv_unsol_event =
                            lpfc_ct_unsol_event;
                        pring->prt[3].profile = 0;      /* Mask 3 */
                        /* NameServer response */
-                       pring->prt[3].rctl = FC_SOL_CTL;
+                       pring->prt[3].rctl = FC_RCTL_DD_SOL_CTL;
                        /* NameServer */
-                       pring->prt[3].type = FC_COMMON_TRANSPORT_ULP;
+                       pring->prt[3].type = FC_TYPE_CT;
                        pring->prt[3].lpfc_sli_rcv_unsol_event =
                            lpfc_ct_unsol_event;
+                       /* abort unsolicited sequence */
+                       pring->prt[4].profile = 0;      /* Mask 4 */
+                       pring->prt[4].rctl = FC_RCTL_BA_ABTS;
+                       pring->prt[4].type = FC_TYPE_BLS;
+                       pring->prt[4].lpfc_sli_rcv_unsol_event =
+                           lpfc_sli4_ct_abort_unsol_event;
                        break;
                }
                totiocbsize += (pring->numCiocb * pring->sizeCiocb) +
@@ -6976,8 +6951,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                abort_iotag = cmdiocb->iocb.un.acxri.abortIoTag;
 
                spin_lock_irq(&phba->hbalock);
-               if (abort_iotag != 0 && abort_iotag <= phba->sli.last_iotag)
-                       abort_iocb = phba->sli.iocbq_lookup[abort_iotag];
+               if (phba->sli_rev < LPFC_SLI_REV4) {
+                       if (abort_iotag != 0 &&
+                               abort_iotag <= phba->sli.last_iotag)
+                               abort_iocb =
+                                       phba->sli.iocbq_lookup[abort_iotag];
+               } else
+                       /* For sli4 the abort_tag is the XRI,
+                        * so the abort routine puts the iotag  of the iocb
+                        * being aborted in the context field of the abort
+                        * IOCB.
+                        */
+                       abort_iocb = phba->sli.iocbq_lookup[abort_context];
 
                lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI,
                                "0327 Cannot abort els iocb %p "
@@ -6991,9 +6976,18 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
                 *  might have completed already. Do not free it again.
                 */
                if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
-                       spin_unlock_irq(&phba->hbalock);
-                       lpfc_sli_release_iocbq(phba, cmdiocb);
-                       return;
+                       if (irsp->un.ulpWord[4] != IOERR_NO_XRI) {
+                               spin_unlock_irq(&phba->hbalock);
+                               lpfc_sli_release_iocbq(phba, cmdiocb);
+                               return;
+                       }
+                       /* For SLI4 the ulpContext field for abort IOCB
+                        * holds the iotag of the IOCB being aborted so
+                        * the local abort_context needs to be reset to
+                        * match the aborted IOCBs ulpContext.
+                        */
+                       if (abort_iocb && phba->sli_rev == LPFC_SLI_REV4)
+                               abort_context = abort_iocb->iocb.ulpContext;
                }
                /*
                 * make sure we have the right iocbq before taking it
@@ -7112,13 +7106,18 @@ 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;
-       if (phba->sli_rev == LPFC_SLI_REV4)
+       if (phba->sli_rev == LPFC_SLI_REV4) {
                iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
+               iabt->un.acxri.abortContextTag = cmdiocb->iotag;
+       }
        else
                iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
        iabt->ulpLe = 1;
        iabt->ulpClass = icmd->ulpClass;
 
+       /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+       abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx;
+
        if (phba->link_state >= LPFC_LINK_UP)
                iabt->ulpCommand = CMD_ABORT_XRI_CN;
        else
@@ -7322,6 +7321,9 @@ lpfc_sli_abort_iocb(struct lpfc_vport *vport, struct lpfc_sli_ring *pring,
                abtsiocb->iocb.ulpClass = cmd->ulpClass;
                abtsiocb->vport = phba->pport;
 
+               /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+               abtsiocb->fcp_wqidx = iocbq->fcp_wqidx;
+
                if (lpfc_is_link_up(phba))
                        abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
                else
@@ -7687,31 +7689,28 @@ 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);
-                       phba->work_status[0] = uerr_sta_lo;
-                       phba->work_status[1] = uerr_sta_hi;
-                       /* Set the driver HA work bitmap */
-                       phba->work_ha |= HA_ERATT;
-                       /* Indicate polling handles this ERATT */
-                       phba->hba_flag |= HBA_ERATT_HANDLED;
-                       return 1;
-               }
+       uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
+       uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
+       if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) ||
+           (~phba->sli4_hba.ue_mask_hi & 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, "
+                               "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+                               uerr_sta_lo, uerr_sta_hi,
+                               phba->sli4_hba.ue_mask_lo,
+                               phba->sli4_hba.ue_mask_hi);
+               phba->work_status[0] = uerr_sta_lo;
+               phba->work_status[1] = uerr_sta_hi;
+               /* Set the driver HA work bitmap */
+               phba->work_ha |= HA_ERATT;
+               /* Indicate polling handles this ERATT */
+               phba->hba_flag |= HBA_ERATT_HANDLED;
+               return 1;
        }
        return 0;
 }
@@ -7834,7 +7833,7 @@ irqreturn_t
 lpfc_sli_sp_intr_handler(int irq, void *dev_id)
 {
        struct lpfc_hba  *phba;
-       uint32_t ha_copy;
+       uint32_t ha_copy, hc_copy;
        uint32_t work_ha_copy;
        unsigned long status;
        unsigned long iflag;
@@ -7892,8 +7891,13 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
                }
 
                /* Clear up only attention source related to slow-path */
+               hc_copy = readl(phba->HCregaddr);
+               writel(hc_copy & ~(HC_MBINT_ENA | HC_R2INT_ENA |
+                       HC_LAINT_ENA | HC_ERINT_ENA),
+                       phba->HCregaddr);
                writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
                        phba->HAregaddr);
+               writel(hc_copy, phba->HCregaddr);
                readl(phba->HAregaddr); /* flush */
                spin_unlock_irqrestore(&phba->hbalock, iflag);
        } else
@@ -8049,7 +8053,7 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id)
                                                        KERN_ERR,
                                                        LOG_MBOX | LOG_SLI,
                                                        "0350 rc should have"
-                                                       "been MBX_BUSY");
+                                                       "been MBX_BUSY\n");
                                                if (rc != MBX_NOT_FINISHED)
                                                        goto send_current_mbox;
                                        }
@@ -8078,7 +8082,7 @@ send_current_mbox:
                        if (rc != MBX_SUCCESS)
                                lpfc_printf_log(phba, KERN_ERR, LOG_MBOX |
                                                LOG_SLI, "0349 rc should be "
-                                               "MBX_SUCCESS");
+                                               "MBX_SUCCESS\n");
                }
 
                spin_lock_irqsave(&phba->hbalock, iflag);
@@ -8203,6 +8207,7 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
        struct lpfc_hba  *phba;
        irqreturn_t sp_irq_rc, fp_irq_rc;
        unsigned long status1, status2;
+       uint32_t hc_copy;
 
        /*
         * Get the driver's phba structure from the dev_id and
@@ -8240,7 +8245,12 @@ lpfc_sli_intr_handler(int irq, void *dev_id)
        }
 
        /* Clear attention sources except link and error attentions */
+       hc_copy = readl(phba->HCregaddr);
+       writel(hc_copy & ~(HC_MBINT_ENA | HC_R0INT_ENA | HC_R1INT_ENA
+               | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA),
+               phba->HCregaddr);
        writel((phba->ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
+       writel(hc_copy, phba->HCregaddr);
        readl(phba->HAregaddr); /* flush */
        spin_unlock(&phba->hbalock);
 
@@ -8351,8 +8361,6 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
 
        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)
@@ -8364,16 +8372,49 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
                        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_els_wcqe_to_rspiocbq - Get response iocbq from els wcqe
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an ELS work-queue completion event and construct
+ * a pseudo response ELS IODBQ from the SLI4 ELS WCQE for the common
+ * discovery engine to handle.
+ *
+ * Return: Pointer to the receive IOCBQ, NULL otherwise.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba,
+                              struct lpfc_iocbq *irspiocbq)
+{
+       struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+       struct lpfc_iocbq *cmdiocbq;
+       struct lpfc_wcqe_complete *wcqe;
+       unsigned long iflags;
+
+       wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl;
+       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));
+               lpfc_sli_release_iocbq(phba, irspiocbq);
+               return NULL;
        }
+
+       /* Fake the irspiocbq and copy necessary response information */
+       lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
+
+       return irspiocbq;
 }
 
 /**
@@ -8566,45 +8607,26 @@ 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 */
+       /* Get an irspiocbq for later ELS response processing use */
        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;
+               return false;
        }
-       lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
 
-       /* Add the irspiocb to the response IOCB work list */
+       /* Save off the slow-path queue event for work thread to process */
+       memcpy(&irspiocbq->cq_event.cqe.wcqe_cmpl, wcqe, sizeof(*wcqe));
        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));
+       list_add_tail(&irspiocbq->cq_event.list,
+                     &phba->sli4_hba.sp_queue_event);
+       phba->hba_flag |= HBA_SP_QUEUE_EVT;
        spin_unlock_irqrestore(&phba->hbalock, iflags);
-       workposted = true;
 
-       return workposted;
+       return true;
 }
 
 /**
@@ -8689,52 +8711,6 @@ lpfc_sli4_sp_handle_abort_xri_wcqe(struct lpfc_hba *phba,
        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.
@@ -8745,9 +8721,8 @@ lpfc_sli4_sp_handle_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
  * 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)
+lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_rcqe *rcqe)
 {
-       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;
@@ -8755,31 +8730,28 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
        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)
+       if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id)
                goto out;
 
-       status = bf_get(lpfc_rcqe_status, &rcqe);
+       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:
+               lpfc_sli4_rq_release(hrq, drq);
                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));
+               memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe));
                /* save off the frame for the word thread to process */
-               list_add_tail(&dma_buf->dbuf.list, &phba->rb_pend_list);
+               list_add_tail(&dma_buf->cq_event.list,
+                             &phba->sli4_hba.sp_queue_event);
                /* Frame received */
-               phba->hba_flag |= HBA_RECEIVE_BUFFER;
+               phba->hba_flag |= HBA_SP_QUEUE_EVT;
                spin_unlock_irqrestore(&phba->hbalock, iflags);
                workposted = true;
                break;
@@ -8794,7 +8766,58 @@ lpfc_sli4_sp_handle_rcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe)
        }
 out:
        return workposted;
+}
+
+/**
+ * lpfc_sli4_sp_handle_cqe - Process a slow path 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 or recieve queue completion queue
+ * entry.
+ *
+ * Return: true if work posted to worker thread, otherwise false.
+ **/
+static bool
+lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
+                        struct lpfc_cqe *cqe)
+{
+       struct lpfc_cqe cqevt;
+       bool workposted = false;
+
+       /* Copy the work queue CQE and convert endian order if needed */
+       lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe));
 
+       /* Check and process for different type of WCQE and dispatch */
+       switch (bf_get(lpfc_cqe_code, &cqevt)) {
+       case CQE_CODE_COMPL_WQE:
+               /* Process the WQ/RQ complete event */
+               workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
+                               (struct lpfc_wcqe_complete *)&cqevt);
+               break;
+       case CQE_CODE_RELEASE_WQE:
+               /* Process the WQ release event */
+               lpfc_sli4_sp_handle_rel_wcqe(phba,
+                               (struct lpfc_wcqe_release *)&cqevt);
+               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 *)&cqevt);
+               break;
+       case CQE_CODE_RECEIVE:
+               /* Process the RQ event */
+               workposted = lpfc_sli4_sp_handle_rcqe(phba,
+                               (struct lpfc_rcqe *)&cqevt);
+               break;
+       default:
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "0388 Not a valid WCQE code: x%x\n",
+                               bf_get(lpfc_cqe_code, &cqevt));
+               break;
+       }
+       return workposted;
 }
 
 /**
@@ -8858,14 +8881,7 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe)
                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);
+                       workposted |= lpfc_sli4_sp_handle_cqe(phba, cq, cqe);
                        if (!(++ecount % LPFC_GET_QE_REL_INT))
                                lpfc_sli4_cq_release(cq, LPFC_QUEUE_NOARM);
                }
@@ -10427,8 +10443,7 @@ lpfc_sli4_next_xritag(struct lpfc_hba *phba)
                return xritag;
        }
        spin_unlock_irq(&phba->hbalock);
-
-       lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+       lpfc_printf_log(phba, KERN_WARNING, 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,
@@ -10492,15 +10507,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
                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 */
@@ -10524,8 +10531,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba)
                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);
+       bf_set(lpfc_post_sgl_pages_xricnt, sgl, els_xri_cnt);
        /* Perform endian conversion if necessary */
        sgl->word0 = cpu_to_le32(sgl->word0);
 
@@ -10607,15 +10613,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist,
                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 */
@@ -10801,6 +10799,105 @@ lpfc_fc_frame_to_vport(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr,
        return vport;
 }
 
+/**
+ * lpfc_update_rcv_time_stamp - Update vport's rcv seq time stamp
+ * @vport: The vport to work on.
+ *
+ * This function updates the receive sequence time stamp for this vport. The
+ * receive sequence time stamp indicates the time that the last frame of the
+ * the sequence that has been idle for the longest amount of time was received.
+ * the driver uses this time stamp to indicate if any received sequences have
+ * timed out.
+ **/
+void
+lpfc_update_rcv_time_stamp(struct lpfc_vport *vport)
+{
+       struct lpfc_dmabuf *h_buf;
+       struct hbq_dmabuf *dmabuf = NULL;
+
+       /* get the oldest sequence on the rcv list */
+       h_buf = list_get_first(&vport->rcv_buffer_list,
+                              struct lpfc_dmabuf, list);
+       if (!h_buf)
+               return;
+       dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+       vport->rcv_buffer_time_stamp = dmabuf->time_stamp;
+}
+
+/**
+ * lpfc_cleanup_rcv_buffers - Cleans up all outstanding receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function cleans up all outstanding received sequences. This is called
+ * by the driver when a link event or user action invalidates all the received
+ * sequences.
+ **/
+void
+lpfc_cleanup_rcv_buffers(struct lpfc_vport *vport)
+{
+       struct lpfc_dmabuf *h_buf, *hnext;
+       struct lpfc_dmabuf *d_buf, *dnext;
+       struct hbq_dmabuf *dmabuf = NULL;
+
+       /* start with the oldest sequence on the rcv list */
+       list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+               dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+               list_del_init(&dmabuf->hbuf.list);
+               list_for_each_entry_safe(d_buf, dnext,
+                                        &dmabuf->dbuf.list, list) {
+                       list_del_init(&d_buf->list);
+                       lpfc_in_buf_free(vport->phba, d_buf);
+               }
+               lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+       }
+}
+
+/**
+ * lpfc_rcv_seq_check_edtov - Cleans up timed out receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function determines whether any received sequences have timed out by
+ * first checking the vport's rcv_buffer_time_stamp. If this time_stamp
+ * indicates that there is at least one timed out sequence this routine will
+ * go through the received sequences one at a time from most inactive to most
+ * active to determine which ones need to be cleaned up. Once it has determined
+ * that a sequence needs to be cleaned up it will simply free up the resources
+ * without sending an abort.
+ **/
+void
+lpfc_rcv_seq_check_edtov(struct lpfc_vport *vport)
+{
+       struct lpfc_dmabuf *h_buf, *hnext;
+       struct lpfc_dmabuf *d_buf, *dnext;
+       struct hbq_dmabuf *dmabuf = NULL;
+       unsigned long timeout;
+       int abort_count = 0;
+
+       timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+                  vport->rcv_buffer_time_stamp);
+       if (list_empty(&vport->rcv_buffer_list) ||
+           time_before(jiffies, timeout))
+               return;
+       /* start with the oldest sequence on the rcv list */
+       list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+               dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+               timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+                          dmabuf->time_stamp);
+               if (time_before(jiffies, timeout))
+                       break;
+               abort_count++;
+               list_del_init(&dmabuf->hbuf.list);
+               list_for_each_entry_safe(d_buf, dnext,
+                                        &dmabuf->dbuf.list, list) {
+                       list_del_init(&d_buf->list);
+                       lpfc_in_buf_free(vport->phba, d_buf);
+               }
+               lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+       }
+       if (abort_count)
+               lpfc_update_rcv_time_stamp(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
@@ -10823,6 +10920,8 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
        struct hbq_dmabuf *seq_dmabuf = NULL;
        struct hbq_dmabuf *temp_dmabuf = NULL;
 
+       INIT_LIST_HEAD(&dmabuf->dbuf.list);
+       dmabuf->time_stamp = jiffies;
        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) {
@@ -10841,13 +10940,21 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
                 * Queue the buffer on the vport's rcv_buffer_list.
                 */
                list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+               lpfc_update_rcv_time_stamp(vport);
                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);
+               list_del_init(&seq_dmabuf->hbuf.list);
+               list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+               list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list);
+               lpfc_update_rcv_time_stamp(vport);
                return dmabuf;
        }
+       /* move this sequence to the tail to indicate a young sequence */
+       list_move_tail(&seq_dmabuf->hbuf.list, &vport->rcv_buffer_list);
+       seq_dmabuf->time_stamp = jiffies;
+       lpfc_update_rcv_time_stamp(vport);
        /* 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);
@@ -10864,6 +10971,210 @@ lpfc_fc_frame_add(struct lpfc_vport *vport, struct hbq_dmabuf *dmabuf)
        return NULL;
 }
 
+/**
+ * lpfc_sli4_abort_partial_seq - Abort partially assembled unsol sequence
+ * @vport: pointer to a vitural port
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function tries to abort from the partially assembed sequence, described
+ * by the information from basic abbort @dmabuf. It checks to see whether such
+ * partially assembled sequence held by the driver. If so, it shall free up all
+ * the frames from the partially assembled sequence.
+ *
+ * Return
+ * true  -- if there is matching partially assembled sequence present and all
+ *          the frames freed with the sequence;
+ * false -- if there is no matching partially assembled sequence present so
+ *          nothing got aborted in the lower layer driver
+ **/
+static bool
+lpfc_sli4_abort_partial_seq(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, *n_buf, *h_buf;
+       struct hbq_dmabuf *seq_dmabuf = NULL;
+
+       /* Use the hdr_buf to find the sequence that matches this frame */
+       INIT_LIST_HEAD(&dmabuf->dbuf.list);
+       INIT_LIST_HEAD(&dmabuf->hbuf.list);
+       new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
+       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;
+       }
+
+       /* Free up all the frames from the partially assembled sequence */
+       if (seq_dmabuf) {
+               list_for_each_entry_safe(d_buf, n_buf,
+                                        &seq_dmabuf->dbuf.list, list) {
+                       list_del_init(&d_buf->list);
+                       lpfc_in_buf_free(vport->phba, d_buf);
+               }
+               return true;
+       }
+       return false;
+}
+
+/**
+ * lpfc_sli4_seq_abort_acc_cmpl - Accept seq abort iocb complete handler
+ * @phba: Pointer to HBA context object.
+ * @cmd_iocbq: pointer to the command iocbq structure.
+ * @rsp_iocbq: pointer to the response iocbq structure.
+ *
+ * This function handles the sequence abort accept iocb command complete
+ * event. It properly releases the memory allocated to the sequence abort
+ * accept iocb.
+ **/
+static void
+lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba,
+                            struct lpfc_iocbq *cmd_iocbq,
+                            struct lpfc_iocbq *rsp_iocbq)
+{
+       if (cmd_iocbq)
+               lpfc_sli_release_iocbq(phba, cmd_iocbq);
+}
+
+/**
+ * lpfc_sli4_seq_abort_acc - Accept sequence abort
+ * @phba: Pointer to HBA context object.
+ * @fc_hdr: pointer to a FC frame header.
+ *
+ * This function sends a basic accept to a previous unsol sequence abort
+ * event after aborting the sequence handling.
+ **/
+static void
+lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba,
+                       struct fc_frame_header *fc_hdr)
+{
+       struct lpfc_iocbq *ctiocb = NULL;
+       struct lpfc_nodelist *ndlp;
+       uint16_t oxid, rxid;
+       uint32_t sid, fctl;
+       IOCB_t *icmd;
+
+       if (!lpfc_is_link_up(phba))
+               return;
+
+       sid = sli4_sid_from_fc_hdr(fc_hdr);
+       oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+       rxid = be16_to_cpu(fc_hdr->fh_rx_id);
+
+       ndlp = lpfc_findnode_did(phba->pport, sid);
+       if (!ndlp) {
+               lpfc_printf_log(phba, KERN_WARNING, LOG_ELS,
+                               "1268 Find ndlp returned NULL for oxid:x%x "
+                               "SID:x%x\n", oxid, sid);
+               return;
+       }
+
+       /* Allocate buffer for acc iocb */
+       ctiocb = lpfc_sli_get_iocbq(phba);
+       if (!ctiocb)
+               return;
+
+       /* Extract the F_CTL field from FC_HDR */
+       fctl = sli4_fctl_from_fc_hdr(fc_hdr);
+
+       icmd = &ctiocb->iocb;
+       icmd->un.xseq64.bdl.bdeSize = 0;
+       icmd->un.xseq64.bdl.ulpIoTag32 = 0;
+       icmd->un.xseq64.w5.hcsw.Dfctl = 0;
+       icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC;
+       icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS;
+
+       /* Fill in the rest of iocb fields */
+       icmd->ulpCommand = CMD_XMIT_BLS_RSP64_CX;
+       icmd->ulpBdeCount = 0;
+       icmd->ulpLe = 1;
+       icmd->ulpClass = CLASS3;
+       icmd->ulpContext = ndlp->nlp_rpi;
+
+       ctiocb->iocb_cmpl = NULL;
+       ctiocb->vport = phba->pport;
+       ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl;
+
+       if (fctl & FC_FC_EX_CTX) {
+               /* ABTS sent by responder to CT exchange, construction
+                * of BA_ACC will use OX_ID from ABTS for the XRI_TAG
+                * field and RX_ID from ABTS for RX_ID field.
+                */
+               bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_RSP);
+               bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, rxid);
+               ctiocb->sli4_xritag = oxid;
+       } else {
+               /* ABTS sent by initiator to CT exchange, construction
+                * of BA_ACC will need to allocate a new XRI as for the
+                * XRI_TAG and RX_ID fields.
+                */
+               bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_INT);
+               bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, NO_XRI);
+               ctiocb->sli4_xritag = NO_XRI;
+       }
+       bf_set(lpfc_abts_oxid, &icmd->un.bls_acc, oxid);
+
+       /* Xmit CT abts accept on exchange <xid> */
+       lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
+                       "1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n",
+                       CMD_XMIT_BLS_RSP64_CX, phba->link_state);
+       lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0);
+}
+
+/**
+ * lpfc_sli4_handle_unsol_abort - Handle sli-4 unsolicited abort event
+ * @vport: Pointer to the vport on which this sequence was received
+ * @dmabuf: pointer to a dmabuf that describes the FC sequence
+ *
+ * This function handles an SLI-4 unsolicited abort event. If the unsolicited
+ * receive sequence is only partially assembed by the driver, it shall abort
+ * the partially assembled frames for the sequence. Otherwise, if the
+ * unsolicited receive sequence has been completely assembled and passed to
+ * the Upper Layer Protocol (UPL), it then mark the per oxid status for the
+ * unsolicited sequence has been aborted. After that, it will issue a basic
+ * accept to accept the abort.
+ **/
+void
+lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport,
+                            struct hbq_dmabuf *dmabuf)
+{
+       struct lpfc_hba *phba = vport->phba;
+       struct fc_frame_header fc_hdr;
+       uint32_t fctl;
+       bool abts_par;
+
+       /* Make a copy of fc_hdr before the dmabuf being released */
+       memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
+       fctl = sli4_fctl_from_fc_hdr(&fc_hdr);
+
+       if (fctl & FC_FC_EX_CTX) {
+               /*
+                * ABTS sent by responder to exchange, just free the buffer
+                */
+               lpfc_in_buf_free(phba, &dmabuf->dbuf);
+       } else {
+               /*
+                * ABTS sent by initiator to exchange, need to do cleanup
+                */
+               /* Try to abort partially assembled seq */
+               abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
+
+               /* Send abort to ULP if partially seq abort failed */
+               if (abts_par == false)
+                       lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
+               else
+                       lpfc_in_buf_free(phba, &dmabuf->dbuf);
+       }
+       /* Send basic accept (BA_ACC) to the abort requester */
+       lpfc_sli4_seq_abort_acc(phba, &fc_hdr);
+}
+
 /**
  * lpfc_seq_complete - Indicates if a sequence is complete
  * @dmabuf: pointer to a dmabuf that describes the FC sequence
@@ -10935,10 +11246,9 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
        fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
        /* remove from receive buffer list */
        list_del_init(&seq_dmabuf->hbuf.list);
+       lpfc_update_rcv_time_stamp(vport);
        /* 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]);
+       sid = sli4_sid_from_fc_hdr(fc_hdr);
        /* Get an iocbq struct to fill in. */
        first_iocbq = lpfc_sli_get_iocbq(vport->phba);
        if (first_iocbq) {
@@ -10957,7 +11267,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
                                                        LPFC_DATA_BUF_SIZE;
                first_iocbq->iocb.un.rcvels.remoteID = sid;
                first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
-                               bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+                               bf_get(lpfc_rcqe_length,
+                                      &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
        }
        iocbq = first_iocbq;
        /*
@@ -10975,7 +11286,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
                        iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
                                                        LPFC_DATA_BUF_SIZE;
                        first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
-                               bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+                               bf_get(lpfc_rcqe_length,
+                                      &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
                } else {
                        iocbq = lpfc_sli_get_iocbq(vport->phba);
                        if (!iocbq) {
@@ -10994,7 +11306,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
                        iocbq->iocb.un.cont64[0].tus.f.bdeSize =
                                                        LPFC_DATA_BUF_SIZE;
                        first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
-                               bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
+                               bf_get(lpfc_rcqe_length,
+                                      &seq_dmabuf->cq_event.cqe.rcqe_cmpl);
                        iocbq->iocb.un.rcvels.remoteID = sid;
                        list_add_tail(&iocbq->list, &first_iocbq->list);
                }
@@ -11002,6 +11315,43 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
        return first_iocbq;
 }
 
+static void
+lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *vport,
+                         struct hbq_dmabuf *seq_dmabuf)
+{
+       struct fc_frame_header *fc_hdr;
+       struct lpfc_iocbq *iocbq, *curr_iocb, *next_iocb;
+       struct lpfc_hba *phba = vport->phba;
+
+       fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
+       iocbq = lpfc_prep_seq(vport, seq_dmabuf);
+       if (!iocbq) {
+               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                               "2707 Ring %d handler: Failed to allocate "
+                               "iocb Rctl x%x Type x%x received\n",
+                               LPFC_ELS_RING,
+                               fc_hdr->fh_r_ctl, fc_hdr->fh_type);
+               return;
+       }
+       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);
+
+       /* Free iocb created in lpfc_prep_seq */
+       list_for_each_entry_safe(curr_iocb, next_iocb,
+               &iocbq->list, list) {
+               list_del_init(&curr_iocb->list);
+               lpfc_sli_release_iocbq(phba, curr_iocb);
+       }
+       lpfc_sli_release_iocbq(phba, iocbq);
+}
+
 /**
  * lpfc_sli4_handle_received_buffer - Handle received buffers from firmware
  * @phba: Pointer to HBA context object.
@@ -11014,67 +11364,54 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
  * 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)
+void
+lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba,
+                                struct hbq_dmabuf *dmabuf)
 {
-       LIST_HEAD(cmplq);
-       struct hbq_dmabuf *dmabuf, *seq_dmabuf;
+       struct hbq_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;
+       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);
+               return;
+       }
+       fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl);
+       vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
+       if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) {
+               /* throw out the frame */
+               lpfc_in_buf_free(phba, &dmabuf->dbuf);
+               return;
+       }
+       /* Handle the basic abort sequence (BA_ABTS) event */
+       if (fc_hdr->fh_r_ctl == FC_RCTL_BA_ABTS) {
+               lpfc_sli4_handle_unsol_abort(vport, dmabuf);
+               return;
+       }
+
+       /* 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);
+               return;
+       }
+       /* 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;
+               return;
+       }
+       /* Send the complete sequence to the upper layer protocol */
+       lpfc_sli4_send_seq_to_ulp(vport, seq_dmabuf);
 }
 
 /**
@@ -11091,7 +11428,7 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba)
  * sequential.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *      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
@@ -11129,7 +11466,7 @@ lpfc_sli4_post_all_rpi_hdrs(struct lpfc_hba *phba)
  * maps up to 64 rpi context regions.
  *
  * Return codes
- *     0 - sucessful
+ *     0 - successful
  *     ENOMEM - No available memory
  *      EIO - The mailbox failed to complete successfully.
  **/
@@ -11191,7 +11528,7 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page)
  * PAGE_SIZE modulo 64 rpi context headers.
  *
  * Returns
- *     A nonzero rpi defined as rpi_base <= rpi < max_rpi if sucessful
+ *     A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful
  *     LPFC_RPI_ALLOC_ERROR if no rpis are available.
  **/
 int
@@ -11334,6 +11671,7 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
 {
        LPFC_MBOXQ_t *mboxq;
        int rc = 0;
+       int retval = MBX_SUCCESS;
        uint32_t mbox_tmo;
 
        if (vpi == 0)
@@ -11344,16 +11682,17 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
        lpfc_init_vpi(phba, 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;
+               retval = -EIO;
        }
-       return rc;
+       if (rc != MBX_TIMEOUT)
+               mempool_free(mboxq, phba->mbox_mem_pool);
+
+       return retval;
 }
 
 /**
@@ -11438,13 +11777,6 @@ lpfc_sli4_add_fcf_record(struct lpfc_hba *phba, struct fcf_record *fcf_record)
         */
        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
@@ -11542,7 +11874,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
                lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
                                "2000 Failed to allocate mbox for "
                                "READ_FCF cmd\n");
-               return -ENOMEM;
+               error = -ENOMEM;
+               goto fail_fcfscan;
        }
 
        req_len = sizeof(struct fcf_record) +
@@ -11558,8 +11891,8 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
                                "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;
+               error = -ENOMEM;
+               goto fail_fcfscan;
        }
 
        /* Get the first SGE entry from the non-embedded DMA memory. This
@@ -11567,13 +11900,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
         */
        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;
 
@@ -11586,7 +11912,6 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
        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 {
                spin_lock_irq(&phba->hbalock);
@@ -11594,6 +11919,15 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
                spin_unlock_irq(&phba->hbalock);
                error = 0;
        }
+fail_fcfscan:
+       if (error) {
+               if (mboxq)
+                       lpfc_sli4_mbox_cmd_free(phba, mboxq);
+               /* FCF scan failed, clear FCF_DISC_INPROGRESS flag */
+               spin_lock_irq(&phba->hbalock);
+               phba->hba_flag &= ~FCF_DISC_INPROGRESS;
+               spin_unlock_irq(&phba->hbalock);
+       }
        return error;
 }
 
index 3c53316..ba38de3 100644 (file)
@@ -29,14 +29,17 @@ 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;
+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;
+               struct lpfc_wcqe_complete       wcqe_cmpl;
+       } cqe;
 };
 
 /* This structure is used to handle IOCB requests / responses */
@@ -46,6 +49,7 @@ struct lpfc_iocbq {
        struct list_head clist;
        uint16_t iotag;         /* pre-assigned IO tag */
        uint16_t sli4_xritag;   /* pre-assigned XRI, (OXID) tag. */
+       struct lpfc_cq_event cq_event;
 
        IOCB_t iocb;            /* IOCB cmd */
        uint8_t retry;          /* retry counter for IOCB cmd - if needed */
@@ -56,11 +60,13 @@ struct lpfc_iocbq {
 #define LPFC_DRIVER_ABORTED    8       /* driver aborted this request */
 #define LPFC_IO_FABRIC         0x10    /* Iocb send using fabric scheduler */
 #define LPFC_DELAY_MEM_FREE    0x20    /* Defer free'ing of FC data */
-#define LPFC_FIP_ELS           0x40
+#define LPFC_FIP_ELS_ID_MASK   0xc0    /* ELS_ID range 0-3 */
+#define LPFC_FIP_ELS_ID_SHIFT  6
 
        uint8_t abort_count;
        uint8_t rsvd2;
        uint32_t drvrTimeout;   /* driver timeout in seconds */
+       uint32_t fcp_wqidx;     /* index to FCP work queue */
        struct lpfc_vport *vport;/* virtual port pointer */
        void *context1;         /* caller context information */
        void *context2;         /* caller context information */
@@ -76,7 +82,6 @@ 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 */
@@ -110,7 +115,7 @@ typedef struct lpfcMboxq {
                                   return */
 #define MBX_NOWAIT      2      /* issue command then return immediately */
 
-#define LPFC_MAX_RING_MASK  4  /* max num of rctl/type masks allowed per
+#define LPFC_MAX_RING_MASK  5  /* max num of rctl/type masks allowed per
                                   ring */
 #define LPFC_MAX_RING       4  /* max num of SLI rings used by driver */
 
index b5f4ba1..25d66d0 100644 (file)
 #define LPFC_FCOE_FKA_ADV_PER  0
 #define LPFC_FCOE_FIP_PRIORITY 0x80
 
+#define sli4_sid_from_fc_hdr(fc_hdr)  \
+       ((fc_hdr)->fh_s_id[0] << 16 | \
+        (fc_hdr)->fh_s_id[1] <<  8 | \
+        (fc_hdr)->fh_s_id[2])
+
+#define sli4_fctl_from_fc_hdr(fc_hdr)  \
+       ((fc_hdr)->fh_f_ctl[0] << 16 | \
+        (fc_hdr)->fh_f_ctl[1] <<  8 | \
+        (fc_hdr)->fh_f_ctl[2])
+
 enum lpfc_sli4_queue_type {
        LPFC_EQ,
        LPFC_GCQ,
@@ -110,18 +120,6 @@ struct lpfc_queue {
        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;
@@ -166,7 +164,7 @@ struct lpfc_fip_param_hdr {
 #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_ON                            0x1
 #define        FIPP_MODE_OFF                           0x0
 #define FIPP_VLAN_VALID                                0x1
 };
@@ -295,9 +293,8 @@ struct lpfc_sli4_hba {
        /* 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 *UEMASKLOregaddr; /* Address to UE_MASK_LO register */
+       void __iomem *UEMASKHIregaddr; /* Address to UE_MASK_HI register */
        void __iomem *SCRATCHPADregaddr; /* Address to scratchpad register */
        /* BAR1 FCoE function CSR register memory map */
        void __iomem *STAregaddr;    /* Address to HST_STATE register */
@@ -311,6 +308,8 @@ struct lpfc_sli4_hba {
        void __iomem *MQDBregaddr;   /* Address to MQ_DOORBELL register */
        void __iomem *BMBXregaddr;   /* Address to BootStrap MBX register */
 
+       uint32_t ue_mask_lo;
+       uint32_t ue_mask_hi;
        struct msix_entry *msix_entries;
        uint32_t cfg_eqn;
        struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */
@@ -325,7 +324,6 @@ struct lpfc_sli4_hba {
        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;
@@ -360,7 +358,7 @@ struct lpfc_sli4_hba {
        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_queue_event;
        struct list_head sp_cqe_event_pool;
        struct list_head sp_asynce_work_queue;
        struct list_head sp_fcp_xri_aborted_work_queue;
index 9ae20af..c7f3aed 100644 (file)
@@ -18,8 +18,7 @@
  * included with this package.                                     *
  *******************************************************************/
 
-#define LPFC_DRIVER_VERSION "8.3.4"
-
+#define LPFC_DRIVER_VERSION "8.3.6"
 #define LPFC_DRIVER_NAME               "lpfc"
 #define LPFC_SP_DRIVER_HANDLER_NAME    "lpfc:sp"
 #define LPFC_FP_DRIVER_HANDLER_NAME    "lpfc:fp"
index 606efa7..7d6dd83 100644 (file)
@@ -389,7 +389,7 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
         * by the port.
         */
        if ((phba->sli_rev == LPFC_SLI_REV4) &&
-               (pport->vfi_state & LPFC_VFI_REGISTERED)) {
+           (pport->vpi_state & LPFC_VPI_REGISTERED)) {
                rc = lpfc_sli4_init_vpi(phba, vpi);
                if (rc) {
                        lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
@@ -700,6 +700,8 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
                        }
                        spin_unlock_irq(&phba->ndlp_lock);
                }
+               if (vport->vpi_state != LPFC_VPI_REGISTERED)
+                       goto skip_logo;
                vport->unreg_vpi_cmpl = VPORT_INVAL;
                timeout = msecs_to_jiffies(phba->fc_ratov * 2000);
                if (!lpfc_issue_els_npiv_logo(vport, ndlp))
index 512c2cc..d310f49 100644 (file)
@@ -381,7 +381,7 @@ typedef struct {
        u8      battery_status; /*
                                 * BIT 0: battery module missing
                                 * BIT 1: VBAD
-                                * BIT 2: temprature high
+                                * BIT 2: temperature high
                                 * BIT 3: battery pack missing
                                 * BIT 4,5:
                                 *   00 - charge complete
index b25b747..ce2487a 100644 (file)
@@ -497,7 +497,7 @@ typedef struct {
  * @inserted_drive             : channel:Id of inserted drive
  * @battery_status             : bit 0: battery module missing
  *                             bit 1: VBAD
- *                             bit 2: temprature high
+ *                             bit 2: temperature high
  *                             bit 3: battery pack missing
  *                             bit 4,5:
  *                                     00 - charge complete
index 234f0b7..7f97796 100644 (file)
@@ -335,12 +335,17 @@ static struct device_attribute *megaraid_sdev_attrs[] = {
  * megaraid_change_queue_depth - Change the device's queue depth
  * @sdev:      scsi device struct
  * @qdepth:    depth to set
+ * @reason:    calling context
  *
  * Return value:
  *     actual depth set
  */
-static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth)
+static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth,
+                                      int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (qdepth > MBOX_MAX_SCSI_CMDS)
                qdepth = MBOX_MAX_SCSI_CMDS;
        scsi_adjust_queue_depth(sdev, 0, qdepth);
@@ -2704,7 +2709,7 @@ megaraid_reset_handler(struct scsi_cmnd *scp)
        }
        else {
                con_log(CL_ANN, (KERN_NOTICE
-               "megaraid mbox: reset sequence completed sucessfully\n"));
+               "megaraid mbox: reset sequence completed successfully\n"));
        }
 
 
index a39addc..134c63e 100644 (file)
@@ -10,7 +10,7 @@
  *        2 of the License, or (at your option) any later version.
  *
  * FILE                : megaraid_sas.c
- * Version     : v00.00.04.01-rc1
+ * Version     : v00.00.04.12-rc1
  *
  * Authors:
  *     (email-id : megaraidlinux@lsi.com)
@@ -40,6 +40,7 @@
 #include <linux/compat.h>
 #include <linux/blkdev.h>
 #include <linux/mutex.h>
+#include <linux/poll.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -75,6 +76,10 @@ static struct pci_device_id megasas_pci_table[] = {
        /* gen2*/
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0079GEN2)},
        /* gen2*/
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0073SKINNY)},
+       /* skinny*/
+       {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0071SKINNY)},
+       /* skinny*/
        {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
        /* xscale IOP, vega */
        {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)},
@@ -89,8 +94,14 @@ static struct megasas_mgmt_info megasas_mgmt_info;
 static struct fasync_struct *megasas_async_queue;
 static DEFINE_MUTEX(megasas_async_queue_mutex);
 
+static int megasas_poll_wait_aen;
+static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait);
+static u32 support_poll_for_event;
 static u32 megasas_dbg_lvl;
 
+/* define lock for aen poll */
+spinlock_t poll_aen_lock;
+
 static void
 megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
                     u8 alt_status);
@@ -215,7 +226,10 @@ megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs)
  * @regs :                     MFI register set
  */
 static inline void 
-megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs)
+megasas_fire_cmd_xscale(struct megasas_instance *instance,
+               dma_addr_t frame_phys_addr,
+               u32 frame_count,
+               struct megasas_register_set __iomem *regs)
 {
        writel((frame_phys_addr >> 3)|(frame_count),
               &(regs)->inbound_queue_port);
@@ -312,7 +326,10 @@ megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs)
  * @regs :                     MFI register set
  */
 static inline void 
-megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs)
+megasas_fire_cmd_ppc(struct megasas_instance *instance,
+               dma_addr_t frame_phys_addr,
+               u32 frame_count,
+               struct megasas_register_set __iomem *regs)
 {
        writel((frame_phys_addr | (frame_count<<1))|1, 
                        &(regs)->inbound_queue_port);
@@ -327,6 +344,104 @@ static struct megasas_instance_template megasas_instance_template_ppc = {
        .read_fw_status_reg = megasas_read_fw_status_reg_ppc,
 };
 
+/**
+ * megasas_enable_intr_skinny -        Enables interrupts
+ * @regs:                      MFI register set
+ */
+static inline void
+megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+       writel(0xFFFFFFFF, &(regs)->outbound_intr_mask);
+
+       writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask);
+
+       /* Dummy readl to force pci flush */
+       readl(&regs->outbound_intr_mask);
+}
+
+/**
+ * megasas_disable_intr_skinny -       Disables interrupt
+ * @regs:                      MFI register set
+ */
+static inline void
+megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+       u32 mask = 0xFFFFFFFF;
+       writel(mask, &regs->outbound_intr_mask);
+       /* Dummy readl to force pci flush */
+       readl(&regs->outbound_intr_mask);
+}
+
+/**
+ * megasas_read_fw_status_reg_skinny - returns the current FW status value
+ * @regs:                      MFI register set
+ */
+static u32
+megasas_read_fw_status_reg_skinny(struct megasas_register_set __iomem *regs)
+{
+       return readl(&(regs)->outbound_scratch_pad);
+}
+
+/**
+ * megasas_clear_interrupt_skinny -    Check & clear interrupt
+ * @regs:                              MFI register set
+ */
+static int
+megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs)
+{
+       u32 status;
+       /*
+        * Check if it is our interrupt
+        */
+       status = readl(&regs->outbound_intr_status);
+
+       if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) {
+               return 1;
+       }
+
+       /*
+        * Clear the interrupt by writing back the same value
+        */
+       writel(status, &regs->outbound_intr_status);
+
+       /*
+       * dummy read to flush PCI
+       */
+       readl(&regs->outbound_intr_status);
+
+       return 0;
+}
+
+/**
+ * megasas_fire_cmd_skinny -   Sends command to the FW
+ * @frame_phys_addr :          Physical address of cmd
+ * @frame_count :              Number of frames for the command
+ * @regs :                     MFI register set
+ */
+static inline void
+megasas_fire_cmd_skinny(struct megasas_instance *instance,
+                       dma_addr_t frame_phys_addr,
+                       u32 frame_count,
+                       struct megasas_register_set __iomem *regs)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&instance->fire_lock, flags);
+       writel(0, &(regs)->inbound_high_queue_port);
+       writel((frame_phys_addr | (frame_count<<1))|1,
+               &(regs)->inbound_low_queue_port);
+       spin_unlock_irqrestore(&instance->fire_lock, flags);
+}
+
+static struct megasas_instance_template megasas_instance_template_skinny = {
+
+       .fire_cmd = megasas_fire_cmd_skinny,
+       .enable_intr = megasas_enable_intr_skinny,
+       .disable_intr = megasas_disable_intr_skinny,
+       .clear_intr = megasas_clear_intr_skinny,
+       .read_fw_status_reg = megasas_read_fw_status_reg_skinny,
+};
+
+
 /**
 *      The following functions are defined for gen2 (deviceid : 0x78 0x79)
 *      controllers
@@ -404,7 +519,9 @@ megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs)
  * @regs :                     MFI register set
  */
 static inline void
-megasas_fire_cmd_gen2(dma_addr_t frame_phys_addr, u32 frame_count,
+megasas_fire_cmd_gen2(struct megasas_instance *instance,
+                       dma_addr_t frame_phys_addr,
+                       u32 frame_count,
                        struct megasas_register_set __iomem *regs)
 {
        writel((frame_phys_addr | (frame_count<<1))|1,
@@ -446,7 +563,8 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd)
        /*
         * Issue the frame using inbound queue port
         */
-       instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+       instance->instancet->fire_cmd(instance,
+                       cmd->frame_phys_addr, 0, instance->reg_set);
 
        /*
         * Wait for cmd_status to change
@@ -477,7 +595,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance,
 {
        cmd->cmd_status = ENODATA;
 
-       instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+       instance->instancet->fire_cmd(instance,
+                       cmd->frame_phys_addr, 0, instance->reg_set);
 
        wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA),
                MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ);
@@ -522,7 +641,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance,
        cmd->sync_cmd = 1;
        cmd->cmd_status = 0xFF;
 
-       instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+       instance->instancet->fire_cmd(instance,
+                       cmd->frame_phys_addr, 0, instance->reg_set);
 
        /*
         * Wait for this cmd to complete
@@ -592,6 +712,35 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
        return sge_count;
 }
 
+/**
+ * megasas_make_sgl_skinny - Prepares IEEE SGL
+ * @instance:           Adapter soft state
+ * @scp:                SCSI command from the mid-layer
+ * @mfi_sgl:            SGL to be filled in
+ *
+ * If successful, this function returns the number of SG elements. Otherwise,
+ * it returnes -1.
+ */
+static int
+megasas_make_sgl_skinny(struct megasas_instance *instance,
+               struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl)
+{
+       int i;
+       int sge_count;
+       struct scatterlist *os_sgl;
+
+       sge_count = scsi_dma_map(scp);
+
+       if (sge_count) {
+               scsi_for_each_sg(scp, os_sgl, sge_count, i) {
+                       mfi_sgl->sge_skinny[i].length = sg_dma_len(os_sgl);
+                       mfi_sgl->sge_skinny[i].phys_addr =
+                                               sg_dma_address(os_sgl);
+               }
+       }
+       return sge_count;
+}
+
  /**
  * megasas_get_frame_count - Computes the number of frames
  * @frame_type         : type of frame- io or pthru frame
@@ -600,7 +749,8 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp,
  * Returns the number of frames required for numnber of sge's (sge_count)
  */
 
-static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
+static u32 megasas_get_frame_count(struct megasas_instance *instance,
+                       u8 sge_count, u8 frame_type)
 {
        int num_cnt;
        int sge_bytes;
@@ -610,6 +760,10 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
        sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
            sizeof(struct megasas_sge32);
 
+       if (instance->flag_ieee) {
+               sge_sz = sizeof(struct megasas_sge_skinny);
+       }
+
        /*
         * Main frame can contain 2 SGEs for 64-bit SGLs and
         * 3 SGEs for 32-bit SGLs for ldio &
@@ -617,12 +771,16 @@ static u32 megasas_get_frame_count(u8 sge_count, u8 frame_type)
         * 2 SGEs for 32-bit SGLs for pthru frame
         */
        if (unlikely(frame_type == PTHRU_FRAME)) {
-               if (IS_DMA64)
+               if (instance->flag_ieee == 1) {
+                       num_cnt = sge_count - 1;
+               } else if (IS_DMA64)
                        num_cnt = sge_count - 1;
                else
                        num_cnt = sge_count - 2;
        } else {
-               if (IS_DMA64)
+               if (instance->flag_ieee == 1) {
+                       num_cnt = sge_count - 1;
+               } else if (IS_DMA64)
                        num_cnt = sge_count - 2;
                else
                        num_cnt = sge_count - 3;
@@ -671,6 +829,10 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
        else if (scp->sc_data_direction == PCI_DMA_NONE)
                flags = MFI_FRAME_DIR_NONE;
 
+       if (instance->flag_ieee == 1) {
+               flags |= MFI_FRAME_IEEE;
+       }
+
        /*
         * Prepare the DCDB frame
         */
@@ -686,10 +848,25 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
 
        memcpy(pthru->cdb, scp->cmnd, scp->cmd_len);
 
+       /*
+       * If the command is for the tape device, set the
+       * pthru timeout to the os layer timeout value.
+       */
+       if (scp->device->type == TYPE_TAPE) {
+               if ((scp->request->timeout / HZ) > 0xFFFF)
+                       pthru->timeout = 0xFFFF;
+               else
+                       pthru->timeout = scp->request->timeout / HZ;
+       }
+
        /*
         * Construct SGL
         */
-       if (IS_DMA64) {
+       if (instance->flag_ieee == 1) {
+               pthru->flags |= MFI_FRAME_SGL64;
+               pthru->sge_count = megasas_make_sgl_skinny(instance, scp,
+                                                     &pthru->sgl);
+       } else if (IS_DMA64) {
                pthru->flags |= MFI_FRAME_SGL64;
                pthru->sge_count = megasas_make_sgl64(instance, scp,
                                                      &pthru->sgl);
@@ -708,7 +885,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp,
         * Compute the total number of frames this command consumes. FW uses
         * this number to pull sufficient number of frames from host memory.
         */
-       cmd->frame_count = megasas_get_frame_count(pthru->sge_count,
+       cmd->frame_count = megasas_get_frame_count(instance, pthru->sge_count,
                                                        PTHRU_FRAME);
 
        return cmd->frame_count;
@@ -739,6 +916,10 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
        else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE)
                flags = MFI_FRAME_DIR_READ;
 
+       if (instance->flag_ieee == 1) {
+               flags |= MFI_FRAME_IEEE;
+       }
+
        /*
         * Prepare the Logical IO frame: 2nd bit is zero for all read cmds
         */
@@ -809,7 +990,11 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
        /*
         * Construct SGL
         */
-       if (IS_DMA64) {
+       if (instance->flag_ieee) {
+               ldio->flags |= MFI_FRAME_SGL64;
+               ldio->sge_count = megasas_make_sgl_skinny(instance, scp,
+                                             &ldio->sgl);
+       } else if (IS_DMA64) {
                ldio->flags |= MFI_FRAME_SGL64;
                ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl);
        } else
@@ -826,7 +1011,8 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp,
         * Compute the total number of frames this command consumes. FW uses
         * this number to pull sufficient number of frames from host memory.
         */
-       cmd->frame_count = megasas_get_frame_count(ldio->sge_count, IO_FRAME);
+       cmd->frame_count = megasas_get_frame_count(instance,
+                       ldio->sge_count, IO_FRAME);
 
        return cmd->frame_count;
 }
@@ -983,7 +1169,8 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
         */
        atomic_inc(&instance->fw_outstanding);
 
-       instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set);
+       instance->instancet->fire_cmd(instance, cmd->frame_phys_addr,
+                               cmd->frame_count-1, instance->reg_set);
        /*
         * Check if we have pend cmds to be completed
         */
@@ -1000,24 +1187,76 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *))
        return 0;
 }
 
+static struct megasas_instance *megasas_lookup_instance(u16 host_no)
+{
+       int i;
+
+       for (i = 0; i < megasas_mgmt_info.max_index; i++) {
+
+               if ((megasas_mgmt_info.instance[i]) &&
+                   (megasas_mgmt_info.instance[i]->host->host_no == host_no))
+                       return megasas_mgmt_info.instance[i];
+       }
+
+       return NULL;
+}
+
 static int megasas_slave_configure(struct scsi_device *sdev)
 {
+       u16             pd_index = 0;
+       struct  megasas_instance *instance ;
+
+       instance = megasas_lookup_instance(sdev->host->host_no);
+
        /*
-        * Don't export physical disk devices to the disk driver.
-        *
-        * FIXME: Currently we don't export them to the midlayer at all.
-        *        That will be fixed once LSI engineers have audited the
-        *        firmware for possible issues.
-        */
-       if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK)
+       * Don't export physical disk devices to the disk driver.
+       *
+       * FIXME: Currently we don't export them to the midlayer at all.
+       *        That will be fixed once LSI engineers have audited the
+       *        firmware for possible issues.
+       */
+       if (sdev->channel < MEGASAS_MAX_PD_CHANNELS &&
+                               sdev->type == TYPE_DISK) {
+               pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+                                                               sdev->id;
+               if (instance->pd_list[pd_index].driveState ==
+                                               MR_PD_STATE_SYSTEM) {
+                       blk_queue_rq_timeout(sdev->request_queue,
+                               MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+                       return 0;
+               }
                return -ENXIO;
+       }
 
        /*
-        * The RAID firmware may require extended timeouts.
-        */
-       if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS)
-               blk_queue_rq_timeout(sdev->request_queue,
-                                    MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+       * The RAID firmware may require extended timeouts.
+       */
+       blk_queue_rq_timeout(sdev->request_queue,
+               MEGASAS_DEFAULT_CMD_TIMEOUT * HZ);
+       return 0;
+}
+
+static int megasas_slave_alloc(struct scsi_device *sdev)
+{
+       u16             pd_index = 0;
+       struct megasas_instance *instance ;
+       instance = megasas_lookup_instance(sdev->host->host_no);
+       if ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) &&
+                               (sdev->type == TYPE_DISK)) {
+               /*
+                * Open the OS scan to the SYSTEM PD
+                */
+               pd_index =
+                       (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) +
+                       sdev->id;
+               if ((instance->pd_list[pd_index].driveState ==
+                                       MR_PD_STATE_SYSTEM) &&
+                       (instance->pd_list[pd_index].driveType ==
+                                               TYPE_DISK)) {
+                       return 0;
+               }
+               return -ENXIO;
+       }
        return 0;
 }
 
@@ -1072,7 +1311,14 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr)
 
                spin_lock_irqsave(instance->host->host_lock, flags);
                instance->flag &= ~MEGASAS_FW_BUSY;
-               instance->host->can_queue =
+               if ((instance->pdev->device ==
+                       PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+                       (instance->pdev->device ==
+                       PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+                       instance->host->can_queue =
+                               instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
+               } else
+                       instance->host->can_queue =
                                instance->max_fw_cmds - MEGASAS_INT_CMDS;
 
                spin_unlock_irqrestore(instance->host->host_lock, flags);
@@ -1117,8 +1363,16 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance)
                * Send signal to FW to stop processing any pending cmds.
                * The controller will be taken offline by the OS now.
                */
-               writel(MFI_STOP_ADP,
+               if ((instance->pdev->device ==
+                       PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+                       (instance->pdev->device ==
+                       PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+                       writel(MFI_STOP_ADP,
+                               &instance->reg_set->reserved_0[0]);
+               } else {
+                       writel(MFI_STOP_ADP,
                                &instance->reg_set->inbound_doorbell);
+               }
                megasas_dump_pending_frames(instance);
                instance->hw_crit_error = 1;
                return FAILED;
@@ -1266,6 +1520,8 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
        return 0;
 }
 
+static void megasas_aen_polling(struct work_struct *work);
+
 /**
  * megasas_service_aen -       Processes an event notification
  * @instance:                  Adapter soft state
@@ -1281,16 +1537,36 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
 static void
 megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd)
 {
+       unsigned long flags;
        /*
         * Don't signal app if it is just an aborted previously registered aen
         */
-       if (!cmd->abort_aen)
+       if ((!cmd->abort_aen) && (instance->unload == 0)) {
+               spin_lock_irqsave(&poll_aen_lock, flags);
+               megasas_poll_wait_aen = 1;
+               spin_unlock_irqrestore(&poll_aen_lock, flags);
+               wake_up(&megasas_poll_wait);
                kill_fasync(&megasas_async_queue, SIGIO, POLL_IN);
+       }
        else
                cmd->abort_aen = 0;
 
        instance->aen_cmd = NULL;
        megasas_return_cmd(instance, cmd);
+
+       if (instance->unload == 0) {
+               struct megasas_aen_event *ev;
+               ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+               if (!ev) {
+                       printk(KERN_ERR "megasas_service_aen: out of memory\n");
+               } else {
+                       ev->instance = instance;
+                       instance->ev = ev;
+                       INIT_WORK(&ev->hotplug_work, megasas_aen_polling);
+                       schedule_delayed_work(
+                               (struct delayed_work *)&ev->hotplug_work, 0);
+               }
+       }
 }
 
 /*
@@ -1302,6 +1578,7 @@ static struct scsi_host_template megasas_template = {
        .name = "LSI SAS based MegaRAID driver",
        .proc_name = "megaraid_sas",
        .slave_configure = megasas_slave_configure,
+       .slave_alloc = megasas_slave_alloc,
        .queuecommand = megasas_queue_command,
        .eh_device_reset_handler = megasas_reset_device,
        .eh_bus_reset_handler = megasas_reset_bus_host,
@@ -1370,6 +1647,7 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
 {
        int exception = 0;
        struct megasas_header *hdr = &cmd->frame->hdr;
+       unsigned long flags;
 
        if (cmd->scmd)
                cmd->scmd->SCp.ptr = NULL;
@@ -1459,6 +1737,12 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd,
        case MFI_CMD_SMP:
        case MFI_CMD_STP:
        case MFI_CMD_DCMD:
+               if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET_INFO ||
+                       cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET) {
+                       spin_lock_irqsave(&poll_aen_lock, flags);
+                       megasas_poll_wait_aen = 0;
+                       spin_unlock_irqrestore(&poll_aen_lock, flags);
+               }
 
                /*
                 * See if got an event notification
@@ -1536,6 +1820,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
        u8 max_wait;
        u32 fw_state;
        u32 cur_state;
+       u32 abs_state, curr_abs_state;
 
        fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK;
 
@@ -1545,6 +1830,9 @@ megasas_transition_to_ready(struct megasas_instance* instance)
 
        while (fw_state != MFI_STATE_READY) {
 
+               abs_state =
+               instance->instancet->read_fw_status_reg(instance->reg_set);
+
                switch (fw_state) {
 
                case MFI_STATE_FAULT:
@@ -1556,18 +1844,36 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                        /*
                         * Set the CLR bit in inbound doorbell
                         */
-                       writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
-                               &instance->reg_set->inbound_doorbell);
+                       if ((instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+                               (instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+
+                               writel(
+                                 MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
+                                 &instance->reg_set->reserved_0[0]);
+                       } else {
+                               writel(
+                                   MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG,
+                                       &instance->reg_set->inbound_doorbell);
+                       }
 
-                       max_wait = 2;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_WAIT_HANDSHAKE;
                        break;
 
                case MFI_STATE_BOOT_MESSAGE_PENDING:
-                       writel(MFI_INIT_HOTPLUG,
-                               &instance->reg_set->inbound_doorbell);
+                       if ((instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+                       (instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+                               writel(MFI_INIT_HOTPLUG,
+                               &instance->reg_set->reserved_0[0]);
+                       } else
+                               writel(MFI_INIT_HOTPLUG,
+                                       &instance->reg_set->inbound_doorbell);
 
-                       max_wait = 10;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_BOOT_MESSAGE_PENDING;
                        break;
 
@@ -1576,9 +1882,17 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                         * Bring it to READY state; assuming max wait 10 secs
                         */
                        instance->instancet->disable_intr(instance->reg_set);
-                       writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell);
+                       if ((instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+                               (instance->pdev->device ==
+                               PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+                               writel(MFI_RESET_FLAGS,
+                                       &instance->reg_set->reserved_0[0]);
+                       } else
+                               writel(MFI_RESET_FLAGS,
+                                       &instance->reg_set->inbound_doorbell);
 
-                       max_wait = 60;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_OPERATIONAL;
                        break;
 
@@ -1586,32 +1900,32 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                        /*
                         * This state should not last for more than 2 seconds
                         */
-                       max_wait = 2;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_UNDEFINED;
                        break;
 
                case MFI_STATE_BB_INIT:
-                       max_wait = 2;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_BB_INIT;
                        break;
 
                case MFI_STATE_FW_INIT:
-                       max_wait = 20;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_FW_INIT;
                        break;
 
                case MFI_STATE_FW_INIT_2:
-                       max_wait = 20;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_FW_INIT_2;
                        break;
 
                case MFI_STATE_DEVICE_SCAN:
-                       max_wait = 20;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_DEVICE_SCAN;
                        break;
 
                case MFI_STATE_FLUSH_CACHE:
-                       max_wait = 20;
+                       max_wait = MEGASAS_RESET_WAIT_TIME;
                        cur_state = MFI_STATE_FLUSH_CACHE;
                        break;
 
@@ -1627,8 +1941,10 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                for (i = 0; i < (max_wait * 1000); i++) {
                        fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) &  
                                        MFI_STATE_MASK ;
+               curr_abs_state =
+               instance->instancet->read_fw_status_reg(instance->reg_set);
 
-                       if (fw_state == cur_state) {
+                       if (abs_state == curr_abs_state) {
                                msleep(1);
                        } else
                                break;
@@ -1637,7 +1953,7 @@ megasas_transition_to_ready(struct megasas_instance* instance)
                /*
                 * Return error if fw_state hasn't changed after max_wait
                 */
-               if (fw_state == cur_state) {
+               if (curr_abs_state == abs_state) {
                        printk(KERN_DEBUG "FW state [%d] hasn't changed "
                               "in %d secs\n", fw_state, max_wait);
                        return -ENODEV;
@@ -1715,6 +2031,10 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
        sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
            sizeof(struct megasas_sge32);
 
+       if (instance->flag_ieee) {
+               sge_sz = sizeof(struct megasas_sge_skinny);
+       }
+
        /*
         * Calculated the number of 64byte frames required for SGL
         */
@@ -1777,6 +2097,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance)
                }
 
                cmd->frame->io.context = cmd->index;
+               cmd->frame->io.pad_0 = 0;
        }
 
        return 0;
@@ -1882,6 +2203,97 @@ static int megasas_alloc_cmds(struct megasas_instance *instance)
        return 0;
 }
 
+/*
+ * megasas_get_pd_list_info -  Returns FW's pd_list structure
+ * @instance:                          Adapter soft state
+ * @pd_list:                           pd_list structure
+ *
+ * Issues an internal command (DCMD) to get the FW's controller PD
+ * list structure.  This information is mainly used to find out SYSTEM
+ * supported by the FW.
+ */
+static int
+megasas_get_pd_list(struct megasas_instance *instance)
+{
+       int ret = 0, pd_index = 0;
+       struct megasas_cmd *cmd;
+       struct megasas_dcmd_frame *dcmd;
+       struct MR_PD_LIST *ci;
+       struct MR_PD_ADDRESS *pd_addr;
+       dma_addr_t ci_h = 0;
+
+       cmd = megasas_get_cmd(instance);
+
+       if (!cmd) {
+               printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n");
+               return -ENOMEM;
+       }
+
+       dcmd = &cmd->frame->dcmd;
+
+       ci = pci_alloc_consistent(instance->pdev,
+                 MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h);
+
+       if (!ci) {
+               printk(KERN_DEBUG "Failed to alloc mem for pd_list\n");
+               megasas_return_cmd(instance, cmd);
+               return -ENOMEM;
+       }
+
+       memset(ci, 0, sizeof(*ci));
+       memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);
+
+       dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST;
+       dcmd->mbox.b[1] = 0;
+       dcmd->cmd = MFI_CMD_DCMD;
+       dcmd->cmd_status = 0xFF;
+       dcmd->sge_count = 1;
+       dcmd->flags = MFI_FRAME_DIR_READ;
+       dcmd->timeout = 0;
+       dcmd->data_xfer_len = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+       dcmd->opcode = MR_DCMD_PD_LIST_QUERY;
+       dcmd->sgl.sge32[0].phys_addr = ci_h;
+       dcmd->sgl.sge32[0].length = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST);
+
+       if (!megasas_issue_polled(instance, cmd)) {
+               ret = 0;
+       } else {
+               ret = -1;
+       }
+
+       /*
+       * the following function will get the instance PD LIST.
+       */
+
+       pd_addr = ci->addr;
+
+       if ( ret == 0 &&
+               (ci->count <
+                 (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) {
+
+               memset(instance->pd_list, 0,
+                       MEGASAS_MAX_PD * sizeof(struct megasas_pd_list));
+
+               for (pd_index = 0; pd_index < ci->count; pd_index++) {
+
+                       instance->pd_list[pd_addr->deviceId].tid        =
+                                                       pd_addr->deviceId;
+                       instance->pd_list[pd_addr->deviceId].driveType  =
+                                                       pd_addr->scsiDevType;
+                       instance->pd_list[pd_addr->deviceId].driveState =
+                                                       MR_PD_STATE_SYSTEM;
+                       pd_addr++;
+               }
+       }
+
+       pci_free_consistent(instance->pdev,
+                               MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST),
+                               ci, ci_h);
+       megasas_return_cmd(instance, cmd);
+
+       return ret;
+}
+
 /**
  * megasas_get_controller_info -       Returns FW's controller structure
  * @instance:                          Adapter soft state
@@ -2081,6 +2493,8 @@ static int megasas_init_mfi(struct megasas_instance *instance)
         * Map the message registers
         */
        if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078GEN2) ||
+               (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) ||
+               (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
                (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0079GEN2)) {
                instance->base_addr = pci_resource_start(instance->pdev, 1);
        } else {
@@ -2111,6 +2525,10 @@ static int megasas_init_mfi(struct megasas_instance *instance)
                case PCI_DEVICE_ID_LSI_SAS0079GEN2:
                        instance->instancet = &megasas_instance_template_gen2;
                        break;
+               case PCI_DEVICE_ID_LSI_SAS0073SKINNY:
+               case PCI_DEVICE_ID_LSI_SAS0071SKINNY:
+                       instance->instancet = &megasas_instance_template_skinny;
+                       break;
                case PCI_DEVICE_ID_LSI_SAS1064R:
                case PCI_DEVICE_ID_DELL_PERC5:
                default:
@@ -2166,6 +2584,10 @@ static int megasas_init_mfi(struct megasas_instance *instance)
        if (megasas_issue_init_mfi(instance))
                goto fail_fw_init;
 
+       memset(instance->pd_list, 0 ,
+               (MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)));
+       megasas_get_pd_list(instance);
+
        ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL);
 
        /*
@@ -2409,6 +2831,11 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
        dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h;
        dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail);
 
+       if (instance->aen_cmd != NULL) {
+               megasas_return_cmd(instance, cmd);
+               return 0;
+       }
+
        /*
         * Store reference to the cmd used to register for AEN. When an
         * application wants us to register for AEN, we have to abort this
@@ -2419,7 +2846,8 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num,
        /*
         * Issue the aen registration frame
         */
-       instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set);
+       instance->instancet->fire_cmd(instance,
+                       cmd->frame_phys_addr, 0, instance->reg_set);
 
        return 0;
 }
@@ -2465,7 +2893,13 @@ static int megasas_io_attach(struct megasas_instance *instance)
         */
        host->irq = instance->pdev->irq;
        host->unique_id = instance->unique_id;
-       host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS;
+       if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+               (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+               host->can_queue =
+                       instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS;
+       } else
+               host->can_queue =
+                       instance->max_fw_cmds - MEGASAS_INT_CMDS;
        host->this_id = instance->init_id;
        host->sg_tablesize = instance->max_num_sge;
        host->max_sectors = instance->max_sectors_per_req;
@@ -2572,6 +3006,9 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 
        *instance->producer = 0;
        *instance->consumer = 0;
+       megasas_poll_wait_aen = 0;
+       instance->flag_ieee = 0;
+       instance->ev = NULL;
 
        instance->evt_detail = pci_alloc_consistent(pdev,
                                                    sizeof(struct
@@ -2595,10 +3032,11 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        init_waitqueue_head(&instance->abort_cmd_wait_q);
 
        spin_lock_init(&instance->cmd_pool_lock);
+       spin_lock_init(&instance->fire_lock);
        spin_lock_init(&instance->completion_lock);
+       spin_lock_init(&poll_aen_lock);
 
        mutex_init(&instance->aen_mutex);
-       sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
 
        /*
         * Initialize PCI related and misc parameters
@@ -2608,8 +3046,16 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        instance->unique_id = pdev->bus->number << 8 | pdev->devfn;
        instance->init_id = MEGASAS_DEFAULT_INIT_ID;
 
+       if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) ||
+               (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) {
+               instance->flag_ieee = 1;
+               sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS);
+       } else
+               sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS);
+
        megasas_dbg_lvl = 0;
        instance->flag = 0;
+       instance->unload = 1;
        instance->last_time = 0;
 
        /*
@@ -2655,6 +3101,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        if (megasas_io_attach(instance))
                goto fail_io_attach;
 
+       instance->unload = 0;
        return 0;
 
       fail_start_aen:
@@ -2778,12 +3225,23 @@ megasas_suspend(struct pci_dev *pdev, pm_message_t state)
 
        instance = pci_get_drvdata(pdev);
        host = instance->host;
+       instance->unload = 1;
 
        if (poll_mode_io)
                del_timer_sync(&instance->io_completion_timer);
 
        megasas_flush_cache(instance);
        megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN);
+
+       /* cancel the delayed work if this work still in queue */
+       if (instance->ev != NULL) {
+               struct megasas_aen_event *ev = instance->ev;
+               cancel_delayed_work(
+                       (struct delayed_work *)&ev->hotplug_work);
+               flush_scheduled_work();
+               instance->ev = NULL;
+       }
+
        tasklet_kill(&instance->isr_tasklet);
 
        pci_set_drvdata(instance->pdev, instance);
@@ -2873,6 +3331,8 @@ megasas_resume(struct pci_dev *pdev)
                megasas_start_timer(instance, &instance->io_completion_timer,
                                megasas_io_completion_timer,
                                MEGASAS_COMPLETION_TIMER_INTERVAL);
+       instance->unload = 0;
+
        return 0;
 
 fail_irq:
@@ -2913,6 +3373,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
        struct megasas_instance *instance;
 
        instance = pci_get_drvdata(pdev);
+       instance->unload = 1;
        host = instance->host;
 
        if (poll_mode_io)
@@ -2921,6 +3382,16 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
        scsi_remove_host(instance->host);
        megasas_flush_cache(instance);
        megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
+
+       /* cancel the delayed work if this work still in queue*/
+       if (instance->ev != NULL) {
+               struct megasas_aen_event *ev = instance->ev;
+               cancel_delayed_work(
+                       (struct delayed_work *)&ev->hotplug_work);
+               flush_scheduled_work();
+               instance->ev = NULL;
+       }
+
        tasklet_kill(&instance->isr_tasklet);
 
        /*
@@ -2969,6 +3440,7 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev)
 static void megasas_shutdown(struct pci_dev *pdev)
 {
        struct megasas_instance *instance = pci_get_drvdata(pdev);
+       instance->unload = 1;
        megasas_flush_cache(instance);
        megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN);
 }
@@ -3015,6 +3487,23 @@ static int megasas_mgmt_fasync(int fd, struct file *filep, int mode)
        return rc;
 }
 
+/**
+ * megasas_mgmt_poll -  char node "poll" entry point
+ * */
+static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait)
+{
+       unsigned int mask;
+       unsigned long flags;
+       poll_wait(file, &megasas_poll_wait, wait);
+       spin_lock_irqsave(&poll_aen_lock, flags);
+       if (megasas_poll_wait_aen)
+               mask =   (POLLIN | POLLRDNORM);
+       else
+               mask = 0;
+       spin_unlock_irqrestore(&poll_aen_lock, flags);
+       return mask;
+}
+
 /**
  * megasas_mgmt_fw_ioctl -     Issues management ioctls to FW
  * @instance:                  Adapter soft state
@@ -3032,7 +3521,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
        int error = 0, i;
        void *sense = NULL;
        dma_addr_t sense_handle;
-       u32 *sense_ptr;
+       unsigned long *sense_ptr;
 
        memset(kbuff_arr, 0, sizeof(kbuff_arr));
 
@@ -3056,6 +3545,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
         */
        memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE);
        cmd->frame->hdr.context = cmd->index;
+       cmd->frame->hdr.pad_0 = 0;
 
        /*
         * The management interface between applications and the fw uses
@@ -3109,7 +3599,7 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
                }
 
                sense_ptr =
-                   (u32 *) ((unsigned long)cmd->frame + ioc->sense_off);
+               (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off);
                *sense_ptr = sense_handle;
        }
 
@@ -3140,8 +3630,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
                 * sense_ptr points to the location that has the user
                 * sense buffer address
                 */
-               sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw +
-                                    ioc->sense_off);
+               sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw +
+                               ioc->sense_off);
 
                if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)),
                                 sense, ioc->sense_len)) {
@@ -3177,20 +3667,6 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
        return error;
 }
 
-static struct megasas_instance *megasas_lookup_instance(u16 host_no)
-{
-       int i;
-
-       for (i = 0; i < megasas_mgmt_info.max_index; i++) {
-
-               if ((megasas_mgmt_info.instance[i]) &&
-                   (megasas_mgmt_info.instance[i]->host->host_no == host_no))
-                       return megasas_mgmt_info.instance[i];
-       }
-
-       return NULL;
-}
-
 static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
 {
        struct megasas_iocpacket __user *user_ioc =
@@ -3214,6 +3690,17 @@ static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg)
                goto out_kfree_ioc;
        }
 
+       if (instance->hw_crit_error == 1) {
+               printk(KERN_DEBUG "Controller in Crit ERROR\n");
+               error = -ENODEV;
+               goto out_kfree_ioc;
+       }
+
+       if (instance->unload == 1) {
+               error = -ENODEV;
+               goto out_kfree_ioc;
+       }
+
        /*
         * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds
         */
@@ -3249,6 +3736,14 @@ static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg)
        if (!instance)
                return -ENODEV;
 
+       if (instance->hw_crit_error == 1) {
+               error = -ENODEV;
+       }
+
+       if (instance->unload == 1) {
+               return -ENODEV;
+       }
+
        mutex_lock(&instance->aen_mutex);
        error = megasas_register_aen(instance, aen.seq_num,
                                     aen.class_locale_word);
@@ -3337,6 +3832,7 @@ static const struct file_operations megasas_mgmt_fops = {
        .open = megasas_mgmt_open,
        .fasync = megasas_mgmt_fasync,
        .unlocked_ioctl = megasas_mgmt_ioctl,
+       .poll = megasas_mgmt_poll,
 #ifdef CONFIG_COMPAT
        .compat_ioctl = megasas_mgmt_compat_ioctl,
 #endif
@@ -3377,6 +3873,15 @@ megasas_sysfs_show_release_date(struct device_driver *dd, char *buf)
 static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
                   NULL);
 
+static ssize_t
+megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
+{
+       return sprintf(buf, "%u\n", support_poll_for_event);
+}
+
+static DRIVER_ATTR(support_poll_for_event, S_IRUGO,
+                       megasas_sysfs_show_support_poll_for_event, NULL);
+
 static ssize_t
 megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf)
 {
@@ -3451,6 +3956,92 @@ out:
        return retval;
 }
 
+static void
+megasas_aen_polling(struct work_struct *work)
+{
+       struct megasas_aen_event *ev =
+               container_of(work, struct megasas_aen_event, hotplug_work);
+       struct megasas_instance *instance = ev->instance;
+       union megasas_evt_class_locale class_locale;
+       struct  Scsi_Host *host;
+       struct  scsi_device *sdev1;
+       u16     pd_index = 0;
+       int     i, j, doscan = 0;
+       u32 seq_num;
+       int error;
+
+       if (!instance) {
+               printk(KERN_ERR "invalid instance!\n");
+               kfree(ev);
+               return;
+       }
+       instance->ev = NULL;
+       host = instance->host;
+       if (instance->evt_detail) {
+
+               switch (instance->evt_detail->code) {
+               case MR_EVT_PD_INSERTED:
+               case MR_EVT_PD_REMOVED:
+               case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED:
+                       doscan = 1;
+                       break;
+               default:
+                       doscan = 0;
+                       break;
+               }
+       } else {
+               printk(KERN_ERR "invalid evt_detail!\n");
+               kfree(ev);
+               return;
+       }
+
+       if (doscan) {
+               printk(KERN_INFO "scanning ...\n");
+               megasas_get_pd_list(instance);
+               for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) {
+                       for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) {
+                               pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j;
+                               sdev1 = scsi_device_lookup(host, i, j, 0);
+                               if (instance->pd_list[pd_index].driveState ==
+                                                       MR_PD_STATE_SYSTEM) {
+                                       if (!sdev1) {
+                                               scsi_add_device(host, i, j, 0);
+                                       }
+                                       if (sdev1)
+                                               scsi_device_put(sdev1);
+                               } else {
+                                       if (sdev1) {
+                                               scsi_remove_device(sdev1);
+                                               scsi_device_put(sdev1);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if ( instance->aen_cmd != NULL ) {
+               kfree(ev);
+               return ;
+       }
+
+       seq_num = instance->evt_detail->seq_num + 1;
+
+       /* Register AEN with FW for latest sequence number plus 1 */
+       class_locale.members.reserved = 0;
+       class_locale.members.locale = MR_EVT_LOCALE_ALL;
+       class_locale.members.class = MR_EVT_CLASS_DEBUG;
+       mutex_lock(&instance->aen_mutex);
+       error = megasas_register_aen(instance, seq_num,
+                                       class_locale.word);
+       mutex_unlock(&instance->aen_mutex);
+
+       if (error)
+               printk(KERN_ERR "register aen failed error %x\n", error);
+
+       kfree(ev);
+}
+
+
 static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUGO,
                megasas_sysfs_show_poll_mode_io,
                megasas_sysfs_set_poll_mode_io);
@@ -3468,6 +4059,8 @@ static int __init megasas_init(void)
        printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
               MEGASAS_EXT_VERSION);
 
+       support_poll_for_event = 2;
+
        memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info));
 
        /*
@@ -3500,6 +4093,12 @@ static int __init megasas_init(void)
                                  &driver_attr_release_date);
        if (rval)
                goto err_dcf_rel_date;
+
+       rval = driver_create_file(&megasas_pci_driver.driver,
+                               &driver_attr_support_poll_for_event);
+       if (rval)
+               goto err_dcf_support_poll_for_event;
+
        rval = driver_create_file(&megasas_pci_driver.driver,
                                  &driver_attr_dbg_lvl);
        if (rval)
@@ -3515,8 +4114,13 @@ err_dcf_poll_mode_io:
        driver_remove_file(&megasas_pci_driver.driver,
                           &driver_attr_dbg_lvl);
 err_dcf_dbg_lvl:
+       driver_remove_file(&megasas_pci_driver.driver,
+                       &driver_attr_support_poll_for_event);
+
+err_dcf_support_poll_for_event:
        driver_remove_file(&megasas_pci_driver.driver,
                           &driver_attr_release_date);
+
 err_dcf_rel_date:
        driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
 err_dcf_attr_ver:
index 0d03324..72b28e4 100644 (file)
@@ -18,9 +18,9 @@
 /*
  * MegaRAID SAS Driver meta data
  */
-#define MEGASAS_VERSION                                "00.00.04.01"
-#define MEGASAS_RELDATE                                "July 24, 2008"
-#define MEGASAS_EXT_VERSION                    "Thu July 24 11:41:51 PST 2008"
+#define MEGASAS_VERSION                                "00.00.04.12-rc1"
+#define MEGASAS_RELDATE                                "Sep. 17, 2009"
+#define MEGASAS_EXT_VERSION                    "Thu Sep. 17 11:41:51 PST 2009"
 
 /*
  * Device IDs
@@ -30,6 +30,8 @@
 #define        PCI_DEVICE_ID_LSI_VERDE_ZCR             0x0413
 #define        PCI_DEVICE_ID_LSI_SAS1078GEN2           0x0078
 #define        PCI_DEVICE_ID_LSI_SAS0079GEN2           0x0079
+#define        PCI_DEVICE_ID_LSI_SAS0073SKINNY         0x0073
+#define        PCI_DEVICE_ID_LSI_SAS0071SKINNY         0x0071
 
 /*
  * =====================================
@@ -94,6 +96,7 @@
 #define MFI_FRAME_DIR_WRITE                    0x0008
 #define MFI_FRAME_DIR_READ                     0x0010
 #define MFI_FRAME_DIR_BOTH                     0x0018
+#define MFI_FRAME_IEEE                          0x0020
 
 /*
  * Definition for cmd_status
 #define MR_DCMD_CLUSTER                                0x08000000
 #define MR_DCMD_CLUSTER_RESET_ALL              0x08010100
 #define MR_DCMD_CLUSTER_RESET_LD               0x08010200
+#define MR_DCMD_PD_LIST_QUERY                   0x02010100
 
 /*
  * MFI command completion codes
@@ -251,9 +255,100 @@ enum MR_EVT_ARGS {
        MR_EVT_ARGS_STR,
        MR_EVT_ARGS_TIME,
        MR_EVT_ARGS_ECC,
+       MR_EVT_ARGS_LD_PROP,
+       MR_EVT_ARGS_PD_SPARE,
+       MR_EVT_ARGS_PD_INDEX,
+       MR_EVT_ARGS_DIAG_PASS,
+       MR_EVT_ARGS_DIAG_FAIL,
+       MR_EVT_ARGS_PD_LBA_LBA,
+       MR_EVT_ARGS_PORT_PHY,
+       MR_EVT_ARGS_PD_MISSING,
+       MR_EVT_ARGS_PD_ADDRESS,
+       MR_EVT_ARGS_BITMAP,
+       MR_EVT_ARGS_CONNECTOR,
+       MR_EVT_ARGS_PD_PD,
+       MR_EVT_ARGS_PD_FRU,
+       MR_EVT_ARGS_PD_PATHINFO,
+       MR_EVT_ARGS_PD_POWER_STATE,
+       MR_EVT_ARGS_GENERIC,
+};
 
+/*
+ * define constants for device list query options
+ */
+enum MR_PD_QUERY_TYPE {
+       MR_PD_QUERY_TYPE_ALL                = 0,
+       MR_PD_QUERY_TYPE_STATE              = 1,
+       MR_PD_QUERY_TYPE_POWER_STATE        = 2,
+       MR_PD_QUERY_TYPE_MEDIA_TYPE         = 3,
+       MR_PD_QUERY_TYPE_SPEED              = 4,
+       MR_PD_QUERY_TYPE_EXPOSED_TO_HOST    = 5,
 };
 
+#define MR_EVT_CFG_CLEARED                              0x0004
+#define MR_EVT_LD_STATE_CHANGE                          0x0051
+#define MR_EVT_PD_INSERTED                              0x005b
+#define MR_EVT_PD_REMOVED                               0x0070
+#define MR_EVT_LD_CREATED                               0x008a
+#define MR_EVT_LD_DELETED                               0x008b
+#define MR_EVT_FOREIGN_CFG_IMPORTED                     0x00db
+#define MR_EVT_LD_OFFLINE                               0x00fc
+#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED             0x0152
+#define MAX_LOGICAL_DRIVES                             64
+
+enum MR_PD_STATE {
+       MR_PD_STATE_UNCONFIGURED_GOOD   = 0x00,
+       MR_PD_STATE_UNCONFIGURED_BAD    = 0x01,
+       MR_PD_STATE_HOT_SPARE           = 0x02,
+       MR_PD_STATE_OFFLINE             = 0x10,
+       MR_PD_STATE_FAILED              = 0x11,
+       MR_PD_STATE_REBUILD             = 0x14,
+       MR_PD_STATE_ONLINE              = 0x18,
+       MR_PD_STATE_COPYBACK            = 0x20,
+       MR_PD_STATE_SYSTEM              = 0x40,
+ };
+
+
+ /*
+ * defines the physical drive address structure
+ */
+struct MR_PD_ADDRESS {
+       u16     deviceId;
+       u16     enclDeviceId;
+
+       union {
+               struct {
+                       u8  enclIndex;
+                       u8  slotNumber;
+               } mrPdAddress;
+               struct {
+                       u8  enclPosition;
+                       u8  enclConnectorIndex;
+               } mrEnclAddress;
+       };
+       u8      scsiDevType;
+       union {
+               u8      connectedPortBitmap;
+               u8      connectedPortNumbers;
+       };
+       u64     sasAddr[2];
+} __packed;
+
+/*
+ * defines the physical drive list structure
+ */
+struct MR_PD_LIST {
+       u32             size;
+       u32             count;
+       struct MR_PD_ADDRESS   addr[1];
+} __packed;
+
+struct megasas_pd_list {
+       u16             tid;
+       u8             driveType;
+       u8             driveState;
+} __packed;
+
 /*
  * SAS controller properties
  */
@@ -282,7 +377,7 @@ struct megasas_ctrl_prop {
        u8 expose_encl_devices;
        u8 reserved[38];
 
-} __attribute__ ((packed));
+} __packed;
 
 /*
  * SAS controller information
@@ -525,7 +620,7 @@ struct megasas_ctrl_info {
 
        u8 pad[0x800 - 0x6a0];
 
-} __attribute__ ((packed));
+} __packed;
 
 /*
  * ===============================
@@ -540,6 +635,8 @@ struct megasas_ctrl_info {
 #define MEGASAS_DEFAULT_INIT_ID                        -1
 #define MEGASAS_MAX_LUN                                8
 #define MEGASAS_MAX_LD                         64
+#define MEGASAS_MAX_PD                          (MEGASAS_MAX_PD_CHANNELS * \
+                                               MEGASAS_MAX_DEV_PER_CHANNEL)
 
 #define MEGASAS_DBG_LVL                                1
 
@@ -570,6 +667,7 @@ struct megasas_ctrl_info {
  * is shown below
  */
 #define MEGASAS_INT_CMDS                       32
+#define MEGASAS_SKINNY_INT_CMDS                        5
 
 /*
  * FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit
@@ -584,6 +682,8 @@ struct megasas_ctrl_info {
 #define MFI_REPLY_1078_MESSAGE_INTERRUPT       0x80000000
 #define MFI_REPLY_GEN2_MESSAGE_INTERRUPT       0x00000001
 #define MFI_GEN2_ENABLE_INTERRUPT_MASK         (0x00000001 | 0x00000004)
+#define MFI_REPLY_SKINNY_MESSAGE_INTERRUPT     0x40000000
+#define MFI_SKINNY_ENABLE_INTERRUPT_MASK       (0x00000001)
 
 /*
 * register set for both 1068 and 1078 controllers
@@ -644,10 +744,17 @@ struct megasas_sge64 {
 
 } __attribute__ ((packed));
 
+struct megasas_sge_skinny {
+       u64 phys_addr;
+       u32 length;
+       u32 flag;
+} __packed;
+
 union megasas_sgl {
 
        struct megasas_sge32 sge32[1];
        struct megasas_sge64 sge64[1];
+       struct megasas_sge_skinny sge_skinny[1];
 
 } __attribute__ ((packed));
 
@@ -1061,16 +1168,10 @@ struct megasas_evt_detail {
 
 } __attribute__ ((packed));
 
- struct megasas_instance_template {
-       void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *);
-
-       void (*enable_intr)(struct megasas_register_set __iomem *) ;
-       void (*disable_intr)(struct megasas_register_set __iomem *);
-
-       int (*clear_intr)(struct megasas_register_set __iomem *);
-
-       u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
- };
+struct megasas_aen_event {
+       struct work_struct hotplug_work;
+       struct megasas_instance *instance;
+};
 
 struct megasas_instance {
 
@@ -1085,17 +1186,21 @@ struct megasas_instance {
        unsigned long base_addr;
        struct megasas_register_set __iomem *reg_set;
 
+       struct megasas_pd_list          pd_list[MEGASAS_MAX_PD];
        s8 init_id;
 
        u16 max_num_sge;
        u16 max_fw_cmds;
        u32 max_sectors_per_req;
+       struct megasas_aen_event *ev;
 
        struct megasas_cmd **cmd_list;
        struct list_head cmd_pool;
        spinlock_t cmd_pool_lock;
        /* used to synch producer, consumer ptrs in dpc */
        spinlock_t completion_lock;
+       /* used to sync fire the cmd to fw */
+       spinlock_t fire_lock;
        struct dma_pool *frame_dma_pool;
        struct dma_pool *sense_dma_pool;
 
@@ -1120,11 +1225,25 @@ struct megasas_instance {
        struct tasklet_struct isr_tasklet;
 
        u8 flag;
+       u8 unload;
+       u8 flag_ieee;
        unsigned long last_time;
 
        struct timer_list io_completion_timer;
 };
 
+struct megasas_instance_template {
+       void (*fire_cmd)(struct megasas_instance *, dma_addr_t, \
+               u32, struct megasas_register_set __iomem *);
+
+       void (*enable_intr)(struct megasas_register_set __iomem *) ;
+       void (*disable_intr)(struct megasas_register_set __iomem *);
+
+       int (*clear_intr)(struct megasas_register_set __iomem *);
+
+       u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *);
+};
+
 #define MEGASAS_IS_LOGICAL(scp)                                                \
        (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
 
index f9f6c08..9141681 100644 (file)
@@ -8,7 +8,7 @@
  *                  scatter/gather formats.
  *  Creation Date:  June 21, 2006
  *
- *  mpi2.h Version:  02.00.12
+ *  mpi2.h Version:  02.00.13
  *
  *  Version History
  *  ---------------
@@ -52,6 +52,7 @@
  *                      MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR and made those
  *                      bytes reserved.
  *                      Added RAID Accelerator functionality.
+ *  07-30-09  02.00.13  Bumped MPI2_HEADER_VERSION_UNIT.
  *  --------------------------------------------------------------------------
  */
 
@@ -77,7 +78,7 @@
 #define MPI2_VERSION_02_00                  (0x0200)
 
 /* versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT            (0x0C)
+#define MPI2_HEADER_VERSION_UNIT            (0x0D)
 #define MPI2_HEADER_VERSION_DEV             (0x00)
 #define MPI2_HEADER_VERSION_UNIT_MASK       (0xFF00)
 #define MPI2_HEADER_VERSION_UNIT_SHIFT      (8)
index ab47c46..1611c57 100644 (file)
@@ -6,7 +6,7 @@
  *          Title:  MPI Configuration messages and pages
  *  Creation Date:  November 10, 2006
  *
- *    mpi2_cnfg.h Version:  02.00.11
+ *    mpi2_cnfg.h Version:  02.00.12
  *
  *  Version History
  *  ---------------
  *                      Added expander reduced functionality data to SAS
  *                      Expander Page 0.
  *                      Added SAS PHY Page 2 and SAS PHY Page 3.
+ *  07-30-09  02.00.12  Added IO Unit Page 7.
+ *                      Added new device ids.
+ *                      Added SAS IO Unit Page 5.
+ *                      Added partial and slumber power management capable flags
+ *                      to SAS Device Page 0 Flags field.
+ *                      Added PhyInfo defines for power condition.
+ *                      Added Ethernet configuration pages.
  *  --------------------------------------------------------------------------
  */
 
@@ -182,6 +189,7 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
 #define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG         (0x16)
 #define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING      (0x17)
 #define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT            (0x18)
+#define MPI2_CONFIG_EXTPAGETYPE_ETHERNET            (0x19)
 
 
 /*****************************************************************************
@@ -268,6 +276,14 @@ typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION
 #define MPI2_DPM_PGAD_START_ENTRY_MASK              (0x0000FFFF)
 
 
+/* Ethernet PageAddress format */
+#define MPI2_ETHERNET_PGAD_FORM_MASK                (0xF0000000)
+#define MPI2_ETHERNET_PGAD_FORM_IF_NUM              (0x00000000)
+
+#define MPI2_ETHERNET_PGAD_IF_NUMBER_MASK           (0x000000FF)
+
+
+
 /****************************************************************************
 *   Configuration messages
 ****************************************************************************/
@@ -349,6 +365,15 @@ typedef struct _MPI2_CONFIG_REPLY
 #define MPI2_MFGPAGE_DEVID_SAS2116_1                (0x0064)
 #define MPI2_MFGPAGE_DEVID_SAS2116_2                (0x0065)
 
+#define MPI2_MFGPAGE_DEVID_SAS2208_1                (0x0080)
+#define MPI2_MFGPAGE_DEVID_SAS2208_2                (0x0081)
+#define MPI2_MFGPAGE_DEVID_SAS2208_3                (0x0082)
+#define MPI2_MFGPAGE_DEVID_SAS2208_4                (0x0083)
+#define MPI2_MFGPAGE_DEVID_SAS2208_5                (0x0084)
+#define MPI2_MFGPAGE_DEVID_SAS2208_6                (0x0085)
+#define MPI2_MFGPAGE_DEVID_SAS2208_7                (0x0086)
+#define MPI2_MFGPAGE_DEVID_SAS2208_8                (0x0087)
+
 
 /* Manufacturing Page 0 */
 
@@ -787,6 +812,56 @@ typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_6 {
 #define MPI2_IOUNITPAGE6_FLAGS_ENABLE_RAID_ACCELERATOR  (0x0001)
 
 
+/* IO Unit Page 7 */
+
+typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7 {
+    MPI2_CONFIG_PAGE_HEADER Header;                                 /* 0x00 */
+    U16                     Reserved1;                              /* 0x04 */
+    U8                      PCIeWidth;                              /* 0x06 */
+    U8                      PCIeSpeed;                              /* 0x07 */
+    U32                     ProcessorState;                         /* 0x08 */
+    U32                     Reserved2;                              /* 0x0C */
+    U16                     IOCTemperature;                         /* 0x10 */
+    U8                      IOCTemperatureUnits;                    /* 0x12 */
+    U8                      IOCSpeed;                               /* 0x13 */
+    U32                     Reserved3;                              /* 0x14 */
+} MPI2_CONFIG_PAGE_IO_UNIT_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_7,
+  Mpi2IOUnitPage7_t, MPI2_POINTER pMpi2IOUnitPage7_t;
+
+#define MPI2_IOUNITPAGE7_PAGEVERSION                    (0x00)
+
+/* defines for IO Unit Page 7 PCIeWidth field */
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X1              (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X2              (0x02)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X4              (0x04)
+#define MPI2_IOUNITPAGE7_PCIE_WIDTH_X8              (0x08)
+
+/* defines for IO Unit Page 7 PCIeSpeed field */
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_2_5_GBPS        (0x00)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_5_0_GBPS        (0x01)
+#define MPI2_IOUNITPAGE7_PCIE_SPEED_8_0_GBPS        (0x02)
+
+/* defines for IO Unit Page 7 ProcessorState field */
+#define MPI2_IOUNITPAGE7_PSTATE_MASK_SECOND         (0x0000000F)
+#define MPI2_IOUNITPAGE7_PSTATE_SHIFT_SECOND        (0)
+
+#define MPI2_IOUNITPAGE7_PSTATE_NOT_PRESENT         (0x00)
+#define MPI2_IOUNITPAGE7_PSTATE_DISABLED            (0x01)
+#define MPI2_IOUNITPAGE7_PSTATE_ENABLED             (0x02)
+
+/* defines for IO Unit Page 7 IOCTemperatureUnits field */
+#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT       (0x00)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT        (0x01)
+#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS           (0x02)
+
+/* defines for IO Unit Page 7 IOCSpeed field */
+#define MPI2_IOUNITPAGE7_IOC_SPEED_FULL             (0x01)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_HALF             (0x02)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_QUARTER          (0x04)
+#define MPI2_IOUNITPAGE7_IOC_SPEED_EIGHTH           (0x08)
+
+
+
 /****************************************************************************
 *   IOC Config Pages
 ****************************************************************************/
@@ -1470,6 +1545,12 @@ typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1
 
 /* values for PhyInfo fields */
 #define MPI2_SAS_PHYINFO_PHY_VACANT                     (0x80000000)
+
+#define MPI2_SAS_PHYINFO_PHY_POWER_CONDITION_MASK       (0x18000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_ACTIVE               (0x00000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_PARTIAL              (0x08000000)
+#define MPI2_SAS_PHYINFO_PHY_POWER_SLUMBER              (0x10000000)
+
 #define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS       (0x04000000)
 #define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT        (0x02000000)
 #define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS               (0x01000000)
@@ -1682,11 +1763,11 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1
 /* values for SAS IO Unit Page 1 PortFlags */
 #define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG                 (0x01)
 
-/* values for SAS IO Unit Page 2 PhyFlags */
+/* values for SAS IO Unit Page 1 PhyFlags */
 #define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE                      (0x10)
 #define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE                        (0x08)
 
-/* values for SAS IO Unit Page 0 MaxMinLinkRate */
+/* values for SAS IO Unit Page 1 MaxMinLinkRate */
 #define MPI2_SASIOUNIT1_MAX_RATE_MASK                               (0xF0)
 #define MPI2_SASIOUNIT1_MAX_RATE_1_5                                (0x80)
 #define MPI2_SASIOUNIT1_MAX_RATE_3_0                                (0x90)
@@ -1745,6 +1826,74 @@ typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4
 #define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK               (0x03)
 
 
+/* SAS IO Unit Page 5 */
+
+typedef struct _MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS {
+    U8          ControlFlags;               /* 0x00 */
+    U8          Reserved1;                  /* 0x01 */
+    U16         InactivityTimerExponent;    /* 0x02 */
+    U8          SATAPartialTimeout;         /* 0x04 */
+    U8          Reserved2;                  /* 0x05 */
+    U8          SATASlumberTimeout;         /* 0x06 */
+    U8          Reserved3;                  /* 0x07 */
+    U8          SASPartialTimeout;          /* 0x08 */
+    U8          Reserved4;                  /* 0x09 */
+    U8          SASSlumberTimeout;          /* 0x0A */
+    U8          Reserved5;                  /* 0x0B */
+} MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+  MPI2_POINTER PTR_MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS,
+  Mpi2SasIOUnit5PhyPmSettings_t, MPI2_POINTER pMpi2SasIOUnit5PhyPmSettings_t;
+
+/* defines for ControlFlags field */
+#define MPI2_SASIOUNIT5_CONTROL_SAS_SLUMBER_ENABLE      (0x08)
+#define MPI2_SASIOUNIT5_CONTROL_SAS_PARTIAL_ENABLE      (0x04)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_SLUMBER_ENABLE     (0x02)
+#define MPI2_SASIOUNIT5_CONTROL_SATA_PARTIAL_ENABLE     (0x01)
+
+/* defines for InactivityTimerExponent field */
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_SLUMBER            (0x7000)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_SLUMBER           (12)
+#define MPI2_SASIOUNIT5_ITE_MASK_SAS_PARTIAL            (0x0700)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SAS_PARTIAL           (8)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_SLUMBER           (0x0070)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_SLUMBER          (4)
+#define MPI2_SASIOUNIT5_ITE_MASK_SATA_PARTIAL           (0x0007)
+#define MPI2_SASIOUNIT5_ITE_SHIFT_SATA_PARTIAL          (0)
+
+#define MPI2_SASIOUNIT5_ITE_TEN_SECONDS                 (7)
+#define MPI2_SASIOUNIT5_ITE_ONE_SECOND                  (6)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MILLISECONDS        (5)
+#define MPI2_SASIOUNIT5_ITE_TEN_MILLISECONDS            (4)
+#define MPI2_SASIOUNIT5_ITE_ONE_MILLISECOND             (3)
+#define MPI2_SASIOUNIT5_ITE_HUNDRED_MICROSECONDS        (2)
+#define MPI2_SASIOUNIT5_ITE_TEN_MICROSECONDS            (1)
+#define MPI2_SASIOUNIT5_ITE_ONE_MICROSECOND             (0)
+
+/*
+ * Host code (drivers, BIOS, utilities, etc.) should leave this define set to
+ * one and check Header.ExtPageLength or NumPhys at runtime.
+ */
+#ifndef MPI2_SAS_IOUNIT5_PHY_MAX
+#define MPI2_SAS_IOUNIT5_PHY_MAX        (1)
+#endif
+
+typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_5 {
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER    Header;                /* 0x00 */
+    U8                                  NumPhys;       /* 0x08 */
+    U8                                  Reserved1;     /* 0x09 */
+    U16                                 Reserved2;     /* 0x0A */
+    U32                                 Reserved3;     /* 0x0C */
+    MPI2_SAS_IO_UNIT5_PHY_PM_SETTINGS   SASPhyPowerManagementSettings
+                                       [MPI2_SAS_IOUNIT5_PHY_MAX];  /* 0x10 */
+} MPI2_CONFIG_PAGE_SASIOUNIT_5,
+  MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_5,
+  Mpi2SasIOUnitPage5_t, MPI2_POINTER pMpi2SasIOUnitPage5_t;
+
+#define MPI2_SASIOUNITPAGE5_PAGEVERSION     (0x00)
+
+
+
+
 /****************************************************************************
 *   SAS Expander Config Pages
 ****************************************************************************/
@@ -1927,6 +2076,8 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0
 /* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */
 
 /* values for SAS Device Page 0 Flags field */
+#define MPI2_SAS_DEVICE0_FLAGS_SLUMBER_PM_CAPABLE           (0x1000)
+#define MPI2_SAS_DEVICE0_FLAGS_PARTIAL_PM_CAPABLE           (0x0800)
 #define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY     (0x0400)
 #define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE             (0x0200)
 #define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE           (0x0100)
@@ -2343,5 +2494,122 @@ typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0
 #define MPI2_DRVMAP0_MAPINFO_MISSING_MASK           (0x000F)
 
 
+/****************************************************************************
+*   Ethernet Config Pages
+****************************************************************************/
+
+/* Ethernet Page 0 */
+
+/* IP address (union of IPv4 and IPv6) */
+typedef union _MPI2_ETHERNET_IP_ADDR {
+    U32     IPv4Addr;
+    U32     IPv6Addr[4];
+} MPI2_ETHERNET_IP_ADDR, MPI2_POINTER PTR_MPI2_ETHERNET_IP_ADDR,
+  Mpi2EthernetIpAddr_t, MPI2_POINTER pMpi2EthernetIpAddr_t;
+
+#define MPI2_ETHERNET_HOST_NAME_LENGTH          (32)
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_0 {
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER    Header;                 /* 0x00 */
+    U8                                  NumInterfaces;          /* 0x08 */
+    U8                                  Reserved0;              /* 0x09 */
+    U16                                 Reserved1;              /* 0x0A */
+    U32                                 Status;                 /* 0x0C */
+    U8                                  MediaState;             /* 0x10 */
+    U8                                  Reserved2;              /* 0x11 */
+    U16                                 Reserved3;              /* 0x12 */
+    U8                                  MacAddress[6];          /* 0x14 */
+    U8                                  Reserved4;              /* 0x1A */
+    U8                                  Reserved5;              /* 0x1B */
+    MPI2_ETHERNET_IP_ADDR               IpAddress;              /* 0x1C */
+    MPI2_ETHERNET_IP_ADDR               SubnetMask;             /* 0x2C */
+    MPI2_ETHERNET_IP_ADDR               GatewayIpAddress;       /* 0x3C */
+    MPI2_ETHERNET_IP_ADDR               DNS1IpAddress;          /* 0x4C */
+    MPI2_ETHERNET_IP_ADDR               DNS2IpAddress;          /* 0x5C */
+    MPI2_ETHERNET_IP_ADDR               DhcpIpAddress;          /* 0x6C */
+    U8                                  HostName
+                               [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_0,
+  Mpi2EthernetPage0_t, MPI2_POINTER pMpi2EthernetPage0_t;
+
+#define MPI2_ETHERNETPAGE0_PAGEVERSION   (0x00)
+
+/* values for Ethernet Page 0 Status field */
+#define MPI2_ETHPG0_STATUS_IPV6_CAPABLE             (0x80000000)
+#define MPI2_ETHPG0_STATUS_IPV4_CAPABLE             (0x40000000)
+#define MPI2_ETHPG0_STATUS_CONSOLE_CONNECTED        (0x20000000)
+#define MPI2_ETHPG0_STATUS_DEFAULT_IF               (0x00000100)
+#define MPI2_ETHPG0_STATUS_FW_DWNLD_ENABLED         (0x00000080)
+#define MPI2_ETHPG0_STATUS_TELNET_ENABLED           (0x00000040)
+#define MPI2_ETHPG0_STATUS_SSH2_ENABLED             (0x00000020)
+#define MPI2_ETHPG0_STATUS_DHCP_CLIENT_ENABLED      (0x00000010)
+#define MPI2_ETHPG0_STATUS_IPV6_ENABLED             (0x00000008)
+#define MPI2_ETHPG0_STATUS_IPV4_ENABLED             (0x00000004)
+#define MPI2_ETHPG0_STATUS_IPV6_ADDRESSES           (0x00000002)
+#define MPI2_ETHPG0_STATUS_ETH_IF_ENABLED           (0x00000001)
+
+/* values for Ethernet Page 0 MediaState field */
+#define MPI2_ETHPG0_MS_DUPLEX_MASK                  (0x80)
+#define MPI2_ETHPG0_MS_HALF_DUPLEX                  (0x00)
+#define MPI2_ETHPG0_MS_FULL_DUPLEX                  (0x80)
+
+#define MPI2_ETHPG0_MS_CONNECT_SPEED_MASK           (0x07)
+#define MPI2_ETHPG0_MS_NOT_CONNECTED                (0x00)
+#define MPI2_ETHPG0_MS_10MBIT                       (0x01)
+#define MPI2_ETHPG0_MS_100MBIT                      (0x02)
+#define MPI2_ETHPG0_MS_1GBIT                        (0x03)
+
+
+/* Ethernet Page 1 */
+
+typedef struct _MPI2_CONFIG_PAGE_ETHERNET_1 {
+    MPI2_CONFIG_EXTENDED_PAGE_HEADER    Header;                 /* 0x00 */
+    U32                                 Reserved0;              /* 0x08 */
+    U32                                 Flags;                  /* 0x0C */
+    U8                                  MediaState;             /* 0x10 */
+    U8                                  Reserved1;              /* 0x11 */
+    U16                                 Reserved2;              /* 0x12 */
+    U8                                  MacAddress[6];          /* 0x14 */
+    U8                                  Reserved3;              /* 0x1A */
+    U8                                  Reserved4;              /* 0x1B */
+    MPI2_ETHERNET_IP_ADDR               StaticIpAddress;        /* 0x1C */
+    MPI2_ETHERNET_IP_ADDR               StaticSubnetMask;       /* 0x2C */
+    MPI2_ETHERNET_IP_ADDR               StaticGatewayIpAddress; /* 0x3C */
+    MPI2_ETHERNET_IP_ADDR               StaticDNS1IpAddress;    /* 0x4C */
+    MPI2_ETHERNET_IP_ADDR               StaticDNS2IpAddress;    /* 0x5C */
+    U32                                 Reserved5;              /* 0x6C */
+    U32                                 Reserved6;              /* 0x70 */
+    U32                                 Reserved7;              /* 0x74 */
+    U32                                 Reserved8;              /* 0x78 */
+    U8                                  HostName
+                               [MPI2_ETHERNET_HOST_NAME_LENGTH];/* 0x7C */
+} MPI2_CONFIG_PAGE_ETHERNET_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_ETHERNET_1,
+  Mpi2EthernetPage1_t, MPI2_POINTER pMpi2EthernetPage1_t;
+
+#define MPI2_ETHERNETPAGE1_PAGEVERSION   (0x00)
+
+/* values for Ethernet Page 1 Flags field */
+#define MPI2_ETHPG1_FLAG_SET_DEFAULT_IF             (0x00000100)
+#define MPI2_ETHPG1_FLAG_ENABLE_FW_DOWNLOAD         (0x00000080)
+#define MPI2_ETHPG1_FLAG_ENABLE_TELNET              (0x00000040)
+#define MPI2_ETHPG1_FLAG_ENABLE_SSH2                (0x00000020)
+#define MPI2_ETHPG1_FLAG_ENABLE_DHCP_CLIENT         (0x00000010)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV6                (0x00000008)
+#define MPI2_ETHPG1_FLAG_ENABLE_IPV4                (0x00000004)
+#define MPI2_ETHPG1_FLAG_USE_IPV6_ADDRESSES         (0x00000002)
+#define MPI2_ETHPG1_FLAG_ENABLE_ETH_IF              (0x00000001)
+
+/* values for Ethernet Page 1 MediaState field */
+#define MPI2_ETHPG1_MS_DUPLEX_MASK                  (0x80)
+#define MPI2_ETHPG1_MS_HALF_DUPLEX                  (0x00)
+#define MPI2_ETHPG1_MS_FULL_DUPLEX                  (0x80)
+
+#define MPI2_ETHPG1_MS_DATA_RATE_MASK               (0x07)
+#define MPI2_ETHPG1_MS_DATA_RATE_AUTO               (0x00)
+#define MPI2_ETHPG1_MS_DATA_RATE_10MBIT             (0x01)
+#define MPI2_ETHPG1_MS_DATA_RATE_100MBIT            (0x02)
+#define MPI2_ETHPG1_MS_DATA_RATE_1GBIT              (0x03)
+
+
 #endif
 
index c294128..ea51ce8 100644 (file)
@@ -6,7 +6,7 @@
  *          Title:  MPI IOC, Port, Event, FW Download, and FW Upload messages
  *  Creation Date:  October 11, 2006
  *
- *  mpi2_ioc.h Version:  02.00.11
+ *  mpi2_ioc.h Version:  02.00.12
  *
  *  Version History
  *  ---------------
@@ -84,6 +84,9 @@
  *                      Added two new reason codes for SAS Device Status Change
  *                      Event.
  *                      Added new event: SAS PHY Counter.
+ *  07-30-09  02.00.12  Added GPIO Interrupt event define and structure.
+ *                      Added MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define.
+ *                      Added new product id family for 2208.
  *  --------------------------------------------------------------------------
  */
 
@@ -274,6 +277,7 @@ typedef struct _MPI2_IOC_FACTS_REPLY
 #define MPI2_IOCFACTS_CAPABILITY_MULTICAST              (0x00000100)
 #define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET   (0x00000080)
 #define MPI2_IOCFACTS_CAPABILITY_EEDP                   (0x00000040)
+#define MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER        (0x00000020)
 #define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER        (0x00000010)
 #define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER      (0x00000008)
 #define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004)
@@ -448,6 +452,7 @@ typedef struct _MPI2_EVENT_NOTIFICATION_REPLY
 #define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST     (0x0020)
 #define MPI2_EVENT_LOG_ENTRY_ADDED                  (0x0021)
 #define MPI2_EVENT_SAS_PHY_COUNTER                  (0x0022)
+#define MPI2_EVENT_GPIO_INTERRUPT                   (0x0023)
 
 
 /* Log Entry Added Event data */
@@ -469,6 +474,16 @@ typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED
   MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED,
   Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t;
 
+/* GPIO Interrupt Event data */
+
+typedef struct _MPI2_EVENT_DATA_GPIO_INTERRUPT {
+    U8          GPIONum;                            /* 0x00 */
+    U8          Reserved1;                          /* 0x01 */
+    U16         Reserved2;                          /* 0x02 */
+} MPI2_EVENT_DATA_GPIO_INTERRUPT,
+  MPI2_POINTER PTR_MPI2_EVENT_DATA_GPIO_INTERRUPT,
+  Mpi2EventDataGpioInterrupt_t, MPI2_POINTER pMpi2EventDataGpioInterrupt_t;
+
 /* Hard Reset Received Event data */
 
 typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED
@@ -1117,6 +1132,7 @@ typedef struct _MPI2_FW_IMAGE_HEADER
 #define MPI2_FW_HEADER_PID_FAMILY_MASK          (0x00FF)
 /* SAS */
 #define MPI2_FW_HEADER_PID_FAMILY_2108_SAS      (0x0010)
+#define MPI2_FW_HEADER_PID_FAMILY_2208_SAS      (0x0011)
 
 /* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */
 
index 7134816..5160c33 100644 (file)
@@ -6,7 +6,7 @@
  *          Title:  MPI Integrated RAID messages and structures
  *  Creation Date:  April 26, 2007
  *
- *    mpi2_raid.h Version:  02.00.03
+ *    mpi2_raid.h Version:  02.00.04
  *
  *  Version History
  *  ---------------
@@ -20,6 +20,8 @@
  *  05-21-08  02.00.03  Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that
  *                      the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT
  *                      can be sized by the build environment.
+ *  07-30-09  02.00.04  Added proper define for the Use Default Settings bit of
+ *                      VolumeCreationFlags and marked the old one as obsolete.
  *  --------------------------------------------------------------------------
  */
 
@@ -217,10 +219,14 @@ typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT
 /* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */
 
 /* defines for the VolumeCreationFlags field */
+#define MPI2_RAID_VOL_CREATION_DEFAULT_SETTINGS     (0x80000000)
+#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT      (0x00000004)
+#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT       (0x00000002)
+#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA         (0x00000001)
+/* The following is an obsolete define.
+ * It must be shifted left 24 bits in order to set the proper bit.
+ */
 #define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80)
-#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT      (0x04)
-#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT       (0x02)
-#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA         (0x01)
 
 
 /* RAID Online Capacity Expansion Structure */
index 007e950..73fcdbf 100644 (file)
@@ -6,7 +6,7 @@
  *          Title:  MPI diagnostic tool structures and definitions
  *  Creation Date:  March 26, 2007
  *
- *    mpi2_tool.h Version:  02.00.03
+ *    mpi2_tool.h Version:  02.00.04
  *
  *  Version History
  *  ---------------
  *                      structures and defines.
  *  02-29-08  02.00.02  Modified various names to make them 32-character unique.
  *  05-06-09  02.00.03  Added ISTWI Read Write Tool and Diagnostic CLI Tool.
+ *  07-30-09  02.00.04  Added ExtendedType field to DiagnosticBufferPost request
+ *                      and reply messages.
+ *                      Added MPI2_DIAG_BUF_TYPE_EXTENDED.
+ *                      Incremented MPI2_DIAG_BUF_TYPE_COUNT.
  *  --------------------------------------------------------------------------
  */
 
@@ -282,7 +286,7 @@ typedef struct _MPI2_TOOLBOX_DIAGNOSTIC_CLI_REPLY {
 
 typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
 {
-    U8                      Reserved1;                  /* 0x00 */
+    U8                      ExtendedType;               /* 0x00 */
     U8                      BufferType;                 /* 0x01 */
     U8                      ChainOffset;                /* 0x02 */
     U8                      Function;                   /* 0x03 */
@@ -301,11 +305,15 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
 } MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST,
   Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t;
 
+/* values for the ExtendedType field */
+#define MPI2_DIAG_EXTENDED_TYPE_UTILIZATION         (0x02)
+
 /* values for the BufferType field */
 #define MPI2_DIAG_BUF_TYPE_TRACE                    (0x00)
 #define MPI2_DIAG_BUF_TYPE_SNAPSHOT                 (0x01)
+#define MPI2_DIAG_BUF_TYPE_EXTENDED                 (0x02)
 /* count of the number of buffer types */
-#define MPI2_DIAG_BUF_TYPE_COUNT                    (0x02)
+#define MPI2_DIAG_BUF_TYPE_COUNT                    (0x03)
 
 
 /****************************************************************************
@@ -314,7 +322,7 @@ typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST
 
 typedef struct _MPI2_DIAG_BUFFER_POST_REPLY
 {
-    U8                      Reserved1;                  /* 0x00 */
+    U8                      ExtendedType;               /* 0x00 */
     U8                      BufferType;                 /* 0x01 */
     U8                      MsgLength;                  /* 0x02 */
     U8                      Function;                   /* 0x03 */
index 670241e..6422e25 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/sort.h>
 #include <linux/io.h>
+#include <linux/time.h>
 
 #include "mpt2sas_base.h"
 
@@ -77,6 +78,44 @@ static int msix_disable = -1;
 module_param(msix_disable, int, 0);
 MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
 
+/* diag_buffer_enable is bitwise
+ * bit 0 set = TRACE
+ * bit 1 set = SNAPSHOT
+ * bit 2 set = EXTENDED
+ *
+ * Either bit can be set, or both
+ */
+static int diag_buffer_enable;
+module_param(diag_buffer_enable, int, 0);
+MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers "
+    "(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)");
+
+int mpt2sas_fwfault_debug;
+MODULE_PARM_DESC(mpt2sas_fwfault_debug, " enable detection of firmware fault "
+    "and halt firmware - (default=0)");
+
+/**
+ * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
+ *
+ */
+static int
+_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
+{
+       int ret = param_set_int(val, kp);
+       struct MPT2SAS_ADAPTER *ioc;
+
+       if (ret)
+               return ret;
+
+       printk(KERN_INFO "setting logging_level(0x%08x)\n",
+                               mpt2sas_fwfault_debug);
+       list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
+               ioc->fwfault_debug = mpt2sas_fwfault_debug;
+       return 0;
+}
+module_param_call(mpt2sas_fwfault_debug, _scsih_set_fwfault_debug,
+    param_get_int, &mpt2sas_fwfault_debug, 0644);
+
 /**
  * _base_fault_reset_work - workq handling ioc fault conditions
  * @work: input argument, used to derive ioc
@@ -121,7 +160,7 @@ _base_fault_reset_work(struct work_struct *work)
 
 /**
  * mpt2sas_base_start_watchdog - start the fault_reset_work_q
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * Context: sleep.
  *
  * Return nothing.
@@ -155,7 +194,7 @@ mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc)
 
 /**
  * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * Context: sleep.
  *
  * Return nothing.
@@ -177,10 +216,55 @@ mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc)
        }
 }
 
+/**
+ * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
+ * @ioc: per adapter object
+ * @fault_code: fault code
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
+{
+       printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
+           ioc->name, fault_code);
+}
+
+/**
+ * mpt2sas_halt_firmware - halt's mpt controller firmware
+ * @ioc: per adapter object
+ *
+ * For debugging timeout related issues.  Writing 0xCOFFEE00
+ * to the doorbell register will halt controller firmware. With
+ * the purpose to stop both driver and firmware, the enduser can
+ * obtain a ring buffer from controller UART.
+ */
+void
+mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc)
+{
+       u32 doorbell;
+
+       if (!ioc->fwfault_debug)
+               return;
+
+       dump_stack();
+
+       doorbell = readl(&ioc->chip->Doorbell);
+       if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT)
+               mpt2sas_base_fault_info(ioc , doorbell);
+       else {
+               writel(0xC0FFEE00, &ioc->chip->Doorbell);
+               printk(MPT2SAS_ERR_FMT "Firmware is halted due to command "
+                   "timeout\n", ioc->name);
+       }
+
+       panic("panic in %s\n", __func__);
+}
+
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
 /**
  * _base_sas_ioc_info - verbose translation of the ioc status
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * @mpi_reply: reply mf payload returned from firmware
  * @request_hdr: request mf
  *
@@ -394,7 +478,7 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply,
 
 /**
  * _base_display_event_data - verbose translation of firmware asyn events
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * @mpi_reply: reply mf payload returned from firmware
  *
  * Return nothing.
@@ -474,7 +558,7 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc,
 
 /**
  * _base_sas_log_info - verbose translation of firmware log info
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * @log_info: log info
  *
  * Return nothing.
@@ -525,23 +609,9 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info)
             sas_loginfo.dw.subcode);
 }
 
-/**
- * mpt2sas_base_fault_info - verbose translation of firmware FAULT code
- * @ioc: pointer to scsi command object
- * @fault_code: fault code
- *
- * Return nothing.
- */
-void
-mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code)
-{
-       printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n",
-           ioc->name, fault_code);
-}
-
 /**
  * _base_display_reply_info -
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * @smid: system request message index
  * @msix_index: MSIX table index supplied by the OS
  * @reply: reply message frame(lower 32bit addr)
@@ -570,7 +640,7 @@ _base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
 
 /**
  * mpt2sas_base_done - base internal command completion routine
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * @smid: system request message index
  * @msix_index: MSIX table index supplied by the OS
  * @reply: reply message frame(lower 32bit addr)
@@ -603,7 +673,7 @@ mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
 
 /**
  * _base_async_event - main callback handler for firmware asyn events
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  * @msix_index: MSIX table index supplied by the OS
  * @reply: reply message frame(lower 32bit addr)
  *
@@ -684,7 +754,7 @@ _base_get_cb_idx(struct MPT2SAS_ADAPTER *ioc, u16 smid)
 
 /**
  * _base_mask_interrupts - disable interrupts
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  *
  * Disabling ResetIRQ, Reply and Doorbell Interrupts
  *
@@ -704,7 +774,7 @@ _base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc)
 
 /**
  * _base_unmask_interrupts - enable interrupts
- * @ioc: pointer to scsi command object
+ * @ioc: per adapter object
  *
  * Enabling only Reply Interrupts
  *
@@ -1258,12 +1328,13 @@ mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid)
  * @ioc: per adapter object
  * @smid: system request message index
  *
- * Returns phys pointer to sense buffer.
+ * Returns phys pointer to the low 32bit address of the sense buffer.
  */
-dma_addr_t
+__le32
 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid)
 {
-       return ioc->sense_dma + ((smid - 1) * SCSI_SENSE_BUFFERSIZE);
+       return cpu_to_le32(ioc->sense_dma +
+                       ((smid - 1) * SCSI_SENSE_BUFFERSIZE));
 }
 
 /**
@@ -1696,6 +1767,12 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
                i++;
        }
 
+       if (ioc->facts.IOCCapabilities &
+           MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
+               printk(KERN_INFO "%sDiag Extended Buffer", i ? "," : "");
+               i++;
+       }
+
        if (ioc->facts.IOCCapabilities &
            MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) {
                printk("%sTask Set Full", i ? "," : "");
@@ -2871,6 +2948,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
        Mpi2IOCInitRequest_t mpi_request;
        Mpi2IOCInitReply_t mpi_reply;
        int r;
+       struct timeval current_time;
+       u16 ioc_status;
 
        dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
            __func__));
@@ -2921,6 +3000,13 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
            cpu_to_le32(ioc->reply_post_free_dma);
 #endif
 
+       /* This time stamp specifies number of milliseconds
+        * since epoch ~ midnight January 1, 1970.
+        */
+       do_gettimeofday(&current_time);
+       mpi_request.TimeStamp = (current_time.tv_sec * 1000) +
+           (current_time.tv_usec >> 3);
+
        if (ioc->logging_level & MPT_DEBUG_INIT) {
                u32 *mfp;
                int i;
@@ -2943,7 +3029,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
                return r;
        }
 
-       if (mpi_reply.IOCStatus != MPI2_IOCSTATUS_SUCCESS ||
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS ||
            mpi_reply.IOCLogInfo) {
                printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__);
                r = -EIO;
@@ -3461,11 +3548,11 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
                return r;
 
        pci_set_drvdata(ioc->pdev, ioc->shost);
-       r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
+       r = _base_get_ioc_facts(ioc, CAN_SLEEP);
        if (r)
                goto out_free_resources;
 
-       r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+       r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET);
        if (r)
                goto out_free_resources;
 
@@ -3531,6 +3618,8 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
                goto out_free_resources;
 
        mpt2sas_base_start_watchdog(ioc);
+       if (diag_buffer_enable != 0)
+               mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable);
        return 0;
 
  out_free_resources:
@@ -3684,6 +3773,9 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
        dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,
            __func__));
 
+       if (mpt2sas_fwfault_debug)
+               mpt2sas_halt_firmware(ioc);
+
        spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
        if (ioc->shost_recovery) {
                spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
index 0cf6bc2..bb4f146 100644 (file)
@@ -69,8 +69,8 @@
 #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         "02.100.03.00"
-#define MPT2SAS_MAJOR_VERSION          02
+#define MPT2SAS_DRIVER_VERSION         "03.100.03.00"
+#define MPT2SAS_MAJOR_VERSION          03
 #define MPT2SAS_MINOR_VERSION          100
 #define MPT2SAS_BUILD_VERSION          03
 #define MPT2SAS_RELEASE_VERSION                00
@@ -278,7 +278,7 @@ struct _internal_cmd {
  * @sas_address: device sas address
  * @device_name: retrieved from the SAS IDENTIFY frame.
  * @handle: device handle
- * @parent_handle: handle to parent device
+ * @sas_address_parent: sas address of parent expander or sas host
  * @enclosure_handle: enclosure handle
  * @enclosure_logical_id: enclosure logical identifier
  * @volume_handle: volume handle (valid when hidden raid member)
@@ -296,7 +296,7 @@ struct _sas_device {
        u64     sas_address;
        u64     device_name;
        u16     handle;
-       u16     parent_handle;
+       u64     sas_address_parent;
        u16     enclosure_handle;
        u64     enclosure_logical_id;
        u16     volume_handle;
@@ -352,8 +352,6 @@ struct _boot_device {
 /**
  * struct _sas_port - wide/narrow sas port information
  * @port_list: list of ports belonging to expander
- * @handle: device handle for this port
- * @sas_address: sas address of this port
  * @num_phys: number of phys belonging to this port
  * @remote_identify: attached device identification
  * @rphy: sas transport rphy object
@@ -362,8 +360,6 @@ struct _boot_device {
  */
 struct _sas_port {
        struct list_head port_list;
-       u16     handle;
-       u64     sas_address;
        u8      num_phys;
        struct sas_identify remote_identify;
        struct sas_rphy *rphy;
@@ -398,7 +394,7 @@ struct _sas_phy {
  * @num_phys: number phys belonging to this sas_host/expander
  * @sas_address: sas address of this sas_host/expander
  * @handle: handle for this sas_host/expander
- * @parent_handle: parent handle
+ * @sas_address_parent: sas address of parent expander or sas host
  * @enclosure_handle: handle for this a member of an enclosure
  * @device_info: bitwise defining capabilities of this sas_host/expander
  * @responding: used in _scsih_expander_device_mark_responding
@@ -411,7 +407,7 @@ struct _sas_node {
        u8      num_phys;
        u64     sas_address;
        u16     handle;
-       u16     parent_handle;
+       u64     sas_address_parent;
        u16     enclosure_handle;
        u64     enclosure_logical_id;
        u8      responding;
@@ -470,6 +466,7 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
  * @chip_phys: physical addrss prior to mapping
  * @pio_chip: I/O mapped register space
  * @logging_level: see mpt2sas_debug.h
+ * @fwfault_debug: debuging FW timeouts
  * @ir_firmware: IR firmware present
  * @bars: bitmask of BAR's that must be configured
  * @mask_interrupts: ignore interrupt
@@ -495,12 +492,14 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
  * @msix_table_backup: backup msix table
  * @scsi_io_cb_idx: shost generated commands
  * @tm_cb_idx: task management commands
+ * @scsih_cb_idx: scsih internal commands
  * @transport_cb_idx: transport internal commands
  * @ctl_cb_idx: clt internal commands
  * @base_cb_idx: base internal commands
  * @config_cb_idx: base internal commands
  * @base_cmds:
  * @transport_cmds:
+ * @scsih_cmds:
  * @tm_cmds:
  * @ctl_cmds:
  * @config_cmds:
@@ -591,6 +590,7 @@ struct MPT2SAS_ADAPTER {
        unsigned long   chip_phys;
        unsigned long   pio_chip;
        int             logging_level;
+       int             fwfault_debug;
        u8              ir_firmware;
        int             bars;
        u8              mask_interrupts;
@@ -626,6 +626,7 @@ struct MPT2SAS_ADAPTER {
        u8              scsi_io_cb_idx;
        u8              tm_cb_idx;
        u8              transport_cb_idx;
+       u8              scsih_cb_idx;
        u8              ctl_cb_idx;
        u8              base_cb_idx;
        u8              config_cb_idx;
@@ -633,6 +634,7 @@ struct MPT2SAS_ADAPTER {
        u8              tm_sas_control_cb_idx;
        struct _internal_cmd base_cmds;
        struct _internal_cmd transport_cmds;
+       struct _internal_cmd scsih_cmds;
        struct _internal_cmd tm_cmds;
        struct _internal_cmd ctl_cmds;
        struct _internal_cmd config_cmds;
@@ -773,7 +775,7 @@ int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
 void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid);
 void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid);
 void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr);
-dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
+__le32 mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc,
     u16 smid);
 
 /* hi-priority queue */
@@ -807,6 +809,8 @@ int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc,
     Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request);
 void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type);
 
+void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc);
+
 /* scsih shared API */
 u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
     u32 reply);
@@ -886,19 +890,22 @@ u8 mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
 void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc,
     Mpi2EventNotificationReply_t *mpi_reply);
 
+void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc,
+       u8 bits_to_regsiter);
+
 /* transport shared API */
 u8 mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
     u32 reply);
 struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc,
-    u16 handle, u16 parent_handle);
+     u16 handle, u64 sas_address);
 void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
-    u16 parent_handle);
+     u64 sas_address_parent);
 int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
     *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
 int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
     *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev);
-void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle,
-   u16 attached_handle, u8 phy_number, u8 link_rate);
+void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
+     u64 sas_address, u16 handle, u8 phy_number, u8 link_rate);
 extern struct sas_function_template mpt2sas_transport_functions;
 extern struct scsi_transport_template *mpt2sas_transport_template;
 extern int scsi_internal_device_block(struct scsi_device *sdev);
index 57d7246..84a124f 100644 (file)
@@ -740,7 +740,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
                Mpi2SCSIIORequest_t *scsiio_request =
                    (Mpi2SCSIIORequest_t *)mpi_request;
                scsiio_request->SenseBufferLowAddress =
-                   (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+                   mpt2sas_base_get_sense_buffer_dma(ioc, smid);
                priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid);
                memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE);
                mpt2sas_base_put_smid_scsi_io(ioc, smid,
@@ -848,8 +848,9 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
                printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: "
                    "IOCStatus(0x%04x), IOCLogInfo(0x%08x), "
                    "TerminationCount(0x%08x)\n", ioc->name,
-                   tm_reply->IOCStatus, tm_reply->IOCLogInfo,
-                   tm_reply->TerminationCount);
+                   le16_to_cpu(tm_reply->IOCStatus),
+                   le32_to_cpu(tm_reply->IOCLogInfo),
+                   le32_to_cpu(tm_reply->TerminationCount));
        }
 #endif
        /* copy out xdata to user */
@@ -896,6 +897,7 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc,
                        printk(MPT2SAS_INFO_FMT "issue target reset: handle "
                            "= (0x%04x)\n", ioc->name,
                            mpi_request->FunctionDependent1);
+                       mpt2sas_halt_firmware(ioc);
                        mutex_lock(&ioc->tm_cmds.mutex);
                        mpt2sas_scsih_issue_tm(ioc,
                            mpi_request->FunctionDependent1, 0,
@@ -1229,7 +1231,7 @@ _ctl_btdh_mapping(void __user *arg)
 /**
  * _ctl_diag_capability - return diag buffer capability
  * @ioc: per adapter object
- * @buffer_type: specifies either TRACE or SNAPSHOT
+ * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED
  *
  * returns 1 when diag buffer support is enabled in firmware
  */
@@ -1249,24 +1251,25 @@ _ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type)
                    MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER)
                        rc = 1;
                break;
+       case MPI2_DIAG_BUF_TYPE_EXTENDED:
+               if (ioc->facts.IOCCapabilities &
+                   MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER)
+                       rc = 1;
        }
 
        return rc;
 }
 
 /**
- * _ctl_diag_register - application register with driver
- * @arg - user space buffer containing ioctl content
- * @state - NON_BLOCKING or BLOCKING
+ * _ctl_diag_register_2 - wrapper for registering diag buffer support
+ * @ioc: per adapter object
+ * @diag_register: the diag_register struct passed in from user space
  *
- * This will allow the driver to setup any required buffers that will be
- * needed by firmware to communicate with the driver.
  */
 static long
-_ctl_diag_register(void __user *arg, enum block_state state)
+_ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc,
+    struct mpt2_diag_register *diag_register)
 {
-       struct mpt2_diag_register karg;
-       struct MPT2SAS_ADAPTER *ioc;
        int rc, i;
        void *request_data = NULL;
        dma_addr_t request_data_dma;
@@ -1279,18 +1282,17 @@ _ctl_diag_register(void __user *arg, enum block_state state)
        u16 ioc_status;
        u8 issue_reset = 0;
 
-       if (copy_from_user(&karg, arg, sizeof(karg))) {
-               printk(KERN_ERR "failure at %s:%d/%s()!\n",
-                   __FILE__, __LINE__, __func__);
-               return -EFAULT;
-       }
-       if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
-               return -ENODEV;
-
        dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
            __func__));
 
-       buffer_type = karg.buffer_type;
+       if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
+               printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
+                   ioc->name, __func__);
+               rc = -EAGAIN;
+               goto out;
+       }
+
+       buffer_type = diag_register->buffer_type;
        if (!_ctl_diag_capability(ioc, buffer_type)) {
                printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for "
                    "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type);
@@ -1305,24 +1307,12 @@ _ctl_diag_register(void __user *arg, enum block_state state)
                return -EINVAL;
        }
 
-       if (karg.requested_buffer_size % 4)  {
+       if (diag_register->requested_buffer_size % 4)  {
                printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size "
                    "is not 4 byte aligned\n", ioc->name, __func__);
                return -EINVAL;
        }
 
-       if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
-               return -EAGAIN;
-       else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
-               return -ERESTARTSYS;
-
-       if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) {
-               printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n",
-                   ioc->name, __func__);
-               rc = -EAGAIN;
-               goto out;
-       }
-
        smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx);
        if (!smid) {
                printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
@@ -1338,12 +1328,12 @@ _ctl_diag_register(void __user *arg, enum block_state state)
        ioc->ctl_cmds.smid = smid;
 
        request_data = ioc->diag_buffer[buffer_type];
-       request_data_sz = karg.requested_buffer_size;
-       ioc->unique_id[buffer_type] = karg.unique_id;
+       request_data_sz = diag_register->requested_buffer_size;
+       ioc->unique_id[buffer_type] = diag_register->unique_id;
        ioc->diag_buffer_status[buffer_type] = 0;
-       memcpy(ioc->product_specific[buffer_type], karg.product_specific,
-           MPT2_PRODUCT_SPECIFIC_DWORDS);
-       ioc->diagnostic_flags[buffer_type] = karg.diagnostic_flags;
+       memcpy(ioc->product_specific[buffer_type],
+           diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS);
+       ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags;
 
        if (request_data) {
                request_data_dma = ioc->diag_buffer_dma[buffer_type];
@@ -1373,8 +1363,8 @@ _ctl_diag_register(void __user *arg, enum block_state state)
        }
 
        mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST;
-       mpi_request->BufferType = karg.buffer_type;
-       mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags);
+       mpi_request->BufferType = diag_register->buffer_type;
+       mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags);
        mpi_request->BufferAddress = cpu_to_le64(request_data_dma);
        mpi_request->BufferLength = cpu_to_le32(request_data_sz);
        mpi_request->VF_ID = 0; /* TODO */
@@ -1422,7 +1412,7 @@ _ctl_diag_register(void __user *arg, enum block_state state)
        } else {
                printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
                    "log_info(0x%08x)\n", ioc->name, __func__,
-                   ioc_status, mpi_reply->IOCLogInfo);
+                   ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
                rc = -EFAULT;
        }
 
@@ -1438,6 +1428,83 @@ _ctl_diag_register(void __user *arg, enum block_state state)
                    request_data, request_data_dma);
 
        ioc->ctl_cmds.status = MPT2_CMD_NOT_USED;
+       return rc;
+}
+
+/**
+ * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time
+ * @ioc: per adapter object
+ * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1
+ *
+ * This is called when command line option diag_buffer_enable is enabled
+ * at driver load time.
+ */
+void
+mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register)
+{
+       struct mpt2_diag_register diag_register;
+
+       memset(&diag_register, 0, sizeof(struct mpt2_diag_register));
+
+       if (bits_to_register & 1) {
+               printk(MPT2SAS_INFO_FMT "registering trace buffer support\n",
+                   ioc->name);
+               diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE;
+               /* register for 1MB buffers  */
+               diag_register.requested_buffer_size = (1024 * 1024);
+               diag_register.unique_id = 0x7075900;
+               _ctl_diag_register_2(ioc,  &diag_register);
+       }
+
+       if (bits_to_register & 2) {
+               printk(MPT2SAS_INFO_FMT "registering snapshot buffer support\n",
+                   ioc->name);
+               diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT;
+               /* register for 2MB buffers  */
+               diag_register.requested_buffer_size = 2 * (1024 * 1024);
+               diag_register.unique_id = 0x7075901;
+               _ctl_diag_register_2(ioc,  &diag_register);
+       }
+
+       if (bits_to_register & 4) {
+               printk(MPT2SAS_INFO_FMT "registering extended buffer support\n",
+                   ioc->name);
+               diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED;
+               /* register for 2MB buffers  */
+               diag_register.requested_buffer_size = 2 * (1024 * 1024);
+               diag_register.unique_id = 0x7075901;
+               _ctl_diag_register_2(ioc,  &diag_register);
+       }
+}
+
+/**
+ * _ctl_diag_register - application register with driver
+ * @arg - user space buffer containing ioctl content
+ * @state - NON_BLOCKING or BLOCKING
+ *
+ * This will allow the driver to setup any required buffers that will be
+ * needed by firmware to communicate with the driver.
+ */
+static long
+_ctl_diag_register(void __user *arg, enum block_state state)
+{
+       struct mpt2_diag_register karg;
+       struct MPT2SAS_ADAPTER *ioc;
+       long rc;
+
+       if (copy_from_user(&karg, arg, sizeof(karg))) {
+               printk(KERN_ERR "failure at %s:%d/%s()!\n",
+                   __FILE__, __LINE__, __func__);
+               return -EFAULT;
+       }
+       if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc)
+               return -ENODEV;
+
+       if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex))
+               return -EAGAIN;
+       else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
+               return -ERESTARTSYS;
+       rc = _ctl_diag_register_2(ioc, &karg);
        mutex_unlock(&ioc->ctl_cmds.mutex);
        return rc;
 }
@@ -1600,7 +1667,7 @@ _ctl_diag_query(void __user *arg)
 /**
  * _ctl_send_release - Diag Release Message
  * @ioc: per adapter object
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
  * @issue_reset - specifies whether host reset is required.
  *
  */
@@ -1690,7 +1757,7 @@ _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset)
        } else {
                printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
                    "log_info(0x%08x)\n", ioc->name, __func__,
-                   ioc_status, mpi_reply->IOCLogInfo);
+                   ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
                rc = -EFAULT;
        }
 
@@ -1951,7 +2018,7 @@ _ctl_diag_read_buffer(void __user *arg, enum block_state state)
        } else {
                printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) "
                    "log_info(0x%08x)\n", ioc->name, __func__,
-                   ioc_status, mpi_reply->IOCLogInfo);
+                   ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo));
                rc = -EFAULT;
        }
 
@@ -2474,6 +2541,43 @@ _ctl_logging_level_store(struct device *cdev, struct device_attribute *attr,
 static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
     _ctl_logging_level_show, _ctl_logging_level_store);
 
+/* device attributes */
+/*
+ * _ctl_fwfault_debug_show - show/store fwfault_debug
+ * @cdev - pointer to embedded class device
+ * @buf - the buffer returned
+ *
+ * mpt2sas_fwfault_debug is command line option
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t
+_ctl_fwfault_debug_show(struct device *cdev,
+    struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug);
+}
+static ssize_t
+_ctl_fwfault_debug_store(struct device *cdev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+       int val = 0;
+
+       if (sscanf(buf, "%d", &val) != 1)
+               return -EINVAL;
+
+       ioc->fwfault_debug = val;
+       printk(MPT2SAS_INFO_FMT "fwfault_debug=%d\n", ioc->name,
+           ioc->fwfault_debug);
+       return strlen(buf);
+}
+static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR,
+    _ctl_fwfault_debug_show, _ctl_fwfault_debug_store);
+
 struct device_attribute *mpt2sas_host_attrs[] = {
        &dev_attr_version_fw,
        &dev_attr_version_bios,
@@ -2487,13 +2591,12 @@ struct device_attribute *mpt2sas_host_attrs[] = {
        &dev_attr_io_delay,
        &dev_attr_device_delay,
        &dev_attr_logging_level,
+       &dev_attr_fwfault_debug,
        &dev_attr_fw_queue_depth,
        &dev_attr_host_sas_address,
        NULL,
 };
 
-/* device attributes */
-
 /**
  * _ctl_device_sas_address_show - sas address
  * @cdev - pointer to embedded class device
index 211f296..8a5eeb1 100644 (file)
@@ -313,7 +313,7 @@ struct mpt2_ioctl_btdh_mapping {
  * struct mpt2_diag_register - application register with driver
  * @hdr - generic header
  * @reserved -
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
  * @application_flags - misc flags
  * @diagnostic_flags - specifies flags affecting command processing
  * @product_specific - product specific information
@@ -352,7 +352,7 @@ struct mpt2_diag_unregister {
  * struct mpt2_diag_query - query relevant info associated with diag buffers
  * @hdr - generic header
  * @reserved -
- * @buffer_type - specifies either TRACE or SNAPSHOT
+ * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED
  * @application_flags - misc flags
  * @diagnostic_flags - specifies flags affecting command processing
  * @product_specific - product specific information
index 86ab32d..efabea1 100644 (file)
@@ -76,6 +76,7 @@ static u8 tm_cb_idx = -1;
 static u8 ctl_cb_idx = -1;
 static u8 base_cb_idx = -1;
 static u8 transport_cb_idx = -1;
+static u8 scsih_cb_idx = -1;
 static u8 config_cb_idx = -1;
 static int mpt_ids;
 
@@ -196,10 +197,28 @@ static struct pci_device_id scsih_pci_table[] = {
                PCI_ANY_ID, PCI_ANY_ID },
        { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
                PCI_ANY_ID, PCI_ANY_ID },
+       /* Meteor ~ 2116 */
        { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
                PCI_ANY_ID, PCI_ANY_ID },
        { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
                PCI_ANY_ID, PCI_ANY_ID },
+       /* Thunderbolt ~ 2208 */
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_7,
+               PCI_ANY_ID, PCI_ANY_ID },
+       { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_8,
+               PCI_ANY_ID, PCI_ANY_ID },
        {0}     /* Terminating entry */
 };
 MODULE_DEVICE_TABLE(pci, scsih_pci_table);
@@ -316,6 +335,47 @@ _scsih_is_boot_device(u64 sas_address, u64 device_name,
        return rc;
 }
 
+/**
+ * _scsih_get_sas_address - set the sas_address for given device handle
+ * @handle: device handle
+ * @sas_address: sas address
+ *
+ * Returns 0 success, non-zero when failure
+ */
+static int
+_scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle,
+    u64 *sas_address)
+{
+       Mpi2SasDevicePage0_t sas_device_pg0;
+       Mpi2ConfigReply_t mpi_reply;
+       u32 ioc_status;
+
+       if (handle <= ioc->sas_hba.num_phys) {
+               *sas_address = ioc->sas_hba.sas_address;
+               return 0;
+       } else
+               *sas_address = 0;
+
+       if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
+           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
+               return -ENXIO;
+       }
+
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
+           MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
+               printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
+                   "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
+                    __FILE__, __LINE__, __func__);
+               return -EIO;
+       }
+
+       *sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
+       return 0;
+}
+
 /**
  * _scsih_determine_boot_device - determine boot device.
  * @ioc: per adapter object
@@ -510,8 +570,6 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
     struct _sas_device *sas_device)
 {
        unsigned long flags;
-       u16 handle, parent_handle;
-       u64 sas_address;
 
        dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle"
            "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
@@ -521,10 +579,8 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
        list_add_tail(&sas_device->list, &ioc->sas_device_list);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       handle = sas_device->handle;
-       parent_handle = sas_device->parent_handle;
-       sas_address = sas_device->sas_address;
-       if (!mpt2sas_transport_port_add(ioc, handle, parent_handle))
+       if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+            sas_device->sas_address_parent))
                _scsih_sas_device_remove(ioc, sas_device);
 }
 
@@ -552,31 +608,6 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
        _scsih_determine_boot_device(ioc, sas_device, 0);
 }
 
-/**
- * mpt2sas_scsih_expander_find_by_handle - expander device search
- * @ioc: per adapter object
- * @handle: expander handle (assigned by firmware)
- * Context: Calling function should acquire ioc->sas_device_lock
- *
- * This searches for expander device based on handle, then returns the
- * sas_node object.
- */
-struct _sas_node *
-mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
-{
-       struct _sas_node *sas_expander, *r;
-
-       r = NULL;
-       list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
-               if (sas_expander->handle != handle)
-                       continue;
-               r = sas_expander;
-               goto out;
-       }
- out:
-       return r;
-}
-
 /**
  * _scsih_raid_device_find_by_id - raid device search
  * @ioc: per adapter object
@@ -698,6 +729,31 @@ _scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc,
        spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
 }
 
+/**
+ * mpt2sas_scsih_expander_find_by_handle - expander device search
+ * @ioc: per adapter object
+ * @handle: expander handle (assigned by firmware)
+ * Context: Calling function should acquire ioc->sas_device_lock
+ *
+ * This searches for expander device based on handle, then returns the
+ * sas_node object.
+ */
+struct _sas_node *
+mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+       struct _sas_node *sas_expander, *r;
+
+       r = NULL;
+       list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
+               if (sas_expander->handle != handle)
+                       continue;
+               r = sas_expander;
+               goto out;
+       }
+ out:
+       return r;
+}
+
 /**
  * mpt2sas_scsih_expander_find_by_sas_address - expander device search
  * @ioc: per adapter object
@@ -1043,17 +1099,46 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,
  * _scsih_change_queue_depth - setting device queue depth
  * @sdev: scsi device struct
  * @qdepth: requested queue depth
+ * @reason: calling context
  *
  * 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, int reason)
 {
        struct Scsi_Host *shost = sdev->host;
        int max_depth;
        int tag_type;
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+       struct MPT2SAS_DEVICE *sas_device_priv_data;
+       struct MPT2SAS_TARGET *sas_target_priv_data;
+       struct _sas_device *sas_device;
+       unsigned long flags;
+
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
 
        max_depth = shost->can_queue;
+
+       /* limit max device queue for SATA to 32 */
+       sas_device_priv_data = sdev->hostdata;
+       if (!sas_device_priv_data)
+               goto not_sata;
+       sas_target_priv_data = sas_device_priv_data->sas_target;
+       if (!sas_target_priv_data)
+               goto not_sata;
+       if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
+               goto not_sata;
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+          sas_device_priv_data->sas_target->sas_address);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       if (sas_device && sas_device->device_info &
+           MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+               max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
+
+ not_sata:
+
        if (!sdev->tagged_supported)
                max_depth = 1;
        if (qdepth > max_depth)
@@ -1488,7 +1573,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, SCSI_QDEPTH_DEFAULT);
                return 0;
        }
 
@@ -1534,7 +1619,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
                        _scsih_display_sata_capabilities(ioc, sas_device, sdev);
        }
 
-       _scsih_change_queue_depth(sdev, qdepth);
+       _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
 
        if (ssp_target)
                sas_read_port_mode_page(sdev);
@@ -1874,6 +1959,8 @@ _scsih_abort(struct scsi_cmnd *scmd)
                goto out;
        }
 
+       mpt2sas_halt_firmware(ioc);
+
        mutex_lock(&ioc->tm_cmds.mutex);
        handle = sas_device_priv_data->sas_target->handle;
        mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun,
@@ -2297,7 +2384,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
        u16 handle;
        u16 reason_code;
        u8 phy_number;
-       u8 link_rate;
 
        for (i = 0; i < event_data->NumEntries; i++) {
                handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
@@ -2308,11 +2394,6 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
                    MPI2_EVENT_SAS_TOPO_RC_MASK;
                if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
                        _scsih_block_io_device(ioc, handle);
-               if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
-                       link_rate = event_data->PHY[i].LinkRate >> 4;
-                       if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
-                               _scsih_ublock_io_device(ioc, handle);
-               }
        }
 }
 
@@ -2349,16 +2430,10 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
-                   ioc->name, __func__);
-               return;
-       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
        /* skip is hidden raid component */
-       if (sas_device->hidden_raid_component)
+       if (sas_device && sas_device->hidden_raid_component)
                return;
 
        smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);
@@ -2371,18 +2446,31 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL;
                list_add_tail(&delayed_tr->list,
                    &ioc->delayed_tr_list);
-               if (sas_device->starget)
+               if (sas_device && sas_device->starget) {
                        dewtprintk(ioc, starget_printk(KERN_INFO,
                            sas_device->starget, "DELAYED:tr:handle(0x%04x), "
-                           "(open)\n", sas_device->handle));
+                           "(open)\n", handle));
+               } else {
+                       dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                           "DELAYED:tr:handle(0x%04x), (open)\n",
+                           ioc->name, handle));
+               }
                return;
        }
 
-       if (sas_device->starget && sas_device->starget->hostdata) {
-               sas_target_priv_data = sas_device->starget->hostdata;
-               sas_target_priv_data->tm_busy = 1;
-               dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
-                   "tr:handle(0x%04x), (open)\n", sas_device->handle));
+       if (sas_device) {
+               sas_device->state |= MPTSAS_STATE_TR_SEND;
+               sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
+               if (sas_device->starget && sas_device->starget->hostdata) {
+                       sas_target_priv_data = sas_device->starget->hostdata;
+                       sas_target_priv_data->tm_busy = 1;
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget, "tr:handle(0x%04x), (open)\n",
+                           handle));
+               }
+       } else {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                   "tr:handle(0x%04x), (open)\n", ioc->name, handle));
        }
 
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
@@ -2390,8 +2478,6 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
        mpi_request->DevHandle = cpu_to_le16(handle);
        mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
-       sas_device->state |= MPTSAS_STATE_TR_SEND;
-       sas_device->state |= MPT2SAS_REQ_SAS_CNTRL;
        mpt2sas_base_put_smid_hi_priority(ioc, smid);
 }
 
@@ -2426,21 +2512,25 @@ _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
-                   ioc->name, __func__);
-               return 1;
-       }
-       sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       if (sas_device->starget)
-               dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
+       if (sas_device) {
+               sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE;
+               if (sas_device->starget)
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget,
+                           "sc_complete:handle(0x%04x), "
+                           "ioc_status(0x%04x), loginfo(0x%08x)\n",
+                           handle, le16_to_cpu(mpi_reply->IOCStatus),
+                           le32_to_cpu(mpi_reply->IOCLogInfo)));
+       } else {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
                    "sc_complete:handle(0x%04x), "
                    "ioc_status(0x%04x), loginfo(0x%08x)\n",
-                   handle, le16_to_cpu(mpi_reply->IOCStatus),
+                   ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus),
                    le32_to_cpu(mpi_reply->IOCLogInfo)));
+       }
+
        return 1;
 }
 
@@ -2478,28 +2568,33 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
        handle = le16_to_cpu(mpi_reply->DevHandle);
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               printk(MPT2SAS_ERR_FMT "%s: failed finding sas_device\n",
-                   ioc->name, __func__);
-               return 1;
-       }
-       sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       if (sas_device->starget)
-               dewtprintk(ioc, starget_printk(KERN_INFO, sas_device->starget,
-                   "tr_complete:handle(0x%04x), (%s) ioc_status(0x%04x), "
-                   "loginfo(0x%08x), completed(%d)\n",
-                   sas_device->handle, (sas_device->state &
-                   MPT2SAS_REQ_SAS_CNTRL) ? "open" : "active",
-                   le16_to_cpu(mpi_reply->IOCStatus),
+       if (sas_device) {
+               sas_device->state |= MPTSAS_STATE_TR_COMPLETE;
+               if (sas_device->starget) {
+                       dewtprintk(ioc, starget_printk(KERN_INFO,
+                           sas_device->starget, "tr_complete:handle(0x%04x), "
+                           "(%s) ioc_status(0x%04x), loginfo(0x%08x), "
+                           "completed(%d)\n", sas_device->handle,
+                           (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ?
+                           "open" : "active",
+                           le16_to_cpu(mpi_reply->IOCStatus),
+                           le32_to_cpu(mpi_reply->IOCLogInfo),
+                           le32_to_cpu(mpi_reply->TerminationCount)));
+                       if (sas_device->starget->hostdata) {
+                               sas_target_priv_data =
+                                   sas_device->starget->hostdata;
+                               sas_target_priv_data->tm_busy = 0;
+                       }
+               }
+       } else {
+               dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
+                   "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), "
+                   "loginfo(0x%08x), completed(%d)\n", ioc->name,
+                   handle, le16_to_cpu(mpi_reply->IOCStatus),
                    le32_to_cpu(mpi_reply->IOCLogInfo),
                    le32_to_cpu(mpi_reply->TerminationCount)));
-
-       if (sas_device->starget && sas_device->starget->hostdata) {
-               sas_target_priv_data = sas_device->starget->hostdata;
-               sas_target_priv_data->tm_busy = 0;
        }
 
        if (!list_empty(&ioc->delayed_tr_list)) {
@@ -2514,8 +2609,7 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
        } else
                rc = 1;
 
-
-       if (!(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
+       if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL))
                return rc;
 
        if (ioc->shost_recovery) {
@@ -2531,12 +2625,14 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
                return rc;
        }
 
+       if (sas_device)
+               sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
+
        mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl);
        memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));
        mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
        mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
        mpi_request->DevHandle = mpi_reply->DevHandle;
-       sas_device->state |= MPTSAS_STATE_CNTRL_SEND;
        mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl);
        return rc;
 }
@@ -2678,8 +2774,6 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
        else
                return;
 
-       mpi_request->EEDPBlockSize = scmd->device->sector_size;
-
        switch (prot_type) {
        case SCSI_PROT_DIF_TYPE1:
 
@@ -2687,8 +2781,7 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
                * enable ref/guard checking
                * auto increment ref tag
                */
-               mpi_request->EEDPFlags = eedp_flags |
-                   MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
+               eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
                    MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
                    MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
                mpi_request->CDB.EEDP32.PrimaryReferenceTag =
@@ -2701,11 +2794,11 @@ _scsih_setup_eedp(struct scsi_cmnd *scmd, Mpi2SCSIIORequest_t *mpi_request)
                /*
                * enable guard checking
                */
-               mpi_request->EEDPFlags = eedp_flags |
-                   MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
-
+               eedp_flags |= MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD;
                break;
        }
+       mpi_request->EEDPBlockSize = cpu_to_le32(scmd->device->sector_size);
+       mpi_request->EEDPFlags = cpu_to_le16(eedp_flags);
 }
 
 /**
@@ -2788,7 +2881,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        }
 
        /* see if we are busy with task managment stuff */
-       if (sas_target_priv_data->tm_busy)
+       if (sas_device_priv_data->block || sas_target_priv_data->tm_busy)
                return SCSI_MLQUEUE_DEVICE_BUSY;
        else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
                return SCSI_MLQUEUE_HOST_BUSY;
@@ -2842,7 +2935,7 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
        mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR;
        mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE;
        mpi_request->SenseBufferLowAddress =
-           (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid);
+           mpt2sas_base_get_sense_buffer_dma(ioc, smid);
        mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4;
        mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI +
            MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR);
@@ -2894,7 +2987,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data)
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
 /**
- * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request
+ * _scsih_scsi_ioc_info - translated non-successfull SCSI_IO request
  * @ioc: per adapter object
  * @scmd: pointer to scsi command object
  * @mpi_reply: reply mf payload returned from firmware
@@ -3059,7 +3152,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
        if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
                response_info = le32_to_cpu(mpi_reply->ResponseInfo);
                response_bytes = (u8 *)&response_info;
-               _scsih_response_code(ioc, response_bytes[3]);
+               _scsih_response_code(ioc, response_bytes[0]);
        }
 }
 #endif
@@ -3177,7 +3270,7 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
        u8 scsi_status;
        u32 log_info;
        struct MPT2SAS_DEVICE *sas_device_priv_data;
-       u32 response_code;
+       u32 response_code = 0;
 
        mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);
        scmd = _scsih_scsi_lookup_get(ioc, smid);
@@ -3199,16 +3292,16 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
        }
 
        /* turning off TLR */
+       scsi_state = mpi_reply->SCSIState;
+       if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID)
+               response_code =
+                   le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;
        if (!sas_device_priv_data->tlr_snoop_check) {
                sas_device_priv_data->tlr_snoop_check++;
-               if (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) {
-                       response_code = (le32_to_cpu(mpi_reply->ResponseInfo)
-                           >> 24);
-                       if (response_code ==
-                           MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
-                               sas_device_priv_data->flags &=
-                                   ~MPT_DEVICE_TLR_ON;
-               }
+               if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
+                   response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME)
+                       sas_device_priv_data->flags &=
+                           ~MPT_DEVICE_TLR_ON;
        }
 
        xfer_cnt = le32_to_cpu(mpi_reply->TransferCount);
@@ -3219,7 +3312,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
        else
                log_info = 0;
        ioc_status &= MPI2_IOCSTATUS_MASK;
-       scsi_state = mpi_reply->SCSIState;
        scsi_status = mpi_reply->SCSIStatus;
 
        if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 &&
@@ -3255,10 +3347,9 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
 
        case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
                if (sas_device_priv_data->block) {
-                       scmd->result = (DID_BUS_BUSY << 16);
-                       break;
+                       scmd->result = DID_TRANSPORT_DISRUPTED << 16;
+                       goto out;
                }
-
        case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
        case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
                scmd->result = DID_RESET << 16;
@@ -3304,8 +3395,10 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
        case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
        case MPI2_IOCSTATUS_SUCCESS:
                scmd->result = (DID_OK << 16) | scsi_status;
-               if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
-                    MPI2_SCSI_STATE_NO_SCSI_STATUS))
+               if (response_code ==
+                   MPI2_SCSITASKMGMT_RSP_INVALID_FRAME ||
+                   (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED |
+                    MPI2_SCSI_STATE_NO_SCSI_STATUS)))
                        scmd->result = DID_SOFT_ERROR << 16;
                else if (scsi_state & MPI2_SCSI_STATE_TERMINATED)
                        scmd->result = DID_RESET << 16;
@@ -3344,7 +3437,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
 /**
  * _scsih_sas_host_refresh - refreshing sas host object contents
  * @ioc: per adapter object
- * @update: update link information
  * Context: user
  *
  * During port enable, fw will send topology events for every device. Its
@@ -3354,13 +3446,14 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
  * Return nothing.
  */
 static void
-_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
+_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc)
 {
        u16 sz;
        u16 ioc_status;
        int i;
        Mpi2ConfigReply_t mpi_reply;
        Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL;
+       u16 attached_handle;
 
        dtmprintk(ioc, printk(MPT2SAS_INFO_FMT
            "updating handles for sas_host(0x%016llx)\n",
@@ -3374,27 +3467,24 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
                    ioc->name, __FILE__, __LINE__, __func__);
                return;
        }
-       if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
-           sas_iounit_pg0, sz))) {
-               ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
-                   MPI2_IOCSTATUS_MASK;
-               if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
-                       goto out;
-               for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
-                       ioc->sas_hba.phy[i].handle =
-                           le16_to_cpu(sas_iounit_pg0->PhyData[i].
-                               ControllerDevHandle);
-                       if (update)
-                               mpt2sas_transport_update_links(
-                                   ioc,
-                                   ioc->sas_hba.phy[i].handle,
-                                   le16_to_cpu(sas_iounit_pg0->PhyData[i].
-                                   AttachedDevHandle), i,
-                                   sas_iounit_pg0->PhyData[i].
-                                   NegotiatedLinkRate >> 4);
-               }
-       }
 
+       if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply,
+           sas_iounit_pg0, sz)) != 0)
+               goto out;
+       ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
+       if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
+               goto out;
+       for (i = 0; i < ioc->sas_hba.num_phys ; i++) {
+               if (i == 0)
+                       ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+                           PhyData[0].ControllerDevHandle);
+               ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
+               attached_handle = le16_to_cpu(sas_iounit_pg0->PhyData[i].
+                   AttachedDevHandle);
+               mpt2sas_transport_update_links(ioc, ioc->sas_hba.sas_address,
+                   attached_handle, i, sas_iounit_pg0->PhyData[i].
+                   NegotiatedLinkRate >> 4);
+       }
  out:
        kfree(sas_iounit_pg0);
 }
@@ -3507,19 +3597,21 @@ _scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc)
                            ioc->name, __FILE__, __LINE__, __func__);
                        goto out;
                }
-               ioc->sas_hba.phy[i].handle =
-                   le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle);
+
+               if (i == 0)
+                       ioc->sas_hba.handle = le16_to_cpu(sas_iounit_pg0->
+                           PhyData[0].ControllerDevHandle);
+               ioc->sas_hba.phy[i].handle = ioc->sas_hba.handle;
                ioc->sas_hba.phy[i].phy_id = i;
                mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i],
                    phy_pg0, ioc->sas_hba.parent_dev);
        }
        if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
-           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) {
+           MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.handle))) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
                goto out;
        }
-       ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle);
        ioc->sas_hba.enclosure_handle =
            le16_to_cpu(sas_device_pg0.EnclosureHandle);
        ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
@@ -3562,7 +3654,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        Mpi2SasEnclosurePage0_t enclosure_pg0;
        u32 ioc_status;
        u16 parent_handle;
-       __le64 sas_address;
+       __le64 sas_address, sas_address_parent = 0;
        int i;
        unsigned long flags;
        struct _sas_port *mpt2sas_port = NULL;
@@ -3591,10 +3683,16 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        /* handle out of order topology events */
        parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle);
-       if (parent_handle >= ioc->sas_hba.num_phys) {
+       if (_scsih_get_sas_address(ioc, parent_handle, &sas_address_parent)
+           != 0) {
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
+               return -1;
+       }
+       if (sas_address_parent != ioc->sas_hba.sas_address) {
                spin_lock_irqsave(&ioc->sas_node_lock, flags);
-               sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
-                   parent_handle);
+               sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+                   sas_address_parent);
                spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
                if (!sas_expander) {
                        rc = _scsih_expander_add(ioc, parent_handle);
@@ -3622,14 +3720,12 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        sas_expander->handle = handle;
        sas_expander->num_phys = expander_pg0.NumPhys;
-       sas_expander->parent_handle = parent_handle;
-       sas_expander->enclosure_handle =
-           le16_to_cpu(expander_pg0.EnclosureHandle);
+       sas_expander->sas_address_parent = sas_address_parent;
        sas_expander->sas_address = sas_address;
 
        printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x),"
            " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name,
-           handle, sas_expander->parent_handle, (unsigned long long)
+           handle, parent_handle, (unsigned long long)
            sas_expander->sas_address, sas_expander->num_phys);
 
        if (!sas_expander->num_phys)
@@ -3645,7 +3741,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        INIT_LIST_HEAD(&sas_expander->sas_port_list);
        mpt2sas_port = mpt2sas_transport_port_add(ioc, handle,
-           sas_expander->parent_handle);
+           sas_address_parent);
        if (!mpt2sas_port) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
@@ -3691,20 +3787,54 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        if (mpt2sas_port)
                mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
-                   sas_expander->parent_handle);
+                   sas_address_parent);
        kfree(sas_expander);
        return rc;
 }
 
+/**
+ * _scsih_done -  scsih callback handler.
+ * @ioc: per adapter object
+ * @smid: system request message index
+ * @msix_index: MSIX table index supplied by the OS
+ * @reply: reply message frame(lower 32bit addr)
+ *
+ * Callback handler when sending internal generated message frames.
+ * The callback index passed is `ioc->scsih_cb_idx`
+ *
+ * Return 1 meaning mf should be freed from _base_interrupt
+ *        0 means the mf is freed from this function.
+ */
+static u8
+_scsih_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
+{
+       MPI2DefaultReply_t *mpi_reply;
+
+       mpi_reply =  mpt2sas_base_get_reply_virt_addr(ioc, reply);
+       if (ioc->scsih_cmds.status == MPT2_CMD_NOT_USED)
+               return 1;
+       if (ioc->scsih_cmds.smid != smid)
+               return 1;
+       ioc->scsih_cmds.status |= MPT2_CMD_COMPLETE;
+       if (mpi_reply) {
+               memcpy(ioc->scsih_cmds.reply, mpi_reply,
+                   mpi_reply->MsgLength*4);
+               ioc->scsih_cmds.status |= MPT2_CMD_REPLY_VALID;
+       }
+       ioc->scsih_cmds.status &= ~MPT2_CMD_PENDING;
+       complete(&ioc->scsih_cmds.done);
+       return 1;
+}
+
 /**
  * _scsih_expander_remove - removing expander object
  * @ioc: per adapter object
- * @handle: expander handle
+ * @sas_address: expander sas_address
  *
  * Return nothing.
  */
 static void
-_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)
 {
        struct _sas_node *sas_expander;
        unsigned long flags;
@@ -3713,7 +3843,8 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                return;
 
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
-       sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+       sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc,
+           sas_address);
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
        _scsih_expander_node_remove(ioc, sas_expander);
 }
@@ -3805,8 +3936,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
        }
 
        sas_device->handle = handle;
-       sas_device->parent_handle =
-           le16_to_cpu(sas_device_pg0.ParentDevHandle);
+       if (_scsih_get_sas_address(ioc, le16_to_cpu
+               (sas_device_pg0.ParentDevHandle),
+               &sas_device->sas_address_parent) != 0)
+               printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+                   ioc->name, __FILE__, __LINE__, __func__);
        sas_device->enclosure_handle =
            le16_to_cpu(sas_device_pg0.EnclosureHandle);
        sas_device->slot =
@@ -3836,43 +3970,39 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
 /**
  * _scsih_remove_device -  removing sas device object
  * @ioc: per adapter object
- * @handle: sas device handle
+ * @sas_device: the sas_device object
  *
  * Return nothing.
  */
 static void
-_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device
+    *sas_device)
 {
        struct MPT2SAS_TARGET *sas_target_priv_data;
-       struct _sas_device *sas_device;
-       unsigned long flags;
        Mpi2SasIoUnitControlReply_t mpi_reply;
        Mpi2SasIoUnitControlRequest_t mpi_request;
-       u16 device_handle;
+       u16 device_handle, handle;
 
-       /* lookup sas_device */
-       spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       if (!sas_device)
                return;
-       }
 
-       dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle"
-           "(0x%04x)\n", ioc->name, __func__, handle));
+       handle = sas_device->handle;
+       dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x),"
+           " sas_addr(0x%016llx)\n", ioc->name, __func__, handle,
+           (unsigned long long) sas_device->sas_address));
 
        if (sas_device->starget && sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
                sas_target_priv_data->deleted = 1;
        }
-       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       if (ioc->remove_host)
+       if (ioc->remove_host || ioc->shost_recovery || !handle)
                goto out;
 
        if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) {
                dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip "
-                  "target_reset handle(0x%04x)\n", ioc->name, handle));
+                  "target_reset handle(0x%04x)\n", ioc->name,
+                  handle));
                goto skip_tr;
        }
 
@@ -3925,10 +4055,10 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        _scsih_ublock_io_device(ioc, handle);
 
        mpt2sas_transport_port_remove(ioc, sas_device->sas_address,
-           sas_device->parent_handle);
+           sas_device->sas_address_parent);
 
        printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr"
-           "(0x%016llx)\n", ioc->name, sas_device->handle,
+           "(0x%016llx)\n", ioc->name, handle,
            (unsigned long long) sas_device->sas_address);
        _scsih_sas_device_remove(ioc, sas_device);
 
@@ -3952,7 +4082,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
        u16 reason_code;
        u8 phy_number;
        char *status_str = NULL;
-       char link_rate[25];
+       u8 link_rate, prev_link_rate;
 
        switch (event_data->ExpStatus) {
        case MPI2_EVENT_SAS_TOPO_ES_ADDED:
@@ -3962,6 +4092,7 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
                status_str = "remove";
                break;
        case MPI2_EVENT_SAS_TOPO_ES_RESPONDING:
+       case 0:
                status_str =  "responding";
                break;
        case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING:
@@ -3987,30 +4118,30 @@ _scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
                    MPI2_EVENT_SAS_TOPO_RC_MASK;
                switch (reason_code) {
                case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
-                       snprintf(link_rate, 25, ": add, link(0x%02x)",
-                           (event_data->PHY[i].LinkRate >> 4));
-                       status_str = link_rate;
+                       status_str = "target add";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
-                       status_str = ": remove";
+                       status_str = "target remove";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING:
-                       status_str = ": remove_delay";
+                       status_str = "delay target remove";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
-                       snprintf(link_rate, 25, ": link(0x%02x)",
-                           (event_data->PHY[i].LinkRate >> 4));
-                       status_str = link_rate;
+                       status_str = "link rate change";
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE:
-                       status_str = ": responding";
+                       status_str = "target responding";
                        break;
                default:
-                       status_str = "unknown";
+                       status_str = "unknown";
                        break;
                }
-               printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n",
-                   phy_number, handle, status_str);
+               link_rate = event_data->PHY[i].LinkRate >> 4;
+               prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
+               printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x): %s:"
+                   " link rate: new(0x%02x), old(0x%02x)\n", phy_number,
+                   handle, status_str, link_rate, prev_link_rate);
+
        }
 }
 #endif
@@ -4031,8 +4162,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
        u16 reason_code;
        u8 phy_number;
        struct _sas_node *sas_expander;
+       struct _sas_device *sas_device;
+       u64 sas_address;
        unsigned long flags;
-       u8 link_rate_;
+       u8 link_rate, prev_link_rate;
        Mpi2EventDataSasTopologyChangeList_t *event_data = fw_event->event_data;
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4040,10 +4173,13 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
                _scsih_sas_topology_change_event_debug(ioc, event_data);
 #endif
 
+       if (ioc->shost_recovery)
+               return;
+
        if (!ioc->sas_hba.num_phys)
                _scsih_sas_host_add(ioc);
        else
-               _scsih_sas_host_refresh(ioc, 0);
+               _scsih_sas_host_refresh(ioc);
 
        if (fw_event->ignore) {
                dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander "
@@ -4058,6 +4194,17 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
                if (_scsih_expander_add(ioc, parent_handle) != 0)
                        return;
 
+       spin_lock_irqsave(&ioc->sas_node_lock, flags);
+       sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc,
+           parent_handle);
+       spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
+       if (sas_expander)
+               sas_address = sas_expander->sas_address;
+       else if (parent_handle < ioc->sas_hba.num_phys)
+               sas_address = ioc->sas_hba.sas_address;
+       else
+               return;
+
        /* handle siblings events */
        for (i = 0; i < event_data->NumEntries; i++) {
                if (fw_event->ignore) {
@@ -4077,48 +4224,47 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,
                handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
                if (!handle)
                        continue;
-               link_rate_ = event_data->PHY[i].LinkRate >> 4;
+               link_rate = event_data->PHY[i].LinkRate >> 4;
+               prev_link_rate = event_data->PHY[i].LinkRate & 0xF;
                switch (reason_code) {
                case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED:
+
+                       if (link_rate == prev_link_rate)
+                               break;
+
+                       mpt2sas_transport_update_links(ioc, sas_address,
+                           handle, phy_number, link_rate);
+
+                       if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
+                               _scsih_ublock_io_device(ioc, handle);
+                       break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
-                       if (!parent_handle) {
-                               if (phy_number < ioc->sas_hba.num_phys)
-                                       mpt2sas_transport_update_links(
-                                       ioc,
-                                       ioc->sas_hba.phy[phy_number].handle,
-                                       handle, phy_number, link_rate_);
-                       } else {
-                               spin_lock_irqsave(&ioc->sas_node_lock, flags);
-                               sas_expander =
-                                   mpt2sas_scsih_expander_find_by_handle(ioc,
-                                       parent_handle);
-                               spin_unlock_irqrestore(&ioc->sas_node_lock,
-                                   flags);
-                               if (sas_expander) {
-                                       if (phy_number < sas_expander->num_phys)
-                                               mpt2sas_transport_update_links(
-                                               ioc,
-                                               sas_expander->
-                                               phy[phy_number].handle,
-                                               handle, phy_number,
-                                               link_rate_);
-                               }
-                       }
-                       if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) {
-                               if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5)
-                                       break;
-                               _scsih_add_device(ioc, handle, phy_number, 0);
-                       }
+
+                       mpt2sas_transport_update_links(ioc, sas_address,
+                           handle, phy_number, link_rate);
+
+                       _scsih_add_device(ioc, handle, phy_number, 0);
                        break;
                case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING:
-                       _scsih_remove_device(ioc, handle);
+
+                       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+                       sas_device = _scsih_sas_device_find_by_handle(ioc,
+                           handle);
+                       if (!sas_device) {
+                               spin_unlock_irqrestore(&ioc->sas_device_lock,
+                                   flags);
+                               break;
+                       }
+                       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+                       _scsih_remove_device(ioc, sas_device);
                        break;
                }
        }
 
        /* handle expander removal */
-       if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING)
-               _scsih_expander_remove(ioc, parent_handle);
+       if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING &&
+           sas_expander)
+               _scsih_expander_remove(ioc, sas_address);
 
 }
 
@@ -4170,6 +4316,12 @@ _scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc,
        case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION:
                reason_str = "internal async notification";
                break;
+       case MPI2_EVENT_SAS_DEV_STAT_RC_EXPANDER_REDUCED_FUNCTIONALITY:
+               reason_str = "expander reduced functionality";
+               break;
+       case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_EXPANDER_REDUCED_FUNCTIONALITY:
+               reason_str = "expander reduced functionality complete";
+               break;
        default:
                reason_str = "unknown reason";
                break;
@@ -4197,11 +4349,43 @@ static void
 _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,
     struct fw_event_work *fw_event)
 {
+       struct MPT2SAS_TARGET *target_priv_data;
+       struct _sas_device *sas_device;
+       __le64 sas_address;
+       unsigned long flags;
+       Mpi2EventDataSasDeviceStatusChange_t *event_data =
+           fw_event->event_data;
+
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)
                _scsih_sas_device_status_change_event_debug(ioc,
-                    fw_event->event_data);
+                    event_data);
 #endif
+
+       if (!(event_data->ReasonCode ==
+           MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET &&
+          event_data->ReasonCode ==
+           MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET))
+               return;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_address = le64_to_cpu(event_data->SASAddress);
+       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+           sas_address);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       if (!sas_device || !sas_device->starget)
+               return;
+
+       target_priv_data = sas_device->starget->hostdata;
+       if (!target_priv_data)
+               return;
+
+       if (event_data->ReasonCode ==
+           MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
+               target_priv_data->tm_busy = 1;
+       else
+               target_priv_data->tm_busy = 0;
 }
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -4281,6 +4465,7 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
        Mpi2EventDataSasBroadcastPrimitive_t *event_data = fw_event->event_data;
 #endif
+       u16 ioc_status;
        dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: "
            "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum,
            event_data->PortWidth));
@@ -4314,8 +4499,9 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
                mpt2sas_scsih_issue_tm(ioc, handle, lun,
                    MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30);
                ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
-
-               if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) &&
+               ioc_status = le16_to_cpu(mpi_reply->IOCStatus)
+                   & MPI2_IOCSTATUS_MASK;
+               if ((ioc_status == MPI2_IOCSTATUS_SUCCESS) &&
                    (mpi_reply->ResponseCode ==
                     MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED ||
                     mpi_reply->ResponseCode ==
@@ -4570,7 +4756,7 @@ _scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc,
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        if (!sas_device)
                return;
-       _scsih_remove_device(ioc, handle);
+       _scsih_remove_device(ioc, sas_device);
 }
 
 /**
@@ -4591,6 +4777,8 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
        Mpi2ConfigReply_t mpi_reply;
        Mpi2SasDevicePage0_t sas_device_pg0;
        u32 ioc_status;
+       u64 sas_address;
+       u16 parent_handle;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
@@ -4615,9 +4803,10 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
                return;
        }
 
-       mpt2sas_transport_update_links(ioc,
-           le16_to_cpu(sas_device_pg0.ParentDevHandle),
-           handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+       parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+       if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+               mpt2sas_transport_update_links(ioc, sas_address, handle,
+                   sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
 
        _scsih_add_device(ioc, handle, 0, 1);
 }
@@ -4857,7 +5046,7 @@ static void
 _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
     struct fw_event_work *fw_event)
 {
-       u16 handle;
+       u16 handle, parent_handle;
        u32 state;
        struct _sas_device *sas_device;
        unsigned long flags;
@@ -4865,6 +5054,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
        Mpi2SasDevicePage0_t sas_device_pg0;
        u32 ioc_status;
        Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;
+       u64 sas_address;
 
        if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)
                return;
@@ -4906,9 +5096,10 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
                        return;
                }
 
-               mpt2sas_transport_update_links(ioc,
-                   le16_to_cpu(sas_device_pg0.ParentDevHandle),
-                   handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
+               parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
+               if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address))
+                       mpt2sas_transport_update_links(ioc, sas_address, handle,
+                           sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
 
                _scsih_add_device(ioc, handle, 0, 1);
 
@@ -4948,11 +5139,17 @@ _scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc,
        case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK:
                reason_str = "consistency check";
                break;
-       default:
-               reason_str = "unknown reason";
+       case MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT:
+               reason_str = "background init";
+               break;
+       case MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT:
+               reason_str = "make data consistent";
                break;
        }
 
+       if (!reason_str)
+               return;
+
        printk(MPT2SAS_INFO_FMT "raid operational status: (%s)"
            "\thandle(0x%04x), percent complete(%d)\n",
            ioc->name, reason_str,
@@ -5252,18 +5449,23 @@ _scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
 {
        struct _sas_node *sas_expander;
        unsigned long flags;
+       int i;
 
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
        list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
-               if (sas_expander->sas_address == sas_address) {
-                       sas_expander->responding = 1;
-                       if (sas_expander->handle != handle) {
-                               printk(KERN_INFO "old handle(0x%04x)\n",
-                                   sas_expander->handle);
-                               sas_expander->handle = handle;
-                       }
+               if (sas_expander->sas_address != sas_address)
+                       continue;
+               sas_expander->responding = 1;
+               if (sas_expander->handle == handle)
                        goto out;
-               }
+               printk(KERN_INFO "\texpander(0x%016llx): handle changed"
+                   " from(0x%04x) to (0x%04x)!!!\n",
+                   (unsigned long long)sas_expander->sas_address,
+                   sas_expander->handle, handle);
+               sas_expander->handle = handle;
+               for (i = 0 ; i < sas_expander->num_phys ; i++)
+                       sas_expander->phy[i].handle = handle;
+               goto out;
        }
  out:
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
@@ -5340,7 +5542,9 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
                            (unsigned long long)
                            sas_device->enclosure_logical_id,
                            sas_device->slot);
-               _scsih_remove_device(ioc, sas_device->handle);
+               /* invalidate the device handle */
+               sas_device->handle = 0;
+               _scsih_remove_device(ioc, sas_device);
        }
 
        list_for_each_entry_safe(raid_device, raid_device_next,
@@ -5366,7 +5570,7 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
                        sas_expander->responding = 0;
                        continue;
                }
-               _scsih_expander_remove(ioc, sas_expander->handle);
+               _scsih_expander_remove(ioc, sas_expander->sas_address);
                goto retry_expander_search;
        }
 }
@@ -5406,7 +5610,7 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
        case MPT2_IOC_DONE_RESET:
                dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
                    "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
-               _scsih_sas_host_refresh(ioc, 0);
+               _scsih_sas_host_refresh(ioc);
                _scsih_search_responding_sas_devices(ioc);
                _scsih_search_responding_raid_devices(ioc);
                _scsih_search_responding_expanders(ioc);
@@ -5646,7 +5850,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
                        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                        if (!sas_device)
                                continue;
-                       _scsih_remove_device(ioc, sas_device->handle);
+                       _scsih_remove_device(ioc, sas_device);
                        if (ioc->shost_recovery)
                                return;
                        goto retry_device_search;
@@ -5669,7 +5873,8 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
                        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
                        if (!expander_sibling)
                                continue;
-                       _scsih_expander_remove(ioc, expander_sibling->handle);
+                       _scsih_expander_remove(ioc,
+                           expander_sibling->sas_address);
                        if (ioc->shost_recovery)
                                return;
                        goto retry_expander_search;
@@ -5677,7 +5882,7 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
        }
 
        mpt2sas_transport_port_remove(ioc, sas_expander->sas_address,
-           sas_expander->parent_handle);
+           sas_expander->sas_address_parent);
 
        printk(MPT2SAS_INFO_FMT "expander_remove: handle"
           "(0x%04x), sas_addr(0x%016llx)\n", ioc->name,
@@ -5689,10 +5894,100 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
        kfree(sas_expander);
 }
 
+/**
+ * _scsih_ir_shutdown - IR shutdown notification
+ * @ioc: per adapter object
+ *
+ * Sending RAID Action to alert the Integrated RAID subsystem of the IOC that
+ * the host system is shutting down.
+ *
+ * Return nothing.
+ */
+static void
+_scsih_ir_shutdown(struct MPT2SAS_ADAPTER *ioc)
+{
+       Mpi2RaidActionRequest_t *mpi_request;
+       Mpi2RaidActionReply_t *mpi_reply;
+       u16 smid;
+
+       /* is IR firmware build loaded ? */
+       if (!ioc->ir_firmware)
+               return;
+
+       /* are there any volumes ? */
+       if (list_empty(&ioc->raid_device_list))
+               return;
+
+       mutex_lock(&ioc->scsih_cmds.mutex);
+
+       if (ioc->scsih_cmds.status != MPT2_CMD_NOT_USED) {
+               printk(MPT2SAS_ERR_FMT "%s: scsih_cmd in use\n",
+                   ioc->name, __func__);
+               goto out;
+       }
+       ioc->scsih_cmds.status = MPT2_CMD_PENDING;
+
+       smid = mpt2sas_base_get_smid(ioc, ioc->scsih_cb_idx);
+       if (!smid) {
+               printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
+                   ioc->name, __func__);
+               ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+               goto out;
+       }
+
+       mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);
+       ioc->scsih_cmds.smid = smid;
+       memset(mpi_request, 0, sizeof(Mpi2RaidActionRequest_t));
+
+       mpi_request->Function = MPI2_FUNCTION_RAID_ACTION;
+       mpi_request->Action = MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED;
+
+       printk(MPT2SAS_INFO_FMT "IR shutdown (sending)\n", ioc->name);
+       init_completion(&ioc->scsih_cmds.done);
+       mpt2sas_base_put_smid_default(ioc, smid);
+       wait_for_completion_timeout(&ioc->scsih_cmds.done, 10*HZ);
+
+       if (!(ioc->scsih_cmds.status & MPT2_CMD_COMPLETE)) {
+               printk(MPT2SAS_ERR_FMT "%s: timeout\n",
+                   ioc->name, __func__);
+               goto out;
+       }
+
+       if (ioc->scsih_cmds.status & MPT2_CMD_REPLY_VALID) {
+               mpi_reply = ioc->scsih_cmds.reply;
+
+               printk(MPT2SAS_INFO_FMT "IR shutdown (complete): "
+                   "ioc_status(0x%04x), loginfo(0x%08x)\n",
+                   ioc->name, le16_to_cpu(mpi_reply->IOCStatus),
+                   le32_to_cpu(mpi_reply->IOCLogInfo));
+       }
+
+ out:
+       ioc->scsih_cmds.status = MPT2_CMD_NOT_USED;
+       mutex_unlock(&ioc->scsih_cmds.mutex);
+}
+
+/**
+ * _scsih_shutdown - routine call during system shutdown
+ * @pdev: PCI device struct
+ *
+ * Return nothing.
+ */
+static void
+_scsih_shutdown(struct pci_dev *pdev)
+{
+       struct Scsi_Host *shost = pci_get_drvdata(pdev);
+       struct MPT2SAS_ADAPTER *ioc = shost_priv(shost);
+
+       _scsih_ir_shutdown(ioc);
+       mpt2sas_base_detach(ioc);
+}
+
 /**
  * _scsih_remove - detach and remove add host
  * @pdev: PCI device struct
  *
+ * Routine called when unloading the driver.
  * Return nothing.
  */
 static void __devexit
@@ -5726,7 +6021,7 @@ _scsih_remove(struct pci_dev *pdev)
                            mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
                           mpt2sas_port->remote_identify.sas_address);
                        if (sas_device) {
-                               _scsih_remove_device(ioc, sas_device->handle);
+                               _scsih_remove_device(ioc, sas_device);
                                goto retry_again;
                        }
                } else {
@@ -5735,7 +6030,7 @@ _scsih_remove(struct pci_dev *pdev)
                            mpt2sas_port->remote_identify.sas_address);
                        if (expander_sibling) {
                                _scsih_expander_remove(ioc,
-                                   expander_sibling->handle);
+                                   expander_sibling->sas_address);
                                goto retry_again;
                        }
                }
@@ -5749,7 +6044,7 @@ _scsih_remove(struct pci_dev *pdev)
        }
 
        sas_remove_host(shost);
-       mpt2sas_base_detach(ioc);
+       _scsih_shutdown(pdev);
        list_del(&ioc->list);
        scsi_remove_host(shost);
        scsi_host_put(shost);
@@ -5770,7 +6065,8 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
        void *device;
        struct _sas_device *sas_device;
        struct _raid_device *raid_device;
-       u16 handle, parent_handle;
+       u16 handle;
+       u64 sas_address_parent;
        u64 sas_address;
        unsigned long flags;
        int rc;
@@ -5799,17 +6095,17 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)
        } else {
                sas_device = device;
                handle = sas_device->handle;
-               parent_handle = sas_device->parent_handle;
+               sas_address_parent = sas_device->sas_address_parent;
                sas_address = sas_device->sas_address;
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
                list_move_tail(&sas_device->list, &ioc->sas_device_list);
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
-                   sas_device->parent_handle)) {
+                   sas_device->sas_address_parent)) {
                        _scsih_sas_device_remove(ioc, sas_device);
                } else if (!sas_device->starget) {
                        mpt2sas_transport_port_remove(ioc, sas_address,
-                           parent_handle);
+                           sas_address_parent);
                        _scsih_sas_device_remove(ioc, sas_device);
                }
        }
@@ -5849,8 +6145,6 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
 {
        struct _sas_device *sas_device, *next;
        unsigned long flags;
-       u16 handle, parent_handle;
-       u64 sas_address;
 
        /* SAS Device List */
        list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
@@ -5859,14 +6153,13 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
                list_move_tail(&sas_device->list, &ioc->sas_device_list);
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-               handle = sas_device->handle;
-               parent_handle = sas_device->parent_handle;
-               sas_address = sas_device->sas_address;
-               if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) {
+               if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
+                   sas_device->sas_address_parent)) {
                        _scsih_sas_device_remove(ioc, sas_device);
                } else if (!sas_device->starget) {
-                       mpt2sas_transport_port_remove(ioc, sas_address,
-                           parent_handle);
+                       mpt2sas_transport_port_remove(ioc,
+                           sas_device->sas_address,
+                           sas_device->sas_address_parent);
                        _scsih_sas_device_remove(ioc, sas_device);
                }
        }
@@ -5935,6 +6228,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        ioc->ctl_cb_idx = ctl_cb_idx;
        ioc->base_cb_idx = base_cb_idx;
        ioc->transport_cb_idx = transport_cb_idx;
+       ioc->scsih_cb_idx = scsih_cb_idx;
        ioc->config_cb_idx = config_cb_idx;
        ioc->tm_tr_cb_idx = tm_tr_cb_idx;
        ioc->tm_sas_control_cb_idx = tm_sas_control_cb_idx;
@@ -6072,6 +6366,7 @@ static struct pci_driver scsih_driver = {
        .id_table       = scsih_pci_table,
        .probe          = _scsih_probe,
        .remove         = __devexit_p(_scsih_remove),
+       .shutdown       = _scsih_shutdown,
 #ifdef CONFIG_PM
        .suspend        = _scsih_suspend,
        .resume         = _scsih_resume,
@@ -6113,6 +6408,9 @@ _scsih_init(void)
        transport_cb_idx = mpt2sas_base_register_callback_handler(
            mpt2sas_transport_done);
 
+       /* scsih internal commands callback handler */
+       scsih_cb_idx = mpt2sas_base_register_callback_handler(_scsih_done);
+
        /* configuration page API internal commands callback handler */
        config_cb_idx = mpt2sas_base_register_callback_handler(
            mpt2sas_config_done);
@@ -6152,6 +6450,7 @@ _scsih_exit(void)
        mpt2sas_base_release_callback_handler(tm_cb_idx);
        mpt2sas_base_release_callback_handler(base_cb_idx);
        mpt2sas_base_release_callback_handler(transport_cb_idx);
+       mpt2sas_base_release_callback_handler(scsih_cb_idx);
        mpt2sas_base_release_callback_handler(config_cb_idx);
        mpt2sas_base_release_callback_handler(ctl_cb_idx);
 
index eb98188..3a82872 100644 (file)
 
 #include "mpt2sas_base.h"
 /**
- * _transport_sas_node_find_by_handle - sas node search
+ * _transport_sas_node_find_by_sas_address - sas node search
  * @ioc: per adapter object
- * @handle: expander or hba handle (assigned by firmware)
+ * @sas_address: sas address of expander or sas host
  * Context: Calling function should acquire ioc->sas_node_lock.
  *
  * Search for either hba phys or expander device based on handle, then returns
  * the sas_node object.
  */
 static struct _sas_node *
-_transport_sas_node_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+_transport_sas_node_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
+    u64 sas_address)
 {
-       int i;
-
-       for (i = 0; i < ioc->sas_hba.num_phys; i++)
-               if (ioc->sas_hba.phy[i].handle == handle)
-                       return &ioc->sas_hba;
-
-       return mpt2sas_scsih_expander_find_by_handle(ioc, handle);
+       if (ioc->sas_hba.sas_address == sas_address)
+               return &ioc->sas_hba;
+       else
+               return mpt2sas_scsih_expander_find_by_sas_address(ioc,
+                   sas_address);
 }
 
 /**
@@ -259,8 +258,7 @@ struct rep_manu_reply{
        u8 response_length;
        u16 expander_change_count;
        u8 reserved0[2];
-       u8 sas_format:1;
-       u8 reserved1:7;
+       u8 sas_format;
        u8 reserved2[3];
        u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN];
        u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN];
@@ -375,7 +373,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
        mpi_request->VP_ID = 0;
        sas_address_le = (u64 *)&mpi_request->SASAddress;
        *sas_address_le = cpu_to_le64(sas_address);
-       mpi_request->RequestDataLength = sizeof(struct rep_manu_request);
+       mpi_request->RequestDataLength =
+           cpu_to_le16(sizeof(struct rep_manu_request));
        psge = &mpi_request->SGL;
 
        /* WRITE sgel first */
@@ -438,8 +437,8 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
                     SAS_EXPANDER_PRODUCT_ID_LEN);
                strncpy(edev->product_rev, manufacture_reply->product_rev,
                     SAS_EXPANDER_PRODUCT_REV_LEN);
-               edev->level = manufacture_reply->sas_format;
-               if (manufacture_reply->sas_format) {
+               edev->level = manufacture_reply->sas_format & 1;
+               if (edev->level) {
                        strncpy(edev->component_vendor_id,
                            manufacture_reply->component_vendor_id,
                             SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN);
@@ -469,7 +468,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
  * mpt2sas_transport_port_add - insert port to the list
  * @ioc: per adapter object
  * @handle: handle of attached device
- * @parent_handle: parent handle(either hba or expander)
+ * @sas_address: sas address of parent expander or sas host
  * Context: This function will acquire ioc->sas_node_lock.
  *
  * Adding new port object to the sas_node->sas_port_list.
@@ -478,7 +477,7 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
  */
 struct _sas_port *
 mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
-    u16 parent_handle)
+    u64 sas_address)
 {
        struct _sas_phy *mpt2sas_phy, *next;
        struct _sas_port *mpt2sas_port;
@@ -488,9 +487,6 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
        int i;
        struct sas_port *port;
 
-       if (!parent_handle)
-               return NULL;
-
        mpt2sas_port = kzalloc(sizeof(struct _sas_port),
            GFP_KERNEL);
        if (!mpt2sas_port) {
@@ -502,17 +498,16 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
        INIT_LIST_HEAD(&mpt2sas_port->port_list);
        INIT_LIST_HEAD(&mpt2sas_port->phy_list);
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
-       sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle);
+       sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
 
        if (!sas_node) {
-               printk(MPT2SAS_ERR_FMT "%s: Could not find parent(0x%04x)!\n",
-                   ioc->name, __func__, parent_handle);
+               printk(MPT2SAS_ERR_FMT "%s: Could not find "
+                   "parent sas_address(0x%016llx)!\n", ioc->name,
+                   __func__, (unsigned long long)sas_address);
                goto out_fail;
        }
 
-       mpt2sas_port->handle = parent_handle;
-       mpt2sas_port->sas_address = sas_node->sas_address;
        if ((_transport_set_identify(ioc, handle,
            &mpt2sas_port->remote_identify))) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
@@ -604,7 +599,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
  * mpt2sas_transport_port_remove - remove port from the list
  * @ioc: per adapter object
  * @sas_address: sas address of attached device
- * @parent_handle: handle to the upstream parent(either hba or expander)
+ * @sas_address_parent: sas address of parent expander or sas host
  * Context: This function will acquire ioc->sas_node_lock.
  *
  * Removing object and freeing associated memory from the
@@ -614,7 +609,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle,
  */
 void
 mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
-    u16 parent_handle)
+    u64 sas_address_parent)
 {
        int i;
        unsigned long flags;
@@ -624,7 +619,8 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
        struct _sas_phy *mpt2sas_phy, *next_phy;
 
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
-       sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle);
+       sas_node = _transport_sas_node_find_by_sas_address(ioc,
+           sas_address_parent);
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
        if (!sas_node)
                return;
@@ -650,8 +646,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,
            &mpt2sas_port->phy_list, port_siblings) {
                if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
                        dev_printk(KERN_INFO, &mpt2sas_port->port->dev,
-                           "remove: parent_handle(0x%04x), "
-                           "sas_addr(0x%016llx), phy(%d)\n", parent_handle,
+                           "remove: sas_addr(0x%016llx), phy(%d)\n",
                            (unsigned long long)
                            mpt2sas_port->remote_identify.sas_address,
                            mpt2sas_phy->phy_id);
@@ -799,8 +794,8 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
 /**
  * mpt2sas_transport_update_links - refreshing phy link changes
  * @ioc: per adapter object
- * @handle: handle to sas_host or expander
- * @attached_handle: attached device handle
+ * @sas_address: sas address of parent expander or sas host
+ * @handle: attached device handle
  * @phy_numberv: phy number
  * @link_rate: new link rate
  *
@@ -808,28 +803,25 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
  */
 void
 mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
-    u16 handle, u16 attached_handle, u8 phy_number, u8 link_rate)
+     u64 sas_address, u16 handle, u8 phy_number, u8 link_rate)
 {
        unsigned long flags;
        struct _sas_node *sas_node;
        struct _sas_phy *mpt2sas_phy;
 
-       if (ioc->shost_recovery) {
-               printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
-                       __func__, ioc->name);
+       if (ioc->shost_recovery)
                return;
-       }
 
        spin_lock_irqsave(&ioc->sas_node_lock, flags);
-       sas_node = _transport_sas_node_find_by_handle(ioc, handle);
+       sas_node = _transport_sas_node_find_by_sas_address(ioc, sas_address);
        spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
        if (!sas_node)
                return;
 
        mpt2sas_phy = &sas_node->phy[phy_number];
-       mpt2sas_phy->attached_handle = attached_handle;
-       if (attached_handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
-               _transport_set_identify(ioc, mpt2sas_phy->attached_handle,
+       mpt2sas_phy->attached_handle = handle;
+       if (handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5))
+               _transport_set_identify(ioc, handle,
                    &mpt2sas_phy->remote_identify);
        else
                memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct
@@ -841,13 +833,11 @@ mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
 
        if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
                dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev,
-                   "refresh: handle(0x%04x), sas_addr(0x%016llx),\n"
+                   "refresh: parent sas_addr(0x%016llx),\n"
                    "\tlink_rate(0x%02x), phy(%d)\n"
                    "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n",
-                   handle, (unsigned long long)
-                   mpt2sas_phy->identify.sas_address, link_rate,
-                   phy_number, attached_handle,
-                   (unsigned long long)
+                   (unsigned long long)sas_address,
+                   link_rate, phy_number, handle, (unsigned long long)
                    mpt2sas_phy->remote_identify.sas_address);
 }
 
@@ -1126,7 +1116,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
                blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
        if (!dma_addr_out) {
-               mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
+               mpt2sas_base_free_smid(ioc, smid);
                goto unmap;
        }
 
@@ -1144,7 +1134,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio),
                                     blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
        if (!dma_addr_in) {
-               mpt2sas_base_free_smid(ioc, le16_to_cpu(smid));
+               mpt2sas_base_free_smid(ioc, smid);
                goto unmap;
        }
 
index e3c482a..a2d5698 100644 (file)
@@ -6495,7 +6495,7 @@ static void ncr_int_ma (struct ncb *np)
        **      we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids 
        **      bloat for such a should_not_happen situation).
        **      In all other situation, we reset the BUS.
-       **      Are these assumptions reasonnable ? (Wait and see ...)
+       **      Are these assumptions reasonable ? (Wait and see ...)
        */
 unexpected_phase:
        dsp -= 8;
index 2be7d5b..2c98a6e 100644 (file)
@@ -1419,7 +1419,7 @@ static irqreturn_t do_nsp32_isr(int irq, void *dev_id)
                nsp32_msg(KERN_ERR, "Received unexpected BMCNTERR IRQ! ");
                /*
                 * TODO: To be implemented improving bus master
-                * transfer reliablity when BMCNTERR is occurred in
+                * transfer reliability when BMCNTERR is occurred in
                 * AutoSCSI phase described in specification.
                 */
        }
index 7a117c1..950202a 100644 (file)
@@ -73,7 +73,8 @@ static const char *_osd_ver_desc(struct osd_request *or)
 
 #define ATTR_DEF_RI(id, len) ATTR_DEF(OSD_APAGE_ROOT_INFORMATION, id, len)
 
-static int _osd_print_system_info(struct osd_dev *od, void *caps)
+static int _osd_get_print_system_info(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi)
 {
        struct osd_request *or;
        struct osd_attr get_attrs[] = {
@@ -137,8 +138,12 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
        OSD_INFO("PRODUCT_SERIAL_NUMBER  [%s]\n",
                (char *)pFirst);
 
-       pFirst = get_attrs[a].val_ptr;
-       OSD_INFO("OSD_NAME               [%s]\n", (char *)pFirst);
+       odi->osdname_len = get_attrs[a].len;
+       /* Avoid NULL for memcmp optimization 0-length is good enough */
+       odi->osdname = kzalloc(odi->osdname_len + 1, GFP_KERNEL);
+       if (odi->osdname_len)
+               memcpy(odi->osdname, get_attrs[a].val_ptr, odi->osdname_len);
+       OSD_INFO("OSD_NAME               [%s]\n", odi->osdname);
        a++;
 
        pFirst = get_attrs[a++].val_ptr;
@@ -171,6 +176,14 @@ static int _osd_print_system_info(struct osd_dev *od, void *caps)
                                   sid_dump, sizeof(sid_dump), true);
                OSD_INFO("OSD_SYSTEM_ID(%d)\n"
                         "        [%s]\n", len, sid_dump);
+
+               if (unlikely(len > sizeof(odi->systemid))) {
+                       OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). "
+                               "device idetification might not work\n", len);
+                       len = sizeof(odi->systemid);
+               }
+               odi->systemid_len = len;
+               memcpy(odi->systemid, get_attrs[a].val_ptr, len);
                a++;
        }
 out:
@@ -178,16 +191,17 @@ out:
        return ret;
 }
 
-int osd_auto_detect_ver(struct osd_dev *od, void *caps)
+int osd_auto_detect_ver(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi)
 {
        int ret;
 
        /* Auto-detect the osd version */
-       ret = _osd_print_system_info(od, caps);
+       ret = _osd_get_print_system_info(od, caps, odi);
        if (ret) {
                osd_dev_set_ver(od, OSD_VER1);
                OSD_DEBUG("converting to OSD1\n");
-               ret = _osd_print_system_info(od, caps);
+               ret = _osd_get_print_system_info(od, caps, odi);
        }
 
        return ret;
@@ -461,7 +475,8 @@ EXPORT_SYMBOL(osd_end_request);
 
 int osd_execute_request(struct osd_request *or)
 {
-       return blk_execute_rq(or->request->q, NULL, or->request, 0);
+       return or->async_error =
+                       blk_execute_rq(or->request->q, NULL, or->request, 0);
 }
 EXPORT_SYMBOL(osd_execute_request);
 
@@ -471,8 +486,12 @@ static void osd_request_async_done(struct request *req, int error)
 
        or->async_error = error;
 
-       if (error)
-               OSD_DEBUG("osd_request_async_done error recieved %d\n", error);
+       if (unlikely(error)) {
+               OSD_DEBUG("osd_request_async_done error recieved %d "
+                         "errors 0x%x\n", error, req->errors);
+               if (!req->errors) /* don't miss out on this one */
+                       req->errors = error;
+       }
 
        if (or->async_done)
                or->async_done(or, or->async_private);
@@ -1153,6 +1172,7 @@ int osd_req_decode_get_attr_list(struct osd_request *or,
                                "c=%d r=%d n=%d\n",
                                cur_bytes, returned_bytes, n);
                        oa->val_ptr = NULL;
+                       cur_bytes = returned_bytes; /* break the caller loop */
                        break;
                }
 
@@ -1436,6 +1456,15 @@ int osd_finalize_request(struct osd_request *or,
 }
 EXPORT_SYMBOL(osd_finalize_request);
 
+static bool _is_osd_security_code(int code)
+{
+       return  (code == osd_security_audit_value_frozen) ||
+               (code == osd_security_working_key_frozen) ||
+               (code == osd_nonce_not_unique) ||
+               (code == osd_nonce_timestamp_out_of_range) ||
+               (code == osd_invalid_dataout_buffer_integrity_check_value);
+}
+
 #define OSD_SENSE_PRINT1(fmt, a...) \
        do { \
                if (__cur_sense_need_output) \
@@ -1458,9 +1487,16 @@ int osd_req_decode_sense_full(struct osd_request *or,
 #else
        bool __cur_sense_need_output = !silent;
 #endif
+       int ret;
 
-       if (!or->request->errors)
+       if (likely(!or->request->errors)) {
+               osi->out_resid = 0;
+               osi->in_resid = 0;
                return 0;
+       }
+
+       osi = osi ? : &local_osi;
+       memset(osi, 0, sizeof(*osi));
 
        ssdb = or->request->sense;
        sense_len = or->request->sense_len;
@@ -1468,17 +1504,15 @@ int osd_req_decode_sense_full(struct osd_request *or,
                OSD_ERR("Block-layer returned error(0x%x) but "
                        "sense_len(%u) || key(%d) is empty\n",
                        or->request->errors, sense_len, ssdb->sense_key);
-               return -EIO;
+               goto analyze;
        }
 
        if ((ssdb->response_code != 0x72) && (ssdb->response_code != 0x73)) {
                OSD_ERR("Unrecognized scsi sense: rcode=%x length=%d\n",
                        ssdb->response_code, sense_len);
-               return -EIO;
+               goto analyze;
        }
 
-       osi = osi ? : &local_osi;
-       memset(osi, 0, sizeof(*osi));
        osi->key = ssdb->sense_key;
        osi->additional_code = be16_to_cpu(ssdb->additional_sense_code);
        original_sense_len = ssdb->additional_sense_length + 8;
@@ -1488,9 +1522,10 @@ int osd_req_decode_sense_full(struct osd_request *or,
                __cur_sense_need_output = (osi->key > scsi_sk_recovered_error);
 #endif
        OSD_SENSE_PRINT1("Main Sense information key=0x%x length(%d, %d) "
-                       "additional_code=0x%x\n",
+                       "additional_code=0x%x async_error=%d errors=0x%x\n",
                        osi->key, original_sense_len, sense_len,
-                       osi->additional_code);
+                       osi->additional_code, or->async_error,
+                       or->request->errors);
 
        if (original_sense_len < sense_len)
                sense_len = original_sense_len;
@@ -1569,15 +1604,14 @@ int osd_req_decode_sense_full(struct osd_request *or,
                {
                        struct osd_sense_attributes_data_descriptor
                                *osadd = cur_descriptor;
-                       int len = min(cur_len, sense_len);
-                       int i = 0;
+                       unsigned len = min(cur_len, sense_len);
                        struct osd_sense_attr *pattr = osadd->sense_attrs;
 
-                       while (len < 0) {
+                       while (len >= sizeof(*pattr)) {
                                u32 attr_page = be32_to_cpu(pattr->attr_page);
                                u32 attr_id = be32_to_cpu(pattr->attr_id);
 
-                               if (i++ == 0) {
+                               if (!osi->attr.attr_page) {
                                        osi->attr.attr_page = attr_page;
                                        osi->attr.attr_id = attr_id;
                                }
@@ -1588,6 +1622,8 @@ int osd_req_decode_sense_full(struct osd_request *or,
                                        bad_attr_list++;
                                        max_attr--;
                                }
+
+                               len -= sizeof(*pattr);
                                OSD_SENSE_PRINT2(
                                        "osd_sense_attribute_identification"
                                        "attr_page=0x%x attr_id=0x%x\n",
@@ -1621,7 +1657,50 @@ int osd_req_decode_sense_full(struct osd_request *or,
                cur_descriptor += cur_len;
        }
 
-       return (osi->key > scsi_sk_recovered_error) ? -EIO : 0;
+analyze:
+       if (!osi->key) {
+               /* scsi sense is Empty, the request was never issued to target
+                * linux return code might tell us what happened.
+                */
+               if (or->async_error == -ENOMEM)
+                       osi->osd_err_pri = OSD_ERR_PRI_RESOURCE;
+               else
+                       osi->osd_err_pri = OSD_ERR_PRI_UNREACHABLE;
+               ret = or->async_error;
+       } else if (osi->key <= scsi_sk_recovered_error) {
+               osi->osd_err_pri = 0;
+               ret = 0;
+       } else if (osi->additional_code == scsi_invalid_field_in_cdb) {
+               if (osi->cdb_field_offset == OSD_CFO_STARTING_BYTE) {
+                       osi->osd_err_pri = OSD_ERR_PRI_CLEAR_PAGES;
+                       ret = -EFAULT; /* caller should recover from this */
+               } else if (osi->cdb_field_offset == OSD_CFO_OBJECT_ID) {
+                       osi->osd_err_pri = OSD_ERR_PRI_NOT_FOUND;
+                       ret = -ENOENT;
+               } else if (osi->cdb_field_offset == OSD_CFO_PERMISSIONS) {
+                       osi->osd_err_pri = OSD_ERR_PRI_NO_ACCESS;
+                       ret = -EACCES;
+               } else {
+                       osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+                       ret = -EINVAL;
+               }
+       } else if (osi->additional_code == osd_quota_error) {
+               osi->osd_err_pri = OSD_ERR_PRI_NO_SPACE;
+               ret = -ENOSPC;
+       } else if (_is_osd_security_code(osi->additional_code)) {
+               osi->osd_err_pri = OSD_ERR_PRI_BAD_CRED;
+               ret = -EINVAL;
+       } else {
+               osi->osd_err_pri = OSD_ERR_PRI_EIO;
+               ret = -EIO;
+       }
+
+       if (or->out.req)
+               osi->out_resid = or->out.req->resid_len ?: or->out.total_bytes;
+       if (or->in.req)
+               osi->in_resid = or->in.req->resid_len ?: or->in.total_bytes;
+
+       return ret;
 }
 EXPORT_SYMBOL(osd_req_decode_sense_full);
 
index 0bdef33..0a90702 100644 (file)
@@ -71,8 +71,7 @@
 #define SCSI_OSD_MAX_MINOR 64
 
 static const char osd_name[] = "osd";
-static const char *osd_version_string = "open-osd 0.1.0";
-const char osd_symlink[] = "scsi_osd";
+static const char *osd_version_string = "open-osd 0.2.0";
 
 MODULE_AUTHOR("Boaz Harrosh <bharrosh@panasas.com>");
 MODULE_DESCRIPTION("open-osd Upper-Layer-Driver osd.ko");
@@ -82,15 +81,25 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_OSD);
 
 struct osd_uld_device {
        int minor;
-       struct kref kref;
+       struct device class_dev;
        struct cdev cdev;
        struct osd_dev od;
+       struct osd_dev_info odi;
        struct gendisk *disk;
-       struct device *class_member;
 };
 
-static void __uld_get(struct osd_uld_device *oud);
-static void __uld_put(struct osd_uld_device *oud);
+struct osd_dev_handle {
+       struct osd_dev od;
+       struct file *file;
+       struct osd_uld_device *oud;
+} ;
+
+static DEFINE_IDA(osd_minor_ida);
+
+static struct class osd_uld_class = {
+       .owner          = THIS_MODULE,
+       .name           = "scsi_osd",
+};
 
 /*
  * Char Device operations
@@ -101,7 +110,7 @@ static int osd_uld_open(struct inode *inode, struct file *file)
        struct osd_uld_device *oud = container_of(inode->i_cdev,
                                        struct osd_uld_device, cdev);
 
-       __uld_get(oud);
+       get_device(&oud->class_dev);
        /* cache osd_uld_device on file handle */
        file->private_data = oud;
        OSD_DEBUG("osd_uld_open %p\n", oud);
@@ -114,7 +123,7 @@ static int osd_uld_release(struct inode *inode, struct file *file)
 
        OSD_DEBUG("osd_uld_release %p\n", file->private_data);
        file->private_data = NULL;
-       __uld_put(oud);
+       put_device(&oud->class_dev);
        return 0;
 }
 
@@ -177,7 +186,7 @@ static const struct file_operations osd_fops = {
 struct osd_dev *osduld_path_lookup(const char *name)
 {
        struct osd_uld_device *oud;
-       struct osd_dev *od;
+       struct osd_dev_handle *odh;
        struct file *file;
        int error;
 
@@ -186,8 +195,8 @@ struct osd_dev *osduld_path_lookup(const char *name)
                return ERR_PTR(-EINVAL);
        }
 
-       od = kzalloc(sizeof(*od), GFP_KERNEL);
-       if (!od)
+       odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+       if (unlikely(!odh))
                return ERR_PTR(-ENOMEM);
 
        file = filp_open(name, O_RDWR, 0);
@@ -203,33 +212,134 @@ struct osd_dev *osduld_path_lookup(const char *name)
 
        oud = file->private_data;
 
-       *od = oud->od;
-       od->file = file;
+       odh->od = oud->od;
+       odh->file = file;
+       odh->oud = oud;
 
-       return od;
+       return &odh->od;
 
 close_file:
        fput(file);
 free_od:
-       kfree(od);
+       kfree(odh);
        return ERR_PTR(error);
 }
 EXPORT_SYMBOL(osduld_path_lookup);
 
-void osduld_put_device(struct osd_dev *od)
+static inline bool _the_same_or_null(const u8 *a1, unsigned a1_len,
+                                    const u8 *a2, unsigned a2_len)
 {
+       if (!a2_len) /* User string is Empty means don't care */
+               return true;
+
+       if (a1_len != a2_len)
+               return false;
+
+       return 0 == memcmp(a1, a2, a1_len);
+}
+
+struct find_oud_t {
+       const struct osd_dev_info *odi;
+       struct device *dev;
+       struct osd_uld_device *oud;
+} ;
+
+int _mach_odi(struct device *dev, void *find_data)
+{
+       struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+                                                 class_dev);
+       struct find_oud_t *fot = find_data;
+       const struct osd_dev_info *odi = fot->odi;
+
+       if (_the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+                             odi->systemid, odi->systemid_len) &&
+           _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+                             odi->osdname, odi->osdname_len)) {
+               OSD_DEBUG("found device sysid_len=%d osdname=%d\n",
+                         odi->systemid_len, odi->osdname_len);
+               fot->oud = oud;
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+/* osduld_info_lookup - Loop through all devices, return the requested osd_dev.
+ *
+ * if @odi->systemid_len and/or @odi->osdname_len are zero, they act as a don't
+ * care. .e.g if they're both zero /dev/osd0 is returned.
+ */
+struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi)
+{
+       struct find_oud_t find = {.odi = odi};
+
+       find.dev = class_find_device(&osd_uld_class, NULL, &find, _mach_odi);
+       if (likely(find.dev)) {
+               struct osd_dev_handle *odh = kzalloc(sizeof(*odh), GFP_KERNEL);
+
+               if (unlikely(!odh)) {
+                       put_device(find.dev);
+                       return ERR_PTR(-ENOMEM);
+               }
 
+               odh->od = find.oud->od;
+               odh->oud = find.oud;
+
+               return &odh->od;
+       }
+
+       return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL(osduld_info_lookup);
+
+void osduld_put_device(struct osd_dev *od)
+{
        if (od && !IS_ERR(od)) {
-               struct osd_uld_device *oud = od->file->private_data;
+               struct osd_dev_handle *odh =
+                               container_of(od, struct osd_dev_handle, od);
+               struct osd_uld_device *oud = odh->oud;
 
                BUG_ON(od->scsi_device != oud->od.scsi_device);
 
-               fput(od->file);
-               kfree(od);
+               /* If scsi has released the device (logout), and exofs has last
+                * reference on oud it will be freed by above osd_uld_release
+                * within fput below. But this will oops in cdev_release which
+                * is called after the fops->release. A get_/put_ pair makes
+                * sure we have a cdev for the duration of fput
+                */
+               if (odh->file) {
+                       get_device(&oud->class_dev);
+                       fput(odh->file);
+               }
+               put_device(&oud->class_dev);
+               kfree(odh);
        }
 }
 EXPORT_SYMBOL(osduld_put_device);
 
+const struct osd_dev_info *osduld_device_info(struct osd_dev *od)
+{
+       struct osd_dev_handle *odh =
+                               container_of(od, struct osd_dev_handle, od);
+       return &odh->oud->odi;
+}
+EXPORT_SYMBOL(osduld_device_info);
+
+bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi)
+{
+       struct osd_dev_handle *odh =
+                               container_of(od, struct osd_dev_handle, od);
+       struct osd_uld_device *oud = odh->oud;
+
+       return (oud->odi.systemid_len == odi->systemid_len) &&
+               _the_same_or_null(oud->odi.systemid, oud->odi.systemid_len,
+                                odi->systemid, odi->systemid_len) &&
+               (oud->odi.osdname_len == odi->osdname_len) &&
+               _the_same_or_null(oud->odi.osdname, oud->odi.osdname_len,
+                                 odi->osdname, odi->osdname_len);
+}
+EXPORT_SYMBOL(osduld_device_same);
+
 /*
  * Scsi Device operations
  */
@@ -250,14 +360,35 @@ static int __detect_osd(struct osd_uld_device *oud)
                OSD_ERR("warning: scsi_test_unit_ready failed\n");
 
        osd_sec_init_nosec_doall_caps(caps, &osd_root_object, false, true);
-       if (osd_auto_detect_ver(&oud->od, caps))
+       if (osd_auto_detect_ver(&oud->od, caps, &oud->odi))
                return -ENODEV;
 
        return 0;
 }
 
-static struct class *osd_sysfs_class;
-static DEFINE_IDA(osd_minor_ida);
+static void __remove(struct device *dev)
+{
+       struct osd_uld_device *oud = container_of(dev, struct osd_uld_device,
+                                                 class_dev);
+       struct scsi_device *scsi_device = oud->od.scsi_device;
+
+       kfree(oud->odi.osdname);
+
+       if (oud->cdev.owner)
+               cdev_del(&oud->cdev);
+
+       osd_dev_fini(&oud->od);
+       scsi_device_put(scsi_device);
+
+       OSD_INFO("osd_remove %s\n",
+                oud->disk ? oud->disk->disk_name : NULL);
+
+       if (oud->disk)
+               put_disk(oud->disk);
+       ida_remove(&osd_minor_ida, oud->minor);
+
+       kfree(oud);
+}
 
 static int osd_probe(struct device *dev)
 {
@@ -289,7 +420,6 @@ static int osd_probe(struct device *dev)
        if (NULL == oud)
                goto err_retract_minor;
 
-       kref_init(&oud->kref);
        dev_set_drvdata(dev, oud);
        oud->minor = minor;
 
@@ -327,18 +457,25 @@ static int osd_probe(struct device *dev)
                OSD_ERR("cdev_add failed\n");
                goto err_put_disk;
        }
-       kobject_get(&oud->cdev.kobj); /* 2nd ref see osd_remove() */
-
-       /* class_member */
-       oud->class_member = device_create(osd_sysfs_class, dev,
-               MKDEV(SCSI_OSD_MAJOR, oud->minor), "%s", disk->disk_name);
-       if (IS_ERR(oud->class_member)) {
-               OSD_ERR("class_device_create failed\n");
-               error = PTR_ERR(oud->class_member);
+
+       /* class device member */
+       oud->class_dev.devt = oud->cdev.dev;
+       oud->class_dev.class = &osd_uld_class;
+       oud->class_dev.parent = dev;
+       oud->class_dev.release = __remove;
+       error = dev_set_name(&oud->class_dev, disk->disk_name);
+       if (error) {
+               OSD_ERR("dev_set_name failed => %d\n", error);
+               goto err_put_cdev;
+       }
+
+       error = device_register(&oud->class_dev);
+       if (error) {
+               OSD_ERR("device_register failed => %d\n", error);
                goto err_put_cdev;
        }
 
-       dev_set_drvdata(oud->class_member, oud);
+       get_device(&oud->class_dev);
 
        OSD_INFO("osd_probe %s\n", disk->disk_name);
        return 0;
@@ -367,54 +504,12 @@ static int osd_remove(struct device *dev)
                        scsi_device);
        }
 
-       if (oud->class_member)
-               device_destroy(osd_sysfs_class,
-                              MKDEV(SCSI_OSD_MAJOR, oud->minor));
+       device_unregister(&oud->class_dev);
 
-       /* We have 2 references to the cdev. One is released here
-        * and also takes down the /dev/osdX mapping. The second
-        * Will be released in __remove() after all users have released
-        * the osd_uld_device.
-        */
-       if (oud->cdev.owner)
-               cdev_del(&oud->cdev);
-
-       __uld_put(oud);
+       put_device(&oud->class_dev);
        return 0;
 }
 
-static void __remove(struct kref *kref)
-{
-       struct osd_uld_device *oud = container_of(kref,
-                                       struct osd_uld_device, kref);
-       struct scsi_device *scsi_device = oud->od.scsi_device;
-
-       /* now let delete the char_dev */
-       kobject_put(&oud->cdev.kobj);
-
-       osd_dev_fini(&oud->od);
-       scsi_device_put(scsi_device);
-
-       OSD_INFO("osd_remove %s\n",
-                oud->disk ? oud->disk->disk_name : NULL);
-
-       if (oud->disk)
-               put_disk(oud->disk);
-
-       ida_remove(&osd_minor_ida, oud->minor);
-       kfree(oud);
-}
-
-static void __uld_get(struct osd_uld_device *oud)
-{
-       kref_get(&oud->kref);
-}
-
-static void __uld_put(struct osd_uld_device *oud)
-{
-       kref_put(&oud->kref, __remove);
-}
-
 /*
  * Global driver and scsi registration
  */
@@ -432,11 +527,10 @@ static int __init osd_uld_init(void)
 {
        int err;
 
-       osd_sysfs_class = class_create(THIS_MODULE, osd_symlink);
-       if (IS_ERR(osd_sysfs_class)) {
-               OSD_ERR("Unable to register sysfs class => %ld\n",
-                       PTR_ERR(osd_sysfs_class));
-               return PTR_ERR(osd_sysfs_class);
+       err = class_register(&osd_uld_class);
+       if (err) {
+               OSD_ERR("Unable to register sysfs class => %d\n", err);
+               return err;
        }
 
        err = register_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0),
@@ -459,7 +553,7 @@ static int __init osd_uld_init(void)
 err_out_chrdev:
        unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
 err_out:
-       class_destroy(osd_sysfs_class);
+       class_unregister(&osd_uld_class);
        return err;
 }
 
@@ -467,7 +561,7 @@ static void __exit osd_uld_exit(void)
 {
        scsi_unregister_driver(&osd_driver.gendrv);
        unregister_chrdev_region(MKDEV(SCSI_OSD_MAJOR, 0), SCSI_OSD_MAX_MINOR);
-       class_destroy(osd_sysfs_class);
+       class_unregister(&osd_uld_class);
        OSD_INFO("UNLOADED %s\n", osd_version_string);
 }
 
diff --git a/drivers/scsi/pm8001/Makefile b/drivers/scsi/pm8001/Makefile
new file mode 100644 (file)
index 0000000..52f0429
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# Kernel configuration file for the PM8001 SAS/SATA 8x6G based HBA driver
+#
+# Copyright (C) 2008-2009  USI Co., Ltd.
+
+
+obj-$(CONFIG_SCSI_PM8001) += pm8001.o
+pm8001-y += pm8001_init.o \
+               pm8001_sas.o  \
+               pm8001_ctl.o  \
+               pm8001_hwi.o
+
diff --git a/drivers/scsi/pm8001/pm8001_chips.h b/drivers/scsi/pm8001/pm8001_chips.h
new file mode 100644 (file)
index 0000000..4efa4d0
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_CHIPS_H_
+#define _PM8001_CHIPS_H_
+
+static inline u32 pm8001_read_32(void *virt_addr)
+{
+       return *((u32 *)virt_addr);
+}
+
+static inline void pm8001_write_32(void *addr, u32 offset, u32 val)
+{
+       *((u32 *)(addr + offset)) = val;
+}
+
+static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar,
+               u32 offset)
+{
+       return readl(pm8001_ha->io_mem[bar].memvirtaddr + offset);
+}
+
+static inline void pm8001_cw32(struct pm8001_hba_info *pm8001_ha, u32 bar,
+               u32 addr, u32 val)
+{
+       writel(val, pm8001_ha->io_mem[bar].memvirtaddr + addr);
+}
+static inline u32 pm8001_mr32(void __iomem *addr, u32 offset)
+{
+       return readl(addr + offset);
+}
+static inline void pm8001_mw32(void __iomem *addr, u32 offset, u32 val)
+{
+       writel(val, addr + offset);
+}
+static inline u32 get_pci_bar_index(u32 pcibar)
+{
+               switch (pcibar) {
+               case 0x18:
+               case 0x1C:
+                       return 1;
+               case 0x20:
+                       return 2;
+               case 0x24:
+                       return 3;
+               default:
+                       return 0;
+       }
+}
+
+#endif  /* _PM8001_CHIPS_H_ */
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
new file mode 100644 (file)
index 0000000..14b13ac
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#include <linux/firmware.h>
+#include "pm8001_sas.h"
+#include "pm8001_ctl.h"
+
+/* scsi host attributes */
+
+/**
+ * pm8001_ctl_mpi_interface_rev_show - MPI interface revision number
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_mpi_interface_rev_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+               pm8001_ha->main_cfg_tbl.interface_rev);
+}
+static
+DEVICE_ATTR(interface_rev, S_IRUGO, pm8001_ctl_mpi_interface_rev_show, NULL);
+
+/**
+ * pm8001_ctl_fw_version_show - firmware version
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_fw_version_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x.%02x\n",
+                      (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 24),
+                      (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 16),
+                      (u8)(pm8001_ha->main_cfg_tbl.firmware_rev >> 8),
+                      (u8)(pm8001_ha->main_cfg_tbl.firmware_rev));
+}
+static DEVICE_ATTR(fw_version, S_IRUGO, pm8001_ctl_fw_version_show, NULL);
+/**
+ * pm8001_ctl_max_out_io_show - max outstanding io supported
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_out_io_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       pm8001_ha->main_cfg_tbl.max_out_io);
+}
+static DEVICE_ATTR(max_out_io, S_IRUGO, pm8001_ctl_max_out_io_show, NULL);
+/**
+ * pm8001_ctl_max_devices_show - max devices support
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_devices_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       return snprintf(buf, PAGE_SIZE, "%04d\n",
+                       (u16)(pm8001_ha->main_cfg_tbl.max_sgl >> 16));
+}
+static DEVICE_ATTR(max_devices, S_IRUGO, pm8001_ctl_max_devices_show, NULL);
+/**
+ * pm8001_ctl_max_sg_list_show - max sg list supported iff not 0.0 for no
+ * hardware limitation
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_max_sg_list_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       return snprintf(buf, PAGE_SIZE, "%04d\n",
+                       pm8001_ha->main_cfg_tbl.max_sgl & 0x0000FFFF);
+}
+static DEVICE_ATTR(max_sg_list, S_IRUGO, pm8001_ctl_max_sg_list_show, NULL);
+
+#define SAS_1_0 0x1
+#define SAS_1_1 0x2
+#define SAS_2_0 0x4
+
+static ssize_t
+show_sas_spec_support_status(unsigned int mode, char *buf)
+{
+       ssize_t len = 0;
+
+       if (mode & SAS_1_1)
+               len = sprintf(buf, "%s", "SAS1.1");
+       if (mode & SAS_2_0)
+               len += sprintf(buf + len, "%s%s", len ? ", " : "", "SAS2.0");
+       len += sprintf(buf + len, "\n");
+
+       return len;
+}
+
+/**
+ * pm8001_ctl_sas_spec_support_show - sas spec supported
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_sas_spec_support_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       unsigned int mode;
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       mode = (pm8001_ha->main_cfg_tbl.ctrl_cap_flag & 0xfe000000)>>25;
+       return show_sas_spec_support_status(mode, buf);
+}
+static DEVICE_ATTR(sas_spec_support, S_IRUGO,
+                  pm8001_ctl_sas_spec_support_show, NULL);
+
+/**
+ * pm8001_ctl_sas_address_show - sas address
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * This is the controller sas address
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_host_sas_address_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       return snprintf(buf, PAGE_SIZE, "0x%016llx\n",
+                       be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr));
+}
+static DEVICE_ATTR(host_sas_address, S_IRUGO,
+                  pm8001_ctl_host_sas_address_show, NULL);
+
+/**
+ * pm8001_ctl_logging_level_show - logging level
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read/write' shost attribute.
+ */
+static ssize_t pm8001_ctl_logging_level_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       return snprintf(buf, PAGE_SIZE, "%08xh\n", pm8001_ha->logging_level);
+}
+static ssize_t pm8001_ctl_logging_level_store(struct device *cdev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       int val = 0;
+
+       if (sscanf(buf, "%x", &val) != 1)
+               return -EINVAL;
+
+       pm8001_ha->logging_level = val;
+       return strlen(buf);
+}
+
+static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR,
+       pm8001_ctl_logging_level_show, pm8001_ctl_logging_level_store);
+/**
+ * pm8001_ctl_aap_log_show - aap1 event log
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_aap_log_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       int i;
+#define AAP1_MEMMAP(r, c) \
+       (*(u32 *)((u8*)pm8001_ha->memoryMap.region[AAP1].virt_ptr + (r) * 32 \
+       + (c)))
+
+       char *str = buf;
+       int max = 2;
+       for (i = 0; i < max; i++) {
+               str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
+                              "0x%08x 0x%08x\n",
+                              AAP1_MEMMAP(i, 0),
+                              AAP1_MEMMAP(i, 4),
+                              AAP1_MEMMAP(i, 8),
+                              AAP1_MEMMAP(i, 12),
+                              AAP1_MEMMAP(i, 16),
+                              AAP1_MEMMAP(i, 20),
+                              AAP1_MEMMAP(i, 24),
+                              AAP1_MEMMAP(i, 28));
+       }
+
+       return str - buf;
+}
+static DEVICE_ATTR(aap_log, S_IRUGO, pm8001_ctl_aap_log_show, NULL);
+/**
+ * pm8001_ctl_aap_log_show - IOP event log
+ * @cdev: pointer to embedded class device
+ * @buf: the buffer returned
+ *
+ * A sysfs 'read-only' shost attribute.
+ */
+static ssize_t pm8001_ctl_iop_log_show(struct device *cdev,
+       struct device_attribute *attr, char *buf)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+#define IOP_MEMMAP(r, c) \
+       (*(u32 *)((u8*)pm8001_ha->memoryMap.region[IOP].virt_ptr + (r) * 32 \
+       + (c)))
+       int i;
+       char *str = buf;
+       int max = 2;
+       for (i = 0; i < max; i++) {
+               str += sprintf(str, "0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x"
+                              "0x%08x 0x%08x\n",
+                              IOP_MEMMAP(i, 0),
+                              IOP_MEMMAP(i, 4),
+                              IOP_MEMMAP(i, 8),
+                              IOP_MEMMAP(i, 12),
+                              IOP_MEMMAP(i, 16),
+                              IOP_MEMMAP(i, 20),
+                              IOP_MEMMAP(i, 24),
+                              IOP_MEMMAP(i, 28));
+       }
+
+       return str - buf;
+}
+static DEVICE_ATTR(iop_log, S_IRUGO, pm8001_ctl_iop_log_show, NULL);
+
+#define FLASH_CMD_NONE      0x00
+#define FLASH_CMD_UPDATE    0x01
+#define FLASH_CMD_SET_NVMD    0x02
+
+struct flash_command {
+     u8      command[8];
+     int     code;
+};
+
+static struct flash_command flash_command_table[] =
+{
+     {"set_nvmd",    FLASH_CMD_SET_NVMD},
+     {"update",      FLASH_CMD_UPDATE},
+     {"",            FLASH_CMD_NONE} /* Last entry should be NULL. */
+};
+
+struct error_fw {
+     char    *reason;
+     int     err_code;
+};
+
+static struct error_fw flash_error_table[] =
+{
+     {"Failed to open fw image file",  FAIL_OPEN_BIOS_FILE},
+     {"image header mismatch",         FLASH_UPDATE_HDR_ERR},
+     {"image offset mismatch",         FLASH_UPDATE_OFFSET_ERR},
+     {"image CRC Error",               FLASH_UPDATE_CRC_ERR},
+     {"image length Error.",           FLASH_UPDATE_LENGTH_ERR},
+     {"Failed to program flash chip",  FLASH_UPDATE_HW_ERR},
+     {"Flash chip not supported.",     FLASH_UPDATE_DNLD_NOT_SUPPORTED},
+     {"Flash update disabled.",                FLASH_UPDATE_DISABLED},
+     {"Flash in progress",             FLASH_IN_PROGRESS},
+     {"Image file size Error",         FAIL_FILE_SIZE},
+     {"Input parameter error",         FAIL_PARAMETERS},
+     {"Out of memory",                 FAIL_OUT_MEMORY},
+     {"OK", 0} /* Last entry err_code = 0. */
+};
+
+static int pm8001_set_nvmd(struct pm8001_hba_info *pm8001_ha)
+{
+       struct pm8001_ioctl_payload     *payload;
+       DECLARE_COMPLETION_ONSTACK(completion);
+       u8              *ioctlbuffer = NULL;
+       u32             length = 0;
+       u32             ret = 0;
+
+       length = 1024 * 5 + sizeof(*payload) - 1;
+       ioctlbuffer = kzalloc(length, GFP_KERNEL);
+       if (!ioctlbuffer)
+               return -ENOMEM;
+       if ((pm8001_ha->fw_image->size <= 0) ||
+           (pm8001_ha->fw_image->size > 4096)) {
+               ret = FAIL_FILE_SIZE;
+               goto out;
+       }
+       payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
+       memcpy((u8 *)payload->func_specific, (u8 *)pm8001_ha->fw_image->data,
+                               pm8001_ha->fw_image->size);
+       payload->length = pm8001_ha->fw_image->size;
+       payload->id = 0;
+       pm8001_ha->nvmd_completion = &completion;
+       ret = PM8001_CHIP_DISP->set_nvmd_req(pm8001_ha, payload);
+       wait_for_completion(&completion);
+out:
+       kfree(ioctlbuffer);
+       return ret;
+}
+
+static int pm8001_update_flash(struct pm8001_hba_info *pm8001_ha)
+{
+       struct pm8001_ioctl_payload     *payload;
+       DECLARE_COMPLETION_ONSTACK(completion);
+       u8              *ioctlbuffer = NULL;
+       u32             length = 0;
+       struct fw_control_info  *fwControl;
+       u32             loopNumber, loopcount = 0;
+       u32             sizeRead = 0;
+       u32             partitionSize, partitionSizeTmp;
+       u32             ret = 0;
+       u32             partitionNumber = 0;
+       struct pm8001_fw_image_header *image_hdr;
+
+       length = 1024 * 16 + sizeof(*payload) - 1;
+       ioctlbuffer = kzalloc(length, GFP_KERNEL);
+       image_hdr = (struct pm8001_fw_image_header *)pm8001_ha->fw_image->data;
+       if (!ioctlbuffer)
+               return -ENOMEM;
+       if (pm8001_ha->fw_image->size < 28) {
+               ret = FAIL_FILE_SIZE;
+               goto out;
+       }
+
+       while (sizeRead < pm8001_ha->fw_image->size) {
+               partitionSizeTmp =
+                       *(u32 *)((u8 *)&image_hdr->image_length + sizeRead);
+               partitionSize = be32_to_cpu(partitionSizeTmp);
+               loopcount = (partitionSize + HEADER_LEN)/IOCTL_BUF_SIZE;
+               if (loopcount % IOCTL_BUF_SIZE)
+                       loopcount++;
+               if (loopcount == 0)
+                       loopcount++;
+               for (loopNumber = 0; loopNumber < loopcount; loopNumber++) {
+                       payload = (struct pm8001_ioctl_payload *)ioctlbuffer;
+                       payload->length = 1024*16;
+                       payload->id = 0;
+                       fwControl =
+                             (struct fw_control_info *)payload->func_specific;
+                       fwControl->len = IOCTL_BUF_SIZE;   /* IN */
+                       fwControl->size = partitionSize + HEADER_LEN;/* IN */
+                       fwControl->retcode = 0;/* OUT */
+                       fwControl->offset = loopNumber * IOCTL_BUF_SIZE;/*OUT */
+
+               /* for the last chunk of data in case file size is not even with
+               4k, load only the rest*/
+               if (((loopcount-loopNumber) == 1) &&
+                       ((partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE)) {
+                       fwControl->len =
+                               (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+                       memcpy((u8 *)fwControl->buffer,
+                               (u8 *)pm8001_ha->fw_image->data + sizeRead,
+                               (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE);
+                       sizeRead +=
+                               (partitionSize + HEADER_LEN) % IOCTL_BUF_SIZE;
+               } else {
+                       memcpy((u8 *)fwControl->buffer,
+                               (u8 *)pm8001_ha->fw_image->data + sizeRead,
+                               IOCTL_BUF_SIZE);
+                       sizeRead += IOCTL_BUF_SIZE;
+               }
+
+               pm8001_ha->nvmd_completion = &completion;
+               ret = PM8001_CHIP_DISP->fw_flash_update_req(pm8001_ha, payload);
+               wait_for_completion(&completion);
+               if (ret || (fwControl->retcode > FLASH_UPDATE_IN_PROGRESS)) {
+                       ret = fwControl->retcode;
+                       kfree(ioctlbuffer);
+                       ioctlbuffer = NULL;
+                       break;
+               }
+       }
+       if (ret)
+               break;
+       partitionNumber++;
+}
+out:
+       kfree(ioctlbuffer);
+       return ret;
+}
+static ssize_t pm8001_store_update_fw(struct device *cdev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       char *cmd_ptr, *filename_ptr;
+       int res, i;
+       int flash_command = FLASH_CMD_NONE;
+       int err = 0;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EACCES;
+
+       cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+
+       if (!cmd_ptr) {
+               err = FAIL_OUT_MEMORY;
+               goto out;
+       }
+
+       filename_ptr = cmd_ptr + count;
+       res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
+       if (res != 2) {
+               err = FAIL_PARAMETERS;
+               goto out1;
+       }
+
+       for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
+               if (!memcmp(flash_command_table[i].command,
+                                cmd_ptr, strlen(cmd_ptr))) {
+                       flash_command = flash_command_table[i].code;
+                       break;
+               }
+       }
+       if (flash_command == FLASH_CMD_NONE) {
+               err = FAIL_PARAMETERS;
+               goto out1;
+       }
+
+       if (pm8001_ha->fw_status == FLASH_IN_PROGRESS) {
+               err = FLASH_IN_PROGRESS;
+               goto out1;
+       }
+       err = request_firmware(&pm8001_ha->fw_image,
+                              filename_ptr,
+                              pm8001_ha->dev);
+
+       if (err) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Failed to load firmware image file %s,"
+                       " error %d\n", filename_ptr, err));
+               err = FAIL_OPEN_BIOS_FILE;
+               goto out1;
+       }
+
+       switch (flash_command) {
+       case FLASH_CMD_UPDATE:
+               pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+               err = pm8001_update_flash(pm8001_ha);
+               break;
+       case FLASH_CMD_SET_NVMD:
+               pm8001_ha->fw_status = FLASH_IN_PROGRESS;
+               err = pm8001_set_nvmd(pm8001_ha);
+               break;
+       default:
+               pm8001_ha->fw_status = FAIL_PARAMETERS;
+               err = FAIL_PARAMETERS;
+               break;
+       }
+       release_firmware(pm8001_ha->fw_image);
+out1:
+       kfree(cmd_ptr);
+out:
+       pm8001_ha->fw_status = err;
+
+       if (!err)
+               return count;
+       else
+               return -err;
+}
+
+static ssize_t pm8001_show_update_fw(struct device *cdev,
+                                    struct device_attribute *attr, char *buf)
+{
+       int i;
+       struct Scsi_Host *shost = class_to_shost(cdev);
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+
+       for (i = 0; flash_error_table[i].err_code != 0; i++) {
+               if (flash_error_table[i].err_code == pm8001_ha->fw_status)
+                       break;
+       }
+       if (pm8001_ha->fw_status != FLASH_IN_PROGRESS)
+               pm8001_ha->fw_status = FLASH_OK;
+
+       return snprintf(buf, PAGE_SIZE, "status=%x %s\n",
+                       flash_error_table[i].err_code,
+                       flash_error_table[i].reason);
+}
+
+static DEVICE_ATTR(update_fw, S_IRUGO|S_IWUGO,
+       pm8001_show_update_fw, pm8001_store_update_fw);
+struct device_attribute *pm8001_host_attrs[] = {
+       &dev_attr_interface_rev,
+       &dev_attr_fw_version,
+       &dev_attr_update_fw,
+       &dev_attr_aap_log,
+       &dev_attr_iop_log,
+       &dev_attr_max_out_io,
+       &dev_attr_max_devices,
+       &dev_attr_max_sg_list,
+       &dev_attr_sas_spec_support,
+       &dev_attr_logging_level,
+       &dev_attr_host_sas_address,
+       NULL,
+};
+
diff --git a/drivers/scsi/pm8001/pm8001_ctl.h b/drivers/scsi/pm8001/pm8001_ctl.h
new file mode 100644 (file)
index 0000000..22644de
--- /dev/null
@@ -0,0 +1,67 @@
+ /*
+  * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+  *
+  * Copyright (c) 2008-2009 USI Co., Ltd.
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  * 1. Redistributions of source code must retain the above copyright
+  *    notice, this list of conditions, and the following disclaimer,
+  *    without modification.
+  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+  *    substantially similar to the "NO WARRANTY" disclaimer below
+  *    ("Disclaimer") and any redistribution must be conditioned upon
+  *    including a substantially similar Disclaimer requirement for further
+  *    binary redistribution.
+  * 3. Neither the names of the above-listed copyright holders nor the names
+  *    of any contributors may be used to endorse or promote products derived
+  *    from this software without specific prior written permission.
+  *
+  * Alternatively, this software may be distributed under the terms of the
+  * GNU General Public License ("GPL") version 2 as published by the Free
+  * Software Foundation.
+  *
+  * NO WARRANTY
+  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  * POSSIBILITY OF SUCH DAMAGES.
+  *
+  */
+
+#ifndef PM8001_CTL_H_INCLUDED
+#define PM8001_CTL_H_INCLUDED
+
+#define IOCTL_BUF_SIZE         4096
+#define HEADER_LEN                     28
+#define SIZE_OFFSET                    16
+
+struct pm8001_ioctl_payload {
+       u32     signature;
+       u16     major_function;
+       u16     minor_function;
+       u16     length;
+       u16     status;
+       u16     offset;
+       u16     id;
+       u8      func_specific[1];
+};
+
+#define FLASH_OK                        0x000000
+#define FAIL_OPEN_BIOS_FILE             0x000100
+#define FAIL_FILE_SIZE                  0x000a00
+#define FAIL_PARAMETERS                 0x000b00
+#define FAIL_OUT_MEMORY                 0x000c00
+#define FLASH_IN_PROGRESS               0x001000
+
+#endif /* PM8001_CTL_H_INCLUDED */
+
diff --git a/drivers/scsi/pm8001/pm8001_defs.h b/drivers/scsi/pm8001/pm8001_defs.h
new file mode 100644 (file)
index 0000000..944afad
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_DEFS_H_
+#define _PM8001_DEFS_H_
+
+enum chip_flavors {
+       chip_8001,
+};
+#define USI_MAX_MEMCNT                 9
+#define PM8001_MAX_DMA_SG              SG_ALL
+enum phy_speed {
+       PHY_SPEED_15 = 0x01,
+       PHY_SPEED_30 = 0x02,
+       PHY_SPEED_60 = 0x04,
+};
+
+enum data_direction {
+       DATA_DIR_NONE = 0x0,    /* NO TRANSFER */
+       DATA_DIR_IN = 0x01,     /* INBOUND */
+       DATA_DIR_OUT = 0x02,    /* OUTBOUND */
+       DATA_DIR_BYRECIPIENT = 0x04, /* UNSPECIFIED */
+};
+
+enum port_type {
+       PORT_TYPE_SAS = (1L << 1),
+       PORT_TYPE_SATA = (1L << 0),
+};
+
+/* driver compile-time configuration */
+#define        PM8001_MAX_CCB           512    /* max ccbs supported */
+#define        PM8001_MAX_INB_NUM       1
+#define        PM8001_MAX_OUTB_NUM      1
+#define        PM8001_CAN_QUEUE         128    /* SCSI Queue depth */
+
+/* unchangeable hardware details */
+#define        PM8001_MAX_PHYS          8      /* max. possible phys */
+#define        PM8001_MAX_PORTS         8      /* max. possible ports */
+#define        PM8001_MAX_DEVICES       1024   /* max supported device */
+
+enum memory_region_num {
+       AAP1 = 0x0, /* application acceleration processor */
+       IOP,        /* IO processor */
+       CI,         /* consumer index */
+       PI,         /* producer index */
+       IB,         /* inbound queue */
+       OB,         /* outbound queue */
+       NVMD,       /* NVM device */
+       DEV_MEM,    /* memory for devices */
+       CCB_MEM,    /* memory for command control block */
+};
+#define        PM8001_EVENT_LOG_SIZE    (128 * 1024)
+
+/*error code*/
+enum mpi_err {
+       MPI_IO_STATUS_SUCCESS = 0x0,
+       MPI_IO_STATUS_BUSY = 0x01,
+       MPI_IO_STATUS_FAIL = 0x02,
+};
+
+/**
+ * Phy Control constants
+ */
+enum phy_control_type {
+       PHY_LINK_RESET = 0x01,
+       PHY_HARD_RESET = 0x02,
+       PHY_NOTIFY_ENABLE_SPINUP = 0x10,
+};
+
+enum pm8001_hba_info_flags {
+       PM8001F_INIT_TIME       = (1U << 0),
+       PM8001F_RUN_TIME        = (1U << 1),
+};
+
+#endif
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
new file mode 100644 (file)
index 0000000..a3de306
--- /dev/null
@@ -0,0 +1,4458 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+ #include "pm8001_sas.h"
+ #include "pm8001_hwi.h"
+ #include "pm8001_chips.h"
+ #include "pm8001_ctl.h"
+
+/**
+ * read_main_config_table - read the configure table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit read_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+       void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+       pm8001_ha->main_cfg_tbl.signature       = pm8001_mr32(address, 0x00);
+       pm8001_ha->main_cfg_tbl.interface_rev   = pm8001_mr32(address, 0x04);
+       pm8001_ha->main_cfg_tbl.firmware_rev    = pm8001_mr32(address, 0x08);
+       pm8001_ha->main_cfg_tbl.max_out_io      = pm8001_mr32(address, 0x0C);
+       pm8001_ha->main_cfg_tbl.max_sgl         = pm8001_mr32(address, 0x10);
+       pm8001_ha->main_cfg_tbl.ctrl_cap_flag   = pm8001_mr32(address, 0x14);
+       pm8001_ha->main_cfg_tbl.gst_offset      = pm8001_mr32(address, 0x18);
+       pm8001_ha->main_cfg_tbl.inbound_queue_offset =
+               pm8001_mr32(address, MAIN_IBQ_OFFSET);
+       pm8001_ha->main_cfg_tbl.outbound_queue_offset =
+               pm8001_mr32(address, MAIN_OBQ_OFFSET);
+       pm8001_ha->main_cfg_tbl.hda_mode_flag   =
+               pm8001_mr32(address, MAIN_HDA_FLAGS_OFFSET);
+
+       /* read analog Setting offset from the configuration table */
+       pm8001_ha->main_cfg_tbl.anolog_setup_table_offset =
+               pm8001_mr32(address, MAIN_ANALOG_SETUP_OFFSET);
+
+       /* read Error Dump Offset and Length */
+       pm8001_ha->main_cfg_tbl.fatal_err_dump_offset0 =
+               pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_OFFSET);
+       pm8001_ha->main_cfg_tbl.fatal_err_dump_length0 =
+               pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP0_LENGTH);
+       pm8001_ha->main_cfg_tbl.fatal_err_dump_offset1 =
+               pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_OFFSET);
+       pm8001_ha->main_cfg_tbl.fatal_err_dump_length1 =
+               pm8001_mr32(address, MAIN_FATAL_ERROR_RDUMP1_LENGTH);
+}
+
+/**
+ * read_general_status_table - read the general status table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_general_status_table(struct pm8001_hba_info *pm8001_ha)
+{
+       void __iomem *address = pm8001_ha->general_stat_tbl_addr;
+       pm8001_ha->gs_tbl.gst_len_mpistate      = pm8001_mr32(address, 0x00);
+       pm8001_ha->gs_tbl.iq_freeze_state0      = pm8001_mr32(address, 0x04);
+       pm8001_ha->gs_tbl.iq_freeze_state1      = pm8001_mr32(address, 0x08);
+       pm8001_ha->gs_tbl.msgu_tcnt             = pm8001_mr32(address, 0x0C);
+       pm8001_ha->gs_tbl.iop_tcnt              = pm8001_mr32(address, 0x10);
+       pm8001_ha->gs_tbl.reserved              = pm8001_mr32(address, 0x14);
+       pm8001_ha->gs_tbl.phy_state[0]  = pm8001_mr32(address, 0x18);
+       pm8001_ha->gs_tbl.phy_state[1]  = pm8001_mr32(address, 0x1C);
+       pm8001_ha->gs_tbl.phy_state[2]  = pm8001_mr32(address, 0x20);
+       pm8001_ha->gs_tbl.phy_state[3]  = pm8001_mr32(address, 0x24);
+       pm8001_ha->gs_tbl.phy_state[4]  = pm8001_mr32(address, 0x28);
+       pm8001_ha->gs_tbl.phy_state[5]  = pm8001_mr32(address, 0x2C);
+       pm8001_ha->gs_tbl.phy_state[6]  = pm8001_mr32(address, 0x30);
+       pm8001_ha->gs_tbl.phy_state[7]  = pm8001_mr32(address, 0x34);
+       pm8001_ha->gs_tbl.reserved1             = pm8001_mr32(address, 0x38);
+       pm8001_ha->gs_tbl.reserved2             = pm8001_mr32(address, 0x3C);
+       pm8001_ha->gs_tbl.reserved3             = pm8001_mr32(address, 0x40);
+       pm8001_ha->gs_tbl.recover_err_info[0]   = pm8001_mr32(address, 0x44);
+       pm8001_ha->gs_tbl.recover_err_info[1]   = pm8001_mr32(address, 0x48);
+       pm8001_ha->gs_tbl.recover_err_info[2]   = pm8001_mr32(address, 0x4C);
+       pm8001_ha->gs_tbl.recover_err_info[3]   = pm8001_mr32(address, 0x50);
+       pm8001_ha->gs_tbl.recover_err_info[4]   = pm8001_mr32(address, 0x54);
+       pm8001_ha->gs_tbl.recover_err_info[5]   = pm8001_mr32(address, 0x58);
+       pm8001_ha->gs_tbl.recover_err_info[6]   = pm8001_mr32(address, 0x5C);
+       pm8001_ha->gs_tbl.recover_err_info[7]   = pm8001_mr32(address, 0x60);
+}
+
+/**
+ * read_inbnd_queue_table - read the inbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+       int inbQ_num = 1;
+       int i;
+       void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+       for (i = 0; i < inbQ_num; i++) {
+               u32 offset = i * 0x20;
+               pm8001_ha->inbnd_q_tbl[i].pi_pci_bar =
+                     get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
+               pm8001_ha->inbnd_q_tbl[i].pi_offset =
+                       pm8001_mr32(address, (offset + 0x18));
+       }
+}
+
+/**
+ * read_outbnd_queue_table - read the outbound queue table and save it.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+read_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha)
+{
+       int outbQ_num = 1;
+       int i;
+       void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+       for (i = 0; i < outbQ_num; i++) {
+               u32 offset = i * 0x24;
+               pm8001_ha->outbnd_q_tbl[i].ci_pci_bar =
+                     get_pci_bar_index(pm8001_mr32(address, (offset + 0x14)));
+               pm8001_ha->outbnd_q_tbl[i].ci_offset =
+                       pm8001_mr32(address, (offset + 0x18));
+       }
+}
+
+/**
+ * init_default_table_values - init the default table.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+init_default_table_values(struct pm8001_hba_info *pm8001_ha)
+{
+       int qn = 1;
+       int i;
+       u32 offsetib, offsetob;
+       void __iomem *addressib = pm8001_ha->inbnd_q_tbl_addr;
+       void __iomem *addressob = pm8001_ha->outbnd_q_tbl_addr;
+
+       pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd                     = 0;
+       pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3                = 0;
+       pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7                = 0;
+       pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3               = 0;
+       pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7               = 0;
+       pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3       = 0;
+       pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7       = 0;
+       pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3   = 0;
+       pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7   = 0;
+       pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3   = 0;
+       pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7   = 0;
+
+       pm8001_ha->main_cfg_tbl.upper_event_log_addr            =
+               pm8001_ha->memoryMap.region[AAP1].phys_addr_hi;
+       pm8001_ha->main_cfg_tbl.lower_event_log_addr            =
+               pm8001_ha->memoryMap.region[AAP1].phys_addr_lo;
+       pm8001_ha->main_cfg_tbl.event_log_size  = PM8001_EVENT_LOG_SIZE;
+       pm8001_ha->main_cfg_tbl.event_log_option                = 0x01;
+       pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr        =
+               pm8001_ha->memoryMap.region[IOP].phys_addr_hi;
+       pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr        =
+               pm8001_ha->memoryMap.region[IOP].phys_addr_lo;
+       pm8001_ha->main_cfg_tbl.iop_event_log_size      = PM8001_EVENT_LOG_SIZE;
+       pm8001_ha->main_cfg_tbl.iop_event_log_option            = 0x01;
+       pm8001_ha->main_cfg_tbl.fatal_err_interrupt             = 0x01;
+       for (i = 0; i < qn; i++) {
+               pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt  =
+                       0x00000100 | (0x00000040 << 16) | (0x00<<30);
+               pm8001_ha->inbnd_q_tbl[i].upper_base_addr       =
+                       pm8001_ha->memoryMap.region[IB].phys_addr_hi;
+               pm8001_ha->inbnd_q_tbl[i].lower_base_addr       =
+               pm8001_ha->memoryMap.region[IB].phys_addr_lo;
+               pm8001_ha->inbnd_q_tbl[i].base_virt             =
+                       (u8 *)pm8001_ha->memoryMap.region[IB].virt_ptr;
+               pm8001_ha->inbnd_q_tbl[i].total_length          =
+                       pm8001_ha->memoryMap.region[IB].total_len;
+               pm8001_ha->inbnd_q_tbl[i].ci_upper_base_addr    =
+                       pm8001_ha->memoryMap.region[CI].phys_addr_hi;
+               pm8001_ha->inbnd_q_tbl[i].ci_lower_base_addr    =
+                       pm8001_ha->memoryMap.region[CI].phys_addr_lo;
+               pm8001_ha->inbnd_q_tbl[i].ci_virt               =
+                       pm8001_ha->memoryMap.region[CI].virt_ptr;
+               offsetib = i * 0x20;
+               pm8001_ha->inbnd_q_tbl[i].pi_pci_bar            =
+                       get_pci_bar_index(pm8001_mr32(addressib,
+                               (offsetib + 0x14)));
+               pm8001_ha->inbnd_q_tbl[i].pi_offset             =
+                       pm8001_mr32(addressib, (offsetib + 0x18));
+               pm8001_ha->inbnd_q_tbl[i].producer_idx          = 0;
+               pm8001_ha->inbnd_q_tbl[i].consumer_index        = 0;
+       }
+       for (i = 0; i < qn; i++) {
+               pm8001_ha->outbnd_q_tbl[i].element_size_cnt     =
+                       256 | (64 << 16) | (1<<30);
+               pm8001_ha->outbnd_q_tbl[i].upper_base_addr      =
+                       pm8001_ha->memoryMap.region[OB].phys_addr_hi;
+               pm8001_ha->outbnd_q_tbl[i].lower_base_addr      =
+                       pm8001_ha->memoryMap.region[OB].phys_addr_lo;
+               pm8001_ha->outbnd_q_tbl[i].base_virt            =
+                       (u8 *)pm8001_ha->memoryMap.region[OB].virt_ptr;
+               pm8001_ha->outbnd_q_tbl[i].total_length         =
+                       pm8001_ha->memoryMap.region[OB].total_len;
+               pm8001_ha->outbnd_q_tbl[i].pi_upper_base_addr   =
+                       pm8001_ha->memoryMap.region[PI].phys_addr_hi;
+               pm8001_ha->outbnd_q_tbl[i].pi_lower_base_addr   =
+                       pm8001_ha->memoryMap.region[PI].phys_addr_lo;
+               pm8001_ha->outbnd_q_tbl[i].interrup_vec_cnt_delay       =
+                       0 | (10 << 16) | (0 << 24);
+               pm8001_ha->outbnd_q_tbl[i].pi_virt              =
+                       pm8001_ha->memoryMap.region[PI].virt_ptr;
+               offsetob = i * 0x24;
+               pm8001_ha->outbnd_q_tbl[i].ci_pci_bar           =
+                       get_pci_bar_index(pm8001_mr32(addressob,
+                       offsetob + 0x14));
+               pm8001_ha->outbnd_q_tbl[i].ci_offset            =
+                       pm8001_mr32(addressob, (offsetob + 0x18));
+               pm8001_ha->outbnd_q_tbl[i].consumer_idx         = 0;
+               pm8001_ha->outbnd_q_tbl[i].producer_index       = 0;
+       }
+}
+
+/**
+ * update_main_config_table - update the main default table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_main_config_table(struct pm8001_hba_info *pm8001_ha)
+{
+       void __iomem *address = pm8001_ha->main_cfg_tbl_addr;
+       pm8001_mw32(address, 0x24,
+               pm8001_ha->main_cfg_tbl.inbound_q_nppd_hppd);
+       pm8001_mw32(address, 0x28,
+               pm8001_ha->main_cfg_tbl.outbound_hw_event_pid0_3);
+       pm8001_mw32(address, 0x2C,
+               pm8001_ha->main_cfg_tbl.outbound_hw_event_pid4_7);
+       pm8001_mw32(address, 0x30,
+               pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid0_3);
+       pm8001_mw32(address, 0x34,
+               pm8001_ha->main_cfg_tbl.outbound_ncq_event_pid4_7);
+       pm8001_mw32(address, 0x38,
+               pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid0_3);
+       pm8001_mw32(address, 0x3C,
+               pm8001_ha->main_cfg_tbl.outbound_tgt_ITNexus_event_pid4_7);
+       pm8001_mw32(address, 0x40,
+               pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid0_3);
+       pm8001_mw32(address, 0x44,
+               pm8001_ha->main_cfg_tbl.outbound_tgt_ssp_event_pid4_7);
+       pm8001_mw32(address, 0x48,
+               pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid0_3);
+       pm8001_mw32(address, 0x4C,
+               pm8001_ha->main_cfg_tbl.outbound_tgt_smp_event_pid4_7);
+       pm8001_mw32(address, 0x50,
+               pm8001_ha->main_cfg_tbl.upper_event_log_addr);
+       pm8001_mw32(address, 0x54,
+               pm8001_ha->main_cfg_tbl.lower_event_log_addr);
+       pm8001_mw32(address, 0x58, pm8001_ha->main_cfg_tbl.event_log_size);
+       pm8001_mw32(address, 0x5C, pm8001_ha->main_cfg_tbl.event_log_option);
+       pm8001_mw32(address, 0x60,
+               pm8001_ha->main_cfg_tbl.upper_iop_event_log_addr);
+       pm8001_mw32(address, 0x64,
+               pm8001_ha->main_cfg_tbl.lower_iop_event_log_addr);
+       pm8001_mw32(address, 0x68, pm8001_ha->main_cfg_tbl.iop_event_log_size);
+       pm8001_mw32(address, 0x6C,
+               pm8001_ha->main_cfg_tbl.iop_event_log_option);
+       pm8001_mw32(address, 0x70,
+               pm8001_ha->main_cfg_tbl.fatal_err_interrupt);
+}
+
+/**
+ * update_inbnd_queue_table - update the inbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_inbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+{
+       void __iomem *address = pm8001_ha->inbnd_q_tbl_addr;
+       u16 offset = number * 0x20;
+       pm8001_mw32(address, offset + 0x00,
+               pm8001_ha->inbnd_q_tbl[number].element_pri_size_cnt);
+       pm8001_mw32(address, offset + 0x04,
+               pm8001_ha->inbnd_q_tbl[number].upper_base_addr);
+       pm8001_mw32(address, offset + 0x08,
+               pm8001_ha->inbnd_q_tbl[number].lower_base_addr);
+       pm8001_mw32(address, offset + 0x0C,
+               pm8001_ha->inbnd_q_tbl[number].ci_upper_base_addr);
+       pm8001_mw32(address, offset + 0x10,
+               pm8001_ha->inbnd_q_tbl[number].ci_lower_base_addr);
+}
+
+/**
+ * update_outbnd_queue_table - update the outbound queue table to the HBA.
+ * @pm8001_ha: our hba card information
+ */
+static void __devinit
+update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
+{
+       void __iomem *address = pm8001_ha->outbnd_q_tbl_addr;
+       u16 offset = number * 0x24;
+       pm8001_mw32(address, offset + 0x00,
+               pm8001_ha->outbnd_q_tbl[number].element_size_cnt);
+       pm8001_mw32(address, offset + 0x04,
+               pm8001_ha->outbnd_q_tbl[number].upper_base_addr);
+       pm8001_mw32(address, offset + 0x08,
+               pm8001_ha->outbnd_q_tbl[number].lower_base_addr);
+       pm8001_mw32(address, offset + 0x0C,
+               pm8001_ha->outbnd_q_tbl[number].pi_upper_base_addr);
+       pm8001_mw32(address, offset + 0x10,
+               pm8001_ha->outbnd_q_tbl[number].pi_lower_base_addr);
+       pm8001_mw32(address, offset + 0x1C,
+               pm8001_ha->outbnd_q_tbl[number].interrup_vec_cnt_delay);
+}
+
+/**
+ * bar4_shift - function is called to shift BAR base address
+ * @pm8001_ha : our hba card infomation
+ * @shiftValue : shifting value in memory bar.
+ */
+static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
+{
+       u32 regVal;
+       u32 max_wait_count;
+
+       /* program the inbound AXI translation Lower Address */
+       pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue);
+
+       /* confirm the setting is written */
+       max_wait_count = 1 * 1000 * 1000;  /* 1 sec */
+       do {
+               udelay(1);
+               regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW);
+       } while ((regVal != shiftValue) && (--max_wait_count));
+
+       if (!max_wait_count) {
+               PM8001_INIT_DBG(pm8001_ha,
+                       pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW"
+                       " = 0x%x\n", regVal));
+               return -1;
+       }
+       return 0;
+}
+
+/**
+ * mpi_set_phys_g3_with_ssc
+ * @pm8001_ha: our hba card information
+ * @SSCbit: set SSCbit to 0 to disable all phys ssc; 1 to enable all phys ssc.
+ */
+static void __devinit
+mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
+{
+       u32 offset;
+       u32 value;
+       u32 i, j;
+       u32 bit_cnt;
+
+#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000
+#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000
+#define SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET 0x1074
+#define SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET 0x1074
+#define PHY_G3_WITHOUT_SSC_BIT_SHIFT 12
+#define PHY_G3_WITH_SSC_BIT_SHIFT 13
+#define SNW3_PHY_CAPABILITIES_PARITY 31
+
+   /*
+    * Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3)
+    * Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7)
+    */
+       if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR))
+               return;
+       /* set SSC bit of PHY 0 - 3 */
+       for (i = 0; i < 4; i++) {
+               offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i;
+               value = pm8001_cr32(pm8001_ha, 2, offset);
+               if (SSCbit) {
+                       value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT;
+                       value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT);
+               } else {
+                       value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT;
+                       value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT);
+               }
+               bit_cnt = 0;
+               for (j = 0; j < 31; j++)
+                       if ((value >> j) & 0x00000001)
+                               bit_cnt++;
+               if (bit_cnt % 2)
+                       value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY);
+               else
+                       value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY;
+
+               pm8001_cw32(pm8001_ha, 2, offset, value);
+       }
+
+       /* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */
+       if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR))
+               return;
+
+       /* set SSC bit of PHY 4 - 7 */
+       for (i = 4; i < 8; i++) {
+               offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
+               value = pm8001_cr32(pm8001_ha, 2, offset);
+               if (SSCbit) {
+                       value |= 0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT;
+                       value &= ~(0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT);
+               } else {
+                       value |= 0x00000001 << PHY_G3_WITHOUT_SSC_BIT_SHIFT;
+                       value &= ~(0x00000001 << PHY_G3_WITH_SSC_BIT_SHIFT);
+               }
+               bit_cnt = 0;
+               for (j = 0; j < 31; j++)
+                       if ((value >> j) & 0x00000001)
+                               bit_cnt++;
+               if (bit_cnt % 2)
+                       value &= ~(0x00000001 << SNW3_PHY_CAPABILITIES_PARITY);
+               else
+                       value |= 0x00000001 << SNW3_PHY_CAPABILITIES_PARITY;
+
+               pm8001_cw32(pm8001_ha, 2, offset, value);
+       }
+
+       /*set the shifted destination address to 0x0 to avoid error operation */
+       bar4_shift(pm8001_ha, 0x0);
+       return;
+}
+
+/**
+ * mpi_set_open_retry_interval_reg
+ * @pm8001_ha: our hba card information
+ * @interval - interval time for each OPEN_REJECT (RETRY). The units are in 1us.
+ */
+static void __devinit
+mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
+                               u32 interval)
+{
+       u32 offset;
+       u32 value;
+       u32 i;
+
+#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000
+#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000
+#define OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET 0x30B4
+#define OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET 0x30B4
+#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF
+
+       value = interval & OPEN_RETRY_INTERVAL_REG_MASK;
+       /* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/
+       if (-1 == bar4_shift(pm8001_ha,
+                            OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR))
+               return;
+       for (i = 0; i < 4; i++) {
+               offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i;
+               pm8001_cw32(pm8001_ha, 2, offset, value);
+       }
+
+       if (-1 == bar4_shift(pm8001_ha,
+                            OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR))
+               return;
+       for (i = 4; i < 8; i++) {
+               offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
+               pm8001_cw32(pm8001_ha, 2, offset, value);
+       }
+       /*set the shifted destination address to 0x0 to avoid error operation */
+       bar4_shift(pm8001_ha, 0x0);
+       return;
+}
+
+/**
+ * mpi_init_check - check firmware initialization status.
+ * @pm8001_ha: our hba card information
+ */
+static int mpi_init_check(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 max_wait_count;
+       u32 value;
+       u32 gst_len_mpistate;
+       /* Write bit0=1 to Inbound DoorBell Register to tell the SPC FW the
+       table is updated */
+       pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_UPDATE);
+       /* wait until Inbound DoorBell Clear Register toggled */
+       max_wait_count = 1 * 1000 * 1000;/* 1 sec */
+       do {
+               udelay(1);
+               value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+               value &= SPC_MSGU_CFG_TABLE_UPDATE;
+       } while ((value != 0) && (--max_wait_count));
+
+       if (!max_wait_count)
+               return -1;
+       /* check the MPI-State for initialization */
+       gst_len_mpistate =
+               pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+               GST_GSTLEN_MPIS_OFFSET);
+       if (GST_MPI_STATE_INIT != (gst_len_mpistate & GST_MPI_STATE_MASK))
+               return -1;
+       /* check MPI Initialization error */
+       gst_len_mpistate = gst_len_mpistate >> 16;
+       if (0x0000 != gst_len_mpistate)
+               return -1;
+       return 0;
+}
+
+/**
+ * check_fw_ready - The LLDD check if the FW is ready, if not, return error.
+ * @pm8001_ha: our hba card information
+ */
+static int check_fw_ready(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 value, value1;
+       u32 max_wait_count;
+       /* check error state */
+       value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+       value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+       /* check AAP error */
+       if (SCRATCH_PAD1_ERR == (value & SCRATCH_PAD_STATE_MASK)) {
+               /* error state */
+               value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+               return -1;
+       }
+
+       /* check IOP error */
+       if (SCRATCH_PAD2_ERR == (value1 & SCRATCH_PAD_STATE_MASK)) {
+               /* error state */
+               value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3);
+               return -1;
+       }
+
+       /* bit 4-31 of scratch pad1 should be zeros if it is not
+       in error state*/
+       if (value & SCRATCH_PAD1_STATE_MASK) {
+               /* error case */
+               pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0);
+               return -1;
+       }
+
+       /* bit 2, 4-31 of scratch pad2 should be zeros if it is not
+       in error state */
+       if (value1 & SCRATCH_PAD2_STATE_MASK) {
+               /* error case */
+               return -1;
+       }
+
+       max_wait_count = 1 * 1000 * 1000;/* 1 sec timeout */
+
+       /* wait until scratch pad 1 and 2 registers in ready state  */
+       do {
+               udelay(1);
+               value = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
+                       & SCRATCH_PAD1_RDY;
+               value1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
+                       & SCRATCH_PAD2_RDY;
+               if ((--max_wait_count) == 0)
+                       return -1;
+       } while ((value != SCRATCH_PAD1_RDY) || (value1 != SCRATCH_PAD2_RDY));
+       return 0;
+}
+
+static void init_pci_device_addresses(struct pm8001_hba_info *pm8001_ha)
+{
+       void __iomem *base_addr;
+       u32     value;
+       u32     offset;
+       u32     pcibar;
+       u32     pcilogic;
+
+       value = pm8001_cr32(pm8001_ha, 0, 0x44);
+       offset = value & 0x03FFFFFF;
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("Scratchpad 0 Offset: %x \n", offset));
+       pcilogic = (value & 0xFC000000) >> 26;
+       pcibar = get_pci_bar_index(pcilogic);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("Scratchpad 0 PCI BAR: %d \n", pcibar));
+       pm8001_ha->main_cfg_tbl_addr = base_addr =
+               pm8001_ha->io_mem[pcibar].memvirtaddr + offset;
+       pm8001_ha->general_stat_tbl_addr =
+               base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x18);
+       pm8001_ha->inbnd_q_tbl_addr =
+               base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x1C);
+       pm8001_ha->outbnd_q_tbl_addr =
+               base_addr + pm8001_cr32(pm8001_ha, pcibar, offset + 0x20);
+}
+
+/**
+ * pm8001_chip_init - the main init function that initialize whole PM8001 chip.
+ * @pm8001_ha: our hba card information
+ */
+static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
+{
+       /* check the firmware status */
+       if (-1 == check_fw_ready(pm8001_ha)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Firmware is not ready!\n"));
+               return -EBUSY;
+       }
+
+       /* Initialize pci space address eg: mpi offset */
+       init_pci_device_addresses(pm8001_ha);
+       init_default_table_values(pm8001_ha);
+       read_main_config_table(pm8001_ha);
+       read_general_status_table(pm8001_ha);
+       read_inbnd_queue_table(pm8001_ha);
+       read_outbnd_queue_table(pm8001_ha);
+       /* update main config table ,inbound table and outbound table */
+       update_main_config_table(pm8001_ha);
+       update_inbnd_queue_table(pm8001_ha, 0);
+       update_outbnd_queue_table(pm8001_ha, 0);
+       mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
+       mpi_set_open_retry_interval_reg(pm8001_ha, 7);
+       /* notify firmware update finished and check initialization status */
+       if (0 == mpi_init_check(pm8001_ha)) {
+               PM8001_INIT_DBG(pm8001_ha,
+                       pm8001_printk("MPI initialize successful!\n"));
+       } else
+               return -EBUSY;
+       /*This register is a 16-bit timer with a resolution of 1us. This is the
+       timer used for interrupt delay/coalescing in the PCIe Application Layer.
+       Zero is not a valid value. A value of 1 in the register will cause the
+       interrupts to be normal. A value greater than 1 will cause coalescing
+       delays.*/
+       pm8001_cw32(pm8001_ha, 1, 0x0033c0, 0x1);
+       pm8001_cw32(pm8001_ha, 1, 0x0033c4, 0x0);
+       return 0;
+}
+
+static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 max_wait_count;
+       u32 value;
+       u32 gst_len_mpistate;
+       init_pci_device_addresses(pm8001_ha);
+       /* Write bit1=1 to Inbound DoorBell Register to tell the SPC FW the
+       table is stop */
+       pm8001_cw32(pm8001_ha, 0, MSGU_IBDB_SET, SPC_MSGU_CFG_TABLE_RESET);
+
+       /* wait until Inbound DoorBell Clear Register toggled */
+       max_wait_count = 1 * 1000 * 1000;/* 1 sec */
+       do {
+               udelay(1);
+               value = pm8001_cr32(pm8001_ha, 0, MSGU_IBDB_SET);
+               value &= SPC_MSGU_CFG_TABLE_RESET;
+       } while ((value != 0) && (--max_wait_count));
+
+       if (!max_wait_count) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("TIMEOUT:IBDB value/=0x%x\n", value));
+               return -1;
+       }
+
+       /* check the MPI-State for termination in progress */
+       /* wait until Inbound DoorBell Clear Register toggled */
+       max_wait_count = 1 * 1000 * 1000;  /* 1 sec */
+       do {
+               udelay(1);
+               gst_len_mpistate =
+                       pm8001_mr32(pm8001_ha->general_stat_tbl_addr,
+                       GST_GSTLEN_MPIS_OFFSET);
+               if (GST_MPI_STATE_UNINIT ==
+                       (gst_len_mpistate & GST_MPI_STATE_MASK))
+                       break;
+       } while (--max_wait_count);
+       if (!max_wait_count) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk(" TIME OUT MPI State = 0x%x\n",
+                               gst_len_mpistate & GST_MPI_STATE_MASK));
+               return -1;
+       }
+       return 0;
+}
+
+/**
+ * soft_reset_ready_check - Function to check FW is ready for soft reset.
+ * @pm8001_ha: our hba card information
+ */
+static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 regVal, regVal1, regVal2;
+       if (mpi_uninit_check(pm8001_ha) != 0) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("MPI state is not ready\n"));
+               return -1;
+       }
+       /* read the scratch pad 2 register bit 2 */
+       regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2)
+               & SCRATCH_PAD2_FWRDY_RST;
+       if (regVal == SCRATCH_PAD2_FWRDY_RST) {
+               PM8001_INIT_DBG(pm8001_ha,
+                       pm8001_printk("Firmware is ready for reset .\n"));
+       } else {
+       /* Trigger NMI twice via RB6 */
+               if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("Shift Bar4 to 0x%x failed\n",
+                                       RB6_ACCESS_REG));
+                       return -1;
+               }
+               pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET,
+                       RB6_MAGIC_NUMBER_RST);
+               pm8001_cw32(pm8001_ha, 2, SPC_RB6_OFFSET, RB6_MAGIC_NUMBER_RST);
+               /* wait for 100 ms */
+               mdelay(100);
+               regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2) &
+                       SCRATCH_PAD2_FWRDY_RST;
+               if (regVal != SCRATCH_PAD2_FWRDY_RST) {
+                       regVal1 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+                       regVal2 = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("TIMEOUT:MSGU_SCRATCH_PAD1"
+                               "=0x%x, MSGU_SCRATCH_PAD2=0x%x\n",
+                               regVal1, regVal2));
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_0)));
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3)));
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/**
+ * pm8001_chip_soft_rst - soft reset the PM8001 chip, so that the clear all
+ * the FW register status to the originated status.
+ * @pm8001_ha: our hba card information
+ * @signature: signature in host scratch pad0 register.
+ */
+static int
+pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
+{
+       u32     regVal, toggleVal;
+       u32     max_wait_count;
+       u32     regVal1, regVal2, regVal3;
+
+       /* step1: Check FW is ready for soft reset */
+       if (soft_reset_ready_check(pm8001_ha) != 0) {
+               PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("FW is not ready\n"));
+               return -1;
+       }
+
+       /* step 2: clear NMI status register on AAP1 and IOP, write the same
+       value to clear */
+       /* map 0x60000 to BAR4(0x20), BAR2(win) */
+       if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Shift Bar4 to 0x%x failed\n",
+                       MBIC_AAP1_ADDR_BASE));
+               return -1;
+       }
+       regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("MBIC - NMI Enable VPE0 (IOP)= 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0);
+       /* map 0x70000 to BAR4(0x20), BAR2(win) */
+       if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Shift Bar4 to 0x%x failed\n",
+                       MBIC_IOP_ADDR_BASE));
+               return -1;
+       }
+       regVal = pm8001_cr32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("MBIC - NMI Enable VPE0 (AAP1)= 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_AAP1, 0x0);
+
+       regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("PCIE -Event Interrupt Enable = 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT_ENABLE, 0x0);
+
+       regVal = pm8001_cr32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("PCIE - Event Interrupt  = 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 1, PCIE_EVENT_INTERRUPT, regVal);
+
+       regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("PCIE -Error Interrupt Enable = 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT_ENABLE, 0x0);
+
+       regVal = pm8001_cr32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("PCIE - Error Interrupt = 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 1, PCIE_ERROR_INTERRUPT, regVal);
+
+       /* read the scratch pad 1 register bit 2 */
+       regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1)
+               & SCRATCH_PAD1_RST;
+       toggleVal = regVal ^ SCRATCH_PAD1_RST;
+
+       /* set signature in host scratch pad0 register to tell SPC that the
+       host performs the soft reset */
+       pm8001_cw32(pm8001_ha, 0, MSGU_HOST_SCRATCH_PAD_0, signature);
+
+       /* read required registers for confirmming */
+       /* map 0x0700000 to BAR4(0x20), BAR2(win) */
+       if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Shift Bar4 to 0x%x failed\n",
+                       GSM_ADDR_BASE));
+               return -1;
+       }
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x0(0x00007b88)-GSM Configuration and"
+               " Reset = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+       /* step 3: host read GSM Configuration and Reset register */
+       regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
+       /* Put those bits to low */
+       /* GSM XCBI offset = 0x70 0000
+       0x00 Bit 13 COM_SLV_SW_RSTB 1
+       0x00 Bit 12 QSSP_SW_RSTB 1
+       0x00 Bit 11 RAAE_SW_RSTB 1
+       0x00 Bit 9 RB_1_SW_RSTB 1
+       0x00 Bit 8 SM_SW_RSTB 1
+       */
+       regVal &= ~(0x00003b00);
+       /* host write GSM Configuration and Reset register */
+       pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x0 (0x00007b88 ==> 0x00004088) - GSM "
+               "Configuration and Reset is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+       /* step 4: */
+       /* disable GSM - Read Address Parity Check */
+       regVal1 = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700038 - Read Address Parity Check "
+               "Enable = 0x%x\n", regVal1));
+       pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, 0x0);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
+               "is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+
+       /* disable GSM - Write Address Parity Check */
+       regVal2 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700040 - Write Address Parity Check"
+               " Enable = 0x%x\n", regVal2));
+       pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, 0x0);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700040 - Write Address Parity Check "
+               "Enable is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+
+       /* disable GSM - Write Data Parity Check */
+       regVal3 = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x300048 - Write Data Parity Check"
+               " Enable = 0x%x\n", regVal3));
+       pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, 0x0);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x300048 - Write Data Parity Check Enable"
+               "is set to = 0x%x\n",
+       pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+
+       /* step 5: delay 10 usec */
+       udelay(10);
+       /* step 5-b: set GPIO-0 output control to tristate anyway */
+       if (-1 == bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
+               PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("Shift Bar4 to 0x%x failed\n",
+                               GPIO_ADDR_BASE));
+               return -1;
+       }
+       regVal = pm8001_cr32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET);
+               PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("GPIO Output Control Register:"
+                               " = 0x%x\n", regVal));
+       /* set GPIO-0 output control to tri-state */
+       regVal &= 0xFFFFFFFC;
+       pm8001_cw32(pm8001_ha, 2, GPIO_GPIO_0_0UTPUT_CTL_OFFSET, regVal);
+
+       /* Step 6: Reset the IOP and AAP1 */
+       /* map 0x00000 to BAR4(0x20), BAR2(win) */
+       if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
+                       SPC_TOP_LEVEL_ADDR_BASE));
+               return -1;
+       }
+       regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("Top Register before resetting IOP/AAP1"
+               ":= 0x%x\n", regVal));
+       regVal &= ~(SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
+       pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+       /* step 7: Reset the BDMA/OSSP */
+       regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("Top Register before resetting BDMA/OSSP"
+               ": = 0x%x\n", regVal));
+       regVal &= ~(SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
+       pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+       /* step 8: delay 10 usec */
+       udelay(10);
+
+       /* step 9: bring the BDMA and OSSP out of reset */
+       regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("Top Register before bringing up BDMA/OSSP"
+               ":= 0x%x\n", regVal));
+       regVal |= (SPC_REG_RESET_BDMA_CORE | SPC_REG_RESET_OSSP);
+       pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+       /* step 10: delay 10 usec */
+       udelay(10);
+
+       /* step 11: reads and sets the GSM Configuration and Reset Register */
+       /* map 0x0700000 to BAR4(0x20), BAR2(win) */
+       if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
+                       GSM_ADDR_BASE));
+               return -1;
+       }
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x0 (0x00007b88)-GSM Configuration and "
+               "Reset = 0x%x\n", pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+       regVal = pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET);
+       /* Put those bits to high */
+       /* GSM XCBI offset = 0x70 0000
+       0x00 Bit 13 COM_SLV_SW_RSTB 1
+       0x00 Bit 12 QSSP_SW_RSTB 1
+       0x00 Bit 11 RAAE_SW_RSTB 1
+       0x00 Bit 9   RB_1_SW_RSTB 1
+       0x00 Bit 8   SM_SW_RSTB 1
+       */
+       regVal |= (GSM_CONFIG_RESET_VALUE);
+       pm8001_cw32(pm8001_ha, 2, GSM_CONFIG_RESET, regVal);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM (0x00004088 ==> 0x00007b88) - GSM"
+               " Configuration and Reset is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_CONFIG_RESET)));
+
+       /* step 12: Restore GSM - Read Address Parity Check */
+       regVal = pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK);
+       /* just for debugging */
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700038 - Read Address Parity Check Enable"
+               " = 0x%x\n", regVal));
+       pm8001_cw32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK, regVal1);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700038 - Read Address Parity"
+               " Check Enable is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_READ_ADDR_PARITY_CHECK)));
+       /* Restore GSM - Write Address Parity Check */
+       regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK);
+       pm8001_cw32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK, regVal2);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700040 - Write Address Parity Check"
+               " Enable is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_WRITE_ADDR_PARITY_CHECK)));
+       /* Restore GSM - Write Data Parity Check */
+       regVal = pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK);
+       pm8001_cw32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK, regVal3);
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("GSM 0x700048 - Write Data Parity Check Enable"
+               "is set to = 0x%x\n",
+               pm8001_cr32(pm8001_ha, 2, GSM_WRITE_DATA_PARITY_CHECK)));
+
+       /* step 13: bring the IOP and AAP1 out of reset */
+       /* map 0x00000 to BAR4(0x20), BAR2(win) */
+       if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Shift Bar4 to 0x%x failed\n",
+                       SPC_TOP_LEVEL_ADDR_BASE));
+               return -1;
+       }
+       regVal = pm8001_cr32(pm8001_ha, 2, SPC_REG_RESET);
+       regVal |= (SPC_REG_RESET_PCS_IOP_SS | SPC_REG_RESET_PCS_AAP1_SS);
+       pm8001_cw32(pm8001_ha, 2, SPC_REG_RESET, regVal);
+
+       /* step 14: delay 10 usec - Normal Mode */
+       udelay(10);
+       /* check Soft Reset Normal mode or Soft Reset HDA mode */
+       if (signature == SPC_SOFT_RESET_SIGNATURE) {
+               /* step 15 (Normal Mode): wait until scratch pad1 register
+               bit 2 toggled */
+               max_wait_count = 2 * 1000 * 1000;/* 2 sec */
+               do {
+                       udelay(1);
+                       regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1) &
+                               SCRATCH_PAD1_RST;
+               } while ((regVal != toggleVal) && (--max_wait_count));
+
+               if (!max_wait_count) {
+                       regVal = pm8001_cr32(pm8001_ha, 0,
+                               MSGU_SCRATCH_PAD_1);
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("TIMEOUT : ToggleVal 0x%x,"
+                               "MSGU_SCRATCH_PAD1 = 0x%x\n",
+                               toggleVal, regVal));
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0,
+                               MSGU_SCRATCH_PAD_0)));
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD2 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0,
+                               MSGU_SCRATCH_PAD_2)));
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0,
+                               MSGU_SCRATCH_PAD_3)));
+                       return -1;
+               }
+
+               /* step 16 (Normal) - Clear ODMR and ODCR */
+               pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+               pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+
+               /* step 17 (Normal Mode): wait for the FW and IOP to get
+               ready - 1 sec timeout */
+               /* Wait for the SPC Configuration Table to be ready */
+               if (check_fw_ready(pm8001_ha) == -1) {
+                       regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_1);
+                       /* return error if MPI Configuration Table not ready */
+                       PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("FW not ready SCRATCH_PAD1"
+                               " = 0x%x\n", regVal));
+                       regVal = pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_2);
+                       /* return error if MPI Configuration Table not ready */
+                       PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("FW not ready SCRATCH_PAD2"
+                               " = 0x%x\n", regVal));
+                       PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD0 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0,
+                               MSGU_SCRATCH_PAD_0)));
+                       PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
+                               pm8001_cr32(pm8001_ha, 0,
+                               MSGU_SCRATCH_PAD_3)));
+                       return -1;
+               }
+       }
+
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("SPC soft reset Complete\n"));
+       return 0;
+}
+
+static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 i;
+       u32 regVal;
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("chip reset start\n"));
+
+       /* do SPC chip reset. */
+       regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
+       regVal &= ~(SPC_REG_RESET_DEVICE);
+       pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);
+
+       /* delay 10 usec */
+       udelay(10);
+
+       /* bring chip reset out of reset */
+       regVal = pm8001_cr32(pm8001_ha, 1, SPC_REG_RESET);
+       regVal |= SPC_REG_RESET_DEVICE;
+       pm8001_cw32(pm8001_ha, 1, SPC_REG_RESET, regVal);
+
+       /* delay 10 usec */
+       udelay(10);
+
+       /* wait for 20 msec until the firmware gets reloaded */
+       i = 20;
+       do {
+               mdelay(1);
+       } while ((--i) != 0);
+
+       PM8001_INIT_DBG(pm8001_ha,
+               pm8001_printk("chip reset finished\n"));
+}
+
+/**
+ * pm8001_chip_iounmap - which maped when initilized.
+ * @pm8001_ha: our hba card information
+ */
+static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha)
+{
+       s8 bar, logical = 0;
+       for (bar = 0; bar < 6; bar++) {
+               /*
+               ** logical BARs for SPC:
+               ** bar 0 and 1 - logical BAR0
+               ** bar 2 and 3 - logical BAR1
+               ** bar4 - logical BAR2
+               ** bar5 - logical BAR3
+               ** Skip the appropriate assignments:
+               */
+               if ((bar == 1) || (bar == 3))
+                       continue;
+               if (pm8001_ha->io_mem[logical].memvirtaddr) {
+                       iounmap(pm8001_ha->io_mem[logical].memvirtaddr);
+                       logical++;
+               }
+       }
+}
+
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_intx_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+{
+       pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_CLEAR_ALL);
+       pm8001_cw32(pm8001_ha, 0, MSGU_ODCR, ODCR_CLEAR_ALL);
+}
+
+ /**
+  * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+  * @pm8001_ha: our hba card information
+  */
+static void
+pm8001_chip_intx_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+       pm8001_cw32(pm8001_ha, 0, MSGU_ODMR, ODMR_MASK_ALL);
+}
+
+/**
+ * pm8001_chip_msix_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_msix_interrupt_enable(struct pm8001_hba_info *pm8001_ha,
+       u32 int_vec_idx)
+{
+       u32 msi_index;
+       u32 value;
+       msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
+       msi_index += MSIX_TABLE_BASE;
+       pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_ENABLE);
+       value = (1 << int_vec_idx);
+       pm8001_cw32(pm8001_ha, 0,  MSGU_ODCR, value);
+
+}
+
+/**
+ * pm8001_chip_msix_interrupt_disable - disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha,
+       u32 int_vec_idx)
+{
+       u32 msi_index;
+       msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
+       msi_index += MSIX_TABLE_BASE;
+       pm8001_cw32(pm8001_ha, 0,  msi_index, MSIX_INTERRUPT_DISABLE);
+
+}
+/**
+ * pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_interrupt_enable(struct pm8001_hba_info *pm8001_ha)
+{
+#ifdef PM8001_USE_MSIX
+       pm8001_chip_msix_interrupt_enable(pm8001_ha, 0);
+       return;
+#endif
+       pm8001_chip_intx_interrupt_enable(pm8001_ha);
+
+}
+
+/**
+ * pm8001_chip_intx_interrupt_disable- disable PM8001 chip interrupt
+ * @pm8001_ha: our hba card information
+ */
+static void
+pm8001_chip_interrupt_disable(struct pm8001_hba_info *pm8001_ha)
+{
+#ifdef PM8001_USE_MSIX
+       pm8001_chip_msix_interrupt_disable(pm8001_ha, 0);
+       return;
+#endif
+       pm8001_chip_intx_interrupt_disable(pm8001_ha);
+
+}
+
+/**
+ * mpi_msg_free_get- get the free message buffer for transfer inbound queue.
+ * @circularQ: the inbound queue  we want to transfer to HBA.
+ * @messageSize: the message size of this transfer, normally it is 64 bytes
+ * @messagePtr: the pointer to message.
+ */
+static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
+                           u16 messageSize, void **messagePtr)
+{
+       u32 offset, consumer_index;
+       struct mpi_msg_hdr *msgHeader;
+       u8 bcCount = 1; /* only support single buffer */
+
+       /* Checks is the requested message size can be allocated in this queue*/
+       if (messageSize > 64) {
+               *messagePtr = NULL;
+               return -1;
+       }
+
+       /* Stores the new consumer index */
+       consumer_index = pm8001_read_32(circularQ->ci_virt);
+       circularQ->consumer_index = cpu_to_le32(consumer_index);
+       if (((circularQ->producer_idx + bcCount) % 256) ==
+               circularQ->consumer_index) {
+               *messagePtr = NULL;
+               return -1;
+       }
+       /* get memory IOMB buffer address */
+       offset = circularQ->producer_idx * 64;
+       /* increment to next bcCount element */
+       circularQ->producer_idx = (circularQ->producer_idx + bcCount) % 256;
+       /* Adds that distance to the base of the region virtual address plus
+       the message header size*/
+       msgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt + offset);
+       *messagePtr = ((void *)msgHeader) + sizeof(struct mpi_msg_hdr);
+       return 0;
+}
+
+/**
+ * mpi_build_cmd- build the message queue for transfer, update the PI to FW
+ * to tell the fw to get this message from IOMB.
+ * @pm8001_ha: our hba card information
+ * @circularQ: the inbound queue we want to transfer to HBA.
+ * @opCode: the operation code represents commands which LLDD and fw recognized.
+ * @payload: the command payload of each operation command.
+ */
+static int mpi_build_cmd(struct pm8001_hba_info *pm8001_ha,
+                        struct inbound_queue_table *circularQ,
+                        u32 opCode, void *payload)
+{
+       u32 Header = 0, hpriority = 0, bc = 1, category = 0x02;
+       u32 responseQueue = 0;
+       void *pMessage;
+
+       if (mpi_msg_free_get(circularQ, 64, &pMessage) < 0) {
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("No free mpi buffer \n"));
+               return -1;
+       }
+       BUG_ON(!payload);
+       /*Copy to the payload*/
+       memcpy(pMessage, payload, (64 - sizeof(struct mpi_msg_hdr)));
+
+       /*Build the header*/
+       Header = ((1 << 31) | (hpriority << 30) | ((bc & 0x1f) << 24)
+               | ((responseQueue & 0x3F) << 16)
+               | ((category & 0xF) << 12) | (opCode & 0xFFF));
+
+       pm8001_write_32((pMessage - 4), 0, cpu_to_le32(Header));
+       /*Update the PI to the firmware*/
+       pm8001_cw32(pm8001_ha, circularQ->pi_pci_bar,
+               circularQ->pi_offset, circularQ->producer_idx);
+       PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("after PI= %d CI= %d \n", circularQ->producer_idx,
+               circularQ->consumer_index));
+       return 0;
+}
+
+static u32 mpi_msg_free_set(struct pm8001_hba_info *pm8001_ha, void *pMsg,
+                           struct outbound_queue_table *circularQ, u8 bc)
+{
+       u32 producer_index;
+       struct mpi_msg_hdr *msgHeader;
+       struct mpi_msg_hdr *pOutBoundMsgHeader;
+
+       msgHeader = (struct mpi_msg_hdr *)(pMsg - sizeof(struct mpi_msg_hdr));
+       pOutBoundMsgHeader = (struct mpi_msg_hdr *)(circularQ->base_virt +
+                               circularQ->consumer_idx * 64);
+       if (pOutBoundMsgHeader != msgHeader) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("consumer_idx = %d msgHeader = %p\n",
+                       circularQ->consumer_idx, msgHeader));
+
+               /* Update the producer index from SPC */
+               producer_index = pm8001_read_32(circularQ->pi_virt);
+               circularQ->producer_index = cpu_to_le32(producer_index);
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("consumer_idx = %d producer_index = %d"
+                       "msgHeader = %p\n", circularQ->consumer_idx,
+                       circularQ->producer_index, msgHeader));
+               return 0;
+       }
+       /* free the circular queue buffer elements associated with the message*/
+       circularQ->consumer_idx = (circularQ->consumer_idx + bc) % 256;
+       /* update the CI of outbound queue */
+       pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar, circularQ->ci_offset,
+               circularQ->consumer_idx);
+       /* Update the producer index from SPC*/
+       producer_index = pm8001_read_32(circularQ->pi_virt);
+       circularQ->producer_index = cpu_to_le32(producer_index);
+       PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk(" CI=%d PI=%d\n", circularQ->consumer_idx,
+               circularQ->producer_index));
+       return 0;
+}
+
+/**
+ * mpi_msg_consume- get the MPI message from  outbound queue message table.
+ * @pm8001_ha: our hba card information
+ * @circularQ: the outbound queue  table.
+ * @messagePtr1: the message contents of this outbound message.
+ * @pBC: the message size.
+ */
+static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
+                          struct outbound_queue_table *circularQ,
+                          void **messagePtr1, u8 *pBC)
+{
+       struct mpi_msg_hdr      *msgHeader;
+       __le32  msgHeader_tmp;
+       u32 header_tmp;
+       do {
+               /* If there are not-yet-delivered messages ... */
+               if (circularQ->producer_index != circularQ->consumer_idx) {
+                       /*Get the pointer to the circular queue buffer element*/
+                       msgHeader = (struct mpi_msg_hdr *)
+                               (circularQ->base_virt +
+                               circularQ->consumer_idx * 64);
+                       /* read header */
+                       header_tmp = pm8001_read_32(msgHeader);
+                       msgHeader_tmp = cpu_to_le32(header_tmp);
+                       if (0 != (msgHeader_tmp & 0x80000000)) {
+                               if (OPC_OUB_SKIP_ENTRY !=
+                                       (msgHeader_tmp & 0xfff)) {
+                                       *messagePtr1 =
+                                               ((u8 *)msgHeader) +
+                                               sizeof(struct mpi_msg_hdr);
+                                       *pBC = (u8)((msgHeader_tmp >> 24) &
+                                               0x1f);
+                                       PM8001_IO_DBG(pm8001_ha,
+                                               pm8001_printk(": CI=%d PI=%d "
+                                               "msgHeader=%x\n",
+                                               circularQ->consumer_idx,
+                                               circularQ->producer_index,
+                                               msgHeader_tmp));
+                                       return MPI_IO_STATUS_SUCCESS;
+                               } else {
+                                       circularQ->consumer_idx =
+                                               (circularQ->consumer_idx +
+                                               ((msgHeader_tmp >> 24) & 0x1f))
+                                               % 256;
+                                       msgHeader_tmp = 0;
+                                       pm8001_write_32(msgHeader, 0, 0);
+                                       /* update the CI of outbound queue */
+                                       pm8001_cw32(pm8001_ha,
+                                               circularQ->ci_pci_bar,
+                                               circularQ->ci_offset,
+                                               circularQ->consumer_idx);
+                               }
+                       } else {
+                               circularQ->consumer_idx =
+                                       (circularQ->consumer_idx +
+                                       ((msgHeader_tmp >> 24) & 0x1f)) % 256;
+                               msgHeader_tmp = 0;
+                               pm8001_write_32(msgHeader, 0, 0);
+                               /* update the CI of outbound queue */
+                               pm8001_cw32(pm8001_ha, circularQ->ci_pci_bar,
+                                       circularQ->ci_offset,
+                                       circularQ->consumer_idx);
+                               return MPI_IO_STATUS_FAIL;
+                       }
+               } else {
+                       u32 producer_index;
+                       void *pi_virt = circularQ->pi_virt;
+                       /* Update the producer index from SPC */
+                       producer_index = pm8001_read_32(pi_virt);
+                       circularQ->producer_index = cpu_to_le32(producer_index);
+               }
+       } while (circularQ->producer_index != circularQ->consumer_idx);
+       /* while we don't have any more not-yet-delivered message */
+       /* report empty */
+       return MPI_IO_STATUS_BUSY;
+}
+
+static void pm8001_work_queue(struct work_struct *work)
+{
+       struct delayed_work *dw = container_of(work, struct delayed_work, work);
+       struct pm8001_wq *wq = container_of(dw, struct pm8001_wq, work_q);
+       struct pm8001_device *pm8001_dev;
+       struct domain_device    *dev;
+
+       switch (wq->handler) {
+       case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+               pm8001_dev = wq->data;
+               dev = pm8001_dev->sas_device;
+               pm8001_I_T_nexus_reset(dev);
+               break;
+       case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+               pm8001_dev = wq->data;
+               dev = pm8001_dev->sas_device;
+               pm8001_I_T_nexus_reset(dev);
+               break;
+       case IO_DS_IN_ERROR:
+               pm8001_dev = wq->data;
+               dev = pm8001_dev->sas_device;
+               pm8001_I_T_nexus_reset(dev);
+               break;
+       case IO_DS_NON_OPERATIONAL:
+               pm8001_dev = wq->data;
+               dev = pm8001_dev->sas_device;
+               pm8001_I_T_nexus_reset(dev);
+               break;
+       }
+       list_del(&wq->entry);
+       kfree(wq);
+}
+
+static int pm8001_handle_event(struct pm8001_hba_info *pm8001_ha, void *data,
+                              int handler)
+{
+       struct pm8001_wq *wq;
+       int ret = 0;
+
+       wq = kmalloc(sizeof(struct pm8001_wq), GFP_ATOMIC);
+       if (wq) {
+               wq->pm8001_ha = pm8001_ha;
+               wq->data = data;
+               wq->handler = handler;
+               INIT_DELAYED_WORK(&wq->work_q, pm8001_work_queue);
+               list_add_tail(&wq->entry, &pm8001_ha->wq_list);
+               schedule_delayed_work(&wq->work_q, 0);
+       } else
+               ret = -ENOMEM;
+
+       return ret;
+}
+
+/**
+ * mpi_ssp_completion- process the event that FW response to the SSP request.
+ * @pm8001_ha: our hba card information
+ * @piomb: the message contents of this outbound message.
+ *
+ * When FW has completed a ssp request for example a IO request, after it has
+ * filled the SG data with the data, it will trigger this event represent
+ * that he has finished the job,please check the coresponding buffer.
+ * So we will tell the caller who maybe waiting the result to tell upper layer
+ * that the task has been finished.
+ */
+static void
+mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+       struct sas_task *t;
+       struct pm8001_ccb_info *ccb;
+       unsigned long flags;
+       u32 status;
+       u32 param;
+       u32 tag;
+       struct ssp_completion_resp *psspPayload;
+       struct task_status_struct *ts;
+       struct ssp_response_iu *iu;
+       struct pm8001_device *pm8001_dev;
+       psspPayload = (struct ssp_completion_resp *)(piomb + 4);
+       status = le32_to_cpu(psspPayload->status);
+       tag = le32_to_cpu(psspPayload->tag);
+       ccb = &pm8001_ha->ccb_info[tag];
+       pm8001_dev = ccb->device;
+       param = le32_to_cpu(psspPayload->param);
+
+       t = ccb->task;
+
+       if (status && status != IO_UNDERFLOW)
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("sas IO status 0x%x\n", status));
+       if (unlikely(!t || !t->lldd_task || !t->dev))
+               return;
+       ts = &t->task_status;
+       switch (status) {
+       case IO_SUCCESS:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS"
+                       ",param = %d \n", param));
+               if (param == 0) {
+                       ts->resp = SAS_TASK_COMPLETE;
+                       ts->stat = SAM_GOOD;
+               } else {
+                       ts->resp = SAS_TASK_COMPLETE;
+                       ts->stat = SAS_PROTO_RESPONSE;
+                       ts->residual = param;
+                       iu = &psspPayload->ssp_resp_iu;
+                       sas_ssp_task_response(pm8001_ha->dev, t, iu);
+               }
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_ABORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_ABORTED IOMB Tag \n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_ABORTED_TASK;
+               break;
+       case IO_UNDERFLOW:
+               /* SSP Completion with error */
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW"
+                       ",param = %d \n", param));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_UNDERRUN;
+               ts->residual = param;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_NO_DEVICE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_NO_DEVICE\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_PHY_DOWN;
+               break;
+       case IO_XFER_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_XFER_ERROR_PHY_NOT_READY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_EPROTO;
+               break;
+       case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               break;
+       case IO_OPEN_CNX_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               if (!t->uldd_task)
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+               break;
+       case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+               break;
+       case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+                       "NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+               break;
+       case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+               break;
+       case IO_XFER_ERROR_NAK_RECEIVED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_NAK_R_ERR;
+               break;
+       case IO_XFER_ERROR_DMA:
+               PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("IO_XFER_ERROR_DMA\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_XFER_OPEN_RETRY_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_XFER_ERROR_OFFSET_MISMATCH:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_PORT_IN_RESET:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_PORT_IN_RESET\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_DS_NON_OPERATIONAL:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               if (!t->uldd_task)
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_DS_NON_OPERATIONAL);
+               break;
+       case IO_DS_IN_RECOVERY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_DS_IN_RECOVERY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_TM_TAG_NOT_FOUND:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_TM_TAG_NOT_FOUND\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_SSP_EXT_IU_ZERO_LEN_ERROR:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_SSP_EXT_IU_ZERO_LEN_ERROR\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+       default:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("Unknown status 0x%x\n", status));
+               /* not allowed case. Therefore, return failed status */
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               break;
+       }
+       PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("scsi_status = %x \n ",
+               psspPayload->ssp_resp_iu.status));
+       spin_lock_irqsave(&t->task_state_lock, flags);
+       t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+       t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+       t->task_state_flags |= SAS_TASK_STATE_DONE;
+       if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+                       " io_status 0x%x resp 0x%x "
+                       "stat 0x%x but aborted by upper layer!\n",
+                       t, status, ts->resp, ts->stat));
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+       } else {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+               mb();/* in order to force CPU ordering */
+               t->task_done(t);
+       }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+       struct sas_task *t;
+       unsigned long flags;
+       struct task_status_struct *ts;
+       struct pm8001_ccb_info *ccb;
+       struct pm8001_device *pm8001_dev;
+       struct ssp_event_resp *psspPayload =
+               (struct ssp_event_resp *)(piomb + 4);
+       u32 event = le32_to_cpu(psspPayload->event);
+       u32 tag = le32_to_cpu(psspPayload->tag);
+       u32 port_id = le32_to_cpu(psspPayload->port_id);
+       u32 dev_id = le32_to_cpu(psspPayload->device_id);
+
+       ccb = &pm8001_ha->ccb_info[tag];
+       t = ccb->task;
+       pm8001_dev = ccb->device;
+       if (event)
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("sas IO status 0x%x\n", event));
+       if (unlikely(!t || !t->lldd_task || !t->dev))
+               return;
+       ts = &t->task_status;
+       PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("port_id = %x,device_id = %x\n",
+               port_id, dev_id));
+       switch (event) {
+       case IO_OVERFLOW:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n");)
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               ts->residual = 0;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_XFER_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_INTERRUPTED;
+               break;
+       case IO_XFER_ERROR_PHY_NOT_READY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+                       "_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_EPROTO;
+               break;
+       case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               break;
+       case IO_OPEN_CNX_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               if (!t->uldd_task)
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+               break;
+       case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+               break;
+       case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+                       "NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+               break;
+       case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                      pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+               break;
+       case IO_XFER_ERROR_NAK_RECEIVED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_NAK_R_ERR;
+               break;
+       case IO_XFER_OPEN_RETRY_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_XFER_ERROR_UNEXPECTED_PHASE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+               PM8001_IO_DBG(pm8001_ha,
+                      pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       case IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       case IO_XFER_ERROR_OFFSET_MISMATCH:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       case IO_XFER_CMD_FRAME_ISSUED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("  IO_XFER_CMD_FRAME_ISSUED\n"));
+               return;
+       default:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("Unknown status 0x%x\n", event));
+               /* not allowed case. Therefore, return failed status */
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               break;
+       }
+       spin_lock_irqsave(&t->task_state_lock, flags);
+       t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+       t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+       t->task_state_flags |= SAS_TASK_STATE_DONE;
+       if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+                       " event 0x%x resp 0x%x "
+                       "stat 0x%x but aborted by upper layer!\n",
+                       t, event, ts->resp, ts->stat));
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+       } else {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+               mb();/* in order to force CPU ordering */
+               t->task_done(t);
+       }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct sas_task *t;
+       struct pm8001_ccb_info *ccb;
+       unsigned long flags;
+       u32 param;
+       u32 status;
+       u32 tag;
+       struct sata_completion_resp *psataPayload;
+       struct task_status_struct *ts;
+       struct ata_task_resp *resp ;
+       u32 *sata_resp;
+       struct pm8001_device *pm8001_dev;
+
+       psataPayload = (struct sata_completion_resp *)(piomb + 4);
+       status = le32_to_cpu(psataPayload->status);
+       tag = le32_to_cpu(psataPayload->tag);
+
+       ccb = &pm8001_ha->ccb_info[tag];
+       param = le32_to_cpu(psataPayload->param);
+       t = ccb->task;
+       ts = &t->task_status;
+       pm8001_dev = ccb->device;
+       if (status)
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("sata IO status 0x%x\n", status));
+       if (unlikely(!t || !t->lldd_task || !t->dev))
+               return;
+
+       switch (status) {
+       case IO_SUCCESS:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+               if (param == 0) {
+                       ts->resp = SAS_TASK_COMPLETE;
+                       ts->stat = SAM_GOOD;
+               } else {
+                       u8 len;
+                       ts->resp = SAS_TASK_COMPLETE;
+                       ts->stat = SAS_PROTO_RESPONSE;
+                       ts->residual = param;
+                       PM8001_IO_DBG(pm8001_ha,
+                               pm8001_printk("SAS_PROTO_RESPONSE len = %d\n",
+                               param));
+                       sata_resp = &psataPayload->sata_resp[0];
+                       resp = (struct ata_task_resp *)ts->buf;
+                       if (t->ata_task.dma_xfer == 0 &&
+                       t->data_dir == PCI_DMA_FROMDEVICE) {
+                               len = sizeof(struct pio_setup_fis);
+                               PM8001_IO_DBG(pm8001_ha,
+                               pm8001_printk("PIO read len = %d\n", len));
+                       } else if (t->ata_task.use_ncq) {
+                               len = sizeof(struct set_dev_bits_fis);
+                               PM8001_IO_DBG(pm8001_ha,
+                                       pm8001_printk("FPDMA len = %d\n", len));
+                       } else {
+                               len = sizeof(struct dev_to_host_fis);
+                               PM8001_IO_DBG(pm8001_ha,
+                               pm8001_printk("other len = %d\n", len));
+                       }
+                       if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
+                               resp->frame_len = len;
+                               memcpy(&resp->ending_fis[0], sata_resp, len);
+                               ts->buf_valid_size = sizeof(*resp);
+                       } else
+                               PM8001_IO_DBG(pm8001_ha,
+                                       pm8001_printk("response to large \n"));
+               }
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_ABORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_ABORTED IOMB Tag \n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_ABORTED_TASK;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+               /* following cases are to do cases */
+       case IO_UNDERFLOW:
+               /* SATA Completion with error */
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_UNDERFLOW param = %d\n", param));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_UNDERRUN;
+               ts->residual =  param;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_NO_DEVICE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_NO_DEVICE\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_PHY_DOWN;
+               break;
+       case IO_XFER_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_INTERRUPTED;
+               break;
+       case IO_XFER_ERROR_PHY_NOT_READY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+                       "_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_EPROTO;
+               break;
+       case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               break;
+       case IO_OPEN_CNX_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+               break;
+       case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               if (!t->uldd_task) {
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+                       ts->resp = SAS_TASK_UNDELIVERED;
+                       ts->stat = SAS_QUEUE_FULL;
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/*in order to force CPU ordering*/
+                       t->task_done(t);
+                       return;
+               }
+               break;
+       case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+               if (!t->uldd_task) {
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+                       ts->resp = SAS_TASK_UNDELIVERED;
+                       ts->stat = SAS_QUEUE_FULL;
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/*ditto*/
+                       t->task_done(t);
+                       return;
+               }
+               break;
+       case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+                       "NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+               break;
+       case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_STP_RESOURCES"
+                       "_BUSY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               if (!t->uldd_task) {
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY);
+                       ts->resp = SAS_TASK_UNDELIVERED;
+                       ts->stat = SAS_QUEUE_FULL;
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/* ditto*/
+                       t->task_done(t);
+                       return;
+               }
+               break;
+       case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                      pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+               break;
+       case IO_XFER_ERROR_NAK_RECEIVED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_NAK_R_ERR;
+               break;
+       case IO_XFER_ERROR_ACK_NAK_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_ACK_NAK_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_NAK_R_ERR;
+               break;
+       case IO_XFER_ERROR_DMA:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_DMA\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_ABORTED_TASK;
+               break;
+       case IO_XFER_ERROR_SATA_LINK_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_SATA_LINK_TIMEOUT\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               break;
+       case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_UNDERRUN;
+               break;
+       case IO_XFER_OPEN_RETRY_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_PORT_IN_RESET:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_PORT_IN_RESET\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               break;
+       case IO_DS_NON_OPERATIONAL:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               if (!t->uldd_task) {
+                       pm8001_handle_event(pm8001_ha, pm8001_dev,
+                                   IO_DS_NON_OPERATIONAL);
+                       ts->resp = SAS_TASK_UNDELIVERED;
+                       ts->stat = SAS_QUEUE_FULL;
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/*ditto*/
+                       t->task_done(t);
+                       return;
+               }
+               break;
+       case IO_DS_IN_RECOVERY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("  IO_DS_IN_RECOVERY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               break;
+       case IO_DS_IN_ERROR:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_DS_IN_ERROR\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               if (!t->uldd_task) {
+                       pm8001_handle_event(pm8001_ha, pm8001_dev,
+                                   IO_DS_IN_ERROR);
+                       ts->resp = SAS_TASK_UNDELIVERED;
+                       ts->stat = SAS_QUEUE_FULL;
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/*ditto*/
+                       t->task_done(t);
+                       return;
+               }
+               break;
+       case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+       default:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("Unknown status 0x%x\n", status));
+               /* not allowed case. Therefore, return failed status */
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               break;
+       }
+       spin_lock_irqsave(&t->task_state_lock, flags);
+       t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+       t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+       t->task_state_flags |= SAS_TASK_STATE_DONE;
+       if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("task 0x%p done with io_status 0x%x"
+                       " resp 0x%x stat 0x%x but aborted by upper layer!\n",
+                       t, status, ts->resp, ts->stat));
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+       } else {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+               mb();/* ditto */
+               t->task_done(t);
+       }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+       struct sas_task *t;
+       unsigned long flags;
+       struct task_status_struct *ts;
+       struct pm8001_ccb_info *ccb;
+       struct pm8001_device *pm8001_dev;
+       struct sata_event_resp *psataPayload =
+               (struct sata_event_resp *)(piomb + 4);
+       u32 event = le32_to_cpu(psataPayload->event);
+       u32 tag = le32_to_cpu(psataPayload->tag);
+       u32 port_id = le32_to_cpu(psataPayload->port_id);
+       u32 dev_id = le32_to_cpu(psataPayload->device_id);
+
+       ccb = &pm8001_ha->ccb_info[tag];
+       t = ccb->task;
+       pm8001_dev = ccb->device;
+       if (event)
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("sata IO status 0x%x\n", event));
+       if (unlikely(!t || !t->lldd_task || !t->dev))
+               return;
+       ts = &t->task_status;
+       PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("port_id = %x,device_id = %x\n",
+               port_id, dev_id));
+       switch (event) {
+       case IO_OVERFLOW:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               ts->residual = 0;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_XFER_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_INTERRUPTED;
+               break;
+       case IO_XFER_ERROR_PHY_NOT_READY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT"
+                       "_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_EPROTO;
+               break;
+       case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               break;
+       case IO_OPEN_CNX_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+               break;
+       case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               if (!t->uldd_task) {
+                       pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+                       ts->resp = SAS_TASK_COMPLETE;
+                       ts->stat = SAS_QUEUE_FULL;
+                       pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+                       mb();/*ditto*/
+                       t->task_done(t);
+                       return;
+               }
+               break;
+       case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+               ts->resp = SAS_TASK_UNDELIVERED;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+               break;
+       case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+                       "NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+               break;
+       case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                      pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+               break;
+       case IO_XFER_ERROR_NAK_RECEIVED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_NAK_RECEIVED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_NAK_R_ERR;
+               break;
+       case IO_XFER_ERROR_PEER_ABORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_PEER_ABORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_NAK_R_ERR;
+               break;
+       case IO_XFER_ERROR_REJECTED_NCQ_MODE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_REJECTED_NCQ_MODE\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_UNDERRUN;
+               break;
+       case IO_XFER_OPEN_RETRY_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_XFER_ERROR_UNEXPECTED_PHASE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_XFER_ERROR_XFER_RDY_OVERRUN:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_XFER_RDY_OVERRUN\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED:
+               PM8001_IO_DBG(pm8001_ha,
+                      pm8001_printk("IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_XFER_ERROR_OFFSET_MISMATCH:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_OFFSET_MISMATCH\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_XFER_ERROR_XFER_ZERO_DATA_LEN:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_XFER_ZERO_DATA_LEN\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       case IO_XFER_CMD_FRAME_ISSUED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_CMD_FRAME_ISSUED\n"));
+               break;
+       case IO_XFER_PIO_SETUP_ERROR:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_PIO_SETUP_ERROR\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       default:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("Unknown status 0x%x\n", event));
+               /* not allowed case. Therefore, return failed status */
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_TO;
+               break;
+       }
+       spin_lock_irqsave(&t->task_state_lock, flags);
+       t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+       t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+       t->task_state_flags |= SAS_TASK_STATE_DONE;
+       if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("task 0x%p done with io_status 0x%x"
+                       " resp 0x%x stat 0x%x but aborted by upper layer!\n",
+                       t, event, ts->resp, ts->stat));
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+       } else {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+               mb();/* in order to force CPU ordering */
+               t->task_done(t);
+       }
+}
+
+/*See the comments for mpi_ssp_completion */
+static void
+mpi_smp_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       u32 param;
+       struct sas_task *t;
+       struct pm8001_ccb_info *ccb;
+       unsigned long flags;
+       u32 status;
+       u32 tag;
+       struct smp_completion_resp *psmpPayload;
+       struct task_status_struct *ts;
+       struct pm8001_device *pm8001_dev;
+
+       psmpPayload = (struct smp_completion_resp *)(piomb + 4);
+       status = le32_to_cpu(psmpPayload->status);
+       tag = le32_to_cpu(psmpPayload->tag);
+
+       ccb = &pm8001_ha->ccb_info[tag];
+       param = le32_to_cpu(psmpPayload->param);
+       t = ccb->task;
+       ts = &t->task_status;
+       pm8001_dev = ccb->device;
+       if (status)
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("smp IO status 0x%x\n", status));
+       if (unlikely(!t || !t->lldd_task || !t->dev))
+               return;
+
+       switch (status) {
+       case IO_SUCCESS:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAM_GOOD;
+       if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_ABORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_ABORTED IOMB\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_ABORTED_TASK;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_OVERFLOW:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_UNDERFLOW\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DATA_OVERRUN;
+               ts->residual = 0;
+               if (pm8001_dev)
+                       pm8001_dev->running_req--;
+               break;
+       case IO_NO_DEVICE:
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("IO_NO_DEVICE\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_PHY_DOWN;
+               break;
+       case IO_ERROR_HW_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_ERROR_HW_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAM_BUSY;
+               break;
+       case IO_XFER_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAM_BUSY;
+               break;
+       case IO_XFER_ERROR_PHY_NOT_READY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAM_BUSY;
+               break;
+       case IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk("IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               break;
+       case IO_OPEN_CNX_ERROR_ZONE_VIOLATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_ZONE_VIOLATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               break;
+       case IO_OPEN_CNX_ERROR_BREAK:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BREAK\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_CONT0;
+               break;
+       case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_UNKNOWN;
+               pm8001_handle_event(pm8001_ha,
+                               pm8001_dev,
+                               IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS);
+               break;
+       case IO_OPEN_CNX_ERROR_BAD_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_BAD_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_BAD_DEST;
+               break;
+       case IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_CONNECTION_RATE_"
+                       "NOT_SUPPORTED\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_CONN_RATE;
+               break;
+       case IO_OPEN_CNX_ERROR_WRONG_DESTINATION:
+               PM8001_IO_DBG(pm8001_ha,
+                      pm8001_printk("IO_OPEN_CNX_ERROR_WRONG_DESTINATION\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_WRONG_DEST;
+               break;
+       case IO_XFER_ERROR_RX_FRAME:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_ERROR_RX_FRAME\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               break;
+       case IO_XFER_OPEN_RETRY_TIMEOUT:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_ERROR_INTERNAL_SMP_RESOURCE:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_ERROR_INTERNAL_SMP_RESOURCE\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_QUEUE_FULL;
+               break;
+       case IO_PORT_IN_RESET:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_PORT_IN_RESET\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_DS_NON_OPERATIONAL:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_DS_NON_OPERATIONAL\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               break;
+       case IO_DS_IN_RECOVERY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_DS_IN_RECOVERY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       case IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_OPEN_REJECT;
+               ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
+       default:
+               PM8001_IO_DBG(pm8001_ha,
+                       pm8001_printk("Unknown status 0x%x\n", status));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAS_DEV_NO_RESPONSE;
+               /* not allowed case. Therefore, return failed status */
+               break;
+       }
+       spin_lock_irqsave(&t->task_state_lock, flags);
+       t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+       t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+       t->task_state_flags |= SAS_TASK_STATE_DONE;
+       if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p done with"
+                       " io_status 0x%x resp 0x%x "
+                       "stat 0x%x but aborted by upper layer!\n",
+                       t, status, ts->resp, ts->stat));
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+       } else {
+               spin_unlock_irqrestore(&t->task_state_lock, flags);
+               pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+               mb();/* in order to force CPU ordering */
+               t->task_done(t);
+       }
+}
+
+static void
+mpi_set_dev_state_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct set_dev_state_resp *pPayload =
+               (struct set_dev_state_resp *)(piomb + 4);
+       u32 tag = le32_to_cpu(pPayload->tag);
+       struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+       struct pm8001_device *pm8001_dev = ccb->device;
+       u32 status = le32_to_cpu(pPayload->status);
+       u32 device_id = le32_to_cpu(pPayload->device_id);
+       u8 pds = le32_to_cpu(pPayload->pds_nds) | PDS_BITS;
+       u8 nds = le32_to_cpu(pPayload->pds_nds) | NDS_BITS;
+       PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set device id = 0x%x state "
+               "from 0x%x to 0x%x status = 0x%x!\n",
+               device_id, pds, nds, status));
+       complete(pm8001_dev->setds_completion);
+       ccb->task = NULL;
+       ccb->ccb_tag = 0xFFFFFFFF;
+       pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static void
+mpi_set_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct get_nvm_data_resp *pPayload =
+               (struct get_nvm_data_resp *)(piomb + 4);
+       u32 tag = le32_to_cpu(pPayload->tag);
+       struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+       u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+       complete(pm8001_ha->nvmd_completion);
+       PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Set nvm data complete!\n"));
+       if ((dlen_status & NVMD_STAT) != 0) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Set nvm data error!\n"));
+               return;
+       }
+       ccb->task = NULL;
+       ccb->ccb_tag = 0xFFFFFFFF;
+       pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static void
+mpi_get_nvmd_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct fw_control_ex    *fw_control_context;
+       struct get_nvm_data_resp *pPayload =
+               (struct get_nvm_data_resp *)(piomb + 4);
+       u32 tag = le32_to_cpu(pPayload->tag);
+       struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+       u32 dlen_status = le32_to_cpu(pPayload->dlen_status);
+       u32 ir_tds_bn_dps_das_nvm =
+               le32_to_cpu(pPayload->ir_tda_bn_dps_das_nvm);
+       void *virt_addr = pm8001_ha->memoryMap.region[NVMD].virt_ptr;
+       fw_control_context = ccb->fw_control_context;
+
+       PM8001_MSG_DBG(pm8001_ha, pm8001_printk("Get nvm data complete!\n"));
+       if ((dlen_status & NVMD_STAT) != 0) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Get nvm data error!\n"));
+               complete(pm8001_ha->nvmd_completion);
+               return;
+       }
+
+       if (ir_tds_bn_dps_das_nvm & IPMode) {
+               /* indirect mode - IR bit set */
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("Get NVMD success, IR=1\n"));
+               if ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == TWI_DEVICE) {
+                       if (ir_tds_bn_dps_das_nvm == 0x80a80200) {
+                               memcpy(pm8001_ha->sas_addr,
+                                     ((u8 *)virt_addr + 4),
+                                      SAS_ADDR_SIZE);
+                               PM8001_MSG_DBG(pm8001_ha,
+                                       pm8001_printk("Get SAS address"
+                                       " from VPD successfully!\n"));
+                       }
+               } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == C_SEEPROM)
+                       || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == VPD_FLASH) ||
+                       ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == EXPAN_ROM)) {
+                               ;
+               } else if (((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == AAP1_RDUMP)
+                       || ((ir_tds_bn_dps_das_nvm & NVMD_TYPE) == IOP_RDUMP)) {
+                       ;
+               } else {
+                       /* Should not be happened*/
+                       PM8001_MSG_DBG(pm8001_ha,
+                               pm8001_printk("(IR=1)Wrong Device type 0x%x\n",
+                               ir_tds_bn_dps_das_nvm));
+               }
+       } else /* direct mode */{
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("Get NVMD success, IR=0, dataLen=%d\n",
+                       (dlen_status & NVMD_LEN) >> 24));
+       }
+       memcpy(fw_control_context->usrAddr,
+               pm8001_ha->memoryMap.region[NVMD].virt_ptr,
+               fw_control_context->len);
+       complete(pm8001_ha->nvmd_completion);
+       ccb->task = NULL;
+       ccb->ccb_tag = 0xFFFFFFFF;
+       pm8001_ccb_free(pm8001_ha, tag);
+}
+
+static int mpi_local_phy_ctl(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct local_phy_ctl_resp *pPayload =
+               (struct local_phy_ctl_resp *)(piomb + 4);
+       u32 status = le32_to_cpu(pPayload->status);
+       u32 phy_id = le32_to_cpu(pPayload->phyop_phyid) & ID_BITS;
+       u32 phy_op = le32_to_cpu(pPayload->phyop_phyid) & OP_BITS;
+       if (status != 0) {
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("%x phy execute %x phy op failed! \n",
+                       phy_id, phy_op));
+       } else
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("%x phy execute %x phy op success! \n",
+                       phy_id, phy_op));
+       return 0;
+}
+
+/**
+ * pm8001_bytes_dmaed - one of the interface function communication with libsas
+ * @pm8001_ha: our hba card information
+ * @i: which phy that received the event.
+ *
+ * when HBA driver received the identify done event or initiate FIS received
+ * event(for SATA), it will invoke this function to notify the sas layer that
+ * the sas toplogy has formed, please discover the the whole sas domain,
+ * while receive a broadcast(change) primitive just tell the sas
+ * layer to discover the changed domain rather than the whole domain.
+ */
+static void pm8001_bytes_dmaed(struct pm8001_hba_info *pm8001_ha, int i)
+{
+       struct pm8001_phy *phy = &pm8001_ha->phy[i];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+       struct sas_ha_struct *sas_ha;
+       if (!phy->phy_attached)
+               return;
+
+       sas_ha = pm8001_ha->sas;
+       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 = phy->maximum_linkrate;
+       }
+
+       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*/
+       }
+       PM8001_MSG_DBG(pm8001_ha, pm8001_printk("phy %d byte dmaded.\n", i));
+
+       sas_phy->frame_rcvd_size = phy->frame_rcvd_size;
+       pm8001_ha->sas->notify_port_event(sas_phy, PORTE_BYTES_DMAED);
+}
+
+/* Get the link rate speed  */
+static void get_lrate_mode(struct pm8001_phy *phy, u8 link_rate)
+{
+       struct sas_phy *sas_phy = phy->sas_phy.phy;
+
+       switch (link_rate) {
+       case PHY_SPEED_60:
+               phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;
+               phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
+               break;
+       case PHY_SPEED_30:
+               phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;
+               phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
+               break;
+       case PHY_SPEED_15:
+               phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;
+               phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
+               break;
+       }
+       sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
+       sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_6_0_GBPS;
+       sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
+       sas_phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS;
+       sas_phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS;
+}
+
+/**
+ * asd_get_attached_sas_addr -- extract/generate attached SAS address
+ * @phy: pointer to asd_phy
+ * @sas_addr: pointer to buffer where the SAS address is to be written
+ *
+ * This function extracts the SAS address from an IDENTIFY frame
+ * received.  If OOB is SATA, then a SAS address is generated from the
+ * HA tables.
+ *
+ * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
+ * buffer.
+ */
+static void pm8001_get_attached_sas_addr(struct pm8001_phy *phy,
+       u8 *sas_addr)
+{
+       if (phy->sas_phy.frame_rcvd[0] == 0x34
+               && phy->sas_phy.oob_mode == SATA_OOB_MODE) {
+               struct pm8001_hba_info *pm8001_ha = phy->sas_phy.ha->lldd_ha;
+               /* FIS device-to-host */
+               u64 addr = be64_to_cpu(*(__be64 *)pm8001_ha->sas_addr);
+               addr += phy->sas_phy.id;
+               *(__be64 *)sas_addr = cpu_to_be64(addr);
+       } else {
+               struct sas_identify_frame *idframe =
+                       (void *) phy->sas_phy.frame_rcvd;
+               memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
+       }
+}
+
+/**
+ * pm8001_hw_event_ack_req- For PM8001,some events need to acknowage to FW.
+ * @pm8001_ha: our hba card information
+ * @Qnum: the outbound queue message number.
+ * @SEA: source of event to ack
+ * @port_id: port id.
+ * @phyId: phy id.
+ * @param0: parameter 0.
+ * @param1: parameter 1.
+ */
+static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
+       u32 Qnum, u32 SEA, u32 port_id, u32 phyId, u32 param0, u32 param1)
+{
+       struct hw_event_ack_req  payload;
+       u32 opc = OPC_INB_SAS_HW_EVENT_ACK;
+
+       struct inbound_queue_table *circularQ;
+
+       memset((u8 *)&payload, 0, sizeof(payload));
+       circularQ = &pm8001_ha->inbnd_q_tbl[Qnum];
+       payload.tag = 1;
+       payload.sea_phyid_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) |
+               ((phyId & 0x0F) << 4) | (port_id & 0x0F));
+       payload.param0 = cpu_to_le32(param0);
+       payload.param1 = cpu_to_le32(param1);
+       mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+}
+
+static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+       u32 phyId, u32 phy_op);
+
+/**
+ * hw_event_sas_phy_up -FW tells me a SAS phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct hw_event_resp *pPayload =
+               (struct hw_event_resp *)(piomb + 4);
+       u32 lr_evt_status_phyid_portid =
+               le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+       u8 link_rate =
+               (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28);
+       u8 phy_id =
+               (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+       struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+       struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+       unsigned long flags;
+       u8 deviceType = pPayload->sas_identify.dev_type;
+
+       PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk("HW_EVENT_SAS_PHY_UP \n"));
+
+       switch (deviceType) {
+       case SAS_PHY_UNUSED:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("device type no device.\n"));
+               break;
+       case SAS_END_DEVICE:
+               PM8001_MSG_DBG(pm8001_ha, pm8001_printk("end device.\n"));
+               pm8001_chip_phy_ctl_req(pm8001_ha, phy_id,
+                       PHY_NOTIFY_ENABLE_SPINUP);
+               get_lrate_mode(phy, link_rate);
+               break;
+       case SAS_EDGE_EXPANDER_DEVICE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("expander device.\n"));
+               get_lrate_mode(phy, link_rate);
+               break;
+       case SAS_FANOUT_EXPANDER_DEVICE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("fanout expander device.\n"));
+               get_lrate_mode(phy, link_rate);
+               break;
+       default:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("unkown device type(%x)\n", deviceType));
+               break;
+       }
+       phy->phy_type |= PORT_TYPE_SAS;
+       phy->identify.device_type = deviceType;
+       phy->phy_attached = 1;
+       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;
+       phy->sas_phy.oob_mode = SAS_OOB_MODE;
+       sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+       spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+       memcpy(phy->frame_rcvd, &pPayload->sas_identify,
+               sizeof(struct sas_identify_frame)-4);
+       phy->frame_rcvd_size = sizeof(struct sas_identify_frame) - 4;
+       pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+       spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+       if (pm8001_ha->flags == PM8001F_RUN_TIME)
+               mdelay(200);/*delay a moment to wait disk to spinup*/
+       pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_sata_phy_up -FW tells me a SATA phy up event.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_sata_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct hw_event_resp *pPayload =
+               (struct hw_event_resp *)(piomb + 4);
+       u32 lr_evt_status_phyid_portid =
+               le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+       u8 link_rate =
+               (u8)((lr_evt_status_phyid_portid & 0xF0000000) >> 28);
+       u8 phy_id =
+               (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+       struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+       struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+       unsigned long flags;
+       get_lrate_mode(phy, link_rate);
+       phy->phy_type |= PORT_TYPE_SATA;
+       phy->phy_attached = 1;
+       phy->sas_phy.oob_mode = SATA_OOB_MODE;
+       sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
+       spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+       memcpy(phy->frame_rcvd, ((u8 *)&pPayload->sata_fis - 4),
+               sizeof(struct dev_to_host_fis));
+       phy->frame_rcvd_size = sizeof(struct dev_to_host_fis);
+       phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
+       phy->identify.device_type = SATA_DEV;
+       pm8001_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
+       spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+       pm8001_bytes_dmaed(pm8001_ha, phy_id);
+}
+
+/**
+ * hw_event_phy_down -we should notify the libsas the phy is down.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void
+hw_event_phy_down(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct hw_event_resp *pPayload =
+               (struct hw_event_resp *)(piomb + 4);
+       u32 lr_evt_status_phyid_portid =
+               le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+       u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F);
+       u8 phy_id =
+               (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+       u32 npip_portstate = le32_to_cpu(pPayload->npip_portstate);
+       u8 portstate = (u8)(npip_portstate & 0x0000000F);
+
+       switch (portstate) {
+       case PORT_VALID:
+               break;
+       case PORT_INVALID:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" PortInvalid portID %d \n", port_id));
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" Last phy Down and port invalid\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+                       port_id, phy_id, 0, 0);
+               break;
+       case PORT_IN_RESET:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" PortInReset portID %d \n", port_id));
+               break;
+       case PORT_NOT_ESTABLISHED:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" phy Down and PORT_NOT_ESTABLISHED\n"));
+               break;
+       case PORT_LOSTCOMM:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" phy Down and PORT_LOSTCOMM\n"));
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" Last phy Down and port invalid\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_PHY_DOWN,
+                       port_id, phy_id, 0, 0);
+               break;
+       default:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" phy Down and(default) = %x\n",
+                       portstate));
+               break;
+
+       }
+}
+
+/**
+ * mpi_reg_resp -process register device ID response.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ *
+ * when sas layer find a device it will notify LLDD, then the driver register
+ * the domain device to FW, this event is the return device ID which the FW
+ * has assigned, from now,inter-communication with FW is no longer using the
+ * SAS address, use device ID which FW assigned.
+ */
+static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       u32 status;
+       u32 device_id;
+       u32 htag;
+       struct pm8001_ccb_info *ccb;
+       struct pm8001_device *pm8001_dev;
+       struct dev_reg_resp *registerRespPayload =
+               (struct dev_reg_resp *)(piomb + 4);
+
+       htag = le32_to_cpu(registerRespPayload->tag);
+       ccb = &pm8001_ha->ccb_info[registerRespPayload->tag];
+       pm8001_dev = ccb->device;
+       status = le32_to_cpu(registerRespPayload->status);
+       device_id = le32_to_cpu(registerRespPayload->device_id);
+       PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk(" register device is status = %d\n", status));
+       switch (status) {
+       case DEVREG_SUCCESS:
+               PM8001_MSG_DBG(pm8001_ha, pm8001_printk("DEVREG_SUCCESS\n"));
+               pm8001_dev->device_id = device_id;
+               break;
+       case DEVREG_FAILURE_OUT_OF_RESOURCE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("DEVREG_FAILURE_OUT_OF_RESOURCE\n"));
+               break;
+       case DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED:
+               PM8001_MSG_DBG(pm8001_ha,
+                  pm8001_printk("DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED\n"));
+               break;
+       case DEVREG_FAILURE_INVALID_PHY_ID:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("DEVREG_FAILURE_INVALID_PHY_ID\n"));
+               break;
+       case DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED:
+               PM8001_MSG_DBG(pm8001_ha,
+                  pm8001_printk("DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED\n"));
+               break;
+       case DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE\n"));
+               break;
+       case DEVREG_FAILURE_PORT_NOT_VALID_STATE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("DEVREG_FAILURE_PORT_NOT_VALID_STATE\n"));
+               break;
+       case DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID:
+               PM8001_MSG_DBG(pm8001_ha,
+                      pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID\n"));
+               break;
+       default:
+               PM8001_MSG_DBG(pm8001_ha,
+                pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n"));
+               break;
+       }
+       complete(pm8001_dev->dcompletion);
+       ccb->task = NULL;
+       ccb->ccb_tag = 0xFFFFFFFF;
+       pm8001_ccb_free(pm8001_ha, htag);
+       return 0;
+}
+
+static int mpi_dereg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       u32 status;
+       u32 device_id;
+       struct dev_reg_resp *registerRespPayload =
+               (struct dev_reg_resp *)(piomb + 4);
+
+       status = le32_to_cpu(registerRespPayload->status);
+       device_id = le32_to_cpu(registerRespPayload->device_id);
+       if (status != 0)
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(" deregister device failed ,status = %x"
+                       ", device_id = %x\n", status, device_id));
+       return 0;
+}
+
+static int
+mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       u32 status;
+       struct fw_control_ex    fw_control_context;
+       struct fw_flash_Update_resp *ppayload =
+               (struct fw_flash_Update_resp *)(piomb + 4);
+       u32 tag = le32_to_cpu(ppayload->tag);
+       struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
+       status = le32_to_cpu(ppayload->status);
+       memcpy(&fw_control_context,
+               ccb->fw_control_context,
+               sizeof(fw_control_context));
+       switch (status) {
+       case FLASH_UPDATE_COMPLETE_PENDING_REBOOT:
+               PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk(": FLASH_UPDATE_COMPLETE_PENDING_REBOOT\n"));
+               break;
+       case FLASH_UPDATE_IN_PROGRESS:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_IN_PROGRESS\n"));
+               break;
+       case FLASH_UPDATE_HDR_ERR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_HDR_ERR\n"));
+               break;
+       case FLASH_UPDATE_OFFSET_ERR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_OFFSET_ERR\n"));
+               break;
+       case FLASH_UPDATE_CRC_ERR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_CRC_ERR\n"));
+               break;
+       case FLASH_UPDATE_LENGTH_ERR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_LENGTH_ERR\n"));
+               break;
+       case FLASH_UPDATE_HW_ERR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_HW_ERR\n"));
+               break;
+       case FLASH_UPDATE_DNLD_NOT_SUPPORTED:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_DNLD_NOT_SUPPORTED\n"));
+               break;
+       case FLASH_UPDATE_DISABLED:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk(": FLASH_UPDATE_DISABLED\n"));
+               break;
+       default:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("No matched status = %d\n", status));
+               break;
+       }
+       ccb->fw_control_context->fw_control->retcode = status;
+       pci_free_consistent(pm8001_ha->pdev,
+                       fw_control_context.len,
+                       fw_control_context.virtAddr,
+                       fw_control_context.phys_addr);
+       complete(pm8001_ha->nvmd_completion);
+       ccb->task = NULL;
+       ccb->ccb_tag = 0xFFFFFFFF;
+       pm8001_ccb_free(pm8001_ha, tag);
+       return 0;
+}
+
+static int
+mpi_general_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
+{
+       u32 status;
+       int i;
+       struct general_event_resp *pPayload =
+               (struct general_event_resp *)(piomb + 4);
+       status = le32_to_cpu(pPayload->status);
+       PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk(" status = 0x%x\n", status));
+       for (i = 0; i < GENERAL_EVENT_PAYLOAD; i++)
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("inb_IOMB_payload[0x%x] 0x%x, \n", i,
+                       pPayload->inb_IOMB_payload[i]));
+       return 0;
+}
+
+static int
+mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       struct sas_task *t;
+       struct pm8001_ccb_info *ccb;
+       unsigned long flags;
+       u32 status ;
+       u32 tag, scp;
+       struct task_status_struct *ts;
+
+       struct task_abort_resp *pPayload =
+               (struct task_abort_resp *)(piomb + 4);
+       ccb = &pm8001_ha->ccb_info[pPayload->tag];
+       t = ccb->task;
+
+
+       status = le32_to_cpu(pPayload->status);
+       tag = le32_to_cpu(pPayload->tag);
+       scp = le32_to_cpu(pPayload->scp);
+       PM8001_IO_DBG(pm8001_ha,
+               pm8001_printk(" status = 0x%x\n", status));
+       if (t == NULL)
+               return -1;
+       ts = &t->task_status;
+       if (status != 0)
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("task abort failed status 0x%x ,"
+                       "tag = 0x%x, scp= 0x%x\n", status, tag, scp));
+       switch (status) {
+       case IO_SUCCESS:
+               PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_SUCCESS\n"));
+               ts->resp = SAS_TASK_COMPLETE;
+               ts->stat = SAM_GOOD;
+               break;
+       case IO_NOT_VALID:
+               PM8001_EH_DBG(pm8001_ha, pm8001_printk("IO_NOT_VALID\n"));
+               ts->resp = TMF_RESP_FUNC_FAILED;
+               break;
+       }
+       spin_lock_irqsave(&t->task_state_lock, flags);
+       t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+       t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+       t->task_state_flags |= SAS_TASK_STATE_DONE;
+       spin_unlock_irqrestore(&t->task_state_lock, flags);
+       pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag);
+       mb();
+       t->task_done(t);
+       return 0;
+}
+
+/**
+ * mpi_hw_event -The hw event has come.
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
+{
+       unsigned long flags;
+       struct hw_event_resp *pPayload =
+               (struct hw_event_resp *)(piomb + 4);
+       u32 lr_evt_status_phyid_portid =
+               le32_to_cpu(pPayload->lr_evt_status_phyid_portid);
+       u8 port_id = (u8)(lr_evt_status_phyid_portid & 0x0000000F);
+       u8 phy_id =
+               (u8)((lr_evt_status_phyid_portid & 0x000000F0) >> 4);
+       u16 eventType =
+               (u16)((lr_evt_status_phyid_portid & 0x00FFFF00) >> 8);
+       u8 status =
+               (u8)((lr_evt_status_phyid_portid & 0x0F000000) >> 24);
+       struct sas_ha_struct *sas_ha = pm8001_ha->sas;
+       struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+       struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
+       PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk("outbound queue HW event & event type : "));
+       switch (eventType) {
+       case HW_EVENT_PHY_START_STATUS:
+               PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk("HW_EVENT_PHY_START_STATUS"
+                       " status = %x\n", status));
+               if (status == 0) {
+                       phy->phy_state = 1;
+                       if (pm8001_ha->flags == PM8001F_RUN_TIME)
+                               complete(phy->enable_completion);
+               }
+               break;
+       case HW_EVENT_SAS_PHY_UP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PHY_START_STATUS \n"));
+               hw_event_sas_phy_up(pm8001_ha, piomb);
+               break;
+       case HW_EVENT_SATA_PHY_UP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_SATA_PHY_UP \n"));
+               hw_event_sata_phy_up(pm8001_ha, piomb);
+               break;
+       case HW_EVENT_PHY_STOP_STATUS:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PHY_STOP_STATUS "
+                       "status = %x\n", status));
+               if (status == 0)
+                       phy->phy_state = 0;
+               break;
+       case HW_EVENT_SATA_SPINUP_HOLD:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_SATA_SPINUP_HOLD \n"));
+               sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
+               break;
+       case HW_EVENT_PHY_DOWN:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PHY_DOWN \n"));
+               sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
+               phy->phy_attached = 0;
+               phy->phy_state = 0;
+               hw_event_phy_down(pm8001_ha, piomb);
+               break;
+       case HW_EVENT_PORT_INVALID:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PORT_INVALID\n"));
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       /* the broadcast change primitive received, tell the LIBSAS this event
+       to revalidate the sas domain*/
+       case HW_EVENT_BROADCAST_CHANGE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_BROADCAST_CHANGE\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0, HW_EVENT_BROADCAST_CHANGE,
+                       port_id, phy_id, 1, 0);
+               spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+               sas_phy->sas_prim = HW_EVENT_BROADCAST_CHANGE;
+               spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+               sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+               break;
+       case HW_EVENT_PHY_ERROR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PHY_ERROR\n"));
+               sas_phy_disconnected(&phy->sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
+               break;
+       case HW_EVENT_BROADCAST_EXP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_BROADCAST_EXP\n"));
+               spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+               sas_phy->sas_prim = HW_EVENT_BROADCAST_EXP;
+               spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+               sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+               break;
+       case HW_EVENT_LINK_ERR_INVALID_DWORD:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_LINK_ERR_INVALID_DWORD\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0,
+                       HW_EVENT_LINK_ERR_INVALID_DWORD, port_id, phy_id, 0, 0);
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_LINK_ERR_DISPARITY_ERROR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_LINK_ERR_DISPARITY_ERROR\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0,
+                       HW_EVENT_LINK_ERR_DISPARITY_ERROR,
+                       port_id, phy_id, 0, 0);
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_LINK_ERR_CODE_VIOLATION:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_LINK_ERR_CODE_VIOLATION\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0,
+                       HW_EVENT_LINK_ERR_CODE_VIOLATION,
+                       port_id, phy_id, 0, 0);
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH:
+               PM8001_MSG_DBG(pm8001_ha,
+                     pm8001_printk("HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0,
+                       HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH,
+                       port_id, phy_id, 0, 0);
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_MALFUNCTION:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_MALFUNCTION\n"));
+               break;
+       case HW_EVENT_BROADCAST_SES:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_BROADCAST_SES\n"));
+               spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
+               sas_phy->sas_prim = HW_EVENT_BROADCAST_SES;
+               spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
+               sas_ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD);
+               break;
+       case HW_EVENT_INBOUND_CRC_ERROR:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_INBOUND_CRC_ERROR\n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0,
+                       HW_EVENT_INBOUND_CRC_ERROR,
+                       port_id, phy_id, 0, 0);
+               break;
+       case HW_EVENT_HARD_RESET_RECEIVED:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_HARD_RESET_RECEIVED\n"));
+               sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
+               break;
+       case HW_EVENT_ID_FRAME_TIMEOUT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_ID_FRAME_TIMEOUT\n"));
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_LINK_ERR_PHY_RESET_FAILED:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_LINK_ERR_PHY_RESET_FAILED \n"));
+               pm8001_hw_event_ack_req(pm8001_ha, 0,
+                       HW_EVENT_LINK_ERR_PHY_RESET_FAILED,
+                       port_id, phy_id, 0, 0);
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_PORT_RESET_TIMER_TMO:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PORT_RESET_TIMER_TMO \n"));
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_PORT_RECOVERY_TIMER_TMO:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PORT_RECOVERY_TIMER_TMO \n"));
+               sas_phy_disconnected(sas_phy);
+               phy->phy_attached = 0;
+               sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
+               break;
+       case HW_EVENT_PORT_RECOVER:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PORT_RECOVER \n"));
+               break;
+       case HW_EVENT_PORT_RESET_COMPLETE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("HW_EVENT_PORT_RESET_COMPLETE \n"));
+               break;
+       case EVENT_BROADCAST_ASYNCH_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("EVENT_BROADCAST_ASYNCH_EVENT\n"));
+               break;
+       default:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("Unknown event type = %x\n", eventType));
+               break;
+       }
+       return 0;
+}
+
+/**
+ * process_one_iomb - process one outbound Queue memory block
+ * @pm8001_ha: our hba card information
+ * @piomb: IO message buffer
+ */
+static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
+{
+       u32 pHeader = (u32)*(u32 *)piomb;
+       u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);
+
+       PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:"));
+
+       switch (opc) {
+       case OPC_OUB_ECHO:
+               PM8001_MSG_DBG(pm8001_ha, pm8001_printk("OPC_OUB_ECHO \n"));
+               break;
+       case OPC_OUB_HW_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_HW_EVENT \n"));
+               mpi_hw_event(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SSP_COMP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SSP_COMP \n"));
+               mpi_ssp_completion(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SMP_COMP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SMP_COMP \n"));
+               mpi_smp_completion(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_LOCAL_PHY_CNTRL:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_LOCAL_PHY_CNTRL\n"));
+               mpi_local_phy_ctl(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_DEV_REGIST:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_DEV_REGIST \n"));
+               mpi_reg_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_DEREG_DEV:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("unresgister the deviece \n"));
+               mpi_dereg_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_GET_DEV_HANDLE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GET_DEV_HANDLE \n"));
+               break;
+       case OPC_OUB_SATA_COMP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SATA_COMP \n"));
+               mpi_sata_completion(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SATA_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SATA_EVENT \n"));
+               mpi_sata_event(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SSP_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SSP_EVENT\n"));
+               mpi_ssp_event(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_DEV_HANDLE_ARRIV:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_DEV_HANDLE_ARRIV\n"));
+               /*This is for target*/
+               break;
+       case OPC_OUB_SSP_RECV_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SSP_RECV_EVENT\n"));
+               /*This is for target*/
+               break;
+       case OPC_OUB_DEV_INFO:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_DEV_INFO\n"));
+               break;
+       case OPC_OUB_FW_FLASH_UPDATE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_FW_FLASH_UPDATE\n"));
+               mpi_fw_flash_update_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_GPIO_RESPONSE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GPIO_RESPONSE\n"));
+               break;
+       case OPC_OUB_GPIO_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GPIO_EVENT\n"));
+               break;
+       case OPC_OUB_GENERAL_EVENT:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GENERAL_EVENT\n"));
+               mpi_general_event(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SSP_ABORT_RSP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SSP_ABORT_RSP\n"));
+               mpi_task_abort_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SATA_ABORT_RSP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SATA_ABORT_RSP\n"));
+               mpi_task_abort_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SAS_DIAG_MODE_START_END:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SAS_DIAG_MODE_START_END\n"));
+               break;
+       case OPC_OUB_SAS_DIAG_EXECUTE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SAS_DIAG_EXECUTE\n"));
+               break;
+       case OPC_OUB_GET_TIME_STAMP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GET_TIME_STAMP\n"));
+               break;
+       case OPC_OUB_SAS_HW_EVENT_ACK:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SAS_HW_EVENT_ACK\n"));
+               break;
+       case OPC_OUB_PORT_CONTROL:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_PORT_CONTROL\n"));
+               break;
+       case OPC_OUB_SMP_ABORT_RSP:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SMP_ABORT_RSP\n"));
+               mpi_task_abort_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_GET_NVMD_DATA:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GET_NVMD_DATA\n"));
+               mpi_get_nvmd_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_SET_NVMD_DATA:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SET_NVMD_DATA\n"));
+               mpi_set_nvmd_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_DEVICE_HANDLE_REMOVAL:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_DEVICE_HANDLE_REMOVAL\n"));
+               break;
+       case OPC_OUB_SET_DEVICE_STATE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SET_DEVICE_STATE\n"));
+               mpi_set_dev_state_resp(pm8001_ha, piomb);
+               break;
+       case OPC_OUB_GET_DEVICE_STATE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_GET_DEVICE_STATE\n"));
+               break;
+       case OPC_OUB_SET_DEV_INFO:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SET_DEV_INFO\n"));
+               break;
+       case OPC_OUB_SAS_RE_INITIALIZE:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("OPC_OUB_SAS_RE_INITIALIZE\n"));
+               break;
+       default:
+               PM8001_MSG_DBG(pm8001_ha,
+                       pm8001_printk("Unknown outbound Queue IOMB OPC = %x\n",
+                       opc));
+               break;
+       }
+}
+
+static int process_oq(struct pm8001_hba_info *pm8001_ha)
+{
+       struct outbound_queue_table *circularQ;
+       void *pMsg1 = NULL;
+       u8 bc = 0;
+       u32 ret = MPI_IO_STATUS_FAIL;
+
+       circularQ = &pm8001_ha->outbnd_q_tbl[0];
+       do {
+               ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
+               if (MPI_IO_STATUS_SUCCESS == ret) {
+                       /* process the outbound message */
+                       process_one_iomb(pm8001_ha, (void *)(pMsg1 - 4));
+                       /* free the message from the outbound circular buffer */
+                       mpi_msg_free_set(pm8001_ha, pMsg1, circularQ, bc);
+               }
+               if (MPI_IO_STATUS_BUSY == ret) {
+                       u32 producer_idx;
+                       /* Update the producer index from SPC */
+                       producer_idx = pm8001_read_32(circularQ->pi_virt);
+                       circularQ->producer_index = cpu_to_le32(producer_idx);
+                       if (circularQ->producer_index ==
+                               circularQ->consumer_idx)
+                               /* OQ is empty */
+                               break;
+               }
+       } while (1);
+       return ret;
+}
+
+/* PCI_DMA_... to our direction translation. */
+static const u8 data_dir_flags[] = {
+       [PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT,/* UNSPECIFIED */
+       [PCI_DMA_TODEVICE]      = DATA_DIR_OUT,/* OUTBOUND */
+       [PCI_DMA_FROMDEVICE]    = DATA_DIR_IN,/* INBOUND */
+       [PCI_DMA_NONE]          = DATA_DIR_NONE,/* NO TRANSFER */
+};
+static void
+pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
+{
+       int i;
+       struct scatterlist *sg;
+       struct pm8001_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->im_len.e = 0;
+               buf_prd++;
+       }
+}
+
+static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd)
+{
+       psmp_cmd->tag = cpu_to_le32(hTag);
+       psmp_cmd->device_id = cpu_to_le32(deviceID);
+       psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1));
+}
+
+/**
+ * pm8001_chip_smp_req - send a SMP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm8001_chip_smp_req(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb)
+{
+       int elem, rc;
+       struct sas_task *task = ccb->task;
+       struct domain_device *dev = task->dev;
+       struct pm8001_device *pm8001_dev = dev->lldd_dev;
+       struct scatterlist *sg_req, *sg_resp;
+       u32 req_len, resp_len;
+       struct smp_req smp_cmd;
+       u32 opc;
+       struct inbound_queue_table *circularQ;
+
+       memset(&smp_cmd, 0, sizeof(smp_cmd));
+       /*
+        * DMA-map SMP request, response buffers
+        */
+       sg_req = &task->smp_task.smp_req;
+       elem = dma_map_sg(pm8001_ha->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(pm8001_ha->dev, 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;
+       }
+
+       opc = OPC_INB_SMP_REQUEST;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       smp_cmd.tag = cpu_to_le32(ccb->ccb_tag);
+       smp_cmd.long_smp_req.long_req_addr =
+               cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
+       smp_cmd.long_smp_req.long_req_size =
+               cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
+       smp_cmd.long_smp_req.long_resp_addr =
+               cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp));
+       smp_cmd.long_smp_req.long_resp_size =
+               cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
+       build_smp_cmd(pm8001_dev->device_id, smp_cmd.tag, &smp_cmd);
+       mpi_build_cmd(pm8001_ha, circularQ, opc, (u32 *)&smp_cmd);
+       return 0;
+
+err_out_2:
+       dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_resp, 1,
+                       PCI_DMA_FROMDEVICE);
+err_out:
+       dma_unmap_sg(pm8001_ha->dev, &ccb->task->smp_task.smp_req, 1,
+                       PCI_DMA_TODEVICE);
+       return rc;
+}
+
+/**
+ * pm8001_chip_ssp_io_req - send a SSP task to FW
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information this request used.
+ */
+static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb)
+{
+       struct sas_task *task = ccb->task;
+       struct domain_device *dev = task->dev;
+       struct pm8001_device *pm8001_dev = dev->lldd_dev;
+       struct ssp_ini_io_start_req ssp_cmd;
+       u32 tag = ccb->ccb_tag;
+       int ret;
+       __le64 phys_addr;
+       struct inbound_queue_table *circularQ;
+       u32 opc = OPC_INB_SSPINIIOSTART;
+       memset(&ssp_cmd, 0, sizeof(ssp_cmd));
+       memcpy(ssp_cmd.ssp_iu.lun, task->ssp_task.LUN, 8);
+       ssp_cmd.dir_m_tlr = data_dir_flags[task->data_dir] << 8 | 0x0;/*0 for
+       SAS 1.1 compatible TLR*/
+       ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+       ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+       ssp_cmd.tag = cpu_to_le32(tag);
+       if (task->ssp_task.enable_first_burst)
+               ssp_cmd.ssp_iu.efb_prio_attr |= 0x80;
+       ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3);
+       ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7);
+       memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cdb, 16);
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+       /* fill in PRD (scatter/gather) table, if any */
+       if (task->num_scatter > 1) {
+               pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
+               phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
+                               offsetof(struct pm8001_ccb_info, buf_prd[0]));
+               ssp_cmd.addr_low = lower_32_bits(phys_addr);
+               ssp_cmd.addr_high = upper_32_bits(phys_addr);
+               ssp_cmd.esgl = cpu_to_le32(1<<31);
+       } else if (task->num_scatter == 1) {
+               __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+               ssp_cmd.addr_low = lower_32_bits(dma_addr);
+               ssp_cmd.addr_high = upper_32_bits(dma_addr);
+               ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+               ssp_cmd.esgl = 0;
+       } else if (task->num_scatter == 0) {
+               ssp_cmd.addr_low = 0;
+               ssp_cmd.addr_high = 0;
+               ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
+               ssp_cmd.esgl = 0;
+       }
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &ssp_cmd);
+       return ret;
+}
+
+static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb)
+{
+       struct sas_task *task = ccb->task;
+       struct domain_device *dev = task->dev;
+       struct pm8001_device *pm8001_ha_dev = dev->lldd_dev;
+       u32 tag = ccb->ccb_tag;
+       int ret;
+       struct sata_start_req sata_cmd;
+       u32 hdr_tag, ncg_tag = 0;
+       __le64 phys_addr;
+       u32 ATAP = 0x0;
+       u32 dir;
+       struct inbound_queue_table *circularQ;
+       u32  opc = OPC_INB_SATA_HOST_OPSTART;
+       memset(&sata_cmd, 0, sizeof(sata_cmd));
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       if (task->data_dir == PCI_DMA_NONE) {
+               ATAP = 0x04;  /* no data*/
+               PM8001_IO_DBG(pm8001_ha, pm8001_printk("no data \n"));
+       } else if (likely(!task->ata_task.device_control_reg_update)) {
+               if (task->ata_task.dma_xfer) {
+                       ATAP = 0x06; /* DMA */
+                       PM8001_IO_DBG(pm8001_ha, pm8001_printk("DMA \n"));
+               } else {
+                       ATAP = 0x05; /* PIO*/
+                       PM8001_IO_DBG(pm8001_ha, pm8001_printk("PIO \n"));
+               }
+               if (task->ata_task.use_ncq &&
+                       dev->sata_dev.command_set != ATAPI_COMMAND_SET) {
+                       ATAP = 0x07; /* FPDMA */
+                       PM8001_IO_DBG(pm8001_ha, pm8001_printk("FPDMA \n"));
+               }
+       }
+       if (task->ata_task.use_ncq && pm8001_get_ncq_tag(task, &hdr_tag))
+               ncg_tag = cpu_to_le32(hdr_tag);
+       dir = data_dir_flags[task->data_dir] << 8;
+       sata_cmd.tag = cpu_to_le32(tag);
+       sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id);
+       sata_cmd.data_len = cpu_to_le32(task->total_xfer_len);
+       sata_cmd.ncqtag_atap_dir_m =
+               cpu_to_le32(((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir);
+       sata_cmd.sata_fis = task->ata_task.fis;
+       if (likely(!task->ata_task.device_control_reg_update))
+               sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */
+       sata_cmd.sata_fis.flags &= 0xF0;/* PM_PORT field shall be 0 */
+       /* fill in PRD (scatter/gather) table, if any */
+       if (task->num_scatter > 1) {
+               pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
+               phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
+                               offsetof(struct pm8001_ccb_info, buf_prd[0]));
+               sata_cmd.addr_low = lower_32_bits(phys_addr);
+               sata_cmd.addr_high = upper_32_bits(phys_addr);
+               sata_cmd.esgl = cpu_to_le32(1 << 31);
+       } else if (task->num_scatter == 1) {
+               __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+               sata_cmd.addr_low = lower_32_bits(dma_addr);
+               sata_cmd.addr_high = upper_32_bits(dma_addr);
+               sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+               sata_cmd.esgl = 0;
+       } else if (task->num_scatter == 0) {
+               sata_cmd.addr_low = 0;
+               sata_cmd.addr_high = 0;
+               sata_cmd.len = cpu_to_le32(task->total_xfer_len);
+               sata_cmd.esgl = 0;
+       }
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sata_cmd);
+       return ret;
+}
+
+/**
+ * pm8001_chip_phy_start_req - start phy via PHY_START COMMAND
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int
+pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id)
+{
+       struct phy_start_req payload;
+       struct inbound_queue_table *circularQ;
+       int ret;
+       u32 tag = 0x01;
+       u32 opcode = OPC_INB_PHYSTART;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       memset(&payload, 0, sizeof(payload));
+       payload.tag = cpu_to_le32(tag);
+       /*
+        ** [0:7]   PHY Identifier
+        ** [8:11]  link rate 1.5G, 3G, 6G
+        ** [12:13] link mode 01b SAS mode; 10b SATA mode; 11b both
+        ** [14]    0b disable spin up hold; 1b enable spin up hold
+        */
+       payload.ase_sh_lm_slr_phyid = cpu_to_le32(SPINHOLD_DISABLE |
+               LINKMODE_AUTO | LINKRATE_15 |
+               LINKRATE_30 | LINKRATE_60 | phy_id);
+       payload.sas_identify.dev_type = SAS_END_DEV;
+       payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL;
+       memcpy(payload.sas_identify.sas_addr,
+               pm8001_ha->sas_addr, SAS_ADDR_SIZE);
+       payload.sas_identify.phy_id = phy_id;
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+       return ret;
+}
+
+/**
+ * pm8001_chip_phy_stop_req - start phy via PHY_STOP COMMAND
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to start up.
+ */
+static int pm8001_chip_phy_stop_req(struct pm8001_hba_info *pm8001_ha,
+       u8 phy_id)
+{
+       struct phy_stop_req payload;
+       struct inbound_queue_table *circularQ;
+       int ret;
+       u32 tag = 0x01;
+       u32 opcode = OPC_INB_PHYSTOP;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       memset(&payload, 0, sizeof(payload));
+       payload.tag = cpu_to_le32(tag);
+       payload.phy_id = cpu_to_le32(phy_id);
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opcode, &payload);
+       return ret;
+}
+
+/**
+ * see comments on mpi_reg_resp.
+ */
+static int pm8001_chip_reg_dev_req(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_device *pm8001_dev, u32 flag)
+{
+       struct reg_dev_req payload;
+       u32     opc;
+       u32 stp_sspsmp_sata = 0x4;
+       struct inbound_queue_table *circularQ;
+       u32 linkrate, phy_id;
+       int rc, tag = 0xdeadbeef;
+       struct pm8001_ccb_info *ccb;
+       u8 retryFlag = 0x1;
+       u16 firstBurstSize = 0;
+       u16 ITNT = 2000;
+       struct domain_device *dev = pm8001_dev->sas_device;
+       struct domain_device *parent_dev = dev->parent;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+
+       memset(&payload, 0, sizeof(payload));
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               return rc;
+       ccb = &pm8001_ha->ccb_info[tag];
+       ccb->device = pm8001_dev;
+       ccb->ccb_tag = tag;
+       payload.tag = cpu_to_le32(tag);
+       if (flag == 1)
+               stp_sspsmp_sata = 0x02; /*direct attached sata */
+       else {
+               if (pm8001_dev->dev_type == SATA_DEV)
+                       stp_sspsmp_sata = 0x00; /* stp*/
+               else if (pm8001_dev->dev_type == SAS_END_DEV ||
+                       pm8001_dev->dev_type == EDGE_DEV ||
+                       pm8001_dev->dev_type == FANOUT_DEV)
+                       stp_sspsmp_sata = 0x01; /*ssp or smp*/
+       }
+       if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type))
+               phy_id = parent_dev->ex_dev.ex_phy->phy_id;
+       else
+               phy_id = pm8001_dev->attached_phy;
+       opc = OPC_INB_REG_DEV;
+       linkrate = (pm8001_dev->sas_device->linkrate < dev->port->linkrate) ?
+                       pm8001_dev->sas_device->linkrate : dev->port->linkrate;
+       payload.phyid_portid =
+               cpu_to_le32(((pm8001_dev->sas_device->port->id) & 0x0F) |
+               ((phy_id & 0x0F) << 4));
+       payload.dtype_dlr_retry = cpu_to_le32((retryFlag & 0x01) |
+               ((linkrate & 0x0F) * 0x1000000) |
+               ((stp_sspsmp_sata & 0x03) * 0x10000000));
+       payload.firstburstsize_ITNexustimeout =
+               cpu_to_le32(ITNT | (firstBurstSize * 0x10000));
+       memcpy(&payload.sas_addr_hi, pm8001_dev->sas_device->sas_addr,
+               SAS_ADDR_SIZE);
+       rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+       return rc;
+}
+
+/**
+ * see comments on mpi_reg_resp.
+ */
+static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
+       u32 device_id)
+{
+       struct dereg_dev_req payload;
+       u32 opc = OPC_INB_DEREG_DEV_HANDLE;
+       int ret;
+       struct inbound_queue_table *circularQ;
+
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       memset(&payload, 0, sizeof(payload));
+       payload.tag = 1;
+       payload.device_id = cpu_to_le32(device_id);
+       PM8001_MSG_DBG(pm8001_ha,
+               pm8001_printk("unregister device device_id = %d\n", device_id));
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+       return ret;
+}
+
+/**
+ * pm8001_chip_phy_ctl_req - support the local phy operation
+ * @pm8001_ha: our hba card information.
+ * @num: the inbound queue number
+ * @phy_id: the phy id which we wanted to operate
+ * @phy_op:
+ */
+static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
+       u32 phyId, u32 phy_op)
+{
+       struct local_phy_ctl_req payload;
+       struct inbound_queue_table *circularQ;
+       int ret;
+       u32 opc = OPC_INB_LOCAL_PHY_CONTROL;
+       memset((u8 *)&payload, 0, sizeof(payload));
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       payload.tag = 1;
+       payload.phyop_phyid =
+               cpu_to_le32(((phy_op & 0xff) << 8) | (phyId & 0x0F));
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+       return ret;
+}
+
+static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 value;
+#ifdef PM8001_USE_MSIX
+       return 1;
+#endif
+       value = pm8001_cr32(pm8001_ha, 0, MSGU_ODR);
+       if (value)
+               return 1;
+       return 0;
+
+}
+
+/**
+ * pm8001_chip_isr - PM8001 isr handler.
+ * @pm8001_ha: our hba card information.
+ * @irq: irq number.
+ * @stat: stat.
+ */
+static irqreturn_t
+pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&pm8001_ha->lock, flags);
+       pm8001_chip_interrupt_disable(pm8001_ha);
+       process_oq(pm8001_ha);
+       pm8001_chip_interrupt_enable(pm8001_ha);
+       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+       return IRQ_HANDLED;
+}
+
+static int send_task_abort(struct pm8001_hba_info *pm8001_ha, u32 opc,
+       u32 dev_id, u8 flag, u32 task_tag, u32 cmd_tag)
+{
+       struct task_abort_req task_abort;
+       struct inbound_queue_table *circularQ;
+       int ret;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       memset(&task_abort, 0, sizeof(task_abort));
+       if (ABORT_SINGLE == (flag & ABORT_MASK)) {
+               task_abort.abort_all = 0;
+               task_abort.device_id = cpu_to_le32(dev_id);
+               task_abort.tag_to_abort = cpu_to_le32(task_tag);
+               task_abort.tag = cpu_to_le32(cmd_tag);
+       } else if (ABORT_ALL == (flag & ABORT_MASK)) {
+               task_abort.abort_all = cpu_to_le32(1);
+               task_abort.device_id = cpu_to_le32(dev_id);
+               task_abort.tag = cpu_to_le32(cmd_tag);
+       }
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &task_abort);
+       return ret;
+}
+
+/**
+ * pm8001_chip_abort_task - SAS abort task when error or exception happened.
+ * @task: the task we wanted to aborted.
+ * @flag: the abort flag.
+ */
+static int pm8001_chip_abort_task(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag, u32 cmd_tag)
+{
+       u32 opc, device_id;
+       int rc = TMF_RESP_FUNC_FAILED;
+       PM8001_EH_DBG(pm8001_ha, pm8001_printk("cmd_tag = %x, abort task tag"
+               " = %x", cmd_tag, task_tag));
+       if (pm8001_dev->dev_type == SAS_END_DEV)
+               opc = OPC_INB_SSP_ABORT;
+       else if (pm8001_dev->dev_type == SATA_DEV)
+               opc = OPC_INB_SATA_ABORT;
+       else
+               opc = OPC_INB_SMP_ABORT;/* SMP */
+       device_id = pm8001_dev->device_id;
+       rc = send_task_abort(pm8001_ha, opc, device_id, flag,
+               task_tag, cmd_tag);
+       if (rc != TMF_RESP_FUNC_COMPLETE)
+               PM8001_EH_DBG(pm8001_ha, pm8001_printk("rc= %d\n", rc));
+       return rc;
+}
+
+/**
+ * pm8001_chip_ssp_tm_req - built the task managment command.
+ * @pm8001_ha: our hba card information.
+ * @ccb: the ccb information.
+ * @tmf: task management function.
+ */
+static int pm8001_chip_ssp_tm_req(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+{
+       struct sas_task *task = ccb->task;
+       struct domain_device *dev = task->dev;
+       struct pm8001_device *pm8001_dev = dev->lldd_dev;
+       u32 opc = OPC_INB_SSPINITMSTART;
+       struct inbound_queue_table *circularQ;
+       struct ssp_ini_tm_start_req sspTMCmd;
+       int ret;
+
+       memset(&sspTMCmd, 0, sizeof(sspTMCmd));
+       sspTMCmd.device_id = cpu_to_le32(pm8001_dev->device_id);
+       sspTMCmd.relate_tag = cpu_to_le32(tmf->tag_of_task_to_be_managed);
+       sspTMCmd.tmf = cpu_to_le32(tmf->tmf);
+       memcpy(sspTMCmd.lun, task->ssp_task.LUN, 8);
+       sspTMCmd.tag = cpu_to_le32(ccb->ccb_tag);
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &sspTMCmd);
+       return ret;
+}
+
+static int pm8001_chip_get_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+       void *payload)
+{
+       u32 opc = OPC_INB_GET_NVMD_DATA;
+       u32 nvmd_type;
+       int rc;
+       u32 tag;
+       struct pm8001_ccb_info *ccb;
+       struct inbound_queue_table *circularQ;
+       struct get_nvm_data_req nvmd_req;
+       struct fw_control_ex *fw_control_context;
+       struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+       nvmd_type = ioctl_payload->minor_function;
+       fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+       fw_control_context->usrAddr = (u8 *)&ioctl_payload->func_specific[0];
+       fw_control_context->len = ioctl_payload->length;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       memset(&nvmd_req, 0, sizeof(nvmd_req));
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               return rc;
+       ccb = &pm8001_ha->ccb_info[tag];
+       ccb->ccb_tag = tag;
+       ccb->fw_control_context = fw_control_context;
+       nvmd_req.tag = cpu_to_le32(tag);
+
+       switch (nvmd_type) {
+       case TWI_DEVICE: {
+               u32 twi_addr, twi_page_size;
+               twi_addr = 0xa8;
+               twi_page_size = 2;
+
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
+                       twi_page_size << 8 | TWI_DEVICE);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       }
+       case C_SEEPROM: {
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       }
+       case VPD_FLASH: {
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       }
+       case EXPAN_ROM: {
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       }
+       default:
+               break;
+       }
+       rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+       return rc;
+}
+
+static int pm8001_chip_set_nvmd_req(struct pm8001_hba_info *pm8001_ha,
+       void *payload)
+{
+       u32 opc = OPC_INB_SET_NVMD_DATA;
+       u32 nvmd_type;
+       int rc;
+       u32 tag;
+       struct pm8001_ccb_info *ccb;
+       struct inbound_queue_table *circularQ;
+       struct set_nvm_data_req nvmd_req;
+       struct fw_control_ex *fw_control_context;
+       struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+       nvmd_type = ioctl_payload->minor_function;
+       fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       memcpy(pm8001_ha->memoryMap.region[NVMD].virt_ptr,
+               ioctl_payload->func_specific,
+               ioctl_payload->length);
+       memset(&nvmd_req, 0, sizeof(nvmd_req));
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               return rc;
+       ccb = &pm8001_ha->ccb_info[tag];
+       ccb->fw_control_context = fw_control_context;
+       ccb->ccb_tag = tag;
+       nvmd_req.tag = cpu_to_le32(tag);
+       switch (nvmd_type) {
+       case TWI_DEVICE: {
+               u32 twi_addr, twi_page_size;
+               twi_addr = 0xa8;
+               twi_page_size = 2;
+               nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | twi_addr << 16 |
+                       twi_page_size << 8 | TWI_DEVICE);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       }
+       case C_SEEPROM:
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | C_SEEPROM);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       case VPD_FLASH:
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | VPD_FLASH);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       case EXPAN_ROM:
+               nvmd_req.len_ir_vpdd = cpu_to_le32(IPMode | EXPAN_ROM);
+               nvmd_req.resp_len = cpu_to_le32(ioctl_payload->length);
+               nvmd_req.reserved[0] = cpu_to_le32(0xFEDCBA98);
+               nvmd_req.resp_addr_hi =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_hi);
+               nvmd_req.resp_addr_lo =
+                   cpu_to_le32(pm8001_ha->memoryMap.region[NVMD].phys_addr_lo);
+               break;
+       default:
+               break;
+       }
+       rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &nvmd_req);
+       return rc;
+}
+
+/**
+ * pm8001_chip_fw_flash_update_build - support the firmware update operation
+ * @pm8001_ha: our hba card information.
+ * @fw_flash_updata_info: firmware flash update param
+ */
+static int
+pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
+       void *fw_flash_updata_info, u32 tag)
+{
+       struct fw_flash_Update_req payload;
+       struct fw_flash_updata_info *info;
+       struct inbound_queue_table *circularQ;
+       int ret;
+       u32 opc = OPC_INB_FW_FLASH_UPDATE;
+
+       memset(&payload, 0, sizeof(struct fw_flash_Update_req));
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       info = fw_flash_updata_info;
+       payload.tag = cpu_to_le32(tag);
+       payload.cur_image_len = cpu_to_le32(info->cur_image_len);
+       payload.cur_image_offset = cpu_to_le32(info->cur_image_offset);
+       payload.total_image_len = cpu_to_le32(info->total_image_len);
+       payload.len = info->sgl.im_len.len ;
+       payload.sgl_addr_lo = lower_32_bits(info->sgl.addr);
+       payload.sgl_addr_hi = upper_32_bits(info->sgl.addr);
+       ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+       return ret;
+}
+
+static int
+pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha,
+       void *payload)
+{
+       struct fw_flash_updata_info flash_update_info;
+       struct fw_control_info *fw_control;
+       struct fw_control_ex *fw_control_context;
+       int rc;
+       u32 tag;
+       struct pm8001_ccb_info *ccb;
+       void *buffer = NULL;
+       dma_addr_t phys_addr;
+       u32 phys_addr_hi;
+       u32 phys_addr_lo;
+       struct pm8001_ioctl_payload *ioctl_payload = payload;
+
+       fw_control_context = kzalloc(sizeof(struct fw_control_ex), GFP_KERNEL);
+       fw_control = (struct fw_control_info *)&ioctl_payload->func_specific[0];
+       if (fw_control->len != 0) {
+               if (pm8001_mem_alloc(pm8001_ha->pdev,
+                       (void **)&buffer,
+                       &phys_addr,
+                       &phys_addr_hi,
+                       &phys_addr_lo,
+                       fw_control->len, 0) != 0) {
+                               PM8001_FAIL_DBG(pm8001_ha,
+                                       pm8001_printk("Mem alloc failure\n"));
+                               return -ENOMEM;
+               }
+       }
+       memset(buffer, 0, fw_control->len);
+       memcpy(buffer, fw_control->buffer, fw_control->len);
+       flash_update_info.sgl.addr = cpu_to_le64(phys_addr);
+       flash_update_info.sgl.im_len.len = cpu_to_le32(fw_control->len);
+       flash_update_info.sgl.im_len.e = 0;
+       flash_update_info.cur_image_offset = fw_control->offset;
+       flash_update_info.cur_image_len = fw_control->len;
+       flash_update_info.total_image_len = fw_control->size;
+       fw_control_context->fw_control = fw_control;
+       fw_control_context->virtAddr = buffer;
+       fw_control_context->len = fw_control->len;
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               return rc;
+       ccb = &pm8001_ha->ccb_info[tag];
+       ccb->fw_control_context = fw_control_context;
+       ccb->ccb_tag = tag;
+       rc = pm8001_chip_fw_flash_update_build(pm8001_ha, &flash_update_info,
+               tag);
+       return rc;
+}
+
+static int
+pm8001_chip_set_dev_state_req(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_device *pm8001_dev, u32 state)
+{
+       struct set_dev_state_req payload;
+       struct inbound_queue_table *circularQ;
+       struct pm8001_ccb_info *ccb;
+       int rc;
+       u32 tag;
+       u32 opc = OPC_INB_SET_DEVICE_STATE;
+       memset(&payload, 0, sizeof(payload));
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               return -1;
+       ccb = &pm8001_ha->ccb_info[tag];
+       ccb->ccb_tag = tag;
+       ccb->device = pm8001_dev;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       payload.tag = cpu_to_le32(tag);
+       payload.device_id = cpu_to_le32(pm8001_dev->device_id);
+       payload.nds = cpu_to_le32(state);
+       rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+       return rc;
+
+}
+
+static int
+pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha)
+{
+       struct sas_re_initialization_req payload;
+       struct inbound_queue_table *circularQ;
+       struct pm8001_ccb_info *ccb;
+       int rc;
+       u32 tag;
+       u32 opc = OPC_INB_SAS_RE_INITIALIZE;
+       memset(&payload, 0, sizeof(payload));
+       rc = pm8001_tag_alloc(pm8001_ha, &tag);
+       if (rc)
+               return -1;
+       ccb = &pm8001_ha->ccb_info[tag];
+       ccb->ccb_tag = tag;
+       circularQ = &pm8001_ha->inbnd_q_tbl[0];
+       payload.tag = cpu_to_le32(tag);
+       payload.SSAHOLT = cpu_to_le32(0xd << 25);
+       payload.sata_hol_tmo = cpu_to_le32(80);
+       payload.open_reject_cmdretries_data_retries = cpu_to_le32(0xff00ff);
+       rc = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
+       return rc;
+
+}
+
+const struct pm8001_dispatch pm8001_8001_dispatch = {
+       .name                   = "pmc8001",
+       .chip_init              = pm8001_chip_init,
+       .chip_soft_rst          = pm8001_chip_soft_rst,
+       .chip_rst               = pm8001_hw_chip_rst,
+       .chip_iounmap           = pm8001_chip_iounmap,
+       .isr                    = pm8001_chip_isr,
+       .is_our_interupt        = pm8001_chip_is_our_interupt,
+       .isr_process_oq         = process_oq,
+       .interrupt_enable       = pm8001_chip_interrupt_enable,
+       .interrupt_disable      = pm8001_chip_interrupt_disable,
+       .make_prd               = pm8001_chip_make_sg,
+       .smp_req                = pm8001_chip_smp_req,
+       .ssp_io_req             = pm8001_chip_ssp_io_req,
+       .sata_req               = pm8001_chip_sata_req,
+       .phy_start_req          = pm8001_chip_phy_start_req,
+       .phy_stop_req           = pm8001_chip_phy_stop_req,
+       .reg_dev_req            = pm8001_chip_reg_dev_req,
+       .dereg_dev_req          = pm8001_chip_dereg_dev_req,
+       .phy_ctl_req            = pm8001_chip_phy_ctl_req,
+       .task_abort             = pm8001_chip_abort_task,
+       .ssp_tm_req             = pm8001_chip_ssp_tm_req,
+       .get_nvmd_req           = pm8001_chip_get_nvmd_req,
+       .set_nvmd_req           = pm8001_chip_set_nvmd_req,
+       .fw_flash_update_req    = pm8001_chip_fw_flash_update_req,
+       .set_dev_state_req      = pm8001_chip_set_dev_state_req,
+       .sas_re_init_req        = pm8001_chip_sas_re_initialization,
+};
+
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
new file mode 100644 (file)
index 0000000..96e4daa
--- /dev/null
@@ -0,0 +1,1030 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#ifndef _PMC8001_REG_H_
+#define _PMC8001_REG_H_
+
+#include <linux/types.h>
+#include <scsi/libsas.h>
+
+
+/* for Request Opcode of IOMB */
+#define OPC_INB_ECHO                           1       /* 0x000 */
+#define OPC_INB_PHYSTART                       4       /* 0x004 */
+#define OPC_INB_PHYSTOP                                5       /* 0x005 */
+#define OPC_INB_SSPINIIOSTART                  6       /* 0x006 */
+#define OPC_INB_SSPINITMSTART                  7       /* 0x007 */
+#define OPC_INB_SSPINIEXTIOSTART               8       /* 0x008 */
+#define OPC_INB_DEV_HANDLE_ACCEPT              9       /* 0x009 */
+#define OPC_INB_SSPTGTIOSTART                  10      /* 0x00A */
+#define OPC_INB_SSPTGTRSPSTART                 11      /* 0x00B */
+#define OPC_INB_SSPINIEDCIOSTART               12      /* 0x00C */
+#define OPC_INB_SSPINIEXTEDCIOSTART            13      /* 0x00D */
+#define OPC_INB_SSPTGTEDCIOSTART               14      /* 0x00E */
+#define OPC_INB_SSP_ABORT                      15      /* 0x00F */
+#define OPC_INB_DEREG_DEV_HANDLE               16      /* 0x010 */
+#define OPC_INB_GET_DEV_HANDLE                 17      /* 0x011 */
+#define OPC_INB_SMP_REQUEST                    18      /* 0x012 */
+/* SMP_RESPONSE is removed */
+#define OPC_INB_SMP_RESPONSE                   19      /* 0x013 */
+#define OPC_INB_SMP_ABORT                      20      /* 0x014 */
+#define OPC_INB_REG_DEV                                22      /* 0x016 */
+#define OPC_INB_SATA_HOST_OPSTART              23      /* 0x017 */
+#define OPC_INB_SATA_ABORT                     24      /* 0x018 */
+#define OPC_INB_LOCAL_PHY_CONTROL              25      /* 0x019 */
+#define OPC_INB_GET_DEV_INFO                   26      /* 0x01A */
+#define OPC_INB_FW_FLASH_UPDATE                        32      /* 0x020 */
+#define OPC_INB_GPIO                           34      /* 0x022 */
+#define OPC_INB_SAS_DIAG_MODE_START_END                35      /* 0x023 */
+#define OPC_INB_SAS_DIAG_EXECUTE               36      /* 0x024 */
+#define OPC_INB_SAS_HW_EVENT_ACK               37      /* 0x025 */
+#define OPC_INB_GET_TIME_STAMP                 38      /* 0x026 */
+#define OPC_INB_PORT_CONTROL                   39      /* 0x027 */
+#define OPC_INB_GET_NVMD_DATA                  40      /* 0x028 */
+#define OPC_INB_SET_NVMD_DATA                  41      /* 0x029 */
+#define OPC_INB_SET_DEVICE_STATE               42      /* 0x02A */
+#define OPC_INB_GET_DEVICE_STATE               43      /* 0x02B */
+#define OPC_INB_SET_DEV_INFO                   44      /* 0x02C */
+#define OPC_INB_SAS_RE_INITIALIZE              45      /* 0x02D */
+
+/* for Response Opcode of IOMB */
+#define OPC_OUB_ECHO                           1       /* 0x001 */
+#define OPC_OUB_HW_EVENT                       4       /* 0x004 */
+#define OPC_OUB_SSP_COMP                       5       /* 0x005 */
+#define OPC_OUB_SMP_COMP                       6       /* 0x006 */
+#define OPC_OUB_LOCAL_PHY_CNTRL                        7       /* 0x007 */
+#define OPC_OUB_DEV_REGIST                     10      /* 0x00A */
+#define OPC_OUB_DEREG_DEV                      11      /* 0x00B */
+#define OPC_OUB_GET_DEV_HANDLE                 12      /* 0x00C */
+#define OPC_OUB_SATA_COMP                      13      /* 0x00D */
+#define OPC_OUB_SATA_EVENT                     14      /* 0x00E */
+#define OPC_OUB_SSP_EVENT                      15      /* 0x00F */
+#define OPC_OUB_DEV_HANDLE_ARRIV               16      /* 0x010 */
+/* SMP_RECEIVED Notification is removed */
+#define OPC_OUB_SMP_RECV_EVENT                 17      /* 0x011 */
+#define OPC_OUB_SSP_RECV_EVENT                 18      /* 0x012 */
+#define OPC_OUB_DEV_INFO                       19      /* 0x013 */
+#define OPC_OUB_FW_FLASH_UPDATE                        20      /* 0x014 */
+#define OPC_OUB_GPIO_RESPONSE                  22      /* 0x016 */
+#define OPC_OUB_GPIO_EVENT                     23      /* 0x017 */
+#define OPC_OUB_GENERAL_EVENT                  24      /* 0x018 */
+#define OPC_OUB_SSP_ABORT_RSP                  26      /* 0x01A */
+#define OPC_OUB_SATA_ABORT_RSP                 27      /* 0x01B */
+#define OPC_OUB_SAS_DIAG_MODE_START_END                28      /* 0x01C */
+#define OPC_OUB_SAS_DIAG_EXECUTE               29      /* 0x01D */
+#define OPC_OUB_GET_TIME_STAMP                 30      /* 0x01E */
+#define OPC_OUB_SAS_HW_EVENT_ACK               31      /* 0x01F */
+#define OPC_OUB_PORT_CONTROL                   32      /* 0x020 */
+#define OPC_OUB_SKIP_ENTRY                     33      /* 0x021 */
+#define OPC_OUB_SMP_ABORT_RSP                  34      /* 0x022 */
+#define OPC_OUB_GET_NVMD_DATA                  35      /* 0x023 */
+#define OPC_OUB_SET_NVMD_DATA                  36      /* 0x024 */
+#define OPC_OUB_DEVICE_HANDLE_REMOVAL          37      /* 0x025 */
+#define OPC_OUB_SET_DEVICE_STATE               38      /* 0x026 */
+#define OPC_OUB_GET_DEVICE_STATE               39      /* 0x027 */
+#define OPC_OUB_SET_DEV_INFO                   40      /* 0x028 */
+#define OPC_OUB_SAS_RE_INITIALIZE              41      /* 0x029 */
+
+/* for phy start*/
+#define SPINHOLD_DISABLE               (0x00 << 14)
+#define SPINHOLD_ENABLE                        (0x01 << 14)
+#define LINKMODE_SAS                   (0x01 << 12)
+#define LINKMODE_DSATA                 (0x02 << 12)
+#define LINKMODE_AUTO                  (0x03 << 12)
+#define LINKRATE_15                    (0x01 << 8)
+#define LINKRATE_30                    (0x02 << 8)
+#define LINKRATE_60                    (0x04 << 8)
+
+struct mpi_msg_hdr{
+       __le32  header; /* Bits [11:0]  - Message operation code */
+       /* Bits [15:12] - Message Category */
+       /* Bits [21:16] - Outboundqueue ID for the
+       operation completion message */
+       /* Bits [23:22] - Reserved */
+       /* Bits [28:24] - Buffer Count, indicates how
+       many buffer are allocated for the massage */
+       /* Bits [30:29] - Reserved */
+       /* Bits [31] - Message Valid bit */
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to describe enable the phy (64 bytes)
+ */
+struct phy_start_req {
+       __le32  tag;
+       __le32  ase_sh_lm_slr_phyid;
+       struct sas_identify_frame sas_identify;
+       u32     reserved[5];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of PHY Start Command
+ * use to disable the phy (64 bytes)
+ */
+struct phy_stop_req {
+       __le32  tag;
+       __le32  phy_id;
+       u32     reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/* set device bits fis - device to host */
+struct  set_dev_bits_fis {
+       u8      fis_type;       /* 0xA1*/
+       u8      n_i_pmport;
+       /* b7 : n Bit. Notification bit. If set device needs attention. */
+       /* b6 : i Bit. Interrupt Bit */
+       /* b5-b4: reserved2 */
+       /* b3-b0: PM Port */
+       u8      status;
+       u8      error;
+       u32     _r_a;
+} __attribute__ ((packed));
+/* PIO setup FIS - device to host */
+struct  pio_setup_fis {
+       u8      fis_type;       /* 0x5f */
+       u8      i_d_pmPort;
+       /* b7 : reserved */
+       /* b6 : i bit. Interrupt bit */
+       /* b5 : d bit. data transfer direction. set to 1 for device to host
+       xfer */
+       /* b4 : reserved */
+       /* b3-b0: PM Port */
+       u8      status;
+       u8      error;
+       u8      lbal;
+       u8      lbam;
+       u8      lbah;
+       u8      device;
+       u8      lbal_exp;
+       u8      lbam_exp;
+       u8      lbah_exp;
+       u8      _r_a;
+       u8      sector_count;
+       u8      sector_count_exp;
+       u8      _r_b;
+       u8      e_status;
+       u8      _r_c[2];
+       u8      transfer_count;
+} __attribute__ ((packed));
+
+/*
+ * brief the data structure of SATA Completion Response
+ * use to discribe the sata task response (64 bytes)
+ */
+struct sata_completion_resp {
+       __le32  tag;
+       __le32  status;
+       __le32  param;
+       u32     sata_resp[12];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of SAS HW Event Notification
+ * use to alert the host about the hardware event(64 bytes)
+ */
+struct hw_event_resp {
+       __le32  lr_evt_status_phyid_portid;
+       __le32  evt_param;
+       __le32  npip_portstate;
+       struct sas_identify_frame       sas_identify;
+       struct dev_to_host_fis  sata_fis;
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of  REGISTER DEVICE Command
+ * use to describe MPI REGISTER DEVICE Command (64 bytes)
+ */
+
+struct reg_dev_req {
+       __le32  tag;
+       __le32  phyid_portid;
+       __le32  dtype_dlr_retry;
+       __le32  firstburstsize_ITNexustimeout;
+       u32     sas_addr_hi;
+       u32     sas_addr_low;
+       __le32  upper_device_id;
+       u32     reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of  DEREGISTER DEVICE Command
+ * use to request spc to remove all internal resources associated
+ * with the device id (64 bytes)
+ */
+
+struct dereg_dev_req {
+       __le32  tag;
+       __le32  device_id;
+       u32     reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of DEVICE_REGISTRATION Response
+ * use to notify the completion of the device registration  (64 bytes)
+ */
+
+struct dev_reg_resp {
+       __le32  tag;
+       __le32  status;
+       __le32  device_id;
+       u32     reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of Local PHY Control Command
+ * use to issue PHY CONTROL to local phy (64 bytes)
+ */
+struct local_phy_ctl_req {
+       __le32  tag;
+       __le32  phyop_phyid;
+       u32     reserved1[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Local Phy Control Response
+ * use to describe MPI Local Phy Control Response (64 bytes)
+ */
+struct local_phy_ctl_resp {
+       __le32  tag;
+       __le32  phyop_phyid;
+       __le32  status;
+       u32     reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+#define OP_BITS 0x0000FF00
+#define ID_BITS 0x0000000F
+
+/*
+ * brief the data structure of PORT Control Command
+ * use to control port properties (64 bytes)
+ */
+
+struct port_ctl_req {
+       __le32  tag;
+       __le32  portop_portid;
+       __le32  param0;
+       __le32  param1;
+       u32     reserved1[11];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of HW Event Ack Command
+ * use to acknowledge receive HW event (64 bytes)
+ */
+
+struct hw_event_ack_req {
+       __le32  tag;
+       __le32  sea_phyid_portid;
+       __le32  param0;
+       __le32  param1;
+       u32     reserved1[11];
+} __attribute__((packed, aligned(4)));
+
+
+/*
+ * brief the data structure of SSP Completion Response
+ * use to indicate a SSP Completion  (n bytes)
+ */
+struct ssp_completion_resp {
+       __le32  tag;
+       __le32  status;
+       __le32  param;
+       __le32  ssptag_rescv_rescpad;
+       struct ssp_response_iu  ssp_resp_iu;
+       __le32  residual_count;
+} __attribute__((packed, aligned(4)));
+
+
+#define SSP_RESCV_BIT  0x00010000
+
+/*
+ * brief the data structure of SATA EVNET esponse
+ * use to indicate a SATA Completion  (64 bytes)
+ */
+
+struct sata_event_resp {
+       __le32  tag;
+       __le32  event;
+       __le32  port_id;
+       __le32  device_id;
+       u32     reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SSP EVNET esponse
+ * use to indicate a SSP Completion  (64 bytes)
+ */
+
+struct ssp_event_resp {
+       __le32  tag;
+       __le32  event;
+       __le32  port_id;
+       __le32  device_id;
+       u32     reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of General Event Notification Response
+ * use to describe MPI General Event Notification Response (64 bytes)
+ */
+struct general_event_resp {
+       __le32  status;
+       __le32  inb_IOMB_payload[14];
+} __attribute__((packed, aligned(4)));
+
+
+#define GENERAL_EVENT_PAYLOAD  14
+#define OPCODE_BITS    0x00000fff
+
+/*
+ * brief the data structure of SMP Request Command
+ * use to describe MPI SMP REQUEST Command (64 bytes)
+ */
+struct smp_req {
+       __le32  tag;
+       __le32  device_id;
+       __le32  len_ip_ir;
+       /* Bits [0]  - Indirect response */
+       /* Bits [1] - Indirect Payload */
+       /* Bits [15:2] - Reserved */
+       /* Bits [23:16] - direct payload Len */
+       /* Bits [31:24] - Reserved */
+       u8      smp_req16[16];
+       union {
+               u8      smp_req[32];
+               struct {
+                       __le64 long_req_addr;/* sg dma address, LE */
+                       __le32 long_req_size;/* LE */
+                       u32     _r_a;
+                       __le64 long_resp_addr;/* sg dma address, LE */
+                       __le32 long_resp_size;/* LE */
+                       u32     _r_b;
+                       } long_smp_req;/* sequencer extension */
+       };
+} __attribute__((packed, aligned(4)));
+/*
+ * brief the data structure of SMP Completion Response
+ * use to describe MPI SMP Completion Response (64 bytes)
+ */
+struct smp_completion_resp {
+       __le32  tag;
+       __le32  status;
+       __le32  param;
+       __le32  _r_a[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ *brief the data structure of SSP SMP SATA Abort Command
+ * use to describe MPI SSP SMP & SATA Abort Command (64 bytes)
+ */
+struct task_abort_req {
+       __le32  tag;
+       __le32  device_id;
+       __le32  tag_to_abort;
+       __le32  abort_all;
+       u32     reserved[11];
+} __attribute__((packed, aligned(4)));
+
+/* These flags used for SSP SMP & SATA Abort */
+#define ABORT_MASK             0x3
+#define ABORT_SINGLE           0x0
+#define ABORT_ALL              0x1
+
+/**
+ * brief the data structure of SSP SATA SMP Abort Response
+ * use to describe SSP SMP & SATA Abort Response ( 64 bytes)
+ */
+struct task_abort_resp {
+       __le32  tag;
+       __le32  status;
+       __le32  scp;
+       u32     reserved[12];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Command
+ * use to describe MPI SAS Diagnostic Start/End Command (64 bytes)
+ */
+struct sas_diag_start_end_req {
+       __le32  tag;
+       __le32  operation_phyid;
+       u32     reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Command
+ * use to describe MPI SAS Diagnostic Execute Command (64 bytes)
+ */
+struct sas_diag_execute_req{
+       __le32  tag;
+       __le32  cmdtype_cmddesc_phyid;
+       __le32  pat1_pat2;
+       __le32  threshold;
+       __le32  codepat_errmsk;
+       __le32  pmon;
+       __le32  pERF1CTL;
+       u32     reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+#define SAS_DIAG_PARAM_BYTES 24
+
+/*
+ * brief the data structure of Set Device State Command
+ * use to describe MPI Set Device State Command (64 bytes)
+ */
+struct set_dev_state_req {
+       __le32  tag;
+       __le32  device_id;
+       __le32  nds;
+       u32     reserved[12];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of sas_re_initialization
+ */
+struct sas_re_initialization_req {
+
+       __le32  tag;
+       __le32  SSAHOLT;/* bit29-set max port;
+                       ** bit28-set open reject cmd retries.
+                       ** bit27-set open reject data retries.
+                       ** bit26-set open reject option, remap:1 or not:0.
+                       ** bit25-set sata head of line time out.
+                       */
+       __le32 reserved_maxPorts;
+       __le32 open_reject_cmdretries_data_retries;/* cmd retries: 31-bit16;
+                                                   * data retries: bit15-bit0.
+                                                   */
+       __le32  sata_hol_tmo;
+       u32     reserved1[10];
+} __attribute__((packed, aligned(4)));
+
+/*
+ * brief the data structure of SATA Start Command
+ * use to describe MPI SATA IO Start Command (64 bytes)
+ */
+
+struct sata_start_req {
+       __le32  tag;
+       __le32  device_id;
+       __le32  data_len;
+       __le32  ncqtag_atap_dir_m;
+       struct host_to_dev_fis  sata_fis;
+       u32     reserved1;
+       u32     reserved2;
+       u32     addr_low;
+       u32     addr_high;
+       __le32  len;
+       __le32  esgl;
+} __attribute__((packed, aligned(4)));
+
+/**
+ * brief the data structure of SSP INI TM Start Command
+ * use to describe MPI SSP INI TM Start Command (64 bytes)
+ */
+struct ssp_ini_tm_start_req {
+       __le32  tag;
+       __le32  device_id;
+       __le32  relate_tag;
+       __le32  tmf;
+       u8      lun[8];
+       __le32  ds_ads_m;
+       u32     reserved[8];
+} __attribute__((packed, aligned(4)));
+
+
+struct ssp_info_unit {
+       u8      lun[8];/* SCSI Logical Unit Number */
+       u8      reserved1;/* reserved */
+       u8      efb_prio_attr;
+       /* B7   : enabledFirstBurst */
+       /* B6-3 : taskPriority */
+       /* B2-0 : taskAttribute */
+       u8      reserved2;      /* reserved */
+       u8      additional_cdb_len;
+       /* B7-2 : additional_cdb_len */
+       /* B1-0 : reserved */
+       u8      cdb[16];/* The SCSI CDB up to 16 bytes length */
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SSP INI IO Start Command
+ * use to describe MPI SSP INI IO Start Command (64 bytes)
+ */
+struct ssp_ini_io_start_req {
+       __le32  tag;
+       __le32  device_id;
+       __le32  data_len;
+       __le32  dir_m_tlr;
+       struct ssp_info_unit    ssp_iu;
+       __le32  addr_low;
+       __le32  addr_high;
+       __le32  len;
+       __le32  esgl;
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Firmware download
+ * use to describe MPI FW DOWNLOAD Command (64 bytes)
+ */
+struct fw_flash_Update_req {
+       __le32  tag;
+       __le32  cur_image_offset;
+       __le32  cur_image_len;
+       __le32  total_image_len;
+       u32     reserved0[7];
+       __le32  sgl_addr_lo;
+       __le32  sgl_addr_hi;
+       __le32  len;
+       __le32  ext_reserved;
+} __attribute__((packed, aligned(4)));
+
+
+#define FWFLASH_IOMB_RESERVED_LEN 0x07
+/**
+ * brief the data structure of FW_FLASH_UPDATE Response
+ * use to describe MPI FW_FLASH_UPDATE Response (64 bytes)
+ *
+ */
+struct fw_flash_Update_resp {
+       dma_addr_t      tag;
+       __le32  status;
+       u32     reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Get NVM Data Command
+ * use to get data from NVM in HBA(64 bytes)
+ */
+struct get_nvm_data_req {
+       __le32  tag;
+       __le32  len_ir_vpdd;
+       __le32  vpd_offset;
+       u32     reserved[8];
+       __le32  resp_addr_lo;
+       __le32  resp_addr_hi;
+       __le32  resp_len;
+       u32     reserved1;
+} __attribute__((packed, aligned(4)));
+
+
+struct set_nvm_data_req {
+       __le32  tag;
+       __le32  len_ir_vpdd;
+       __le32  vpd_offset;
+       u32     reserved[8];
+       __le32  resp_addr_lo;
+       __le32  resp_addr_hi;
+       __le32  resp_len;
+       u32     reserved1;
+} __attribute__((packed, aligned(4)));
+
+
+#define TWI_DEVICE     0x0
+#define C_SEEPROM      0x1
+#define VPD_FLASH      0x4
+#define AAP1_RDUMP     0x5
+#define IOP_RDUMP      0x6
+#define EXPAN_ROM      0x7
+
+#define IPMode         0x80000000
+#define NVMD_TYPE      0x0000000F
+#define NVMD_STAT      0x0000FFFF
+#define NVMD_LEN       0xFF000000
+/**
+ * brief the data structure of Get NVMD Data Response
+ * use to describe MPI Get NVMD Data Response (64 bytes)
+ */
+struct get_nvm_data_resp {
+       __le32          tag;
+       __le32          ir_tda_bn_dps_das_nvm;
+       __le32          dlen_status;
+       __le32          nvm_data[12];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Start/End Response
+ * use to describe MPI SAS Diagnostic Start/End Response (64 bytes)
+ *
+ */
+struct sas_diag_start_end_resp {
+       __le32          tag;
+       __le32          status;
+       u32             reserved[13];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of SAS Diagnostic Execute Response
+ * use to describe MPI SAS Diagnostic Execute Response (64 bytes)
+ *
+ */
+struct sas_diag_execute_resp {
+       __le32          tag;
+       __le32          cmdtype_cmddesc_phyid;
+       __le32          Status;
+       __le32          ReportData;
+       u32             reserved[11];
+} __attribute__((packed, aligned(4)));
+
+
+/**
+ * brief the data structure of Set Device State Response
+ * use to describe MPI Set Device State Response (64 bytes)
+ *
+ */
+struct set_dev_state_resp {
+       __le32          tag;
+       __le32          status;
+       __le32          device_id;
+       __le32          pds_nds;
+       u32             reserved[11];
+} __attribute__((packed, aligned(4)));
+
+
+#define NDS_BITS 0x0F
+#define PDS_BITS 0xF0
+
+/*
+ * HW Events type
+ */
+
+#define HW_EVENT_RESET_START                   0x01
+#define HW_EVENT_CHIP_RESET_COMPLETE           0x02
+#define HW_EVENT_PHY_STOP_STATUS               0x03
+#define HW_EVENT_SAS_PHY_UP                    0x04
+#define HW_EVENT_SATA_PHY_UP                   0x05
+#define HW_EVENT_SATA_SPINUP_HOLD              0x06
+#define HW_EVENT_PHY_DOWN                      0x07
+#define HW_EVENT_PORT_INVALID                  0x08
+#define HW_EVENT_BROADCAST_CHANGE              0x09
+#define HW_EVENT_PHY_ERROR                     0x0A
+#define HW_EVENT_BROADCAST_SES                 0x0B
+#define HW_EVENT_INBOUND_CRC_ERROR             0x0C
+#define HW_EVENT_HARD_RESET_RECEIVED           0x0D
+#define HW_EVENT_MALFUNCTION                   0x0E
+#define HW_EVENT_ID_FRAME_TIMEOUT              0x0F
+#define HW_EVENT_BROADCAST_EXP                 0x10
+#define HW_EVENT_PHY_START_STATUS              0x11
+#define HW_EVENT_LINK_ERR_INVALID_DWORD                0x12
+#define HW_EVENT_LINK_ERR_DISPARITY_ERROR      0x13
+#define HW_EVENT_LINK_ERR_CODE_VIOLATION       0x14
+#define HW_EVENT_LINK_ERR_LOSS_OF_DWORD_SYNCH  0x15
+#define HW_EVENT_LINK_ERR_PHY_RESET_FAILED     0x16
+#define HW_EVENT_PORT_RECOVERY_TIMER_TMO       0x17
+#define HW_EVENT_PORT_RECOVER                  0x18
+#define HW_EVENT_PORT_RESET_TIMER_TMO          0x19
+#define HW_EVENT_PORT_RESET_COMPLETE           0x20
+#define EVENT_BROADCAST_ASYNCH_EVENT           0x21
+
+/* port state */
+#define PORT_NOT_ESTABLISHED                   0x00
+#define PORT_VALID                             0x01
+#define PORT_LOSTCOMM                          0x02
+#define PORT_IN_RESET                          0x04
+#define PORT_INVALID                           0x08
+
+/*
+ * SSP/SMP/SATA IO Completion Status values
+ */
+
+#define IO_SUCCESS                             0x00
+#define IO_ABORTED                             0x01
+#define IO_OVERFLOW                            0x02
+#define IO_UNDERFLOW                           0x03
+#define IO_FAILED                              0x04
+#define IO_ABORT_RESET                         0x05
+#define IO_NOT_VALID                           0x06
+#define IO_NO_DEVICE                           0x07
+#define IO_ILLEGAL_PARAMETER                   0x08
+#define IO_LINK_FAILURE                                0x09
+#define IO_PROG_ERROR                          0x0A
+#define IO_EDC_IN_ERROR                                0x0B
+#define IO_EDC_OUT_ERROR                       0x0C
+#define IO_ERROR_HW_TIMEOUT                    0x0D
+#define IO_XFER_ERROR_BREAK                    0x0E
+#define IO_XFER_ERROR_PHY_NOT_READY            0x0F
+#define IO_OPEN_CNX_ERROR_PROTOCOL_NOT_SUPPORTED       0x10
+#define IO_OPEN_CNX_ERROR_ZONE_VIOLATION               0x11
+#define IO_OPEN_CNX_ERROR_BREAK                                0x12
+#define IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS                        0x13
+#define IO_OPEN_CNX_ERROR_BAD_DESTINATION              0x14
+#define IO_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED        0x15
+#define IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY           0x16
+#define IO_OPEN_CNX_ERROR_WRONG_DESTINATION            0x17
+#define IO_OPEN_CNX_ERROR_UNKNOWN_ERROR                        0x18
+#define IO_XFER_ERROR_NAK_RECEIVED                     0x19
+#define IO_XFER_ERROR_ACK_NAK_TIMEOUT                  0x1A
+#define IO_XFER_ERROR_PEER_ABORTED                     0x1B
+#define IO_XFER_ERROR_RX_FRAME                         0x1C
+#define IO_XFER_ERROR_DMA                              0x1D
+#define IO_XFER_ERROR_CREDIT_TIMEOUT                   0x1E
+#define IO_XFER_ERROR_SATA_LINK_TIMEOUT                        0x1F
+#define IO_XFER_ERROR_SATA                             0x20
+#define IO_XFER_ERROR_ABORTED_DUE_TO_SRST              0x22
+#define IO_XFER_ERROR_REJECTED_NCQ_MODE                        0x21
+#define IO_XFER_ERROR_ABORTED_NCQ_MODE                 0x23
+#define IO_XFER_OPEN_RETRY_TIMEOUT                     0x24
+#define IO_XFER_SMP_RESP_CONNECTION_ERROR              0x25
+#define IO_XFER_ERROR_UNEXPECTED_PHASE                 0x26
+#define IO_XFER_ERROR_XFER_RDY_OVERRUN                 0x27
+#define IO_XFER_ERROR_XFER_RDY_NOT_EXPECTED            0x28
+
+#define IO_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT                0x30
+#define IO_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NAK   0x31
+#define IO_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK        0x32
+
+#define IO_XFER_ERROR_OFFSET_MISMATCH                  0x34
+#define IO_XFER_ERROR_XFER_ZERO_DATA_LEN               0x35
+#define IO_XFER_CMD_FRAME_ISSUED                       0x36
+#define IO_ERROR_INTERNAL_SMP_RESOURCE                 0x37
+#define IO_PORT_IN_RESET                               0x38
+#define IO_DS_NON_OPERATIONAL                          0x39
+#define IO_DS_IN_RECOVERY                              0x3A
+#define IO_TM_TAG_NOT_FOUND                            0x3B
+#define IO_XFER_PIO_SETUP_ERROR                                0x3C
+#define IO_SSP_EXT_IU_ZERO_LEN_ERROR                   0x3D
+#define IO_DS_IN_ERROR                                 0x3E
+#define IO_OPEN_CNX_ERROR_HW_RESOURCE_BUSY             0x3F
+#define IO_ABORT_IN_PROGRESS                           0x40
+#define IO_ABORT_DELAYED                               0x41
+#define IO_INVALID_LENGTH                              0x42
+
+/* WARNING: This error code must always be the last number.
+ * If you add error code, modify this code also
+ * It is used as an index
+ */
+#define IO_ERROR_UNKNOWN_GENERIC                       0x43
+
+/* MSGU CONFIGURATION  TABLE*/
+
+#define SPC_MSGU_CFG_TABLE_UPDATE              0x01/* Inbound doorbell bit0 */
+#define SPC_MSGU_CFG_TABLE_RESET               0x02/* Inbound doorbell bit1 */
+#define SPC_MSGU_CFG_TABLE_FREEZE              0x04/* Inbound doorbell bit2 */
+#define SPC_MSGU_CFG_TABLE_UNFREEZE            0x08/* Inbound doorbell bit4 */
+#define MSGU_IBDB_SET                          0x04
+#define MSGU_HOST_INT_STATUS                   0x08
+#define MSGU_HOST_INT_MASK                     0x0C
+#define MSGU_IOPIB_INT_STATUS                  0x18
+#define MSGU_IOPIB_INT_MASK                    0x1C
+#define MSGU_IBDB_CLEAR                                0x20/* RevB - Host not use */
+#define MSGU_MSGU_CONTROL                      0x24
+#define MSGU_ODR                               0x3C/* RevB */
+#define MSGU_ODCR                              0x40/* RevB */
+#define MSGU_SCRATCH_PAD_0                     0x44
+#define MSGU_SCRATCH_PAD_1                     0x48
+#define MSGU_SCRATCH_PAD_2                     0x4C
+#define MSGU_SCRATCH_PAD_3                     0x50
+#define MSGU_HOST_SCRATCH_PAD_0                        0x54
+#define MSGU_HOST_SCRATCH_PAD_1                        0x58
+#define MSGU_HOST_SCRATCH_PAD_2                        0x5C
+#define MSGU_HOST_SCRATCH_PAD_3                        0x60
+#define MSGU_HOST_SCRATCH_PAD_4                        0x64
+#define MSGU_HOST_SCRATCH_PAD_5                        0x68
+#define MSGU_HOST_SCRATCH_PAD_6                        0x6C
+#define MSGU_HOST_SCRATCH_PAD_7                        0x70
+#define MSGU_ODMR                              0x74/* RevB */
+
+/* bit definition for ODMR register */
+#define ODMR_MASK_ALL                          0xFFFFFFFF/* mask all
+                                       interrupt vector */
+#define ODMR_CLEAR_ALL                         0/* clear all
+                                       interrupt vector */
+/* bit definition for ODCR register */
+#define ODCR_CLEAR_ALL         0xFFFFFFFF   /* mask all
+                                       interrupt vector*/
+/* MSIX Interupts */
+#define MSIX_TABLE_OFFSET              0x2000
+#define MSIX_TABLE_ELEMENT_SIZE                0x10
+#define MSIX_INTERRUPT_CONTROL_OFFSET  0xC
+#define MSIX_TABLE_BASE          (MSIX_TABLE_OFFSET + MSIX_INTERRUPT_CONTROL_OFFSET)
+#define MSIX_INTERRUPT_DISABLE         0x1
+#define MSIX_INTERRUPT_ENABLE          0x0
+
+
+/* state definition for Scratch Pad1 register */
+#define SCRATCH_PAD1_POR               0x00  /* power on reset state */
+#define SCRATCH_PAD1_SFR               0x01  /* soft reset state */
+#define SCRATCH_PAD1_ERR               0x02  /* error state */
+#define SCRATCH_PAD1_RDY               0x03  /* ready state */
+#define SCRATCH_PAD1_RST               0x04  /* soft reset toggle flag */
+#define SCRATCH_PAD1_AAP1RDY_RST       0x08  /* AAP1 ready for soft reset */
+#define SCRATCH_PAD1_STATE_MASK                0xFFFFFFF0   /* ScratchPad1
+ Mask, bit1-0 State, bit2 Soft Reset, bit3 FW RDY for Soft Reset */
+#define SCRATCH_PAD1_RESERVED          0x000003F8   /* Scratch Pad1
+ Reserved bit 3 to 9 */
+
+ /* state definition for Scratch Pad2 register */
+#define SCRATCH_PAD2_POR               0x00  /* power on state */
+#define SCRATCH_PAD2_SFR               0x01  /* soft reset state */
+#define SCRATCH_PAD2_ERR               0x02  /* error state */
+#define SCRATCH_PAD2_RDY               0x03  /* ready state */
+#define SCRATCH_PAD2_FWRDY_RST         0x04  /* FW ready for soft reset flag*/
+#define SCRATCH_PAD2_IOPRDY_RST                0x08  /* IOP ready for soft reset */
+#define SCRATCH_PAD2_STATE_MASK                0xFFFFFFF4 /* ScratchPad 2
+ Mask, bit1-0 State */
+#define SCRATCH_PAD2_RESERVED          0x000003FC   /* Scratch Pad1
+ Reserved bit 2 to 9 */
+
+#define SCRATCH_PAD_ERROR_MASK         0xFFFFFC00   /* Error mask bits */
+#define SCRATCH_PAD_STATE_MASK         0x00000003   /* State Mask bits */
+
+/* main configuration offset - byte offset */
+#define MAIN_SIGNATURE_OFFSET          0x00/* DWORD 0x00 */
+#define MAIN_INTERFACE_REVISION                0x04/* DWORD 0x01 */
+#define MAIN_FW_REVISION               0x08/* DWORD 0x02 */
+#define MAIN_MAX_OUTSTANDING_IO_OFFSET 0x0C/* DWORD 0x03 */
+#define MAIN_MAX_SGL_OFFSET            0x10/* DWORD 0x04 */
+#define MAIN_CNTRL_CAP_OFFSET          0x14/* DWORD 0x05 */
+#define MAIN_GST_OFFSET                        0x18/* DWORD 0x06 */
+#define MAIN_IBQ_OFFSET                        0x1C/* DWORD 0x07 */
+#define MAIN_OBQ_OFFSET                        0x20/* DWORD 0x08 */
+#define MAIN_IQNPPD_HPPD_OFFSET                0x24/* DWORD 0x09 */
+#define MAIN_OB_HW_EVENT_PID03_OFFSET  0x28/* DWORD 0x0A */
+#define MAIN_OB_HW_EVENT_PID47_OFFSET  0x2C/* DWORD 0x0B */
+#define MAIN_OB_NCQ_EVENT_PID03_OFFSET 0x30/* DWORD 0x0C */
+#define MAIN_OB_NCQ_EVENT_PID47_OFFSET 0x34/* DWORD 0x0D */
+#define MAIN_TITNX_EVENT_PID03_OFFSET  0x38/* DWORD 0x0E */
+#define MAIN_TITNX_EVENT_PID47_OFFSET  0x3C/* DWORD 0x0F */
+#define MAIN_OB_SSP_EVENT_PID03_OFFSET 0x40/* DWORD 0x10 */
+#define MAIN_OB_SSP_EVENT_PID47_OFFSET 0x44/* DWORD 0x11 */
+#define MAIN_OB_SMP_EVENT_PID03_OFFSET 0x48/* DWORD 0x12 */
+#define MAIN_OB_SMP_EVENT_PID47_OFFSET 0x4C/* DWORD 0x13 */
+#define MAIN_EVENT_LOG_ADDR_HI         0x50/* DWORD 0x14 */
+#define MAIN_EVENT_LOG_ADDR_LO         0x54/* DWORD 0x15 */
+#define MAIN_EVENT_LOG_BUFF_SIZE       0x58/* DWORD 0x16 */
+#define MAIN_EVENT_LOG_OPTION          0x5C/* DWORD 0x17 */
+#define MAIN_IOP_EVENT_LOG_ADDR_HI     0x60/* DWORD 0x18 */
+#define MAIN_IOP_EVENT_LOG_ADDR_LO     0x64/* DWORD 0x19 */
+#define MAIN_IOP_EVENT_LOG_BUFF_SIZE   0x68/* DWORD 0x1A */
+#define MAIN_IOP_EVENT_LOG_OPTION      0x6C/* DWORD 0x1B */
+#define MAIN_FATAL_ERROR_INTERRUPT     0x70/* DWORD 0x1C */
+#define MAIN_FATAL_ERROR_RDUMP0_OFFSET 0x74/* DWORD 0x1D */
+#define MAIN_FATAL_ERROR_RDUMP0_LENGTH 0x78/* DWORD 0x1E */
+#define MAIN_FATAL_ERROR_RDUMP1_OFFSET 0x7C/* DWORD 0x1F */
+#define MAIN_FATAL_ERROR_RDUMP1_LENGTH 0x80/* DWORD 0x20 */
+#define MAIN_HDA_FLAGS_OFFSET          0x84/* DWORD 0x21 */
+#define MAIN_ANALOG_SETUP_OFFSET       0x88/* DWORD 0x22 */
+
+/* Gereral Status Table offset - byte offset */
+#define GST_GSTLEN_MPIS_OFFSET         0x00
+#define GST_IQ_FREEZE_STATE0_OFFSET    0x04
+#define GST_IQ_FREEZE_STATE1_OFFSET    0x08
+#define GST_MSGUTCNT_OFFSET            0x0C
+#define GST_IOPTCNT_OFFSET             0x10
+#define GST_PHYSTATE_OFFSET            0x18
+#define GST_PHYSTATE0_OFFSET           0x18
+#define GST_PHYSTATE1_OFFSET           0x1C
+#define GST_PHYSTATE2_OFFSET           0x20
+#define GST_PHYSTATE3_OFFSET           0x24
+#define GST_PHYSTATE4_OFFSET           0x28
+#define GST_PHYSTATE5_OFFSET           0x2C
+#define GST_PHYSTATE6_OFFSET           0x30
+#define GST_PHYSTATE7_OFFSET           0x34
+#define GST_RERRINFO_OFFSET            0x44
+
+/* General Status Table - MPI state */
+#define GST_MPI_STATE_UNINIT           0x00
+#define GST_MPI_STATE_INIT             0x01
+#define GST_MPI_STATE_TERMINATION      0x02
+#define GST_MPI_STATE_ERROR            0x03
+#define GST_MPI_STATE_MASK             0x07
+
+#define MBIC_NMI_ENABLE_VPE0_IOP       0x000418
+#define MBIC_NMI_ENABLE_VPE0_AAP1      0x000418
+/* PCIE registers - BAR2(0x18), BAR1(win) 0x010000 */
+#define PCIE_EVENT_INTERRUPT_ENABLE    0x003040
+#define PCIE_EVENT_INTERRUPT           0x003044
+#define PCIE_ERROR_INTERRUPT_ENABLE    0x003048
+#define PCIE_ERROR_INTERRUPT           0x00304C
+/* signature defintion for host scratch pad0 register */
+#define SPC_SOFT_RESET_SIGNATURE       0x252acbcd
+/* Signature for Soft Reset */
+
+/* SPC Reset register - BAR4(0x20), BAR2(win) (need dynamic mapping) */
+#define SPC_REG_RESET                  0x000000/* reset register */
+
+/* bit difination for SPC_RESET register */
+#define   SPC_REG_RESET_OSSP           0x00000001
+#define   SPC_REG_RESET_RAAE           0x00000002
+#define   SPC_REG_RESET_PCS_SPBC       0x00000004
+#define   SPC_REG_RESET_PCS_IOP_SS     0x00000008
+#define   SPC_REG_RESET_PCS_AAP1_SS    0x00000010
+#define   SPC_REG_RESET_PCS_AAP2_SS    0x00000020
+#define   SPC_REG_RESET_PCS_LM         0x00000040
+#define   SPC_REG_RESET_PCS            0x00000080
+#define   SPC_REG_RESET_GSM            0x00000100
+#define   SPC_REG_RESET_DDR2           0x00010000
+#define   SPC_REG_RESET_BDMA_CORE      0x00020000
+#define   SPC_REG_RESET_BDMA_SXCBI     0x00040000
+#define   SPC_REG_RESET_PCIE_AL_SXCBI  0x00080000
+#define   SPC_REG_RESET_PCIE_PWR       0x00100000
+#define   SPC_REG_RESET_PCIE_SFT       0x00200000
+#define   SPC_REG_RESET_PCS_SXCBI      0x00400000
+#define   SPC_REG_RESET_LMS_SXCBI      0x00800000
+#define   SPC_REG_RESET_PMIC_SXCBI     0x01000000
+#define   SPC_REG_RESET_PMIC_CORE      0x02000000
+#define   SPC_REG_RESET_PCIE_PC_SXCBI  0x04000000
+#define   SPC_REG_RESET_DEVICE         0x80000000
+
+/* registers for BAR Shifting - BAR2(0x18), BAR1(win) */
+#define SPC_IBW_AXI_TRANSLATION_LOW    0x003258
+
+#define MBIC_AAP1_ADDR_BASE            0x060000
+#define MBIC_IOP_ADDR_BASE             0x070000
+#define GSM_ADDR_BASE                  0x0700000
+/* Dynamic map through Bar4 - 0x00700000 */
+#define GSM_CONFIG_RESET               0x00000000
+#define RAM_ECC_DB_ERR                 0x00000018
+#define GSM_READ_ADDR_PARITY_INDIC     0x00000058
+#define GSM_WRITE_ADDR_PARITY_INDIC    0x00000060
+#define GSM_WRITE_DATA_PARITY_INDIC    0x00000068
+#define GSM_READ_ADDR_PARITY_CHECK     0x00000038
+#define GSM_WRITE_ADDR_PARITY_CHECK    0x00000040
+#define GSM_WRITE_DATA_PARITY_CHECK    0x00000048
+
+#define RB6_ACCESS_REG                 0x6A0000
+#define HDAC_EXEC_CMD                  0x0002
+#define HDA_C_PA                       0xcb
+#define HDA_SEQ_ID_BITS                        0x00ff0000
+#define HDA_GSM_OFFSET_BITS            0x00FFFFFF
+#define MBIC_AAP1_ADDR_BASE            0x060000
+#define MBIC_IOP_ADDR_BASE             0x070000
+#define GSM_ADDR_BASE                  0x0700000
+#define SPC_TOP_LEVEL_ADDR_BASE                0x000000
+#define GSM_CONFIG_RESET_VALUE          0x00003b00
+#define GPIO_ADDR_BASE                  0x00090000
+#define GPIO_GPIO_0_0UTPUT_CTL_OFFSET   0x0000010c
+
+/* RB6 offset */
+#define SPC_RB6_OFFSET                 0x80C0
+/* Magic number of  soft reset for RB6 */
+#define RB6_MAGIC_NUMBER_RST           0x1234
+
+/* Device Register status */
+#define DEVREG_SUCCESS                                 0x00
+#define DEVREG_FAILURE_OUT_OF_RESOURCE                 0x01
+#define DEVREG_FAILURE_DEVICE_ALREADY_REGISTERED       0x02
+#define DEVREG_FAILURE_INVALID_PHY_ID                  0x03
+#define DEVREG_FAILURE_PHY_ID_ALREADY_REGISTERED       0x04
+#define DEVREG_FAILURE_PORT_ID_OUT_OF_RANGE            0x05
+#define DEVREG_FAILURE_PORT_NOT_VALID_STATE            0x06
+#define DEVREG_FAILURE_DEVICE_TYPE_NOT_VALID           0x07
+
+#endif
+
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
new file mode 100644 (file)
index 0000000..42ebe72
--- /dev/null
@@ -0,0 +1,891 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include "pm8001_sas.h"
+#include "pm8001_chips.h"
+
+static struct scsi_transport_template *pm8001_stt;
+
+static const struct pm8001_chip_info pm8001_chips[] = {
+       [chip_8001] = {  8, &pm8001_8001_dispatch,},
+};
+static int pm8001_id;
+
+LIST_HEAD(hba_list);
+
+/**
+ * The main structure which LLDD must register for scsi core.
+ */
+static struct scsi_host_template pm8001_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .queuecommand           = sas_queuecommand,
+       .target_alloc           = sas_target_alloc,
+       .slave_configure        = pm8001_slave_configure,
+       .slave_destroy          = sas_slave_destroy,
+       .scan_finished          = pm8001_scan_finished,
+       .scan_start             = pm8001_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            = pm8001_slave_alloc,
+       .target_destroy         = sas_target_destroy,
+       .ioctl                  = sas_ioctl,
+       .shost_attrs            = pm8001_host_attrs,
+};
+
+/**
+ * Sas layer call this function to execute specific task.
+ */
+static struct sas_domain_function_template pm8001_transport_ops = {
+       .lldd_dev_found         = pm8001_dev_found,
+       .lldd_dev_gone          = pm8001_dev_gone,
+
+       .lldd_execute_task      = pm8001_queue_command,
+       .lldd_control_phy       = pm8001_phy_control,
+
+       .lldd_abort_task        = pm8001_abort_task,
+       .lldd_abort_task_set    = pm8001_abort_task_set,
+       .lldd_clear_aca         = pm8001_clear_aca,
+       .lldd_clear_task_set    = pm8001_clear_task_set,
+       .lldd_I_T_nexus_reset   = pm8001_I_T_nexus_reset,
+       .lldd_lu_reset          = pm8001_lu_reset,
+       .lldd_query_task        = pm8001_query_task,
+};
+
+/**
+ *pm8001_phy_init - initiate our adapter phys
+ *@pm8001_ha: our hba structure.
+ *@phy_id: phy id.
+ */
+static void __devinit pm8001_phy_init(struct pm8001_hba_info *pm8001_ha,
+       int phy_id)
+{
+       struct pm8001_phy *phy = &pm8001_ha->phy[phy_id];
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+       phy->phy_state = 0;
+       phy->pm8001_ha = pm8001_ha;
+       sas_phy->enabled = (phy_id < pm8001_ha->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 = &pm8001_ha->sas_addr[0];
+       sas_phy->frame_rcvd = &phy->frame_rcvd[0];
+       sas_phy->ha = (struct sas_ha_struct *)pm8001_ha->shost->hostdata;
+       sas_phy->lldd_phy = phy;
+}
+
+/**
+ *pm8001_free - free hba
+ *@pm8001_ha:  our hba structure.
+ *
+ */
+static void pm8001_free(struct pm8001_hba_info *pm8001_ha)
+{
+       int i;
+       struct pm8001_wq *wq;
+
+       if (!pm8001_ha)
+               return;
+
+       for (i = 0; i < USI_MAX_MEMCNT; i++) {
+               if (pm8001_ha->memoryMap.region[i].virt_ptr != NULL) {
+                       pci_free_consistent(pm8001_ha->pdev,
+                               pm8001_ha->memoryMap.region[i].element_size,
+                               pm8001_ha->memoryMap.region[i].virt_ptr,
+                               pm8001_ha->memoryMap.region[i].phys_addr);
+                       }
+       }
+       PM8001_CHIP_DISP->chip_iounmap(pm8001_ha);
+       if (pm8001_ha->shost)
+               scsi_host_put(pm8001_ha->shost);
+       list_for_each_entry(wq, &pm8001_ha->wq_list, entry)
+               cancel_delayed_work(&wq->work_q);
+       kfree(pm8001_ha->tags);
+       kfree(pm8001_ha);
+}
+
+#ifdef PM8001_USE_TASKLET
+static void pm8001_tasklet(unsigned long opaque)
+{
+       struct pm8001_hba_info *pm8001_ha;
+       pm8001_ha = (struct pm8001_hba_info *)opaque;;
+       if (unlikely(!pm8001_ha))
+               BUG_ON(1);
+       PM8001_CHIP_DISP->isr(pm8001_ha);
+}
+#endif
+
+
+ /**
+  * pm8001_interrupt - when HBA originate a interrupt,we should invoke this
+  * dispatcher to handle each case.
+  * @irq: irq number.
+  * @opaque: the passed general host adapter struct
+  */
+static irqreturn_t pm8001_interrupt(int irq, void *opaque)
+{
+       struct pm8001_hba_info *pm8001_ha;
+       irqreturn_t ret = IRQ_HANDLED;
+       struct sas_ha_struct *sha = opaque;
+       pm8001_ha = sha->lldd_ha;
+       if (unlikely(!pm8001_ha))
+               return IRQ_NONE;
+       if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha))
+               return IRQ_NONE;
+#ifdef PM8001_USE_TASKLET
+       tasklet_schedule(&pm8001_ha->tasklet);
+#else
+       ret = PM8001_CHIP_DISP->isr(pm8001_ha);
+#endif
+       return ret;
+}
+
+/**
+ * pm8001_alloc - initiate our hba structure and 6 DMAs area.
+ * @pm8001_ha:our hba structure.
+ *
+ */
+static int __devinit pm8001_alloc(struct pm8001_hba_info *pm8001_ha)
+{
+       int i;
+       spin_lock_init(&pm8001_ha->lock);
+       for (i = 0; i < pm8001_ha->chip->n_phy; i++)
+               pm8001_phy_init(pm8001_ha, i);
+
+       pm8001_ha->tags = kzalloc(PM8001_MAX_CCB, GFP_KERNEL);
+       if (!pm8001_ha->tags)
+               goto err_out;
+       /* MPI Memory region 1 for AAP Event Log for fw */
+       pm8001_ha->memoryMap.region[AAP1].num_elements = 1;
+       pm8001_ha->memoryMap.region[AAP1].element_size = PM8001_EVENT_LOG_SIZE;
+       pm8001_ha->memoryMap.region[AAP1].total_len = PM8001_EVENT_LOG_SIZE;
+       pm8001_ha->memoryMap.region[AAP1].alignment = 32;
+
+       /* MPI Memory region 2 for IOP Event Log for fw */
+       pm8001_ha->memoryMap.region[IOP].num_elements = 1;
+       pm8001_ha->memoryMap.region[IOP].element_size = PM8001_EVENT_LOG_SIZE;
+       pm8001_ha->memoryMap.region[IOP].total_len = PM8001_EVENT_LOG_SIZE;
+       pm8001_ha->memoryMap.region[IOP].alignment = 32;
+
+       /* MPI Memory region 3 for consumer Index of inbound queues */
+       pm8001_ha->memoryMap.region[CI].num_elements = 1;
+       pm8001_ha->memoryMap.region[CI].element_size = 4;
+       pm8001_ha->memoryMap.region[CI].total_len = 4;
+       pm8001_ha->memoryMap.region[CI].alignment = 4;
+
+       /* MPI Memory region 4 for producer Index of outbound queues */
+       pm8001_ha->memoryMap.region[PI].num_elements = 1;
+       pm8001_ha->memoryMap.region[PI].element_size = 4;
+       pm8001_ha->memoryMap.region[PI].total_len = 4;
+       pm8001_ha->memoryMap.region[PI].alignment = 4;
+
+       /* MPI Memory region 5 inbound queues */
+       pm8001_ha->memoryMap.region[IB].num_elements = 256;
+       pm8001_ha->memoryMap.region[IB].element_size = 64;
+       pm8001_ha->memoryMap.region[IB].total_len = 256 * 64;
+       pm8001_ha->memoryMap.region[IB].alignment = 64;
+
+       /* MPI Memory region 6 inbound queues */
+       pm8001_ha->memoryMap.region[OB].num_elements = 256;
+       pm8001_ha->memoryMap.region[OB].element_size = 64;
+       pm8001_ha->memoryMap.region[OB].total_len = 256 * 64;
+       pm8001_ha->memoryMap.region[OB].alignment = 64;
+
+       /* Memory region write DMA*/
+       pm8001_ha->memoryMap.region[NVMD].num_elements = 1;
+       pm8001_ha->memoryMap.region[NVMD].element_size = 4096;
+       pm8001_ha->memoryMap.region[NVMD].total_len = 4096;
+       /* Memory region for devices*/
+       pm8001_ha->memoryMap.region[DEV_MEM].num_elements = 1;
+       pm8001_ha->memoryMap.region[DEV_MEM].element_size = PM8001_MAX_DEVICES *
+               sizeof(struct pm8001_device);
+       pm8001_ha->memoryMap.region[DEV_MEM].total_len = PM8001_MAX_DEVICES *
+               sizeof(struct pm8001_device);
+
+       /* Memory region for ccb_info*/
+       pm8001_ha->memoryMap.region[CCB_MEM].num_elements = 1;
+       pm8001_ha->memoryMap.region[CCB_MEM].element_size = PM8001_MAX_CCB *
+               sizeof(struct pm8001_ccb_info);
+       pm8001_ha->memoryMap.region[CCB_MEM].total_len = PM8001_MAX_CCB *
+               sizeof(struct pm8001_ccb_info);
+
+       for (i = 0; i < USI_MAX_MEMCNT; i++) {
+               if (pm8001_mem_alloc(pm8001_ha->pdev,
+                       &pm8001_ha->memoryMap.region[i].virt_ptr,
+                       &pm8001_ha->memoryMap.region[i].phys_addr,
+                       &pm8001_ha->memoryMap.region[i].phys_addr_hi,
+                       &pm8001_ha->memoryMap.region[i].phys_addr_lo,
+                       pm8001_ha->memoryMap.region[i].total_len,
+                       pm8001_ha->memoryMap.region[i].alignment) != 0) {
+                               PM8001_FAIL_DBG(pm8001_ha,
+                                       pm8001_printk("Mem%d alloc failed\n",
+                                       i));
+                               goto err_out;
+               }
+       }
+
+       pm8001_ha->devices = pm8001_ha->memoryMap.region[DEV_MEM].virt_ptr;
+       for (i = 0; i < PM8001_MAX_DEVICES; i++) {
+               pm8001_ha->devices[i].dev_type = NO_DEVICE;
+               pm8001_ha->devices[i].id = i;
+               pm8001_ha->devices[i].device_id = PM8001_MAX_DEVICES;
+               pm8001_ha->devices[i].running_req = 0;
+       }
+       pm8001_ha->ccb_info = pm8001_ha->memoryMap.region[CCB_MEM].virt_ptr;
+       for (i = 0; i < PM8001_MAX_CCB; i++) {
+               pm8001_ha->ccb_info[i].ccb_dma_handle =
+                       pm8001_ha->memoryMap.region[CCB_MEM].phys_addr +
+                       i * sizeof(struct pm8001_ccb_info);
+               pm8001_ha->ccb_info[i].task = NULL;
+               pm8001_ha->ccb_info[i].ccb_tag = 0xffffffff;
+               pm8001_ha->ccb_info[i].device = NULL;
+               ++pm8001_ha->tags_num;
+       }
+       pm8001_ha->flags = PM8001F_INIT_TIME;
+       /* Initialize tags */
+       pm8001_tag_init(pm8001_ha);
+       return 0;
+err_out:
+       return 1;
+}
+
+/**
+ * pm8001_ioremap - remap the pci high physical address to kernal virtual
+ * address so that we can access them.
+ * @pm8001_ha:our hba structure.
+ */
+static int pm8001_ioremap(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 bar;
+       u32 logicalBar = 0;
+       struct pci_dev *pdev;
+
+       pdev = pm8001_ha->pdev;
+       /* map pci mem (PMC pci base 0-3)*/
+       for (bar = 0; bar < 6; bar++) {
+               /*
+               ** logical BARs for SPC:
+               ** bar 0 and 1 - logical BAR0
+               ** bar 2 and 3 - logical BAR1
+               ** bar4 - logical BAR2
+               ** bar5 - logical BAR3
+               ** Skip the appropriate assignments:
+               */
+               if ((bar == 1) || (bar == 3))
+                       continue;
+               if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) {
+                       pm8001_ha->io_mem[logicalBar].membase =
+                               pci_resource_start(pdev, bar);
+                       pm8001_ha->io_mem[logicalBar].membase &=
+                               (u32)PCI_BASE_ADDRESS_MEM_MASK;
+                       pm8001_ha->io_mem[logicalBar].memsize =
+                               pci_resource_len(pdev, bar);
+                       pm8001_ha->io_mem[logicalBar].memvirtaddr =
+                               ioremap(pm8001_ha->io_mem[logicalBar].membase,
+                               pm8001_ha->io_mem[logicalBar].memsize);
+                       PM8001_INIT_DBG(pm8001_ha,
+                               pm8001_printk("PCI: bar %d, logicalBar %d "
+                               "virt_addr=%lx,len=%d\n", bar, logicalBar,
+                               (unsigned long)
+                               pm8001_ha->io_mem[logicalBar].memvirtaddr,
+                               pm8001_ha->io_mem[logicalBar].memsize));
+               } else {
+                       pm8001_ha->io_mem[logicalBar].membase   = 0;
+                       pm8001_ha->io_mem[logicalBar].memsize   = 0;
+                       pm8001_ha->io_mem[logicalBar].memvirtaddr = 0;
+               }
+               logicalBar++;
+       }
+       return 0;
+}
+
+/**
+ * pm8001_pci_alloc - initialize our ha card structure
+ * @pdev: pci device.
+ * @ent: ent
+ * @shost: scsi host struct which has been initialized before.
+ */
+static struct pm8001_hba_info *__devinit
+pm8001_pci_alloc(struct pci_dev *pdev, u32 chip_id, struct Scsi_Host *shost)
+{
+       struct pm8001_hba_info *pm8001_ha;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+
+       pm8001_ha = sha->lldd_ha;
+       if (!pm8001_ha)
+               return NULL;
+
+       pm8001_ha->pdev = pdev;
+       pm8001_ha->dev = &pdev->dev;
+       pm8001_ha->chip_id = chip_id;
+       pm8001_ha->chip = &pm8001_chips[pm8001_ha->chip_id];
+       pm8001_ha->irq = pdev->irq;
+       pm8001_ha->sas = sha;
+       pm8001_ha->shost = shost;
+       pm8001_ha->id = pm8001_id++;
+       INIT_LIST_HEAD(&pm8001_ha->wq_list);
+       pm8001_ha->logging_level = 0x01;
+       sprintf(pm8001_ha->name, "%s%d", DRV_NAME, pm8001_ha->id);
+#ifdef PM8001_USE_TASKLET
+       tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
+               (unsigned long)pm8001_ha);
+#endif
+       pm8001_ioremap(pm8001_ha);
+       if (!pm8001_alloc(pm8001_ha))
+               return pm8001_ha;
+       pm8001_free(pm8001_ha);
+       return NULL;
+}
+
+/**
+ * pci_go_44 - pm8001 specified, its DMA is 44 bit rather than 64 bit
+ * @pdev: pci device.
+ */
+static int pci_go_44(struct pci_dev *pdev)
+{
+       int rc;
+
+       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(44))) {
+               rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(44));
+               if (rc) {
+                       rc = pci_set_consistent_dma_mask(pdev,
+                               DMA_BIT_MASK(32));
+                       if (rc) {
+                               dev_printk(KERN_ERR, &pdev->dev,
+                                       "44-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;
+}
+
+/**
+ * pm8001_prep_sas_ha_init - allocate memory in general hba struct && init them.
+ * @shost: scsi host which has been allocated outside.
+ * @chip_info: our ha struct.
+ */
+static int __devinit pm8001_prep_sas_ha_init(struct Scsi_Host * shost,
+       const struct pm8001_chip_info *chip_info)
+{
+       int phy_nr, port_nr;
+       struct asd_sas_phy **arr_phy;
+       struct asd_sas_port **arr_port;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+       phy_nr = chip_info->n_phy;
+       port_nr = phy_nr;
+       memset(sha, 0x00, sizeof(*sha));
+       arr_phy = kcalloc(phy_nr, sizeof(void *), GFP_KERNEL);
+       if (!arr_phy)
+               goto exit;
+       arr_port = kcalloc(port_nr, sizeof(void *), GFP_KERNEL);
+       if (!arr_port)
+               goto exit_free2;
+
+       sha->sas_phy = arr_phy;
+       sha->sas_port = arr_port;
+       sha->lldd_ha = kzalloc(sizeof(struct pm8001_hba_info), GFP_KERNEL);
+       if (!sha->lldd_ha)
+               goto exit_free1;
+
+       shost->transportt = pm8001_stt;
+       shost->max_id = PM8001_MAX_DEVICES;
+       shost->max_lun = 8;
+       shost->max_channel = 0;
+       shost->unique_id = pm8001_id;
+       shost->max_cmd_len = 16;
+       shost->can_queue = PM8001_CAN_QUEUE;
+       shost->cmd_per_lun = 32;
+       return 0;
+exit_free1:
+       kfree(arr_port);
+exit_free2:
+       kfree(arr_phy);
+exit:
+       return -1;
+}
+
+/**
+ * pm8001_post_sas_ha_init - initialize general hba struct defined in libsas
+ * @shost: scsi host which has been allocated outside
+ * @chip_info: our ha struct.
+ */
+static void  __devinit pm8001_post_sas_ha_init(struct Scsi_Host *shost,
+       const struct pm8001_chip_info *chip_info)
+{
+       int i = 0;
+       struct pm8001_hba_info *pm8001_ha;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+
+       pm8001_ha = sha->lldd_ha;
+       for (i = 0; i < chip_info->n_phy; i++) {
+               sha->sas_phy[i] = &pm8001_ha->phy[i].sas_phy;
+               sha->sas_port[i] = &pm8001_ha->port[i].sas_port;
+       }
+       sha->sas_ha_name = DRV_NAME;
+       sha->dev = pm8001_ha->dev;
+
+       sha->lldd_module = THIS_MODULE;
+       sha->sas_addr = &pm8001_ha->sas_addr[0];
+       sha->num_phys = chip_info->n_phy;
+       sha->lldd_max_execute_num = 1;
+       sha->lldd_queue_size = PM8001_CAN_QUEUE;
+       sha->core.shost = shost;
+}
+
+/**
+ * pm8001_init_sas_add - initialize sas address
+ * @chip_info: our ha struct.
+ *
+ * Currently we just set the fixed SAS address to our HBA,for manufacture,
+ * it should read from the EEPROM
+ */
+static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha)
+{
+       u8 i;
+#ifdef PM8001_READ_VPD
+       DECLARE_COMPLETION_ONSTACK(completion);
+       pm8001_ha->nvmd_completion = &completion;
+       PM8001_CHIP_DISP->get_nvmd_req(pm8001_ha, 0, 0);
+       wait_for_completion(&completion);
+       for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+               memcpy(&pm8001_ha->phy[i].dev_sas_addr, pm8001_ha->sas_addr,
+                       SAS_ADDR_SIZE);
+               PM8001_INIT_DBG(pm8001_ha,
+                       pm8001_printk("phy %d sas_addr = %x \n", i,
+                       (u64)pm8001_ha->phy[i].dev_sas_addr));
+       }
+#else
+       for (i = 0; i < pm8001_ha->chip->n_phy; i++) {
+               pm8001_ha->phy[i].dev_sas_addr = 0x500e004010000004ULL;
+               pm8001_ha->phy[i].dev_sas_addr =
+                       cpu_to_be64((u64)
+                               (*(u64 *)&pm8001_ha->phy[i].dev_sas_addr));
+       }
+       memcpy(pm8001_ha->sas_addr, &pm8001_ha->phy[0].dev_sas_addr,
+               SAS_ADDR_SIZE);
+#endif
+}
+
+#ifdef PM8001_USE_MSIX
+/**
+ * pm8001_setup_msix - enable MSI-X interrupt
+ * @chip_info: our ha struct.
+ * @irq_handler: irq_handler
+ */
+static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha,
+       irq_handler_t irq_handler)
+{
+       u32 i = 0, j = 0;
+       u32 number_of_intr = 1;
+       int flag = 0;
+       u32 max_entry;
+       int rc;
+       max_entry = sizeof(pm8001_ha->msix_entries) /
+               sizeof(pm8001_ha->msix_entries[0]);
+       flag |= IRQF_DISABLED;
+       for (i = 0; i < max_entry ; i++)
+               pm8001_ha->msix_entries[i].entry = i;
+       rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries,
+               number_of_intr);
+       pm8001_ha->number_of_intr = number_of_intr;
+       if (!rc) {
+               for (i = 0; i < number_of_intr; i++) {
+                       if (request_irq(pm8001_ha->msix_entries[i].vector,
+                               irq_handler, flag, DRV_NAME,
+                               SHOST_TO_SAS_HA(pm8001_ha->shost))) {
+                               for (j = 0; j < i; j++)
+                                       free_irq(
+                                       pm8001_ha->msix_entries[j].vector,
+                                       SHOST_TO_SAS_HA(pm8001_ha->shost));
+                               pci_disable_msix(pm8001_ha->pdev);
+                               break;
+                       }
+               }
+       }
+       return rc;
+}
+#endif
+
+/**
+ * pm8001_request_irq - register interrupt
+ * @chip_info: our ha struct.
+ */
+static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha)
+{
+       struct pci_dev *pdev;
+       irq_handler_t irq_handler = pm8001_interrupt;
+       int rc;
+
+       pdev = pm8001_ha->pdev;
+
+#ifdef PM8001_USE_MSIX
+       if (pci_find_capability(pdev, PCI_CAP_ID_MSIX))
+               return pm8001_setup_msix(pm8001_ha, irq_handler);
+       else
+               goto intx;
+#endif
+
+intx:
+       /* intialize the INT-X interrupt */
+       rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME,
+               SHOST_TO_SAS_HA(pm8001_ha->shost));
+       return rc;
+}
+
+/**
+ * pm8001_pci_probe - probe supported device
+ * @pdev: pci device which kernel has been prepared for.
+ * @ent: pci device id
+ *
+ * This function is the main initialization function, when register a new
+ * pci driver it is invoked, all struct an hardware initilization should be done
+ * here, also, register interrupt
+ */
+static int __devinit pm8001_pci_probe(struct pci_dev *pdev,
+       const struct pci_device_id *ent)
+{
+       unsigned int rc;
+       u32     pci_reg;
+       struct pm8001_hba_info *pm8001_ha;
+       struct Scsi_Host *shost = NULL;
+       const struct pm8001_chip_info *chip;
+
+       dev_printk(KERN_INFO, &pdev->dev,
+               "pm8001: driver version %s\n", DRV_VERSION);
+       rc = pci_enable_device(pdev);
+       if (rc)
+               goto err_out_enable;
+       pci_set_master(pdev);
+       /*
+        * Enable pci slot busmaster by setting pci command register.
+        * This is required by FW for Cyclone card.
+        */
+
+       pci_read_config_dword(pdev, PCI_COMMAND, &pci_reg);
+       pci_reg |= 0x157;
+       pci_write_config_dword(pdev, PCI_COMMAND, pci_reg);
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out_disable;
+       rc = pci_go_44(pdev);
+       if (rc)
+               goto err_out_regions;
+
+       shost = scsi_host_alloc(&pm8001_sht, sizeof(void *));
+       if (!shost) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+       chip = &pm8001_chips[ent->driver_data];
+       SHOST_TO_SAS_HA(shost) =
+               kcalloc(1, sizeof(struct sas_ha_struct), GFP_KERNEL);
+       if (!SHOST_TO_SAS_HA(shost)) {
+               rc = -ENOMEM;
+               goto err_out_free_host;
+       }
+
+       rc = pm8001_prep_sas_ha_init(shost, chip);
+       if (rc) {
+               rc = -ENOMEM;
+               goto err_out_free;
+       }
+       pci_set_drvdata(pdev, SHOST_TO_SAS_HA(shost));
+       pm8001_ha = pm8001_pci_alloc(pdev, chip_8001, shost);
+       if (!pm8001_ha) {
+               rc = -ENOMEM;
+               goto err_out_free;
+       }
+       list_add_tail(&pm8001_ha->list, &hba_list);
+       PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+       rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
+       if (rc)
+               goto err_out_ha_free;
+
+       rc = scsi_add_host(shost, &pdev->dev);
+       if (rc)
+               goto err_out_ha_free;
+       rc = pm8001_request_irq(pm8001_ha);
+       if (rc)
+               goto err_out_shost;
+
+       PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+       pm8001_init_sas_add(pm8001_ha);
+       pm8001_post_sas_ha_init(shost, chip);
+       rc = sas_register_ha(SHOST_TO_SAS_HA(shost));
+       if (rc)
+               goto err_out_shost;
+       scsi_scan_host(pm8001_ha->shost);
+       return 0;
+
+err_out_shost:
+       scsi_remove_host(pm8001_ha->shost);
+err_out_ha_free:
+       pm8001_free(pm8001_ha);
+err_out_free:
+       kfree(SHOST_TO_SAS_HA(shost));
+err_out_free_host:
+       kfree(shost);
+err_out_regions:
+       pci_release_regions(pdev);
+err_out_disable:
+       pci_disable_device(pdev);
+err_out_enable:
+       return rc;
+}
+
+static void __devexit pm8001_pci_remove(struct pci_dev *pdev)
+{
+       struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+       struct pm8001_hba_info *pm8001_ha;
+       int i;
+       pm8001_ha = sha->lldd_ha;
+       pci_set_drvdata(pdev, NULL);
+       sas_unregister_ha(sha);
+       sas_remove_host(pm8001_ha->shost);
+       list_del(&pm8001_ha->list);
+       scsi_remove_host(pm8001_ha->shost);
+       PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+       PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+
+#ifdef PM8001_USE_MSIX
+       for (i = 0; i < pm8001_ha->number_of_intr; i++)
+               synchronize_irq(pm8001_ha->msix_entries[i].vector);
+       for (i = 0; i < pm8001_ha->number_of_intr; i++)
+               free_irq(pm8001_ha->msix_entries[i].vector, sha);
+       pci_disable_msix(pdev);
+#else
+       free_irq(pm8001_ha->irq, sha);
+#endif
+#ifdef PM8001_USE_TASKLET
+       tasklet_kill(&pm8001_ha->tasklet);
+#endif
+       pm8001_free(pm8001_ha);
+       kfree(sha->sas_phy);
+       kfree(sha->sas_port);
+       kfree(sha);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+}
+
+/**
+ * pm8001_pci_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 pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+       struct pm8001_hba_info *pm8001_ha;
+       int i , pos;
+       u32 device_state;
+       pm8001_ha = sha->lldd_ha;
+       flush_scheduled_work();
+       scsi_block_requests(pm8001_ha->shost);
+       pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+       if (pos == 0) {
+               printk(KERN_ERR " PCI PM not supported\n");
+               return -ENODEV;
+       }
+       PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+       PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+#ifdef PM8001_USE_MSIX
+       for (i = 0; i < pm8001_ha->number_of_intr; i++)
+               synchronize_irq(pm8001_ha->msix_entries[i].vector);
+       for (i = 0; i < pm8001_ha->number_of_intr; i++)
+               free_irq(pm8001_ha->msix_entries[i].vector, sha);
+       pci_disable_msix(pdev);
+#else
+       free_irq(pm8001_ha->irq, sha);
+#endif
+#ifdef PM8001_USE_TASKLET
+       tasklet_kill(&pm8001_ha->tasklet);
+#endif
+       device_state = pci_choose_state(pdev, state);
+       pm8001_printk("pdev=0x%p, slot=%s, entering "
+                     "operating state [D%d]\n", pdev,
+                     pm8001_ha->name, device_state);
+       pci_save_state(pdev);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, device_state);
+       return 0;
+}
+
+/**
+ * pm8001_pci_resume - power management resume main entry point
+ * @pdev: PCI device struct
+ *
+ * Returns 0 success, anything else error.
+ */
+static int pm8001_pci_resume(struct pci_dev *pdev)
+{
+       struct sas_ha_struct *sha = pci_get_drvdata(pdev);
+       struct pm8001_hba_info *pm8001_ha;
+       int rc;
+       u32 device_state;
+       pm8001_ha = sha->lldd_ha;
+       device_state = pdev->current_state;
+
+       pm8001_printk("pdev=0x%p, slot=%s, resuming from previous "
+               "operating state [D%d]\n", pdev, pm8001_ha->name, device_state);
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_enable_wake(pdev, PCI_D0, 0);
+       pci_restore_state(pdev);
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               pm8001_printk("slot=%s Enable device failed during resume\n",
+                             pm8001_ha->name);
+               goto err_out_enable;
+       }
+
+       pci_set_master(pdev);
+       rc = pci_go_44(pdev);
+       if (rc)
+               goto err_out_disable;
+
+       PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd);
+       rc = PM8001_CHIP_DISP->chip_init(pm8001_ha);
+       if (rc)
+               goto err_out_disable;
+       PM8001_CHIP_DISP->interrupt_disable(pm8001_ha);
+       rc = pm8001_request_irq(pm8001_ha);
+       if (rc)
+               goto err_out_disable;
+       #ifdef PM8001_USE_TASKLET
+       tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet,
+                   (unsigned long)pm8001_ha);
+       #endif
+       PM8001_CHIP_DISP->interrupt_enable(pm8001_ha);
+       scsi_unblock_requests(pm8001_ha->shost);
+       return 0;
+
+err_out_disable:
+       scsi_remove_host(pm8001_ha->shost);
+       pci_disable_device(pdev);
+err_out_enable:
+       return rc;
+}
+
+static struct pci_device_id __devinitdata pm8001_pci_table[] = {
+       {
+               PCI_VDEVICE(PMC_Sierra, 0x8001), chip_8001
+       },
+       {
+               PCI_DEVICE(0x117c, 0x0042),
+               .driver_data = chip_8001
+       },
+       {} /* terminate list */
+};
+
+static struct pci_driver pm8001_pci_driver = {
+       .name           = DRV_NAME,
+       .id_table       = pm8001_pci_table,
+       .probe          = pm8001_pci_probe,
+       .remove         = __devexit_p(pm8001_pci_remove),
+       .suspend        = pm8001_pci_suspend,
+       .resume         = pm8001_pci_resume,
+};
+
+/**
+ *     pm8001_init - initialize scsi transport template
+ */
+static int __init pm8001_init(void)
+{
+       int rc;
+       pm8001_id = 0;
+       pm8001_stt = sas_domain_attach_transport(&pm8001_transport_ops);
+       if (!pm8001_stt)
+               return -ENOMEM;
+       rc = pci_register_driver(&pm8001_pci_driver);
+       if (rc)
+               goto err_out;
+       return 0;
+err_out:
+       sas_release_transport(pm8001_stt);
+       return rc;
+}
+
+static void __exit pm8001_exit(void)
+{
+       pci_unregister_driver(&pm8001_pci_driver);
+       sas_release_transport(pm8001_stt);
+}
+
+module_init(pm8001_init);
+module_exit(pm8001_exit);
+
+MODULE_AUTHOR("Jack Wang <jack_wang@usish.com>");
+MODULE_DESCRIPTION("PMC-Sierra PM8001 SAS/SATA controller driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, pm8001_pci_table);
+
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
new file mode 100644 (file)
index 0000000..1f767a0
--- /dev/null
@@ -0,0 +1,1103 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include "pm8001_sas.h"
+
+/**
+ * pm8001_find_tag - from sas task to find out  tag that belongs to this task
+ * @task: the task sent to the LLDD
+ * @tag: the found tag associated with the task
+ */
+static int pm8001_find_tag(struct sas_task *task, u32 *tag)
+{
+       if (task->lldd_task) {
+               struct pm8001_ccb_info *ccb;
+               ccb = task->lldd_task;
+               *tag = ccb->ccb_tag;
+               return 1;
+       }
+       return 0;
+}
+
+/**
+  * pm8001_tag_clear - clear the tags bitmap
+  * @pm8001_ha: our hba struct
+  * @tag: the found tag associated with the task
+  */
+static void pm8001_tag_clear(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+       void *bitmap = pm8001_ha->tags;
+       clear_bit(tag, bitmap);
+}
+
+static void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+       pm8001_tag_clear(pm8001_ha, tag);
+}
+
+static void pm8001_tag_set(struct pm8001_hba_info *pm8001_ha, u32 tag)
+{
+       void *bitmap = pm8001_ha->tags;
+       set_bit(tag, bitmap);
+}
+
+/**
+  * pm8001_tag_alloc - allocate a empty tag for task used.
+  * @pm8001_ha: our hba struct
+  * @tag_out: the found empty tag .
+  */
+inline int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out)
+{
+       unsigned int index, tag;
+       void *bitmap = pm8001_ha->tags;
+
+       index = find_first_zero_bit(bitmap, pm8001_ha->tags_num);
+       tag = index;
+       if (tag >= pm8001_ha->tags_num)
+               return -SAS_QUEUE_FULL;
+       pm8001_tag_set(pm8001_ha, tag);
+       *tag_out = tag;
+       return 0;
+}
+
+void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha)
+{
+       int i;
+       for (i = 0; i < pm8001_ha->tags_num; ++i)
+               pm8001_tag_clear(pm8001_ha, i);
+}
+
+ /**
+  * pm8001_mem_alloc - allocate memory for pm8001.
+  * @pdev: pci device.
+  * @virt_addr: the allocated virtual address
+  * @pphys_addr_hi: the physical address high byte address.
+  * @pphys_addr_lo: the physical address low byte address.
+  * @mem_size: memory size.
+  */
+int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
+       dma_addr_t *pphys_addr, u32 *pphys_addr_hi,
+       u32 *pphys_addr_lo, u32 mem_size, u32 align)
+{
+       caddr_t mem_virt_alloc;
+       dma_addr_t mem_dma_handle;
+       u64 phys_align;
+       u64 align_offset = 0;
+       if (align)
+               align_offset = (dma_addr_t)align - 1;
+       mem_virt_alloc =
+               pci_alloc_consistent(pdev, mem_size + align, &mem_dma_handle);
+       if (!mem_virt_alloc) {
+               pm8001_printk("memory allocation error\n");
+               return -1;
+       }
+       memset((void *)mem_virt_alloc, 0, mem_size+align);
+       *pphys_addr = mem_dma_handle;
+       phys_align = (*pphys_addr + align_offset) & ~align_offset;
+       *virt_addr = (void *)mem_virt_alloc + phys_align - *pphys_addr;
+       *pphys_addr_hi = upper_32_bits(phys_align);
+       *pphys_addr_lo = lower_32_bits(phys_align);
+       return 0;
+}
+/**
+  * pm8001_find_ha_by_dev - from domain device which come from sas layer to
+  * find out our hba struct.
+  * @dev: the domain device which from sas layer.
+  */
+static
+struct pm8001_hba_info *pm8001_find_ha_by_dev(struct domain_device *dev)
+{
+       struct sas_ha_struct *sha = dev->port->ha;
+       struct pm8001_hba_info *pm8001_ha = sha->lldd_ha;
+       return pm8001_ha;
+}
+
+/**
+  * pm8001_phy_control - this function should be registered to
+  * sas_domain_function_template to provide libsas used, note: this is just
+  * control the HBA phy rather than other expander phy if you want control
+  * other phy, you should use SMP command.
+  * @sas_phy: which phy in HBA phys.
+  * @func: the operation.
+  * @funcdata: always NULL.
+  */
+int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+       void *funcdata)
+{
+       int rc = 0, phy_id = sas_phy->id;
+       struct pm8001_hba_info *pm8001_ha = NULL;
+       struct sas_phy_linkrates *rates;
+       DECLARE_COMPLETION_ONSTACK(completion);
+       pm8001_ha = sas_phy->ha->lldd_ha;
+       pm8001_ha->phy[phy_id].enable_completion = &completion;
+       switch (func) {
+       case PHY_FUNC_SET_LINK_RATE:
+               rates = funcdata;
+               if (rates->minimum_linkrate) {
+                       pm8001_ha->phy[phy_id].minimum_linkrate =
+                               rates->minimum_linkrate;
+               }
+               if (rates->maximum_linkrate) {
+                       pm8001_ha->phy[phy_id].maximum_linkrate =
+                               rates->maximum_linkrate;
+               }
+               if (pm8001_ha->phy[phy_id].phy_state == 0) {
+                       PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+                       wait_for_completion(&completion);
+               }
+               PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+                                             PHY_LINK_RESET);
+               break;
+       case PHY_FUNC_HARD_RESET:
+               if (pm8001_ha->phy[phy_id].phy_state == 0) {
+                       PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+                       wait_for_completion(&completion);
+               }
+               PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+                                             PHY_HARD_RESET);
+               break;
+       case PHY_FUNC_LINK_RESET:
+               if (pm8001_ha->phy[phy_id].phy_state == 0) {
+                       PM8001_CHIP_DISP->phy_start_req(pm8001_ha, phy_id);
+                       wait_for_completion(&completion);
+               }
+               PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+                                             PHY_LINK_RESET);
+               break;
+       case PHY_FUNC_RELEASE_SPINUP_HOLD:
+               PM8001_CHIP_DISP->phy_ctl_req(pm8001_ha, phy_id,
+                                             PHY_LINK_RESET);
+               break;
+       case PHY_FUNC_DISABLE:
+               PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id);
+               break;
+       default:
+               rc = -EOPNOTSUPP;
+       }
+       msleep(300);
+       return rc;
+}
+
+int pm8001_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);
+}
+
+/**
+  * pm8001_scan_start - we should enable all HBA phys by sending the phy_start
+  * command to HBA.
+  * @shost: the scsi host data.
+  */
+void pm8001_scan_start(struct Scsi_Host *shost)
+{
+       int i;
+       struct pm8001_hba_info *pm8001_ha;
+       struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost);
+       pm8001_ha = sha->lldd_ha;
+       PM8001_CHIP_DISP->sas_re_init_req(pm8001_ha);
+       for (i = 0; i < pm8001_ha->chip->n_phy; ++i)
+               PM8001_CHIP_DISP->phy_start_req(pm8001_ha, i);
+}
+
+int pm8001_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;
+}
+
+/**
+  * pm8001_task_prep_smp - the dispatcher function, prepare data for smp task
+  * @pm8001_ha: our hba card information
+  * @ccb: the ccb which attached to smp task
+  */
+static int pm8001_task_prep_smp(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb)
+{
+       return PM8001_CHIP_DISP->smp_req(pm8001_ha, ccb);
+}
+
+u32 pm8001_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;
+}
+
+/**
+  * pm8001_task_prep_ata - the dispatcher function, prepare data for sata task
+  * @pm8001_ha: our hba card information
+  * @ccb: the ccb which attached to sata task
+  */
+static int pm8001_task_prep_ata(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb)
+{
+       return PM8001_CHIP_DISP->sata_req(pm8001_ha, ccb);
+}
+
+/**
+  * pm8001_task_prep_ssp_tm - the dispatcher function, prepare task management data
+  * @pm8001_ha: our hba card information
+  * @ccb: the ccb which attached to TM
+  * @tmf: the task management IU
+  */
+static int pm8001_task_prep_ssp_tm(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf)
+{
+       return PM8001_CHIP_DISP->ssp_tm_req(pm8001_ha, ccb, tmf);
+}
+
+/**
+  * pm8001_task_prep_ssp - the dispatcher function,prepare ssp data for ssp task
+  * @pm8001_ha: our hba card information
+  * @ccb: the ccb which attached to ssp task
+  */
+static int pm8001_task_prep_ssp(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_ccb_info *ccb)
+{
+       return PM8001_CHIP_DISP->ssp_io_req(pm8001_ha, ccb);
+}
+int pm8001_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)) {
+       #ifdef PM8001_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;
+}
+/**
+  * pm8001_task_exec - queue the task(ssp, smp && ata) to the hardware.
+  * @task: the task to be execute.
+  * @num: if can_queue great than 1, the task can be queued up. for SMP task,
+  * we always execute one one time.
+  * @gfp_flags: gfp_flags.
+  * @is_tmf: if it is task management task.
+  * @tmf: the task management IU
+  */
+#define DEV_IS_GONE(pm8001_dev)        \
+       ((!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE)))
+static int pm8001_task_exec(struct sas_task *task, const int num,
+       gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf)
+{
+       struct domain_device *dev = task->dev;
+       struct pm8001_hba_info *pm8001_ha;
+       struct pm8001_device *pm8001_dev;
+       struct sas_task *t = task;
+       struct pm8001_ccb_info *ccb;
+       u32 tag = 0xdeadbeef, rc, n_elem = 0;
+       u32 n = num;
+       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;
+               if (dev->dev_type != SATA_DEV)
+                       t->task_done(t);
+               return 0;
+       }
+       pm8001_ha = pm8001_find_ha_by_dev(task->dev);
+       PM8001_IO_DBG(pm8001_ha, pm8001_printk("pm8001_task_exec device \n "));
+       spin_lock_irqsave(&pm8001_ha->lock, flags);
+       do {
+               dev = t->dev;
+               pm8001_dev = dev->lldd_dev;
+               if (DEV_IS_GONE(pm8001_dev)) {
+                       if (pm8001_dev) {
+                               PM8001_IO_DBG(pm8001_ha,
+                                       pm8001_printk("device %d not ready.\n",
+                                       pm8001_dev->device_id));
+                       } else {
+                               PM8001_IO_DBG(pm8001_ha,
+                                       pm8001_printk("device %016llx not "
+                                       "ready.\n", SAS_ADDR(dev->sas_addr)));
+                       }
+                       rc = SAS_PHY_DOWN;
+                       goto out_done;
+               }
+               rc = pm8001_tag_alloc(pm8001_ha, &tag);
+               if (rc)
+                       goto err_out;
+               ccb = &pm8001_ha->ccb_info[tag];
+
+               if (!sas_protocol_ata(t->task_proto)) {
+                       if (t->num_scatter) {
+                               n_elem = dma_map_sg(pm8001_ha->dev,
+                                       t->scatter,
+                                       t->num_scatter,
+                                       t->data_dir);
+                               if (!n_elem) {
+                                       rc = -ENOMEM;
+                                       goto err_out_tag;
+                               }
+                       }
+               } else {
+                       n_elem = t->num_scatter;
+               }
+
+               t->lldd_task = ccb;
+               ccb->n_elem = n_elem;
+               ccb->ccb_tag = tag;
+               ccb->task = t;
+               switch (t->task_proto) {
+               case SAS_PROTOCOL_SMP:
+                       rc = pm8001_task_prep_smp(pm8001_ha, ccb);
+                       break;
+               case SAS_PROTOCOL_SSP:
+                       if (is_tmf)
+                               rc = pm8001_task_prep_ssp_tm(pm8001_ha,
+                                       ccb, tmf);
+                       else
+                               rc = pm8001_task_prep_ssp(pm8001_ha, ccb);
+                       break;
+               case SAS_PROTOCOL_SATA:
+               case SAS_PROTOCOL_STP:
+               case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
+                       rc = pm8001_task_prep_ata(pm8001_ha, ccb);
+                       break;
+               default:
+                       dev_printk(KERN_ERR, pm8001_ha->dev,
+                               "unknown sas_task proto: 0x%x\n",
+                               t->task_proto);
+                       rc = -EINVAL;
+                       break;
+               }
+
+               if (rc) {
+                       PM8001_IO_DBG(pm8001_ha,
+                               pm8001_printk("rc is %x\n", rc));
+                       goto err_out_tag;
+               }
+               /* 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);
+               pm8001_dev->running_req++;
+               if (n > 1)
+                       t = list_entry(t->list.next, struct sas_task, list);
+       } while (--n);
+       rc = 0;
+       goto out_done;
+
+err_out_tag:
+       pm8001_tag_free(pm8001_ha, tag);
+err_out:
+       dev_printk(KERN_ERR, pm8001_ha->dev, "pm8001 exec failed[%d]!\n", rc);
+       if (!sas_protocol_ata(t->task_proto))
+               if (n_elem)
+                       dma_unmap_sg(pm8001_ha->dev, t->scatter, n_elem,
+                               t->data_dir);
+out_done:
+       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+       return rc;
+}
+
+/**
+  * pm8001_queue_command - register for upper layer used, all IO commands sent
+  * to HBA are from this interface.
+  * @task: the task to be execute.
+  * @num: if can_queue great than 1, the task can be queued up. for SMP task,
+  * we always execute one one time
+  * @gfp_flags: gfp_flags
+  */
+int pm8001_queue_command(struct sas_task *task, const int num,
+               gfp_t gfp_flags)
+{
+       return pm8001_task_exec(task, num, gfp_flags, 0, NULL);
+}
+
+void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx)
+{
+       pm8001_tag_clear(pm8001_ha, ccb_idx);
+}
+
+/**
+  * pm8001_ccb_task_free - free the sg for ssp and smp command, free the ccb.
+  * @pm8001_ha: our hba card information
+  * @ccb: the ccb which attached to ssp task
+  * @task: the task to be free.
+  * @ccb_idx: ccb index.
+  */
+void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
+       struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx)
+{
+       if (!ccb->task)
+               return;
+       if (!sas_protocol_ata(task->task_proto))
+               if (ccb->n_elem)
+                       dma_unmap_sg(pm8001_ha->dev, task->scatter,
+                               task->num_scatter, task->data_dir);
+
+       switch (task->task_proto) {
+       case SAS_PROTOCOL_SMP:
+               dma_unmap_sg(pm8001_ha->dev, &task->smp_task.smp_resp, 1,
+                       PCI_DMA_FROMDEVICE);
+               dma_unmap_sg(pm8001_ha->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;
+       }
+       task->lldd_task = NULL;
+       ccb->task = NULL;
+       ccb->ccb_tag = 0xFFFFFFFF;
+       pm8001_ccb_free(pm8001_ha, ccb_idx);
+}
+
+ /**
+  * pm8001_alloc_dev - find a empty pm8001_device
+  * @pm8001_ha: our hba card information
+  */
+struct pm8001_device *pm8001_alloc_dev(struct pm8001_hba_info *pm8001_ha)
+{
+       u32 dev;
+       for (dev = 0; dev < PM8001_MAX_DEVICES; dev++) {
+               if (pm8001_ha->devices[dev].dev_type == NO_DEVICE) {
+                       pm8001_ha->devices[dev].id = dev;
+                       return &pm8001_ha->devices[dev];
+               }
+       }
+       if (dev == PM8001_MAX_DEVICES) {
+               PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("max support %d devices, ignore ..\n",
+                       PM8001_MAX_DEVICES));
+       }
+       return NULL;
+}
+
+static void pm8001_free_dev(struct pm8001_device *pm8001_dev)
+{
+       u32 id = pm8001_dev->id;
+       memset(pm8001_dev, 0, sizeof(*pm8001_dev));
+       pm8001_dev->id = id;
+       pm8001_dev->dev_type = NO_DEVICE;
+       pm8001_dev->device_id = PM8001_MAX_DEVICES;
+       pm8001_dev->sas_device = NULL;
+}
+
+/**
+  * pm8001_dev_found_notify - libsas notify a device is found.
+  * @dev: the device structure which sas layer used.
+  *
+  * when libsas find a sas domain device, it should tell the LLDD that
+  * device is found, and then LLDD register this device to HBA firmware
+  * by the command "OPC_INB_REG_DEV", after that the HBA will assign a
+  * device ID(according to device's sas address) and returned it to LLDD. From
+  * now on, we communicate with HBA FW with the device ID which HBA assigned
+  * rather than sas address. it is the neccessary step for our HBA but it is
+  * the optional for other HBA driver.
+  */
+static int pm8001_dev_found_notify(struct domain_device *dev)
+{
+       unsigned long flags = 0;
+       int res = 0;
+       struct pm8001_hba_info *pm8001_ha = NULL;
+       struct domain_device *parent_dev = dev->parent;
+       struct pm8001_device *pm8001_device;
+       DECLARE_COMPLETION_ONSTACK(completion);
+       u32 flag = 0;
+       pm8001_ha = pm8001_find_ha_by_dev(dev);
+       spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+       pm8001_device = pm8001_alloc_dev(pm8001_ha);
+       pm8001_device->sas_device = dev;
+       if (!pm8001_device) {
+               res = -1;
+               goto found_out;
+       }
+       dev->lldd_dev = pm8001_device;
+       pm8001_device->dev_type = dev->dev_type;
+       pm8001_device->dcompletion = &completion;
+       if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) {
+               int phy_id;
+               struct ex_phy *phy;
+               for (phy_id = 0; phy_id < parent_dev->ex_dev.num_phys;
+               phy_id++) {
+                       phy = &parent_dev->ex_dev.ex_phy[phy_id];
+                       if (SAS_ADDR(phy->attached_sas_addr)
+                               == SAS_ADDR(dev->sas_addr)) {
+                               pm8001_device->attached_phy = phy_id;
+                               break;
+                       }
+               }
+               if (phy_id == parent_dev->ex_dev.num_phys) {
+                       PM8001_FAIL_DBG(pm8001_ha,
+                       pm8001_printk("Error: no attached dev:%016llx"
+                       " at ex:%016llx.\n", SAS_ADDR(dev->sas_addr),
+                               SAS_ADDR(parent_dev->sas_addr)));
+                       res = -1;
+               }
+       } else {
+               if (dev->dev_type == SATA_DEV) {
+                       pm8001_device->attached_phy =
+                               dev->rphy->identify.phy_identifier;
+                               flag = 1; /* directly sata*/
+               }
+       } /*register this device to HBA*/
+       PM8001_DISC_DBG(pm8001_ha, pm8001_printk("Found device \n"));
+       PM8001_CHIP_DISP->reg_dev_req(pm8001_ha, pm8001_device, flag);
+       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+       wait_for_completion(&completion);
+       if (dev->dev_type == SAS_END_DEV)
+               msleep(50);
+       pm8001_ha->flags = PM8001F_RUN_TIME ;
+       return 0;
+found_out:
+       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+       return res;
+}
+
+int pm8001_dev_found(struct domain_device *dev)
+{
+       return pm8001_dev_found_notify(dev);
+}
+
+/**
+  * pm8001_alloc_task - allocate a task structure for TMF
+  */
+static struct sas_task *pm8001_alloc_task(void)
+{
+       struct sas_task *task = kzalloc(sizeof(*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 pm8001_free_task(struct sas_task *task)
+{
+       if (task) {
+               BUG_ON(!list_empty(&task->list));
+               kfree(task);
+       }
+}
+
+static void pm8001_task_done(struct sas_task *task)
+{
+       if (!del_timer(&task->timer))
+               return;
+       complete(&task->completion);
+}
+
+static void pm8001_tmf_timedout(unsigned long data)
+{
+       struct sas_task *task = (struct sas_task *)data;
+
+       task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+       complete(&task->completion);
+}
+
+#define PM8001_TASK_TIMEOUT 20
+/**
+  * pm8001_exec_internal_tmf_task - execute some task management commands.
+  * @dev: the wanted device.
+  * @tmf: which task management wanted to be take.
+  * @para_len: para_len.
+  * @parameter: ssp task parameter.
+  *
+  * when errors or exception happened, we may want to do something, for example
+  * abort the issued task which result in this execption, it is done by calling
+  * this function, note it is also with the task execute interface.
+  */
+static int pm8001_exec_internal_tmf_task(struct domain_device *dev,
+       void *parameter, u32 para_len, struct pm8001_tmf_task *tmf)
+{
+       int res, retry;
+       struct sas_task *task = NULL;
+       struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+       for (retry = 0; retry < 3; retry++) {
+               task = pm8001_alloc_task();
+               if (!task)
+                       return -ENOMEM;
+
+               task->dev = dev;
+               task->task_proto = dev->tproto;
+               memcpy(&task->ssp_task, parameter, para_len);
+               task->task_done = pm8001_task_done;
+               task->timer.data = (unsigned long)task;
+               task->timer.function = pm8001_tmf_timedout;
+               task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
+               add_timer(&task->timer);
+
+               res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf);
+
+               if (res) {
+                       del_timer(&task->timer);
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("Executing internal task "
+                               "failed\n"));
+                       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)) {
+                               PM8001_FAIL_DBG(pm8001_ha,
+                                       pm8001_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) {
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("Blocked task error.\n"));
+                       res = -EMSGSIZE;
+                       break;
+               } else {
+                       PM8001_EH_DBG(pm8001_ha,
+                               pm8001_printk(" Task to dev %016llx response:"
+                               "0x%x status 0x%x\n",
+                               SAS_ADDR(dev->sas_addr),
+                               task->task_status.resp,
+                               task->task_status.stat));
+                       pm8001_free_task(task);
+                       task = NULL;
+               }
+       }
+ex_err:
+       BUG_ON(retry == 3 && task != NULL);
+       if (task != NULL)
+               pm8001_free_task(task);
+       return res;
+}
+
+static int
+pm8001_exec_internal_task_abort(struct pm8001_hba_info *pm8001_ha,
+       struct pm8001_device *pm8001_dev, struct domain_device *dev, u32 flag,
+       u32 task_tag)
+{
+       int res, retry;
+       u32 ccb_tag;
+       struct pm8001_ccb_info *ccb;
+       struct sas_task *task = NULL;
+
+       for (retry = 0; retry < 3; retry++) {
+               task = pm8001_alloc_task();
+               if (!task)
+                       return -ENOMEM;
+
+               task->dev = dev;
+               task->task_proto = dev->tproto;
+               task->task_done = pm8001_task_done;
+               task->timer.data = (unsigned long)task;
+               task->timer.function = pm8001_tmf_timedout;
+               task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
+               add_timer(&task->timer);
+
+               res = pm8001_tag_alloc(pm8001_ha, &ccb_tag);
+               if (res)
+                       return res;
+               ccb = &pm8001_ha->ccb_info[ccb_tag];
+               ccb->device = pm8001_dev;
+               ccb->ccb_tag = ccb_tag;
+               ccb->task = task;
+
+               res = PM8001_CHIP_DISP->task_abort(pm8001_ha,
+                       pm8001_dev, flag, task_tag, ccb_tag);
+
+               if (res) {
+                       del_timer(&task->timer);
+                       PM8001_FAIL_DBG(pm8001_ha,
+                               pm8001_printk("Executing internal task "
+                               "failed\n"));
+                       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)) {
+                               PM8001_FAIL_DBG(pm8001_ha,
+                                       pm8001_printk("TMF task timeout.\n"));
+                               goto ex_err;
+                       }
+               }
+
+               if (task->task_status.resp == SAS_TASK_COMPLETE &&
+                       task->task_status.stat == SAM_GOOD) {
+                       res = TMF_RESP_FUNC_COMPLETE;
+                       break;
+
+               } else {
+                       PM8001_EH_DBG(pm8001_ha,
+                               pm8001_printk(" Task to dev %016llx response: "
+                                       "0x%x status 0x%x\n",
+                               SAS_ADDR(dev->sas_addr),
+                               task->task_status.resp,
+                               task->task_status.stat));
+                       pm8001_free_task(task);
+                       task = NULL;
+               }
+       }
+ex_err:
+       BUG_ON(retry == 3 && task != NULL);
+       if (task != NULL)
+               pm8001_free_task(task);
+       return res;
+}
+
+/**
+  * pm8001_dev_gone_notify - see the comments for "pm8001_dev_found_notify"
+  * @dev: the device structure which sas layer used.
+  */
+static void pm8001_dev_gone_notify(struct domain_device *dev)
+{
+       unsigned long flags = 0;
+       u32 tag;
+       struct pm8001_hba_info *pm8001_ha;
+       struct pm8001_device *pm8001_dev = dev->lldd_dev;
+       u32 device_id = pm8001_dev->device_id;
+       pm8001_ha = pm8001_find_ha_by_dev(dev);
+       spin_lock_irqsave(&pm8001_ha->lock, flags);
+       pm8001_tag_alloc(pm8001_ha, &tag);
+       if (pm8001_dev) {
+               PM8001_DISC_DBG(pm8001_ha,
+                       pm8001_printk("found dev[%d:%x] is gone.\n",
+                       pm8001_dev->device_id, pm8001_dev->dev_type));
+               if (pm8001_dev->running_req) {
+                       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+                       pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+                               dev, 1, 0);
+                       spin_lock_irqsave(&pm8001_ha->lock, flags);
+               }
+               PM8001_CHIP_DISP->dereg_dev_req(pm8001_ha, device_id);
+               pm8001_free_dev(pm8001_dev);
+       } else {
+               PM8001_DISC_DBG(pm8001_ha,
+                       pm8001_printk("Found dev has gone.\n"));
+       }
+       dev->lldd_dev = NULL;
+       spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+}
+
+void pm8001_dev_gone(struct domain_device *dev)
+{
+       pm8001_dev_gone_notify(dev);
+}
+
+static int pm8001_issue_ssp_tmf(struct domain_device *dev,
+       u8 *lun, struct pm8001_tmf_task *tmf)
+{
+       struct sas_ssp_task ssp_task;
+       if (!(dev->tproto & SAS_PROTOCOL_SSP))
+               return TMF_RESP_FUNC_ESUPP;
+
+       strncpy((u8 *)&ssp_task.LUN, lun, 8);
+       return pm8001_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
+  */
+int pm8001_I_T_nexus_reset(struct domain_device *dev)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct pm8001_device *pm8001_dev;
+       struct pm8001_hba_info *pm8001_ha;
+       struct sas_phy *phy;
+       if (!dev || !dev->lldd_dev)
+               return -1;
+
+       pm8001_dev = dev->lldd_dev;
+       pm8001_ha = pm8001_find_ha_by_dev(dev);
+       phy = sas_find_local_phy(dev);
+
+       if (dev_is_sata(dev)) {
+               DECLARE_COMPLETION_ONSTACK(completion_setstate);
+               rc = sas_phy_reset(phy, 1);
+               msleep(2000);
+               rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+                       dev, 1, 0);
+               pm8001_dev->setds_completion = &completion_setstate;
+               rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+                       pm8001_dev, 0x01);
+               wait_for_completion(&completion_setstate);
+       } else{
+       rc = sas_phy_reset(phy, 1);
+       msleep(2000);
+       }
+       PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n",
+               pm8001_dev->device_id, rc));
+       return rc;
+}
+
+/* mandatory SAM-3, the task reset the specified LUN*/
+int pm8001_lu_reset(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct pm8001_tmf_task tmf_task;
+       struct pm8001_device *pm8001_dev = dev->lldd_dev;
+       struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+       if (dev_is_sata(dev)) {
+               struct sas_phy *phy = sas_find_local_phy(dev);
+               rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
+                       dev, 1, 0);
+               rc = sas_phy_reset(phy, 1);
+               rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
+                       pm8001_dev, 0x01);
+               msleep(2000);
+       } else {
+               tmf_task.tmf = TMF_LU_RESET;
+               rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+       }
+       /* If failed, fall-through I_T_Nexus reset */
+       PM8001_EH_DBG(pm8001_ha, pm8001_printk("for device[%x]:rc=%d\n",
+               pm8001_dev->device_id, rc));
+       return rc;
+}
+
+/* optional SAM-3 */
+int pm8001_query_task(struct sas_task *task)
+{
+       u32 tag = 0xdeadbeef;
+       int i = 0;
+       struct scsi_lun lun;
+       struct pm8001_tmf_task tmf_task;
+       int rc = TMF_RESP_FUNC_FAILED;
+       if (unlikely(!task || !task->lldd_task || !task->dev))
+               return rc;
+
+       if (task->task_proto & SAS_PROTOCOL_SSP) {
+               struct scsi_cmnd *cmnd = task->uldd_task;
+               struct domain_device *dev = task->dev;
+               struct pm8001_hba_info *pm8001_ha =
+                       pm8001_find_ha_by_dev(dev);
+
+               int_to_scsilun(cmnd->device->lun, &lun);
+               rc = pm8001_find_tag(task, &tag);
+               if (rc == 0) {
+                       rc = TMF_RESP_FUNC_FAILED;
+                       return rc;
+               }
+               PM8001_EH_DBG(pm8001_ha, pm8001_printk("Query:["));
+               for (i = 0; i < 16; i++)
+                       printk(KERN_INFO "%02x ", cmnd->cmnd[i]);
+               printk(KERN_INFO "]\n");
+               tmf_task.tmf =  TMF_QUERY_TASK;
+               tmf_task.tag_of_task_to_be_managed = tag;
+
+               rc = pm8001_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:
+                       PM8001_EH_DBG(pm8001_ha,
+                               pm8001_printk("The task is still in Lun \n"));
+               /* The task is not in Lun or failed, reset the phy */
+               case TMF_RESP_FUNC_FAILED:
+               case TMF_RESP_FUNC_COMPLETE:
+                       PM8001_EH_DBG(pm8001_ha,
+                       pm8001_printk("The task is not in Lun or failed,"
+                       " reset the phy \n"));
+                       break;
+               }
+       }
+       pm8001_printk(":rc= %d\n", rc);
+       return rc;
+}
+
+/*  mandatory SAM-3, still need free task/ccb info, abord the specified task */
+int pm8001_abort_task(struct sas_task *task)
+{
+       unsigned long flags;
+       u32 tag = 0xdeadbeef;
+       u32 device_id;
+       struct domain_device *dev ;
+       struct pm8001_hba_info *pm8001_ha = NULL;
+       struct pm8001_ccb_info *ccb;
+       struct scsi_lun lun;
+       struct pm8001_device *pm8001_dev;
+       struct pm8001_tmf_task tmf_task;
+       int rc = TMF_RESP_FUNC_FAILED;
+       if (unlikely(!task || !task->lldd_task || !task->dev))
+               return rc;
+       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->task_proto & SAS_PROTOCOL_SSP) {
+               struct scsi_cmnd *cmnd = task->uldd_task;
+               dev = task->dev;
+               ccb = task->lldd_task;
+               pm8001_dev = dev->lldd_dev;
+               pm8001_ha = pm8001_find_ha_by_dev(dev);
+               int_to_scsilun(cmnd->device->lun, &lun);
+               rc = pm8001_find_tag(task, &tag);
+               if (rc == 0) {
+                       printk(KERN_INFO "No such tag in %s\n", __func__);
+                       rc = TMF_RESP_FUNC_FAILED;
+                       return rc;
+               }
+               device_id = pm8001_dev->device_id;
+               PM8001_EH_DBG(pm8001_ha,
+                       pm8001_printk("abort io to deviceid= %d\n", device_id));
+               tmf_task.tmf = TMF_ABORT_TASK;
+               tmf_task.tag_of_task_to_be_managed = tag;
+               rc = pm8001_issue_ssp_tmf(dev, lun.scsi_lun, &tmf_task);
+               pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+                       pm8001_dev->sas_device, 0, tag);
+       } else if (task->task_proto & SAS_PROTOCOL_SATA ||
+               task->task_proto & SAS_PROTOCOL_STP) {
+               dev = task->dev;
+               pm8001_dev = dev->lldd_dev;
+               pm8001_ha = pm8001_find_ha_by_dev(dev);
+               rc = pm8001_find_tag(task, &tag);
+               if (rc == 0) {
+                       printk(KERN_INFO "No such tag in %s\n", __func__);
+                       rc = TMF_RESP_FUNC_FAILED;
+                       return rc;
+               }
+               rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+                       pm8001_dev->sas_device, 0, tag);
+       } else if (task->task_proto & SAS_PROTOCOL_SMP) {
+               /* SMP */
+               dev = task->dev;
+               pm8001_dev = dev->lldd_dev;
+               pm8001_ha = pm8001_find_ha_by_dev(dev);
+               rc = pm8001_find_tag(task, &tag);
+               if (rc == 0) {
+                       printk(KERN_INFO "No such tag in %s\n", __func__);
+                       rc = TMF_RESP_FUNC_FAILED;
+                       return rc;
+               }
+               rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev,
+                       pm8001_dev->sas_device, 0, tag);
+
+       }
+out:
+       if (rc != TMF_RESP_FUNC_COMPLETE)
+               pm8001_printk("rc= %d\n", rc);
+       return rc;
+}
+
+int pm8001_abort_task_set(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct pm8001_tmf_task tmf_task;
+
+       tmf_task.tmf = TMF_ABORT_TASK_SET;
+       rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+       return rc;
+}
+
+int pm8001_clear_aca(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct pm8001_tmf_task tmf_task;
+
+       tmf_task.tmf = TMF_CLEAR_ACA;
+       rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+
+       return rc;
+}
+
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun)
+{
+       int rc = TMF_RESP_FUNC_FAILED;
+       struct pm8001_tmf_task tmf_task;
+       struct pm8001_device *pm8001_dev = dev->lldd_dev;
+       struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
+
+       PM8001_EH_DBG(pm8001_ha,
+               pm8001_printk("I_T_L_Q clear task set[%x]\n",
+               pm8001_dev->device_id));
+       tmf_task.tmf = TMF_CLEAR_TASK_SET;
+       rc = pm8001_issue_ssp_tmf(dev, lun, &tmf_task);
+       return rc;
+}
+
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
new file mode 100644 (file)
index 0000000..30f2ede
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * PMC-Sierra SPC 8001 SAS/SATA based host adapters driver
+ *
+ * Copyright (c) 2008-2009 USI Co., Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    substantially similar to the "NO WARRANTY" disclaimer below
+ *    ("Disclaimer") and any redistribution must be conditioned upon
+ *    including a substantially similar Disclaimer requirement for further
+ *    binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#ifndef _PM8001_SAS_H_
+#define _PM8001_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/interrupt.h>
+#include <linux/smp_lock.h>
+#include <scsi/libsas.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/sas_ata.h>
+#include <asm/atomic.h>
+#include "pm8001_defs.h"
+
+#define DRV_NAME               "pm8001"
+#define DRV_VERSION            "0.1.36"
+#define PM8001_FAIL_LOGGING    0x01 /* libsas EH function logging */
+#define PM8001_INIT_LOGGING    0x02 /* driver init logging */
+#define PM8001_DISC_LOGGING    0x04 /* discovery layer logging */
+#define PM8001_IO_LOGGING      0x08 /* I/O path logging */
+#define PM8001_EH_LOGGING      0x10 /* Error message logging */
+#define PM8001_IOCTL_LOGGING   0x20 /* IOCTL message logging */
+#define PM8001_MSG_LOGGING     0x40 /* misc message logging */
+#define pm8001_printk(format, arg...)  printk(KERN_INFO "%s %d:" format,\
+                               __func__, __LINE__, ## arg)
+#define PM8001_CHECK_LOGGING(HBA, LEVEL, CMD)  \
+do {                                           \
+       if (unlikely(HBA->logging_level & LEVEL))       \
+               do {                                    \
+                       CMD;                            \
+               } while (0);                            \
+} while (0);
+
+#define PM8001_EH_DBG(HBA, CMD)                        \
+       PM8001_CHECK_LOGGING(HBA, PM8001_EH_LOGGING, CMD)
+
+#define PM8001_INIT_DBG(HBA, CMD)              \
+       PM8001_CHECK_LOGGING(HBA, PM8001_INIT_LOGGING, CMD)
+
+#define PM8001_DISC_DBG(HBA, CMD)              \
+       PM8001_CHECK_LOGGING(HBA, PM8001_DISC_LOGGING, CMD)
+
+#define PM8001_IO_DBG(HBA, CMD)                \
+       PM8001_CHECK_LOGGING(HBA, PM8001_IO_LOGGING, CMD)
+
+#define PM8001_FAIL_DBG(HBA, CMD)              \
+       PM8001_CHECK_LOGGING(HBA, PM8001_FAIL_LOGGING, CMD)
+
+#define PM8001_IOCTL_DBG(HBA, CMD)             \
+       PM8001_CHECK_LOGGING(HBA, PM8001_IOCTL_LOGGING, CMD)
+
+#define PM8001_MSG_DBG(HBA, CMD)               \
+       PM8001_CHECK_LOGGING(HBA, PM8001_MSG_LOGGING, CMD)
+
+
+#define PM8001_USE_TASKLET
+#define PM8001_USE_MSIX
+
+
+#define DEV_IS_EXPANDER(type)  ((type == EDGE_DEV) || (type == FANOUT_DEV))
+
+#define PM8001_NAME_LENGTH             32/* generic length of strings */
+extern struct list_head hba_list;
+extern const struct pm8001_dispatch pm8001_8001_dispatch;
+
+struct pm8001_hba_info;
+struct pm8001_ccb_info;
+struct pm8001_device;
+struct pm8001_tmf_task;
+struct pm8001_dispatch {
+       char *name;
+       int (*chip_init)(struct pm8001_hba_info *pm8001_ha);
+       int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha, u32 signature);
+       void (*chip_rst)(struct pm8001_hba_info *pm8001_ha);
+       int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha);
+       void (*chip_iounmap)(struct pm8001_hba_info *pm8001_ha);
+       irqreturn_t (*isr)(struct pm8001_hba_info *pm8001_ha);
+       u32 (*is_our_interupt)(struct pm8001_hba_info *pm8001_ha);
+       int (*isr_process_oq)(struct pm8001_hba_info *pm8001_ha);
+       void (*interrupt_enable)(struct pm8001_hba_info *pm8001_ha);
+       void (*interrupt_disable)(struct pm8001_hba_info *pm8001_ha);
+       void (*make_prd)(struct scatterlist *scatter, int nr, void *prd);
+       int (*smp_req)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_ccb_info *ccb);
+       int (*ssp_io_req)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_ccb_info *ccb);
+       int (*sata_req)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_ccb_info *ccb);
+       int (*phy_start_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id);
+       int (*phy_stop_req)(struct pm8001_hba_info *pm8001_ha, u8 phy_id);
+       int (*reg_dev_req)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_device *pm8001_dev, u32 flag);
+       int (*dereg_dev_req)(struct pm8001_hba_info *pm8001_ha, u32 device_id);
+       int (*phy_ctl_req)(struct pm8001_hba_info *pm8001_ha,
+               u32 phy_id, u32 phy_op);
+       int (*task_abort)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_device *pm8001_dev, u8 flag, u32 task_tag,
+               u32 cmd_tag);
+       int (*ssp_tm_req)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_ccb_info *ccb, struct pm8001_tmf_task *tmf);
+       int (*get_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
+       int (*set_nvmd_req)(struct pm8001_hba_info *pm8001_ha, void *payload);
+       int (*fw_flash_update_req)(struct pm8001_hba_info *pm8001_ha,
+               void *payload);
+       int (*set_dev_state_req)(struct pm8001_hba_info *pm8001_ha,
+               struct pm8001_device *pm8001_dev, u32 state);
+       int (*sas_diag_start_end_req)(struct pm8001_hba_info *pm8001_ha,
+               u32 state);
+       int (*sas_diag_execute_req)(struct pm8001_hba_info *pm8001_ha,
+               u32 state);
+       int (*sas_re_init_req)(struct pm8001_hba_info *pm8001_ha);
+};
+
+struct pm8001_chip_info {
+       u32     n_phy;
+       const struct pm8001_dispatch    *dispatch;
+};
+#define PM8001_CHIP_DISP       (pm8001_ha->chip->dispatch)
+
+struct pm8001_port {
+       struct asd_sas_port     sas_port;
+};
+
+struct pm8001_phy {
+       struct pm8001_hba_info  *pm8001_ha;
+       struct pm8001_port      *port;
+       struct asd_sas_phy      sas_phy;
+       struct sas_identify     identify;
+       struct scsi_device      *sdev;
+       u64                     dev_sas_addr;
+       u32                     phy_type;
+       struct completion       *enable_completion;
+       u32                     frame_rcvd_size;
+       u8                      frame_rcvd[32];
+       u8                      phy_attached;
+       u8                      phy_state;
+       enum sas_linkrate       minimum_linkrate;
+       enum sas_linkrate       maximum_linkrate;
+};
+
+struct pm8001_device {
+       enum sas_dev_type       dev_type;
+       struct domain_device    *sas_device;
+       u32                     attached_phy;
+       u32                     id;
+       struct completion       *dcompletion;
+       struct completion       *setds_completion;
+       u32                     device_id;
+       u32                     running_req;
+};
+
+struct pm8001_prd_imt {
+       __le32                  len;
+       __le32                  e;
+};
+
+struct pm8001_prd {
+       __le64                  addr;           /* 64-bit buffer address */
+       struct pm8001_prd_imt   im_len;         /* 64-bit length */
+} __attribute__ ((packed));
+/*
+ * CCB(Command Control Block)
+ */
+struct pm8001_ccb_info {
+       struct list_head        entry;
+       struct sas_task         *task;
+       u32                     n_elem;
+       u32                     ccb_tag;
+       dma_addr_t              ccb_dma_handle;
+       struct pm8001_device    *device;
+       struct pm8001_prd       buf_prd[PM8001_MAX_DMA_SG];
+       struct fw_control_ex    *fw_control_context;
+};
+
+struct mpi_mem {
+       void                    *virt_ptr;
+       dma_addr_t              phys_addr;
+       u32                     phys_addr_hi;
+       u32                     phys_addr_lo;
+       u32                     total_len;
+       u32                     num_elements;
+       u32                     element_size;
+       u32                     alignment;
+};
+
+struct mpi_mem_req {
+       /* The number of element in the  mpiMemory array */
+       u32                     count;
+       /* The array of structures that define memroy regions*/
+       struct mpi_mem          region[USI_MAX_MEMCNT];
+};
+
+struct main_cfg_table {
+       u32                     signature;
+       u32                     interface_rev;
+       u32                     firmware_rev;
+       u32                     max_out_io;
+       u32                     max_sgl;
+       u32                     ctrl_cap_flag;
+       u32                     gst_offset;
+       u32                     inbound_queue_offset;
+       u32                     outbound_queue_offset;
+       u32                     inbound_q_nppd_hppd;
+       u32                     outbound_hw_event_pid0_3;
+       u32                     outbound_hw_event_pid4_7;
+       u32                     outbound_ncq_event_pid0_3;
+       u32                     outbound_ncq_event_pid4_7;
+       u32                     outbound_tgt_ITNexus_event_pid0_3;
+       u32                     outbound_tgt_ITNexus_event_pid4_7;
+       u32                     outbound_tgt_ssp_event_pid0_3;
+       u32                     outbound_tgt_ssp_event_pid4_7;
+       u32                     outbound_tgt_smp_event_pid0_3;
+       u32                     outbound_tgt_smp_event_pid4_7;
+       u32                     upper_event_log_addr;
+       u32                     lower_event_log_addr;
+       u32                     event_log_size;
+       u32                     event_log_option;
+       u32                     upper_iop_event_log_addr;
+       u32                     lower_iop_event_log_addr;
+       u32                     iop_event_log_size;
+       u32                     iop_event_log_option;
+       u32                     fatal_err_interrupt;
+       u32                     fatal_err_dump_offset0;
+       u32                     fatal_err_dump_length0;
+       u32                     fatal_err_dump_offset1;
+       u32                     fatal_err_dump_length1;
+       u32                     hda_mode_flag;
+       u32                     anolog_setup_table_offset;
+};
+struct general_status_table {
+       u32                     gst_len_mpistate;
+       u32                     iq_freeze_state0;
+       u32                     iq_freeze_state1;
+       u32                     msgu_tcnt;
+       u32                     iop_tcnt;
+       u32                     reserved;
+       u32                     phy_state[8];
+       u32                     reserved1;
+       u32                     reserved2;
+       u32                     reserved3;
+       u32                     recover_err_info[8];
+};
+struct inbound_queue_table {
+       u32                     element_pri_size_cnt;
+       u32                     upper_base_addr;
+       u32                     lower_base_addr;
+       u32                     ci_upper_base_addr;
+       u32                     ci_lower_base_addr;
+       u32                     pi_pci_bar;
+       u32                     pi_offset;
+       u32                     total_length;
+       void                    *base_virt;
+       void                    *ci_virt;
+       u32                     reserved;
+       __le32                  consumer_index;
+       u32                     producer_idx;
+};
+struct outbound_queue_table {
+       u32                     element_size_cnt;
+       u32                     upper_base_addr;
+       u32                     lower_base_addr;
+       void                    *base_virt;
+       u32                     pi_upper_base_addr;
+       u32                     pi_lower_base_addr;
+       u32                     ci_pci_bar;
+       u32                     ci_offset;
+       u32                     total_length;
+       void                    *pi_virt;
+       u32                     interrup_vec_cnt_delay;
+       u32                     dinterrup_to_pci_offset;
+       __le32                  producer_index;
+       u32                     consumer_idx;
+};
+struct pm8001_hba_memspace {
+       void __iomem            *memvirtaddr;
+       u64                     membase;
+       u32                     memsize;
+};
+struct pm8001_hba_info {
+       char                    name[PM8001_NAME_LENGTH];
+       struct list_head        list;
+       unsigned long           flags;
+       spinlock_t              lock;/* host-wide lock */
+       struct pci_dev          *pdev;/* our device */
+       struct device           *dev;
+       struct pm8001_hba_memspace io_mem[6];
+       struct mpi_mem_req      memoryMap;
+       void __iomem    *msg_unit_tbl_addr;/*Message Unit Table Addr*/
+       void __iomem    *main_cfg_tbl_addr;/*Main Config Table Addr*/
+       void __iomem    *general_stat_tbl_addr;/*General Status Table Addr*/
+       void __iomem    *inbnd_q_tbl_addr;/*Inbound Queue Config Table Addr*/
+       void __iomem    *outbnd_q_tbl_addr;/*Outbound Queue Config Table Addr*/
+       struct main_cfg_table   main_cfg_tbl;
+       struct general_status_table     gs_tbl;
+       struct inbound_queue_table      inbnd_q_tbl[PM8001_MAX_INB_NUM];
+       struct outbound_queue_table     outbnd_q_tbl[PM8001_MAX_OUTB_NUM];
+       u8                      sas_addr[SAS_ADDR_SIZE];
+       struct sas_ha_struct    *sas;/* SCSI/SAS glue */
+       struct Scsi_Host        *shost;
+       u32                     chip_id;
+       const struct pm8001_chip_info   *chip;
+       struct completion       *nvmd_completion;
+       int                     tags_num;
+       unsigned long           *tags;
+       struct pm8001_phy       phy[PM8001_MAX_PHYS];
+       struct pm8001_port      port[PM8001_MAX_PHYS];
+       u32                     id;
+       u32                     irq;
+       struct pm8001_device    *devices;
+       struct pm8001_ccb_info  *ccb_info;
+#ifdef PM8001_USE_MSIX
+       struct msix_entry       msix_entries[16];/*for msi-x interrupt*/
+       int                     number_of_intr;/*will be used in remove()*/
+#endif
+#ifdef PM8001_USE_TASKLET
+       struct tasklet_struct   tasklet;
+#endif
+       struct list_head        wq_list;
+       u32                     logging_level;
+       u32                     fw_status;
+       const struct firmware   *fw_image;
+};
+
+struct pm8001_wq {
+       struct delayed_work work_q;
+       struct pm8001_hba_info *pm8001_ha;
+       void *data;
+       int handler;
+       struct list_head entry;
+};
+
+struct pm8001_fw_image_header {
+       u8 vender_id[8];
+       u8 product_id;
+       u8 hardware_rev;
+       u8 dest_partition;
+       u8 reserved;
+       u8 fw_rev[4];
+       __be32  image_length;
+       __be32 image_crc;
+       __be32 startup_entry;
+} __attribute__((packed, aligned(4)));
+
+/* define task management IU */
+struct pm8001_tmf_task {
+       u8      tmf;
+       u32     tag_of_task_to_be_managed;
+};
+/**
+ * FW Flash Update status values
+ */
+#define FLASH_UPDATE_COMPLETE_PENDING_REBOOT   0x00
+#define FLASH_UPDATE_IN_PROGRESS               0x01
+#define FLASH_UPDATE_HDR_ERR                   0x02
+#define FLASH_UPDATE_OFFSET_ERR                        0x03
+#define FLASH_UPDATE_CRC_ERR                   0x04
+#define FLASH_UPDATE_LENGTH_ERR                        0x05
+#define FLASH_UPDATE_HW_ERR                    0x06
+#define FLASH_UPDATE_DNLD_NOT_SUPPORTED                0x10
+#define FLASH_UPDATE_DISABLED                  0x11
+
+/**
+ * brief param structure for firmware flash update.
+ */
+struct fw_flash_updata_info {
+       u32                     cur_image_offset;
+       u32                     cur_image_len;
+       u32                     total_image_len;
+       struct pm8001_prd       sgl;
+};
+
+struct fw_control_info {
+       u32                     retcode;/*ret code (status)*/
+       u32                     phase;/*ret code phase*/
+       u32                     phaseCmplt;/*percent complete for the current
+       update phase */
+       u32                     version;/*Hex encoded firmware version number*/
+       u32                     offset;/*Used for downloading firmware  */
+       u32                     len; /*len of buffer*/
+       u32                     size;/* Used in OS VPD and Trace get size
+       operations.*/
+       u32                     reserved;/* padding required for 64 bit
+       alignment */
+       u8                      buffer[1];/* Start of buffer */
+};
+struct fw_control_ex {
+       struct fw_control_info *fw_control;
+       void                    *buffer;/* keep buffer pointer to be
+       freed when the responce comes*/
+       void                    *virtAddr;/* keep virtual address of the data */
+       void                    *usrAddr;/* keep virtual address of the
+       user data */
+       dma_addr_t              phys_addr;
+       u32                     len; /* len of buffer  */
+       void                    *payload; /* pointer to IOCTL Payload */
+       u8                      inProgress;/*if 1 - the IOCTL request is in
+       progress */
+       void                    *param1;
+       void                    *param2;
+       void                    *param3;
+};
+
+/******************** function prototype *********************/
+int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out);
+void pm8001_tag_init(struct pm8001_hba_info *pm8001_ha);
+u32 pm8001_get_ncq_tag(struct sas_task *task, u32 *tag);
+void pm8001_ccb_free(struct pm8001_hba_info *pm8001_ha, u32 ccb_idx);
+void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
+       struct sas_task *task, struct pm8001_ccb_info *ccb, u32 ccb_idx);
+int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
+       void *funcdata);
+int pm8001_slave_alloc(struct scsi_device *scsi_dev);
+int pm8001_slave_configure(struct scsi_device *sdev);
+void pm8001_scan_start(struct Scsi_Host *shost);
+int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time);
+int pm8001_queue_command(struct sas_task *task, const int num,
+       gfp_t gfp_flags);
+int pm8001_abort_task(struct sas_task *task);
+int pm8001_abort_task_set(struct domain_device *dev, u8 *lun);
+int pm8001_clear_aca(struct domain_device *dev, u8 *lun);
+int pm8001_clear_task_set(struct domain_device *dev, u8 *lun);
+int pm8001_dev_found(struct domain_device *dev);
+void pm8001_dev_gone(struct domain_device *dev);
+int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
+int pm8001_I_T_nexus_reset(struct domain_device *dev);
+int pm8001_query_task(struct sas_task *task);
+int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
+       dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo,
+       u32 mem_size, u32 align);
+
+
+/* ctl shared API */
+extern struct device_attribute *pm8001_host_attrs[];
+
+#endif
+
index 0a97bc9..34c6b89 100644 (file)
@@ -278,12 +278,17 @@ static void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
  * pmcraid_change_queue_depth - Change the device's queue depth
  * @scsi_dev: scsi device struct
  * @depth: depth to set
+ * @reason: calling context
  *
  * Return value
  *     actual depth set
  */
-static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
+static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth,
+                                     int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        if (depth > PMCRAID_MAX_CMD_PER_LUN)
                depth = PMCRAID_MAX_CMD_PER_LUN;
 
@@ -3342,7 +3347,7 @@ static int pmcraid_chr_fasync(int fd, struct file *filep, int mode)
  * @direction : data transfer direction
  *
  * Return value
- *  0 on sucess, non-zero error code on failure
+ *  0 on success, non-zero error code on failure
  */
 static int pmcraid_build_passthrough_ioadls(
        struct pmcraid_cmd *cmd,
@@ -3401,7 +3406,7 @@ static int pmcraid_build_passthrough_ioadls(
  * @direction: data transfer direction
  *
  * Return value
- *  0 on sucess, non-zero error code on failure
+ *  0 on success, non-zero error code on failure
  */
 static void pmcraid_release_passthrough_ioadls(
        struct pmcraid_cmd *cmd,
@@ -3429,7 +3434,7 @@ static void pmcraid_release_passthrough_ioadls(
  * @arg: pointer to pmcraid_passthrough_buffer user buffer
  *
  * Return value
- *  0 on sucess, non-zero error code on failure
+ *  0 on success, non-zero error code on failure
  */
 static long pmcraid_ioctl_passthrough(
        struct pmcraid_instance *pinstance,
index 3441b3f..2752b56 100644 (file)
@@ -771,11 +771,11 @@ static struct pmcraid_ioasc_error pmcraid_ioasc_error_table[] = {
        {0x01180600, IOASC_LOG_LEVEL_MUST,
         "Recovered Error, soft media error, sector reassignment suggested"},
        {0x015D0000, IOASC_LOG_LEVEL_MUST,
-        "Recovered Error, failure prediction thresold exceeded"},
+        "Recovered Error, failure prediction threshold exceeded"},
        {0x015D9200, IOASC_LOG_LEVEL_MUST,
-        "Recovered Error, soft Cache Card Battery error thresold"},
+        "Recovered Error, soft Cache Card Battery error threshold"},
        {0x015D9200, IOASC_LOG_LEVEL_MUST,
-        "Recovered Error, soft Cache Card Battery error thresold"},
+        "Recovered Error, soft Cache Card Battery error threshold"},
        {0x02048000, IOASC_LOG_LEVEL_MUST,
         "Not Ready, IOA Reset Required"},
        {0x02408500, IOASC_LOG_LEVEL_MUST,
index fbcb82a..21e2bc4 100644 (file)
@@ -1654,7 +1654,8 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
                        fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
        }
 
-       if (scsi_add_host(vha->host, &fc_vport->dev)) {
+       if (scsi_add_host_with_dma(vha->host, &fc_vport->dev,
+                                  &ha->pdev->dev)) {
                DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n",
                        vha->host_no, vha->vp_idx));
                goto vport_create_failed_2;
index cca8e4a..cb2eca4 100644 (file)
@@ -377,6 +377,24 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
        return ptr + sizeof(struct qla2xxx_mq_chain);
 }
 
+static void
+qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval)
+{
+       struct qla_hw_data *ha = vha->hw;
+
+       if (rval != QLA_SUCCESS) {
+               qla_printk(KERN_WARNING, ha,
+                   "Failed to dump firmware (%x)!!!\n", rval);
+               ha->fw_dumped = 0;
+       } else {
+               qla_printk(KERN_INFO, ha,
+                   "Firmware dump saved to temp buffer (%ld/%p).\n",
+                   vha->host_no, ha->fw_dump);
+               ha->fw_dumped = 1;
+               qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
+       }
+}
+
 /**
  * qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
  * @ha: HA context
@@ -530,17 +548,7 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        if (rval == QLA_SUCCESS)
                qla2xxx_copy_queues(ha, nxt);
 
-       if (rval != QLA_SUCCESS) {
-               qla_printk(KERN_WARNING, ha,
-                   "Failed to dump firmware (%x)!!!\n", rval);
-               ha->fw_dumped = 0;
-
-       } else {
-               qla_printk(KERN_INFO, ha,
-                   "Firmware dump saved to temp buffer (%ld/%p).\n",
-                   base_vha->host_no, ha->fw_dump);
-               ha->fw_dumped = 1;
-       }
+       qla2xxx_dump_post_process(base_vha, rval);
 
 qla2300_fw_dump_failed:
        if (!hardware_locked)
@@ -737,17 +745,7 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        if (rval == QLA_SUCCESS)
                qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]);
 
-       if (rval != QLA_SUCCESS) {
-               qla_printk(KERN_WARNING, ha,
-                   "Failed to dump firmware (%x)!!!\n", rval);
-               ha->fw_dumped = 0;
-
-       } else {
-               qla_printk(KERN_INFO, ha,
-                   "Firmware dump saved to temp buffer (%ld/%p).\n",
-                   base_vha->host_no, ha->fw_dump);
-               ha->fw_dumped = 1;
-       }
+       qla2xxx_dump_post_process(base_vha, rval);
 
 qla2100_fw_dump_failed:
        if (!hardware_locked)
@@ -984,17 +982,7 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        qla24xx_copy_eft(ha, nxt);
 
 qla24xx_fw_dump_failed_0:
-       if (rval != QLA_SUCCESS) {
-               qla_printk(KERN_WARNING, ha,
-                   "Failed to dump firmware (%x)!!!\n", rval);
-               ha->fw_dumped = 0;
-
-       } else {
-               qla_printk(KERN_INFO, ha,
-                   "Firmware dump saved to temp buffer (%ld/%p).\n",
-                   base_vha->host_no, ha->fw_dump);
-               ha->fw_dumped = 1;
-       }
+       qla2xxx_dump_post_process(base_vha, rval);
 
 qla24xx_fw_dump_failed:
        if (!hardware_locked)
@@ -1305,17 +1293,7 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        }
 
 qla25xx_fw_dump_failed_0:
-       if (rval != QLA_SUCCESS) {
-               qla_printk(KERN_WARNING, ha,
-                   "Failed to dump firmware (%x)!!!\n", rval);
-               ha->fw_dumped = 0;
-
-       } else {
-               qla_printk(KERN_INFO, ha,
-                   "Firmware dump saved to temp buffer (%ld/%p).\n",
-                   base_vha->host_no, ha->fw_dump);
-               ha->fw_dumped = 1;
-       }
+       qla2xxx_dump_post_process(base_vha, rval);
 
 qla25xx_fw_dump_failed:
        if (!hardware_locked)
@@ -1628,17 +1606,7 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
        }
 
 qla81xx_fw_dump_failed_0:
-       if (rval != QLA_SUCCESS) {
-               qla_printk(KERN_WARNING, ha,
-                   "Failed to dump firmware (%x)!!!\n", rval);
-               ha->fw_dumped = 0;
-
-       } else {
-               qla_printk(KERN_INFO, ha,
-                   "Firmware dump saved to temp buffer (%ld/%p).\n",
-                   base_vha->host_no, ha->fw_dump);
-               ha->fw_dumped = 1;
-       }
+       qla2xxx_dump_post_process(base_vha, rval);
 
 qla81xx_fw_dump_failed:
        if (!hardware_locked)
index 2150618..6b9bf23 100644 (file)
@@ -2123,6 +2123,7 @@ enum qla_work_type {
        QLA_EVT_ASYNC_LOGIN_DONE,
        QLA_EVT_ASYNC_LOGOUT,
        QLA_EVT_ASYNC_LOGOUT_DONE,
+       QLA_EVT_UEVENT,
 };
 
 
@@ -2146,6 +2147,10 @@ struct qla_work_evt {
 #define QLA_LOGIO_LOGIN_RETRIED        BIT_0
                        u16 data[2];
                } logio;
+               struct {
+                       u32 code;
+#define QLA_UEVENT_CODE_FW_DUMP        0
+               } uevent;
        } u;
 };
 
@@ -2435,11 +2440,11 @@ struct qla_hw_data {
        dma_addr_t      edc_data_dma;
        uint16_t        edc_data_len;
 
-#define XGMAC_DATA_SIZE        PAGE_SIZE
+#define XGMAC_DATA_SIZE        4096
        void            *xgmac_data;
        dma_addr_t      xgmac_data_dma;
 
-#define DCBX_TLV_DATA_SIZE PAGE_SIZE
+#define DCBX_TLV_DATA_SIZE 4096
        void            *dcbx_tlv;
        dma_addr_t      dcbx_tlv_dma;
 
index f3d1d1a..e218513 100644 (file)
@@ -92,6 +92,7 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
     uint16_t *);
 extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
     fc_port_t *, uint16_t *);
+extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
 
 extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
 
@@ -246,7 +247,7 @@ qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
 
 extern int
 qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *,
-    uint16_t *, uint16_t *, uint16_t *);
+    uint16_t *, uint16_t *, uint16_t *, uint16_t *);
 
 extern int
 qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
index 9e3eaac..b74924b 100644 (file)
@@ -277,7 +277,6 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha)
        vha->marker_needed = 0;
        ha->isp_abort_cnt = 0;
        ha->beacon_blink_led = 0;
-       set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
 
        set_bit(0, ha->req_qid_map);
        set_bit(0, ha->rsp_qid_map);
@@ -1203,7 +1202,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
                                }
                                qla2x00_get_resource_cnts(vha, NULL,
                                    &ha->fw_xcb_count, NULL, NULL,
-                                   &ha->max_npiv_vports);
+                                   &ha->max_npiv_vports, NULL);
 
                                if (!fw_major_version && ql2xallocfwdump)
                                        qla2x00_alloc_fw_dump(vha);
@@ -3573,6 +3572,15 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
                        ha->isp_abort_cnt = 0;
                        clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
 
+                       if (IS_QLA81XX(ha))
+                               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 (ha->fce) {
                                ha->flags.fce_enabled = 1;
                                memset(ha->fce, 0,
index b20a716..8049873 100644 (file)
@@ -313,10 +313,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
        static char     *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
        char            *link_speed;
        uint16_t        handle_cnt;
-       uint16_t        cnt;
+       uint16_t        cnt, mbx;
        uint32_t        handles[5];
        struct qla_hw_data *ha = vha->hw;
        struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+       struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
        uint32_t        rscn_entry, host_pid;
        uint8_t         rscn_queue_index;
        unsigned long   flags;
@@ -395,9 +396,10 @@ skip_rio:
                break;
 
        case MBA_SYSTEM_ERR:            /* System Error */
+               mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox7) : 0;
                qla_printk(KERN_INFO, ha,
-                   "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh.\n",
-                   mb[1], mb[2], mb[3]);
+                   "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
+                   "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
 
                ha->isp_ops->fw_dump(vha, 1);
 
@@ -419,9 +421,10 @@ skip_rio:
                break;
 
        case MBA_REQ_TRANSFER_ERR:      /* Request Transfer Error */
-               DEBUG2(printk("scsi(%ld): ISP Request Transfer Error.\n",
-                   vha->host_no));
-               qla_printk(KERN_WARNING, ha, "ISP Request Transfer Error.\n");
+               DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n",
+                   vha->host_no, mb[1]));
+               qla_printk(KERN_WARNING, ha,
+                   "ISP Request Transfer Error (%x).\n", mb[1]);
 
                set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
                break;
@@ -485,10 +488,13 @@ skip_rio:
                break;
 
        case MBA_LOOP_DOWN:             /* Loop Down Event */
+               mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox4) : 0;
                DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN "
-                   "(%x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3]));
-               qla_printk(KERN_INFO, ha, "LOOP DOWN detected (%x %x %x).\n",
-                   mb[1], mb[2], mb[3]);
+                   "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3],
+                   mbx));
+               qla_printk(KERN_INFO, ha,
+                   "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3],
+                   mbx);
 
                if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
                        atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -1347,16 +1353,22 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
 
        sense_len = rsp_info_len = resid_len = fw_resid_len = 0;
        if (IS_FWI2_CAPABLE(ha)) {
-               sense_len = le32_to_cpu(sts24->sense_len);
-               rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
-               resid_len = le32_to_cpu(sts24->rsp_residual_count);
-               fw_resid_len = le32_to_cpu(sts24->residual_len);
+               if (scsi_status & SS_SENSE_LEN_VALID)
+                       sense_len = le32_to_cpu(sts24->sense_len);
+               if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+                       rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+               if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
+                       resid_len = le32_to_cpu(sts24->rsp_residual_count);
+               if (comp_status == CS_DATA_UNDERRUN)
+                       fw_resid_len = le32_to_cpu(sts24->residual_len);
                rsp_info = sts24->data;
                sense_data = sts24->data;
                host_to_fcp_swap(sts24->data, sizeof(sts24->data));
        } else {
-               sense_len = le16_to_cpu(sts->req_sense_length);
-               rsp_info_len = le16_to_cpu(sts->rsp_info_len);
+               if (scsi_status & SS_SENSE_LEN_VALID)
+                       sense_len = le16_to_cpu(sts->req_sense_length);
+               if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+                       rsp_info_len = le16_to_cpu(sts->rsp_info_len);
                resid_len = le32_to_cpu(sts->residual_length);
                rsp_info = sts->rsp_info;
                sense_data = sts->req_sense_data;
@@ -1443,38 +1455,62 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                break;
 
        case CS_DATA_UNDERRUN:
-               resid = resid_len;
+               DEBUG2(printk(KERN_INFO
+                   "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x. "
+                   "resid=0x%x fw_resid=0x%x cdb=0x%x os_underflow=0x%x\n",
+                   vha->host_no, cp->device->id, cp->device->lun, comp_status,
+                   scsi_status, resid_len, fw_resid_len, cp->cmnd[0],
+                   cp->underflow));
+
                /* Use F/W calculated residual length. */
-               if (IS_FWI2_CAPABLE(ha)) {
-                       if (!(scsi_status & SS_RESIDUAL_UNDER)) {
-                               lscsi_status = 0;
-                       } else if (resid != fw_resid_len) {
-                               scsi_status &= ~SS_RESIDUAL_UNDER;
-                               lscsi_status = 0;
+               resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len;
+               scsi_set_resid(cp, resid);
+               if (scsi_status & SS_RESIDUAL_UNDER) {
+                       if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
+                               DEBUG2(printk(
+                                   "scsi(%ld:%d:%d:%d) Dropped frame(s) "
+                                   "detected (%x of %x bytes)...residual "
+                                   "length mismatch...retrying command.\n",
+                                   vha->host_no, cp->device->channel,
+                                   cp->device->id, cp->device->lun, resid,
+                                   scsi_bufflen(cp)));
+
+                               cp->result = DID_ERROR << 16 | lscsi_status;
+                               break;
                        }
-                       resid = fw_resid_len;
-               }
 
-               if (scsi_status & SS_RESIDUAL_UNDER) {
-                       scsi_set_resid(cp, resid);
-               } else {
-                       DEBUG2(printk(KERN_INFO
-                           "scsi(%ld:%d:%d) UNDERRUN status detected "
-                           "0x%x-0x%x. resid=0x%x fw_resid=0x%x cdb=0x%x "
-                           "os_underflow=0x%x\n", vha->host_no,
-                           cp->device->id, cp->device->lun, comp_status,
-                           scsi_status, resid_len, resid, cp->cmnd[0],
-                           cp->underflow));
+                       if (!lscsi_status &&
+                           ((unsigned)(scsi_bufflen(cp) - resid) <
+                           cp->underflow)) {
+                               qla_printk(KERN_INFO, ha,
+                                   "scsi(%ld:%d:%d:%d): Mid-layer underflow "
+                                   "detected (%x of %x bytes)...returning "
+                                   "error status.\n", vha->host_no,
+                                   cp->device->channel, cp->device->id,
+                                   cp->device->lun, resid, scsi_bufflen(cp));
+
+                               cp->result = DID_ERROR << 16;
+                               break;
+                       }
+               } else if (!lscsi_status) {
+                       DEBUG2(printk(
+                           "scsi(%ld:%d:%d:%d) Dropped frame(s) detected "
+                           "(%x of %x bytes)...firmware reported underrun..."
+                           "retrying command.\n", vha->host_no,
+                           cp->device->channel, cp->device->id,
+                           cp->device->lun, resid, scsi_bufflen(cp)));
 
+                       cp->result = DID_ERROR << 16;
+                       break;
                }
 
+               cp->result = DID_OK << 16 | lscsi_status;
+
                /*
                 * Check to see if SCSI Status is non zero. If so report SCSI
                 * Status.
                 */
                if (lscsi_status != 0) {
-                       cp->result = DID_OK << 16 | lscsi_status;
-
                        if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
                                DEBUG2(printk(KERN_INFO
                                    "scsi(%ld): QUEUE FULL status detected "
@@ -1501,42 +1537,6 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
                                break;
 
                        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 an error.
-                        */
-                       if (!(scsi_status & SS_RESIDUAL_UNDER)) {
-                               DEBUG2(printk("scsi(%ld:%d:%d:%d) Dropped "
-                                             "frame(s) detected (%x of %x bytes)..."
-                                             "retrying command.\n",
-                                       vha->host_no, cp->device->channel,
-                                       cp->device->id, cp->device->lun, resid,
-                                       scsi_bufflen(cp)));
-
-                               scsi_set_resid(cp, resid);
-                               cp->result = DID_ERROR << 16;
-                               break;
-                       }
-
-                       /* Handle mid-layer underflow */
-                       if ((unsigned)(scsi_bufflen(cp) - resid) <
-                           cp->underflow) {
-                               qla_printk(KERN_INFO, ha,
-                                          "scsi(%ld:%d:%d:%d): Mid-layer underflow "
-                                          "detected (%x of %x bytes)...returning "
-                                          "error status.\n", vha->host_no,
-                                          cp->device->channel, cp->device->id,
-                                          cp->device->lun, resid,
-                                          scsi_bufflen(cp));
-
-                               cp->result = DID_ERROR << 16;
-                               break;
-                       }
-
-                       /* Everybody online, looking good... */
-                       cp->result = DID_OK << 16;
                }
                break;
 
index b6202fe..05d595d 100644 (file)
@@ -2006,7 +2006,7 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
 int
 qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
     uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
-    uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports)
+    uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs)
 {
        int rval;
        mbx_cmd_t mc;
@@ -2017,6 +2017,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
        mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
        mcp->out_mb = MBX_0;
        mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+       if (IS_QLA81XX(vha->hw))
+               mcp->in_mb |= MBX_12;
        mcp->tov = MBX_TOV_SECONDS;
        mcp->flags = 0;
        rval = qla2x00_mailbox_command(vha, mcp);
@@ -2027,9 +2029,10 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
                    vha->host_no, mcp->mb[0]));
        } else {
                DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x "
-                   "mb7=%x mb10=%x mb11=%x.\n", __func__, vha->host_no,
-                   mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[6], mcp->mb[7],
-                   mcp->mb[10], mcp->mb[11]));
+                   "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__,
+                   vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3],
+                   mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11],
+                   mcp->mb[12]));
 
                if (cur_xchg_cnt)
                        *cur_xchg_cnt = mcp->mb[3];
@@ -2041,6 +2044,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
                        *orig_iocb_cnt = mcp->mb[10];
                if (vha->hw->flags.npiv_supported && max_npiv_vports)
                        *max_npiv_vports = mcp->mb[11];
+               if (IS_QLA81XX(vha->hw) && max_fcfs)
+                       *max_fcfs = mcp->mb[12];
        }
 
        return (rval);
@@ -2313,6 +2318,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
 {
        int             rval, rval2;
        struct tsk_mgmt_cmd *tsk;
+       struct sts_entry_24xx *sts;
        dma_addr_t      tsk_dma;
        scsi_qla_host_t *vha;
        struct qla_hw_data *ha;
@@ -2352,20 +2358,37 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
                    sizeof(tsk->p.tsk.lun));
        }
 
+       sts = &tsk->p.sts;
        rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
        if (rval != QLA_SUCCESS) {
                DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB "
                    "(%x).\n", __func__, vha->host_no, name, rval));
-       } else if (tsk->p.sts.entry_status != 0) {
+       } else if (sts->entry_status != 0) {
                DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
                    "-- error status (%x).\n", __func__, vha->host_no,
-                   tsk->p.sts.entry_status));
+                   sts->entry_status));
                rval = QLA_FUNCTION_FAILED;
-       } else if (tsk->p.sts.comp_status !=
+       } else if (sts->comp_status !=
            __constant_cpu_to_le16(CS_COMPLETE)) {
                DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
                    "-- completion status (%x).\n", __func__,
-                   vha->host_no, le16_to_cpu(tsk->p.sts.comp_status)));
+                   vha->host_no, le16_to_cpu(sts->comp_status)));
+               rval = QLA_FUNCTION_FAILED;
+       } else if (!(le16_to_cpu(sts->scsi_status) &
+           SS_RESPONSE_INFO_LEN_VALID)) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                   "-- no response info (%x).\n", __func__, vha->host_no,
+                   le16_to_cpu(sts->scsi_status)));
+               rval = QLA_FUNCTION_FAILED;
+       } else if (le32_to_cpu(sts->rsp_data_len) < 4) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                   "-- not enough response info (%d).\n", __func__,
+                   vha->host_no, le32_to_cpu(sts->rsp_data_len)));
+               rval = QLA_FUNCTION_FAILED;
+       } else if (sts->data[3]) {
+               DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB "
+                   "-- response (%x).\n", __func__,
+                   vha->host_no, sts->data[3]));
                rval = QLA_FUNCTION_FAILED;
        }
 
@@ -2759,8 +2782,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
                    vp_idx, MSB(stat),
                    rptid_entry->port_id[2], rptid_entry->port_id[1],
                    rptid_entry->port_id[0]));
-               if (vp_idx == 0)
-                       return;
+
+               vp = vha;
+               if (vp_idx == 0 && (MSB(stat) != 1))
+                       goto reg_needed;
 
                if (MSB(stat) == 1) {
                        DEBUG2(printk("scsi(%ld): Could not acquire ID for "
@@ -2783,8 +2808,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
                 * response queue. Handle it in dpc context.
                 */
                set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
-               set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
 
+reg_needed:
+               set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
+               set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+               set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
                qla2xxx_wake_dpc(vha);
        }
 }
index e07b361..a47d343 100644 (file)
@@ -382,8 +382,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
        vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
 
        vha->dpc_flags = 0L;
-       set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
-       set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
 
        /*
         * To fix the issue of processing a parent's RSCN for the vport before
index b79fca7..4166935 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/delay.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
+#include <linux/kobject.h>
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
@@ -137,7 +138,7 @@ static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
 
-static int qla2x00_change_queue_depth(struct scsi_device *, int);
+static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
 static int qla2x00_change_queue_type(struct scsi_device *, int);
 
 struct scsi_host_template qla2xxx_driver_template = {
@@ -727,23 +728,6 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport)
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 }
 
-static void
-qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
-{
-       struct Scsi_Host *shost = cmnd->device->host;
-       struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
-       unsigned long flags;
-
-       spin_lock_irqsave(shost->host_lock, flags);
-       while (rport->port_state == FC_PORTSTATE_BLOCKED) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               msleep(1000);
-               spin_lock_irqsave(shost->host_lock, flags);
-       }
-       spin_unlock_irqrestore(shost->host_lock, flags);
-       return;
-}
-
 /**************************************************************************
 * qla2xxx_eh_abort
 *
@@ -773,7 +757,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        struct req_que *req = vha->req;
        srb_t *spt;
 
-       qla2x00_block_error_handler(cmd);
+       fc_block_scsi_eh(cmd);
 
        if (!CMD_SP(cmd))
                return SUCCESS;
@@ -904,7 +888,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
        int err;
 
-       qla2x00_block_error_handler(cmd);
+       fc_block_scsi_eh(cmd);
 
        if (!fcport)
                return FAILED;
@@ -984,7 +968,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
        unsigned long serial;
        srb_t *sp = (srb_t *) CMD_SP(cmd);
 
-       qla2x00_block_error_handler(cmd);
+       fc_block_scsi_eh(cmd);
 
        id = cmd->device->id;
        lun = cmd->device->lun;
@@ -1047,7 +1031,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
        srb_t *sp = (srb_t *) CMD_SP(cmd);
        scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
 
-       qla2x00_block_error_handler(cmd);
+       fc_block_scsi_eh(cmd);
 
        id = cmd->device->id;
        lun = cmd->device->lun;
@@ -1234,8 +1218,11 @@ qla2xxx_slave_destroy(struct scsi_device *sdev)
 }
 
 static int
-qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth)
+qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
 {
+       if (reason != SCSI_QDEPTH_DEFAULT)
+               return -EOPNOTSUPP;
+
        scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
        return sdev->queue_depth;
 }
@@ -2653,6 +2640,37 @@ qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
 qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
 qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
 
+int
+qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code)
+{
+       struct qla_work_evt *e;
+
+       e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.uevent.code = code;
+       return qla2x00_post_work(vha, e);
+}
+
+static void
+qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code)
+{
+       char event_string[40];
+       char *envp[] = { event_string, NULL };
+
+       switch (code) {
+       case QLA_UEVENT_CODE_FW_DUMP:
+               snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+                   vha->host_no);
+               break;
+       default:
+               /* do nothing */
+               break;
+       }
+       kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
 void
 qla2x00_do_work(struct scsi_qla_host *vha)
 {
@@ -2690,6 +2708,9 @@ qla2x00_do_work(struct scsi_qla_host *vha)
                        qla2x00_async_logout_done(vha, e->u.logio.fcport,
                            e->u.logio.data);
                        break;
+               case QLA_EVT_UEVENT:
+                       qla2x00_uevent_emit(vha, e->u.uevent.code);
+                       break;
                }
                if (e->flags & QLA_EVT_FLAG_FREE)
                        kfree(e);
index ac107a2..807e0db 100644 (file)
@@ -7,7 +7,7 @@
 /*
  * Driver version
  */
-#define QLA2XXX_VERSION      "8.03.01-k6"
+#define QLA2XXX_VERSION      "8.03.01-k7"
 
 #define QLA_DRIVER_MAJOR_VER   8
 #define QLA_DRIVER_MINOR_VER   3
index 9c053bb..e3c74d1 100644 (file)
@@ -43,7 +43,7 @@
  * determined for each queue request anew.
  */
 #define QLOGICPTI_REQ_QUEUE_LEN        255     /* must be power of two - 1 */
-#define QLOGICPTI_MAX_SG(ql)   (4 + ((ql) > 0) ? 7*((ql) - 1) : 0)
+#define QLOGICPTI_MAX_SG(ql)   (4 + (((ql) > 0) ? 7*((ql) - 1) : 0))
 
 /* mailbox command complete status codes */
 #define MBOX_COMMAND_COMPLETE          0x4000
index dd098ca..a60da55 100644 (file)
@@ -940,10 +940,16 @@ EXPORT_SYMBOL(scsi_adjust_queue_depth);
  */
 int scsi_track_queue_full(struct scsi_device *sdev, int depth)
 {
-       if ((jiffies >> 4) == sdev->last_queue_full_time)
+
+       /*
+        * Don't let QUEUE_FULLs on the same
+        * jiffies count, they could all be from
+        * same event.
+        */
+       if ((jiffies >> 4) == (sdev->last_queue_full_time >> 4))
                return 0;
 
-       sdev->last_queue_full_time = (jiffies >> 4);
+       sdev->last_queue_full_time = jiffies;
        if (sdev->last_queue_full_depth != depth) {
                sdev->last_queue_full_count = 1;
                sdev->last_queue_full_depth = depth;
index c4103be..0b575c8 100644 (file)
@@ -44,6 +44,8 @@
 
 #include <net/checksum.h>
 
+#include <asm/unaligned.h>
+
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
@@ -105,6 +107,10 @@ static const char * scsi_debug_version_date = "20070104";
 #define DEF_ATO 1
 #define DEF_PHYSBLK_EXP 0
 #define DEF_LOWEST_ALIGNED 0
+#define DEF_UNMAP_MAX_BLOCKS 0
+#define DEF_UNMAP_MAX_DESC 0
+#define DEF_UNMAP_GRANULARITY 0
+#define DEF_UNMAP_ALIGNMENT 0
 
 /* bit mask values for scsi_debug_opts */
 #define SCSI_DEBUG_OPT_NOISE   1
@@ -162,6 +168,10 @@ static int scsi_debug_guard = DEF_GUARD;
 static int scsi_debug_ato = DEF_ATO;
 static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP;
 static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED;
+static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC;
+static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
+static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
+static int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
 
 static int scsi_debug_cmnd_count = 0;
 
@@ -223,7 +233,9 @@ static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
 
 static unsigned char * fake_storep;    /* ramdisk storage */
 static unsigned char *dif_storep;      /* protection info */
+static void *map_storep;               /* provisioning map */
 
+static unsigned long map_size;
 static int num_aborts = 0;
 static int num_dev_resets = 0;
 static int num_bus_resets = 0;
@@ -317,6 +329,7 @@ static void get_data_transfer_info(unsigned char *cmd,
                        (u32)cmd[28] << 24;
                break;
 
+       case WRITE_SAME_16:
        case WRITE_16:
        case READ_16:
                *lba = (u64)cmd[9] | (u64)cmd[8] << 8 |
@@ -335,6 +348,7 @@ static void get_data_transfer_info(unsigned char *cmd,
                *num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 |
                        (u32)cmd[6] << 24;
                break;
+       case WRITE_SAME:
        case WRITE_10:
        case READ_10:
        case XDWRITEREAD_10:
@@ -671,10 +685,12 @@ static int inquiry_evpd_89(unsigned char * arr)
 }
 
 
+/* Block limits VPD page (SBC-3) */
 static unsigned char vpdb0_data[] = {
-       /* from 4th byte */ 0,0,0,4,
-       0,0,0x4,0,
-       0,0,0,64,
+       /* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 };
 
 static int inquiry_evpd_b0(unsigned char * arr)
@@ -691,14 +707,40 @@ static int inquiry_evpd_b0(unsigned char * arr)
                arr[6] = (sdebug_store_sectors >> 8) & 0xff;
                arr[7] = sdebug_store_sectors & 0xff;
        }
+
+       if (scsi_debug_unmap_max_desc) {
+               unsigned int blocks;
+
+               if (scsi_debug_unmap_max_blocks)
+                       blocks = scsi_debug_unmap_max_blocks;
+               else
+                       blocks = 0xffffffff;
+
+               put_unaligned_be32(blocks, &arr[16]);
+               put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]);
+       }
+
+       if (scsi_debug_unmap_alignment) {
+               put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]);
+               arr[28] |= 0x80; /* UGAVALID */
+       }
+
+       if (scsi_debug_unmap_granularity) {
+               put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]);
+               return 0x3c; /* Mandatory page length for thin provisioning */
+       }
+
        return sizeof(vpdb0_data);
 }
 
+/* Block device characteristics VPD page (SBC-3) */
 static int inquiry_evpd_b1(unsigned char *arr)
 {
        memset(arr, 0, 0x3c);
        arr[0] = 0;
-       arr[1] = 1;
+       arr[1] = 1;     /* non rotating medium (e.g. solid state) */
+       arr[2] = 0;
+       arr[3] = 5;     /* less than 1.8" */
 
        return 0x3c;
 }
@@ -974,6 +1016,10 @@ static int resp_readcap16(struct scsi_cmnd * scp,
        arr[11] = scsi_debug_sector_size & 0xff;
        arr[13] = scsi_debug_physblk_exp & 0xf;
        arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
+
+       if (scsi_debug_unmap_granularity)
+               arr[14] |= 0x80; /* TPE */
+
        arr[15] = scsi_debug_lowest_aligned & 0xff;
 
        if (scsi_debug_dif) {
@@ -1887,6 +1933,70 @@ out:
        return ret;
 }
 
+static unsigned int map_state(sector_t lba, unsigned int *num)
+{
+       unsigned int granularity, alignment, mapped;
+       sector_t block, next, end;
+
+       granularity = scsi_debug_unmap_granularity;
+       alignment = granularity - scsi_debug_unmap_alignment;
+       block = lba + alignment;
+       do_div(block, granularity);
+
+       mapped = test_bit(block, map_storep);
+
+       if (mapped)
+               next = find_next_zero_bit(map_storep, map_size, block);
+       else
+               next = find_next_bit(map_storep, map_size, block);
+
+       end = next * granularity - scsi_debug_unmap_alignment;
+       *num = end - lba;
+
+       return mapped;
+}
+
+static void map_region(sector_t lba, unsigned int len)
+{
+       unsigned int granularity, alignment;
+       sector_t end = lba + len;
+
+       granularity = scsi_debug_unmap_granularity;
+       alignment = granularity - scsi_debug_unmap_alignment;
+
+       while (lba < end) {
+               sector_t block, rem;
+
+               block = lba + alignment;
+               rem = do_div(block, granularity);
+
+               set_bit(block, map_storep);
+
+               lba += granularity - rem;
+       }
+}
+
+static void unmap_region(sector_t lba, unsigned int len)
+{
+       unsigned int granularity, alignment;
+       sector_t end = lba + len;
+
+       granularity = scsi_debug_unmap_granularity;
+       alignment = granularity - scsi_debug_unmap_alignment;
+
+       while (lba < end) {
+               sector_t block, rem;
+
+               block = lba + alignment;
+               rem = do_div(block, granularity);
+
+               if (rem == 0 && lba + granularity <= end)
+                       clear_bit(block, map_storep);
+
+               lba += granularity - rem;
+       }
+}
+
 static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
                      unsigned int num, struct sdebug_dev_info *devip,
                      u32 ei_lba)
@@ -1910,6 +2020,8 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
 
        write_lock_irqsave(&atomic_rw, iflags);
        ret = do_device_access(SCpnt, devip, lba, num, 1);
+       if (scsi_debug_unmap_granularity)
+               map_region(lba, num);
        write_unlock_irqrestore(&atomic_rw, iflags);
        if (-1 == ret)
                return (DID_ERROR << 16);
@@ -1917,9 +2029,143 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
                 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
                printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
                       " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
+
+       return 0;
+}
+
+static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
+                     unsigned int num, struct sdebug_dev_info *devip,
+                          u32 ei_lba, unsigned int unmap)
+{
+       unsigned long iflags;
+       unsigned long long i;
+       int ret;
+
+       ret = check_device_access_params(devip, lba, num);
+       if (ret)
+               return ret;
+
+       write_lock_irqsave(&atomic_rw, iflags);
+
+       if (unmap && scsi_debug_unmap_granularity) {
+               unmap_region(lba, num);
+               goto out;
+       }
+
+       /* Else fetch one logical block */
+       ret = fetch_to_dev_buffer(scmd,
+                                 fake_storep + (lba * scsi_debug_sector_size),
+                                 scsi_debug_sector_size);
+
+       if (-1 == ret) {
+               write_unlock_irqrestore(&atomic_rw, iflags);
+               return (DID_ERROR << 16);
+       } else if ((ret < (num * scsi_debug_sector_size)) &&
+                (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
+               printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, "
+                      " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
+
+       /* Copy first sector to remaining blocks */
+       for (i = 1 ; i < num ; i++)
+               memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size),
+                      fake_storep + (lba * scsi_debug_sector_size),
+                      scsi_debug_sector_size);
+
+       if (scsi_debug_unmap_granularity)
+               map_region(lba, num);
+out:
+       write_unlock_irqrestore(&atomic_rw, iflags);
+
        return 0;
 }
 
+struct unmap_block_desc {
+       __be64  lba;
+       __be32  blocks;
+       __be32  __reserved;
+};
+
+static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip)
+{
+       unsigned char *buf;
+       struct unmap_block_desc *desc;
+       unsigned int i, payload_len, descriptors;
+       int ret;
+
+       ret = check_readiness(scmd, 1, devip);
+       if (ret)
+               return ret;
+
+       payload_len = get_unaligned_be16(&scmd->cmnd[7]);
+       BUG_ON(scsi_bufflen(scmd) != payload_len);
+
+       descriptors = (payload_len - 8) / 16;
+
+       buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC);
+       if (!buf)
+               return check_condition_result;
+
+       scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
+
+       BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2);
+       BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16);
+
+       desc = (void *)&buf[8];
+
+       for (i = 0 ; i < descriptors ; i++) {
+               unsigned long long lba = get_unaligned_be64(&desc[i].lba);
+               unsigned int num = get_unaligned_be32(&desc[i].blocks);
+
+               ret = check_device_access_params(devip, lba, num);
+               if (ret)
+                       goto out;
+
+               unmap_region(lba, num);
+       }
+
+       ret = 0;
+
+out:
+       kfree(buf);
+
+       return ret;
+}
+
+#define SDEBUG_GET_LBA_STATUS_LEN 32
+
+static int resp_get_lba_status(struct scsi_cmnd * scmd,
+                              struct sdebug_dev_info * devip)
+{
+       unsigned long long lba;
+       unsigned int alloc_len, mapped, num;
+       unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN];
+       int ret;
+
+       ret = check_readiness(scmd, 1, devip);
+       if (ret)
+               return ret;
+
+       lba = get_unaligned_be64(&scmd->cmnd[2]);
+       alloc_len = get_unaligned_be32(&scmd->cmnd[10]);
+
+       if (alloc_len < 24)
+               return 0;
+
+       ret = check_device_access_params(devip, lba, 1);
+       if (ret)
+               return ret;
+
+       mapped = map_state(lba, &num);
+
+       memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
+       put_unaligned_be32(16, &arr[0]);        /* Parameter Data Length */
+       put_unaligned_be64(lba, &arr[8]);       /* LBA */
+       put_unaligned_be32(num, &arr[16]);      /* Number of blocks */
+       arr[20] = !mapped;                      /* mapped = 0, unmapped = 1 */
+
+       return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN);
+}
+
 #define SDEBUG_RLUN_ARR_SZ 256
 
 static int resp_report_luns(struct scsi_cmnd * scp,
@@ -2430,6 +2676,10 @@ module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
 module_param_named(ato, scsi_debug_ato, int, S_IRUGO);
 module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO);
 module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
+module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO);
+module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO);
+module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO);
+module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -2458,6 +2708,10 @@ MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)");
 MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)");
 MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
 MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)");
+MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0)");
+MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=0)");
+MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=0)");
+MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
 
 static char sdebug_info[256];
 
@@ -2816,6 +3070,23 @@ static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf)
 }
 DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL);
 
+static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf)
+{
+       ssize_t count;
+
+       if (scsi_debug_unmap_granularity == 0)
+               return scnprintf(buf, PAGE_SIZE, "0-%u\n",
+                                sdebug_store_sectors);
+
+       count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size);
+
+       buf[count++] = '\n';
+       buf[count++] = 0;
+
+       return count;
+}
+DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL);
+
 
 /* Note: The following function creates attribute files in the
    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
@@ -2847,11 +3118,13 @@ static int do_create_driverfs_files(void)
        ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif);
        ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard);
        ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato);
+       ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map);
        return ret;
 }
 
 static void do_remove_driverfs_files(void)
 {
+       driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map);
        driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato);
        driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard);
        driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif);
@@ -2989,6 +3262,36 @@ static int __init scsi_debug_init(void)
                memset(dif_storep, 0xff, dif_size);
        }
 
+       if (scsi_debug_unmap_granularity) {
+               unsigned int map_bytes;
+
+               if (scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) {
+                       printk(KERN_ERR
+                              "%s: ERR: unmap_granularity < unmap_alignment\n",
+                              __func__);
+                       return -EINVAL;
+               }
+
+               map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity);
+               map_bytes = map_size >> 3;
+               map_storep = vmalloc(map_bytes);
+
+               printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n",
+                      map_size);
+
+               if (map_storep == NULL) {
+                       printk(KERN_ERR "scsi_debug_init: out of mem. (MAP)\n");
+                       ret = -ENOMEM;
+                       goto free_vm;
+               }
+
+               memset(map_storep, 0x0, map_bytes);
+
+               /* Map first 1KB for partition table */
+               if (scsi_debug_num_parts)
+                       map_region(0, 2);
+       }
+
        ret = device_register(&pseudo_primary);
        if (ret < 0) {
                printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
@@ -3041,6 +3344,8 @@ bus_unreg:
 dev_unreg:
        device_unregister(&pseudo_primary);
 free_vm:
+       if (map_storep)
+               vfree(map_storep);
        if (dif_storep)
                vfree(dif_storep);
        vfree(fake_storep);
@@ -3167,6 +3472,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
        int inj_dif = 0;
        int inj_dix = 0;
        int delay_override = 0;
+       int unmap = 0;
 
        scsi_set_resid(SCpnt, 0);
        if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
@@ -3272,13 +3578,21 @@ int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
                errsts = resp_readcap(SCpnt, devip);
                break;
        case SERVICE_ACTION_IN:
-               if (SAI_READ_CAPACITY_16 != cmd[1]) {
+               if (cmd[1] == SAI_READ_CAPACITY_16)
+                       errsts = resp_readcap16(SCpnt, devip);
+               else if (cmd[1] == SAI_GET_LBA_STATUS) {
+
+                       if (scsi_debug_unmap_max_desc == 0) {
+                               mk_sense_buffer(devip, ILLEGAL_REQUEST,
+                                               INVALID_COMMAND_OPCODE, 0);
+                               errsts = check_condition_result;
+                       } else
+                               errsts = resp_get_lba_status(SCpnt, devip);
+               } else {
                        mk_sense_buffer(devip, ILLEGAL_REQUEST,
                                        INVALID_OPCODE, 0);
                        errsts = check_condition_result;
-                       break;
                }
-               errsts = resp_readcap16(SCpnt, devip);
                break;
        case MAINTENANCE_IN:
                if (MI_REPORT_TARGET_PGS != cmd[1]) {
@@ -3378,6 +3692,29 @@ write:
                        errsts = illegal_condition_result;
                }
                break;
+       case WRITE_SAME_16:
+               if (cmd[1] & 0x8)
+                       unmap = 1;
+               /* fall through */
+       case WRITE_SAME:
+               errsts = check_readiness(SCpnt, 0, devip);
+               if (errsts)
+                       break;
+               get_data_transfer_info(cmd, &lba, &num, &ei_lba);
+               errsts = resp_write_same(SCpnt, lba, num, devip, ei_lba, unmap);
+               break;
+       case UNMAP:
+               errsts = check_readiness(SCpnt, 0, devip);
+               if (errsts)
+                       break;
+
+               if (scsi_debug_unmap_max_desc == 0) {
+                       mk_sense_buffer(devip, ILLEGAL_REQUEST,
+                                       INVALID_COMMAND_OPCODE, 0);
+                       errsts = check_condition_result;
+               } else
+                       errsts = resp_unmap(SCpnt, devip);
+               break;
        case MODE_SENSE:
        case MODE_SENSE_10:
                errsts = resp_mode_sense(SCpnt, target, devip);
index 93c2622..37af178 100644 (file)
@@ -168,11 +168,10 @@ static struct {
        {"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36},
        {"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36},
        {"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36},
-       {"HITACHI", "DF400", "*", BLIST_SPARSELUN},
-       {"HITACHI", "DF500", "*", BLIST_SPARSELUN},
-       {"HITACHI", "DF600", "*", BLIST_SPARSELUN},
-       {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN},
-       {"HITACHI", "OPEN-E", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN},
+       {"HITACHI", "DF400", "*", BLIST_REPORTLUN2},
+       {"HITACHI", "DF500", "*", BLIST_REPORTLUN2},
+       {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2},
+       {"HITACHI", "OPEN-", "*", BLIST_REPORTLUN2},
        {"HITACHI", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
        {"HITACHI", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
        {"HITACHI", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN},
@@ -454,7 +453,7 @@ int scsi_get_device_flags(struct scsi_device *sdev,
 
 
 /**
- * get_device_flags_keyed - get device specific flags from the dynamic device list.
+ * scsi_get_device_flags_keyed - get device specific flags from the dynamic device list
  * @sdev:       &scsi_device to get flags for
  * @vendor:    vendor name
  * @model:     model name
@@ -685,7 +684,7 @@ MODULE_PARM_DESC(default_dev_flags,
                 "scsi default device flag integer value");
 
 /**
- * scsi_dev_info_list_delete - called from scsi.c:exit_scsi to remove the scsi_dev_info_list.
+ * scsi_exit_devinfo - remove /proc/scsi/device_info & the scsi_dev_info_list
  **/
 void scsi_exit_devinfo(void)
 {
index 1b0060b..08ed506 100644 (file)
@@ -331,6 +331,64 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
        }
 }
 
+static void scsi_handle_queue_ramp_up(struct scsi_device *sdev)
+{
+       struct scsi_host_template *sht = sdev->host->hostt;
+       struct scsi_device *tmp_sdev;
+
+       if (!sht->change_queue_depth ||
+           sdev->queue_depth >= sdev->max_queue_depth)
+               return;
+
+       if (time_before(jiffies,
+           sdev->last_queue_ramp_up + sdev->queue_ramp_up_period))
+               return;
+
+       if (time_before(jiffies,
+           sdev->last_queue_full_time + sdev->queue_ramp_up_period))
+               return;
+
+       /*
+        * Walk all devices of a target and do
+        * ramp up on them.
+        */
+       shost_for_each_device(tmp_sdev, sdev->host) {
+               if (tmp_sdev->channel != sdev->channel ||
+                   tmp_sdev->id != sdev->id ||
+                   tmp_sdev->queue_depth == sdev->max_queue_depth)
+                       continue;
+               /*
+                * call back into LLD to increase queue_depth by one
+                * with ramp up reason code.
+                */
+               sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1,
+                                       SCSI_QDEPTH_RAMP_UP);
+               sdev->last_queue_ramp_up = jiffies;
+       }
+}
+
+static void scsi_handle_queue_full(struct scsi_device *sdev)
+{
+       struct scsi_host_template *sht = sdev->host->hostt;
+       struct scsi_device *tmp_sdev;
+
+       if (!sht->change_queue_depth)
+               return;
+
+       shost_for_each_device(tmp_sdev, sdev->host) {
+               if (tmp_sdev->channel != sdev->channel ||
+                   tmp_sdev->id != sdev->id)
+                       continue;
+               /*
+                * We do not know the number of commands that were at
+                * the device when we got the queue full so we start
+                * from the highest possible value and work our way down.
+                */
+               sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1,
+                                       SCSI_QDEPTH_QFULL);
+       }
+}
+
 /**
  * scsi_eh_completed_normally - Disposition a eh cmd on return from LLD.
  * @scmd:      SCSI cmd to examine.
@@ -371,6 +429,7 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
         */
        switch (status_byte(scmd->result)) {
        case GOOD:
+               scsi_handle_queue_ramp_up(scmd->device);
        case COMMAND_TERMINATED:
                return SUCCESS;
        case CHECK_CONDITION:
@@ -387,8 +446,10 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
                 * let issuer deal with this, it could be just fine
                 */
                return SUCCESS;
-       case BUSY:
        case QUEUE_FULL:
+               scsi_handle_queue_full(scmd->device);
+               /* fall through */
+       case BUSY:
        default:
                return FAILED;
        }
@@ -1387,6 +1448,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
         */
        switch (status_byte(scmd->result)) {
        case QUEUE_FULL:
+               scsi_handle_queue_full(scmd->device);
                /*
                 * the case of trying to send too many commands to a
                 * tagged queueing device.
@@ -1400,6 +1462,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
                 */
                return ADD_TO_MLQUEUE;
        case GOOD:
+               scsi_handle_queue_ramp_up(scmd->device);
        case COMMAND_TERMINATED:
                return SUCCESS;
        case TASK_ABORTED:
index b98f763..d9564fb 100644 (file)
@@ -308,6 +308,9 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
                case SG_SCSI_RESET_DEVICE:
                        val = SCSI_TRY_RESET_DEVICE;
                        break;
+               case SG_SCSI_RESET_TARGET:
+                       val = SCSI_TRY_RESET_TARGET;
+                       break;
                case SG_SCSI_RESET_BUS:
                        val = SCSI_TRY_RESET_BUS;
                        break;
index 5987da8..e495d38 100644 (file)
@@ -898,7 +898,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
                                scsi_print_sense("", cmd);
                        scsi_print_command(cmd);
                }
-               if (blk_end_request_err(req, -EIO))
+               if (blk_end_request_err(req, error))
                        scsi_requeue_command(q, cmd);
                else
                        scsi_next_command(cmd);
@@ -1359,9 +1359,9 @@ static int scsi_lld_busy(struct request_queue *q)
 static void scsi_kill_request(struct request *req, struct request_queue *q)
 {
        struct scsi_cmnd *cmd = req->special;
-       struct scsi_device *sdev = cmd->device;
-       struct scsi_target *starget = scsi_target(sdev);
-       struct Scsi_Host *shost = sdev->host;
+       struct scsi_device *sdev;
+       struct scsi_target *starget;
+       struct Scsi_Host *shost;
 
        blk_start_request(req);
 
@@ -1371,6 +1371,9 @@ static void scsi_kill_request(struct request *req, struct request_queue *q)
                BUG();
        }
 
+       sdev = cmd->device;
+       starget = scsi_target(sdev);
+       shost = sdev->host;
        scsi_init_cmd_errh(cmd);
        cmd->result = DID_NO_CONNECT << 16;
        atomic_inc(&cmd->device->iorequest_cnt);
index ac6855c..dcd1285 100644 (file)
@@ -23,7 +23,7 @@ int scsi_dma_map(struct scsi_cmnd *cmd)
        int nseg = 0;
 
        if (scsi_sg_count(cmd)) {
-               struct device *dev = cmd->device->host->shost_gendev.parent;
+               struct device *dev = cmd->device->host->dma_dev;
 
                nseg = dma_map_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd),
                                  cmd->sc_data_direction);
@@ -41,7 +41,7 @@ EXPORT_SYMBOL(scsi_dma_map);
 void scsi_dma_unmap(struct scsi_cmnd *cmd)
 {
        if (scsi_sg_count(cmd)) {
-               struct device *dev = cmd->device->host->shost_gendev.parent;
+               struct device *dev = cmd->device->host->dma_dev;
 
                dma_unmap_sg(dev, scsi_sglist(cmd), scsi_sg_count(cmd),
                             cmd->sc_data_direction);
index 723fdec..0fd6ae6 100644 (file)
@@ -613,7 +613,7 @@ EXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg);
  * @data_buf:          pointer to vendor unique data buffer
  *
  * Returns:
- *   0 on succesful return
+ *   0 on successful return
  *   otherwise, failing error code
  *
  * Notes:
index 47291bc..012f73a 100644 (file)
@@ -251,6 +251,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
        sdev->model = scsi_null_device_strs;
        sdev->rev = scsi_null_device_strs;
        sdev->host = shost;
+       sdev->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD;
        sdev->id = starget->id;
        sdev->lun = lun;
        sdev->channel = starget->channel;
@@ -941,6 +942,8 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
                }
        }
 
+       sdev->max_queue_depth = sdev->queue_depth;
+
        /*
         * Ok, the device is now all set up, we can
         * register it and tell the rest of the kernel
index 392d8db..5a06505 100644 (file)
@@ -766,10 +766,13 @@ sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr,
        if (depth < 1)
                return -EINVAL;
 
-       retval = sht->change_queue_depth(sdev, depth);
+       retval = sht->change_queue_depth(sdev, depth,
+                                        SCSI_QDEPTH_DEFAULT);
        if (retval < 0)
                return retval;
 
+       sdev->max_queue_depth = sdev->queue_depth;
+
        return count;
 }
 
@@ -777,6 +780,37 @@ static struct device_attribute sdev_attr_queue_depth_rw =
        __ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth,
               sdev_store_queue_depth_rw);
 
+static ssize_t
+sdev_show_queue_ramp_up_period(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct scsi_device *sdev;
+       sdev = to_scsi_device(dev);
+       return snprintf(buf, 20, "%u\n",
+                       jiffies_to_msecs(sdev->queue_ramp_up_period));
+}
+
+static ssize_t
+sdev_store_queue_ramp_up_period(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       unsigned long period;
+
+       if (strict_strtoul(buf, 10, &period))
+               return -EINVAL;
+
+       sdev->queue_ramp_up_period = msecs_to_jiffies(period);
+       return period;
+}
+
+static struct device_attribute sdev_attr_queue_ramp_up_period =
+       __ATTR(queue_ramp_up_period, S_IRUGO | S_IWUSR,
+              sdev_show_queue_ramp_up_period,
+              sdev_store_queue_ramp_up_period);
+
 static ssize_t
 sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr,
                         const char *buf, size_t count)
@@ -867,8 +901,12 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
        sdev->is_visible = 1;
 
        /* create queue files, which may be writable, depending on the host */
-       if (sdev->host->hostt->change_queue_depth)
-               error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw);
+       if (sdev->host->hostt->change_queue_depth) {
+               error = device_create_file(&sdev->sdev_gendev,
+                                          &sdev_attr_queue_depth_rw);
+               error = device_create_file(&sdev->sdev_gendev,
+                                          &sdev_attr_queue_ramp_up_period);
+       }
        else
                error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth);
        if (error)
index c6f70da..6531c91 100644 (file)
@@ -27,6 +27,7 @@
  */
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
@@ -2384,6 +2385,7 @@ fc_rport_final_delete(struct work_struct *work)
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
        unsigned long flags;
+       int do_callback = 0;
 
        /*
         * if a scan is pending, flush the SCSI Host work_q so that
@@ -2422,8 +2424,15 @@ fc_rport_final_delete(struct work_struct *work)
         * Avoid this call if we already called it when we preserved the
         * rport for the binding.
         */
+       spin_lock_irqsave(shost->host_lock, flags);
        if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
-           (i->f->dev_loss_tmo_callbk))
+           (i->f->dev_loss_tmo_callbk)) {
+               rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+               do_callback = 1;
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       if (do_callback)
                i->f->dev_loss_tmo_callbk(rport);
 
        fc_bsg_remove(rport->rqst_q);
@@ -2970,6 +2979,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
        struct fc_internal *i = to_fc_internal(shost->transportt);
        struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
        unsigned long flags;
+       int do_callback = 0;
 
        spin_lock_irqsave(shost->host_lock, flags);
 
@@ -3035,7 +3045,6 @@ fc_timeout_deleted_rport(struct work_struct *work)
        rport->roles = FC_PORT_ROLE_UNKNOWN;
        rport->port_state = FC_PORTSTATE_NOTPRESENT;
        rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
-       rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
 
        /*
         * Pre-emptively kill I/O rather than waiting for the work queue
@@ -3045,32 +3054,40 @@ fc_timeout_deleted_rport(struct work_struct *work)
        spin_unlock_irqrestore(shost->host_lock, flags);
        fc_terminate_rport_io(rport);
 
-       BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+       spin_lock_irqsave(shost->host_lock, flags);
+
+       if (rport->port_state == FC_PORTSTATE_NOTPRESENT) {     /* still missing */
 
-       /* remove the identifiers that aren't used in the consisting binding */
-       switch (fc_host->tgtid_bind_type) {
-       case FC_TGTID_BIND_BY_WWPN:
-               rport->node_name = -1;
-               rport->port_id = -1;
-               break;
-       case FC_TGTID_BIND_BY_WWNN:
-               rport->port_name = -1;
-               rport->port_id = -1;
-               break;
-       case FC_TGTID_BIND_BY_ID:
-               rport->node_name = -1;
-               rport->port_name = -1;
-               break;
-       case FC_TGTID_BIND_NONE:        /* to keep compiler happy */
-               break;
+               /* remove the identifiers that aren't used in the consisting binding */
+               switch (fc_host->tgtid_bind_type) {
+               case FC_TGTID_BIND_BY_WWPN:
+                       rport->node_name = -1;
+                       rport->port_id = -1;
+                       break;
+               case FC_TGTID_BIND_BY_WWNN:
+                       rport->port_name = -1;
+                       rport->port_id = -1;
+                       break;
+               case FC_TGTID_BIND_BY_ID:
+                       rport->node_name = -1;
+                       rport->port_name = -1;
+                       break;
+               case FC_TGTID_BIND_NONE:        /* to keep compiler happy */
+                       break;
+               }
+
+               /*
+                * As this only occurs if the remote port (scsi target)
+                * went away and didn't come back - we'll remove
+                * all attached scsi devices.
+                */
+               rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+               fc_queue_work(shost, &rport->stgt_delete_work);
+
+               do_callback = 1;
        }
 
-       /*
-        * As this only occurs if the remote port (scsi target)
-        * went away and didn't come back - we'll remove
-        * all attached scsi devices.
-        */
-       fc_queue_work(shost, &rport->stgt_delete_work);
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        /*
         * Notify the driver that the rport is now dead. The LLDD will
@@ -3078,7 +3095,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
         *
         * Note: we set the CALLBK_DONE flag above to correspond
         */
-       if (i->f->dev_loss_tmo_callbk)
+       if (do_callback && i->f->dev_loss_tmo_callbk)
                i->f->dev_loss_tmo_callbk(rport);
 }
 
@@ -3128,6 +3145,31 @@ fc_scsi_scan_rport(struct work_struct *work)
        spin_unlock_irqrestore(shost->host_lock, flags);
 }
 
+/**
+ * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * blocks the scsi_eh thread until the fc_rport leaves the
+ * FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh
+ * failing recovery actions for blocked rports which would lead to
+ * offlined SCSI devices.
+ */
+void fc_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       while (rport->port_state == FC_PORTSTATE_BLOCKED) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               msleep(1000);
+               spin_lock_irqsave(shost->host_lock, flags);
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+}
+EXPORT_SYMBOL(fc_block_scsi_eh);
 
 /**
  * fc_vport_setup - allocates and creates a FC virtual port.
@@ -3769,8 +3811,9 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
                return;
 
        while (!blk_queue_plugged(q)) {
-               if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED))
-                               break;
+               if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) &&
+                   !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
+                       break;
 
                req = blk_fetch_request(q);
                if (!req)
index ad897df..ea3892e 100644 (file)
@@ -30,7 +30,7 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/iscsi_if.h>
 
-#define ISCSI_SESSION_ATTRS 21
+#define ISCSI_SESSION_ATTRS 22
 #define ISCSI_CONN_ATTRS 13
 #define ISCSI_HOST_ATTRS 4
 
@@ -627,8 +627,10 @@ static void __iscsi_block_session(struct work_struct *work)
        spin_unlock_irqrestore(&session->lock, flags);
        scsi_target_block(&session->dev);
        ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
-       queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
-                          session->recovery_tmo * HZ);
+       if (session->recovery_tmo >= 0)
+               queue_delayed_work(iscsi_eh_timer_workq,
+                                  &session->recovery_work,
+                                  session->recovery_tmo * HZ);
 }
 
 void iscsi_block_session(struct iscsi_cls_session *session)
@@ -1348,8 +1350,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
        switch (ev->u.set_param.param) {
        case ISCSI_PARAM_SESS_RECOVERY_TMO:
                sscanf(data, "%d", &value);
-               if (value != 0)
-                       session->recovery_tmo = value;
+               session->recovery_tmo = value;
                break;
        default:
                err = transport->set_param(conn, ev->u.set_param.param,
@@ -1759,6 +1760,7 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
 iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
 iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
 iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
 iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
 iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0)
 
@@ -2000,6 +2002,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
        SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
        SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
        SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+       SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO);
        SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
        SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
        SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
index fd47cb1..f27e52d 100644 (file)
@@ -666,7 +666,7 @@ EXPORT_SYMBOL(sas_phy_add);
  *
  * Note:
  *   This function must only be called on a PHY that has not
- *   sucessfully been added using sas_phy_add().
+ *   successfully been added using sas_phy_add().
  */
 void sas_phy_free(struct sas_phy *phy)
 {
@@ -896,7 +896,7 @@ EXPORT_SYMBOL(sas_port_add);
  *
  * Note:
  *   This function must only be called on a PORT that has not
- *   sucessfully been added using sas_port_add().
+ *   successfully been added using sas_port_add().
  */
 void sas_port_free(struct sas_port *port)
 {
@@ -1476,7 +1476,7 @@ EXPORT_SYMBOL(sas_rphy_add);
  *
  * Note:
  *   This function must only be called on a remote
- *   PHY that has not sucessfully been added using
+ *   PHY that has not successfully been added using
  *   sas_rphy_add() (or has been sas_rphy_remove()'d)
  */
 void sas_rphy_free(struct sas_rphy *rphy)
index 12d58a7..ad59abb 100644 (file)
@@ -2280,7 +2280,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
        } else if (code == MT_ST_SET_CLN) {
                value = (options & ~MT_ST_OPTIONS) & 0xff;
                if (value != 0 &&
-                   value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE)
+                       (value < EXTENDED_SENSE_START ||
+                               value >= SCSI_SENSE_BUFFERSIZE))
                        return (-EINVAL);
                STp->cln_mode = value;
                STp->cln_sense_mask = (options >> 8) & 0xff;
index 09fa886..3058bb1 100644 (file)
 #include <scsi/scsi_eh.h>
 
 #define DRV_NAME "stex"
-#define ST_DRIVER_VERSION "4.6.0000.3"
+#define ST_DRIVER_VERSION "4.6.0000.4"
 #define ST_VER_MAJOR           4
 #define ST_VER_MINOR           6
 #define ST_OEM                 0
-#define ST_BUILD_VER           3
+#define ST_BUILD_VER           4
 
 enum {
        /* MU register offset */
@@ -64,24 +64,24 @@ enum {
        YH2I_REQ_HI                             = 0xc4,
 
        /* MU register value */
-       MU_INBOUND_DOORBELL_HANDSHAKE           = 1,
-       MU_INBOUND_DOORBELL_REQHEADCHANGED      = 2,
-       MU_INBOUND_DOORBELL_STATUSTAILCHANGED   = 4,
-       MU_INBOUND_DOORBELL_HMUSTOPPED          = 8,
-       MU_INBOUND_DOORBELL_RESET               = 16,
-
-       MU_OUTBOUND_DOORBELL_HANDSHAKE          = 1,
-       MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = 2,
-       MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED  = 4,
-       MU_OUTBOUND_DOORBELL_BUSCHANGE          = 8,
-       MU_OUTBOUND_DOORBELL_HASEVENT           = 16,
+       MU_INBOUND_DOORBELL_HANDSHAKE           = (1 << 0),
+       MU_INBOUND_DOORBELL_REQHEADCHANGED      = (1 << 1),
+       MU_INBOUND_DOORBELL_STATUSTAILCHANGED   = (1 << 2),
+       MU_INBOUND_DOORBELL_HMUSTOPPED          = (1 << 3),
+       MU_INBOUND_DOORBELL_RESET               = (1 << 4),
+
+       MU_OUTBOUND_DOORBELL_HANDSHAKE          = (1 << 0),
+       MU_OUTBOUND_DOORBELL_REQUESTTAILCHANGED = (1 << 1),
+       MU_OUTBOUND_DOORBELL_STATUSHEADCHANGED  = (1 << 2),
+       MU_OUTBOUND_DOORBELL_BUSCHANGE          = (1 << 3),
+       MU_OUTBOUND_DOORBELL_HASEVENT           = (1 << 4),
+       MU_OUTBOUND_DOORBELL_REQUEST_RESET      = (1 << 27),
 
        /* MU status code */
        MU_STATE_STARTING                       = 1,
-       MU_STATE_FMU_READY_FOR_HANDSHAKE        = 2,
-       MU_STATE_SEND_HANDSHAKE_FRAME           = 3,
-       MU_STATE_STARTED                        = 4,
-       MU_STATE_RESETTING                      = 5,
+       MU_STATE_STARTED                        = 2,
+       MU_STATE_RESETTING                      = 3,
+       MU_STATE_FAILED                         = 4,
 
        MU_MAX_DELAY                            = 120,
        MU_HANDSHAKE_SIGNATURE                  = 0x55aaaa55,
@@ -111,6 +111,8 @@ enum {
 
        SS_H2I_INT_RESET                        = 0x100,
 
+       SS_I2H_REQUEST_RESET                    = 0x2000,
+
        SS_MU_OPERATIONAL                       = 0x80000000,
 
        STEX_CDB_LENGTH                         = 16,
@@ -160,6 +162,7 @@ enum {
        INQUIRY_EVPD                            = 0x01,
 
        ST_ADDITIONAL_MEM                       = 0x200000,
+       ST_ADDITIONAL_MEM_MIN                   = 0x80000,
 };
 
 struct st_sgitem {
@@ -311,6 +314,10 @@ struct st_hba {
        struct st_ccb *wait_ccb;
        __le32 *scratch;
 
+       char work_q_name[20];
+       struct workqueue_struct *work_q;
+       struct work_struct reset_work;
+       wait_queue_head_t reset_waitq;
        unsigned int mu_status;
        unsigned int cardtype;
        int msi_enabled;
@@ -577,6 +584,9 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *))
        lun = cmd->device->lun;
        hba = (struct st_hba *) &host->hostdata[0];
 
+       if (unlikely(hba->mu_status == MU_STATE_RESETTING))
+               return SCSI_MLQUEUE_HOST_BUSY;
+
        switch (cmd->cmnd[0]) {
        case MODE_SENSE_10:
        {
@@ -841,7 +851,6 @@ static irqreturn_t stex_intr(int irq, void *__hba)
        void __iomem *base = hba->mmio_base;
        u32 data;
        unsigned long flags;
-       int handled = 0;
 
        spin_lock_irqsave(hba->host->host_lock, flags);
 
@@ -852,12 +861,16 @@ static irqreturn_t stex_intr(int irq, void *__hba)
                writel(data, base + ODBL);
                readl(base + ODBL); /* flush */
                stex_mu_intr(hba, data);
-               handled = 1;
+               spin_unlock_irqrestore(hba->host->host_lock, flags);
+               if (unlikely(data & MU_OUTBOUND_DOORBELL_REQUEST_RESET &&
+                       hba->cardtype == st_shasta))
+                       queue_work(hba->work_q, &hba->reset_work);
+               return IRQ_HANDLED;
        }
 
        spin_unlock_irqrestore(hba->host->host_lock, flags);
 
-       return IRQ_RETVAL(handled);
+       return IRQ_NONE;
 }
 
 static void stex_ss_mu_intr(struct st_hba *hba)
@@ -939,7 +952,6 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
        void __iomem *base = hba->mmio_base;
        u32 data;
        unsigned long flags;
-       int handled = 0;
 
        spin_lock_irqsave(hba->host->host_lock, flags);
 
@@ -948,12 +960,15 @@ static irqreturn_t stex_ss_intr(int irq, void *__hba)
                /* clear the interrupt */
                writel(data, base + YI2H_INT_C);
                stex_ss_mu_intr(hba);
-               handled = 1;
+               spin_unlock_irqrestore(hba->host->host_lock, flags);
+               if (unlikely(data & SS_I2H_REQUEST_RESET))
+                       queue_work(hba->work_q, &hba->reset_work);
+               return IRQ_HANDLED;
        }
 
        spin_unlock_irqrestore(hba->host->host_lock, flags);
 
-       return IRQ_RETVAL(handled);
+       return IRQ_NONE;
 }
 
 static int stex_common_handshake(struct st_hba *hba)
@@ -1001,7 +1016,7 @@ static int stex_common_handshake(struct st_hba *hba)
        h->partner_type = HMU_PARTNER_TYPE;
        if (hba->extra_offset) {
                h->extra_offset = cpu_to_le32(hba->extra_offset);
-               h->extra_size = cpu_to_le32(ST_ADDITIONAL_MEM);
+               h->extra_size = cpu_to_le32(hba->dma_size - hba->extra_offset);
        } else
                h->extra_offset = h->extra_size = 0;
 
@@ -1046,7 +1061,7 @@ static int stex_ss_handshake(struct st_hba *hba)
        struct st_msg_header *msg_h;
        struct handshake_frame *h;
        __le32 *scratch;
-       u32 data;
+       u32 data, scratch_size;
        unsigned long before;
        int ret = 0;
 
@@ -1074,13 +1089,16 @@ static int stex_ss_handshake(struct st_hba *hba)
        stex_gettime(&h->hosttime);
        h->partner_type = HMU_PARTNER_TYPE;
        h->extra_offset = h->extra_size = 0;
-       h->scratch_size = cpu_to_le32((hba->sts_count+1)*sizeof(u32));
+       scratch_size = (hba->sts_count+1)*sizeof(u32);
+       h->scratch_size = cpu_to_le32(scratch_size);
 
        data = readl(base + YINT_EN);
        data &= ~4;
        writel(data, base + YINT_EN);
        writel((hba->dma_handle >> 16) >> 16, base + YH2I_REQ_HI);
+       readl(base + YH2I_REQ_HI);
        writel(hba->dma_handle, base + YH2I_REQ);
+       readl(base + YH2I_REQ); /* flush */
 
        scratch = hba->scratch;
        before = jiffies;
@@ -1096,7 +1114,7 @@ static int stex_ss_handshake(struct st_hba *hba)
                msleep(1);
        }
 
-       *scratch = 0;
+       memset(scratch, 0, scratch_size);
        msg_h->flag = 0;
        return ret;
 }
@@ -1105,19 +1123,24 @@ static int stex_handshake(struct st_hba *hba)
 {
        int err;
        unsigned long flags;
+       unsigned int mu_status;
 
        err = (hba->cardtype == st_yel) ?
                stex_ss_handshake(hba) : stex_common_handshake(hba);
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       mu_status = hba->mu_status;
        if (err == 0) {
-               spin_lock_irqsave(hba->host->host_lock, flags);
                hba->req_head = 0;
                hba->req_tail = 0;
                hba->status_head = 0;
                hba->status_tail = 0;
                hba->out_req_cnt = 0;
                hba->mu_status = MU_STATE_STARTED;
-               spin_unlock_irqrestore(hba->host->host_lock, flags);
-       }
+       } else
+               hba->mu_status = MU_STATE_FAILED;
+       if (mu_status == MU_STATE_RESETTING)
+               wake_up_all(&hba->reset_waitq);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
        return err;
 }
 
@@ -1137,17 +1160,11 @@ static int stex_abort(struct scsi_cmnd *cmd)
 
        base = hba->mmio_base;
        spin_lock_irqsave(host->host_lock, flags);
-       if (tag < host->can_queue && hba->ccb[tag].cmd == cmd)
+       if (tag < host->can_queue &&
+               hba->ccb[tag].req && hba->ccb[tag].cmd == cmd)
                hba->wait_ccb = &hba->ccb[tag];
-       else {
-               for (tag = 0; tag < host->can_queue; tag++)
-                       if (hba->ccb[tag].cmd == cmd) {
-                               hba->wait_ccb = &hba->ccb[tag];
-                               break;
-                       }
-               if (tag >= host->can_queue)
-                       goto out;
-       }
+       else
+               goto out;
 
        if (hba->cardtype == st_yel) {
                data = readl(base + YI2H_INT);
@@ -1221,6 +1238,37 @@ static void stex_hard_reset(struct st_hba *hba)
                        hba->pdev->saved_config_space[i]);
 }
 
+static int stex_yos_reset(struct st_hba *hba)
+{
+       void __iomem *base;
+       unsigned long flags, before;
+       int ret = 0;
+
+       base = hba->mmio_base;
+       writel(MU_INBOUND_DOORBELL_RESET, base + IDBL);
+       readl(base + IDBL); /* flush */
+       before = jiffies;
+       while (hba->out_req_cnt > 0) {
+               if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
+                       printk(KERN_WARNING DRV_NAME
+                               "(%s): reset timeout\n", pci_name(hba->pdev));
+                       ret = -1;
+                       break;
+               }
+               msleep(1);
+       }
+
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       if (ret == -1)
+               hba->mu_status = MU_STATE_FAILED;
+       else
+               hba->mu_status = MU_STATE_STARTED;
+       wake_up_all(&hba->reset_waitq);
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       return ret;
+}
+
 static void stex_ss_reset(struct st_hba *hba)
 {
        writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
@@ -1228,66 +1276,86 @@ static void stex_ss_reset(struct st_hba *hba)
        ssleep(5);
 }
 
-static int stex_reset(struct scsi_cmnd *cmd)
+static int stex_do_reset(struct st_hba *hba)
 {
-       struct st_hba *hba;
-       void __iomem *base;
-       unsigned long flags, before;
+       struct st_ccb *ccb;
+       unsigned long flags;
+       unsigned int mu_status = MU_STATE_RESETTING;
+       u16 tag;
 
-       hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       if (hba->mu_status == MU_STATE_STARTING) {
+               spin_unlock_irqrestore(hba->host->host_lock, flags);
+               printk(KERN_INFO DRV_NAME "(%s): request reset during init\n",
+                       pci_name(hba->pdev));
+               return 0;
+       }
+       while (hba->mu_status == MU_STATE_RESETTING) {
+               spin_unlock_irqrestore(hba->host->host_lock, flags);
+               wait_event_timeout(hba->reset_waitq,
+                                  hba->mu_status != MU_STATE_RESETTING,
+                                  MU_MAX_DELAY * HZ);
+               spin_lock_irqsave(hba->host->host_lock, flags);
+               mu_status = hba->mu_status;
+       }
 
-       printk(KERN_INFO DRV_NAME
-               "(%s): resetting host\n", pci_name(hba->pdev));
-       scsi_print_command(cmd);
+       if (mu_status != MU_STATE_RESETTING) {
+               spin_unlock_irqrestore(hba->host->host_lock, flags);
+               return (mu_status == MU_STATE_STARTED) ? 0 : -1;
+       }
 
        hba->mu_status = MU_STATE_RESETTING;
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       if (hba->cardtype == st_yosemite)
+               return stex_yos_reset(hba);
 
        if (hba->cardtype == st_shasta)
                stex_hard_reset(hba);
        else if (hba->cardtype == st_yel)
                stex_ss_reset(hba);
 
-       if (hba->cardtype != st_yosemite) {
-               if (stex_handshake(hba)) {
-                       printk(KERN_WARNING DRV_NAME
-                               "(%s): resetting: handshake failed\n",
-                               pci_name(hba->pdev));
-                       return FAILED;
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       for (tag = 0; tag < hba->host->can_queue; tag++) {
+               ccb = &hba->ccb[tag];
+               if (ccb->req == NULL)
+                       continue;
+               ccb->req = NULL;
+               if (ccb->cmd) {
+                       scsi_dma_unmap(ccb->cmd);
+                       ccb->cmd->result = DID_RESET << 16;
+                       ccb->cmd->scsi_done(ccb->cmd);
+                       ccb->cmd = NULL;
                }
-               return SUCCESS;
        }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
 
-       /* st_yosemite */
-       writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL);
-       readl(hba->mmio_base + IDBL); /* flush */
-       before = jiffies;
-       while (hba->out_req_cnt > 0) {
-               if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) {
-                       printk(KERN_WARNING DRV_NAME
-                               "(%s): reset timeout\n", pci_name(hba->pdev));
-                       return FAILED;
-               }
-               msleep(1);
-       }
+       if (stex_handshake(hba) == 0)
+               return 0;
 
-       base = hba->mmio_base;
-       writel(0, base + IMR0);
-       readl(base + IMR0);
-       writel(0, base + OMR0);
-       readl(base + OMR0);
-       writel(0, base + IMR1);
-       readl(base + IMR1);
-       writel(0, base + OMR1);
-       readl(base + OMR1); /* flush */
-       spin_lock_irqsave(hba->host->host_lock, flags);
-       hba->req_head = 0;
-       hba->req_tail = 0;
-       hba->status_head = 0;
-       hba->status_tail = 0;
-       hba->out_req_cnt = 0;
-       hba->mu_status = MU_STATE_STARTED;
-       spin_unlock_irqrestore(hba->host->host_lock, flags);
-       return SUCCESS;
+       printk(KERN_WARNING DRV_NAME "(%s): resetting: handshake failed\n",
+               pci_name(hba->pdev));
+       return -1;
+}
+
+static int stex_reset(struct scsi_cmnd *cmd)
+{
+       struct st_hba *hba;
+
+       hba = (struct st_hba *) &cmd->device->host->hostdata[0];
+
+       printk(KERN_INFO DRV_NAME
+               "(%s): resetting host\n", pci_name(hba->pdev));
+       scsi_print_command(cmd);
+
+       return stex_do_reset(hba) ? FAILED : SUCCESS;
+}
+
+static void stex_reset_work(struct work_struct *work)
+{
+       struct st_hba *hba = container_of(work, struct st_hba, reset_work);
+
+       stex_do_reset(hba);
 }
 
 static int stex_biosparam(struct scsi_device *sdev,
@@ -1420,8 +1488,8 @@ static int stex_set_dma_mask(struct pci_dev * pdev)
 {
        int ret;
 
-       if (!pci_set_dma_mask(pdev,  DMA_BIT_MASK(64))
-               && !pci_set_consistent_dma_mask(pdev,  DMA_BIT_MASK(64)))
+       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))
+               && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)))
                return 0;
        ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
        if (!ret)
@@ -1528,10 +1596,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        hba->dma_mem = dma_alloc_coherent(&pdev->dev,
                hba->dma_size, &hba->dma_handle, GFP_KERNEL);
        if (!hba->dma_mem) {
-               err = -ENOMEM;
-               printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
-                       pci_name(pdev));
-               goto out_iounmap;
+               /* Retry minimum coherent mapping for st_seq and st_vsc */
+               if (hba->cardtype == st_seq ||
+                   (hba->cardtype == st_vsc && (pdev->subsystem_device & 1))) {
+                       printk(KERN_WARNING DRV_NAME
+                               "(%s): allocating min buffer for controller\n",
+                               pci_name(pdev));
+                       hba->dma_size = hba->extra_offset
+                               + ST_ADDITIONAL_MEM_MIN;
+                       hba->dma_mem = dma_alloc_coherent(&pdev->dev,
+                               hba->dma_size, &hba->dma_handle, GFP_KERNEL);
+               }
+
+               if (!hba->dma_mem) {
+                       err = -ENOMEM;
+                       printk(KERN_ERR DRV_NAME "(%s): dma mem alloc failed\n",
+                               pci_name(pdev));
+                       goto out_iounmap;
+               }
        }
 
        hba->ccb = kcalloc(ci->rq_count, sizeof(struct st_ccb), GFP_KERNEL);
@@ -1568,12 +1650,24 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        hba->host = host;
        hba->pdev = pdev;
+       init_waitqueue_head(&hba->reset_waitq);
+
+       snprintf(hba->work_q_name, sizeof(hba->work_q_name),
+                "stex_wq_%d", host->host_no);
+       hba->work_q = create_singlethread_workqueue(hba->work_q_name);
+       if (!hba->work_q) {
+               printk(KERN_ERR DRV_NAME "(%s): create workqueue failed\n",
+                       pci_name(pdev));
+               err = -ENOMEM;
+               goto out_ccb_free;
+       }
+       INIT_WORK(&hba->reset_work, stex_reset_work);
 
        err = stex_request_irq(hba);
        if (err) {
                printk(KERN_ERR DRV_NAME "(%s): request irq failed\n",
                        pci_name(pdev));
-               goto out_ccb_free;
+               goto out_free_wq;
        }
 
        err = stex_handshake(hba);
@@ -1602,6 +1696,8 @@ stex_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
 out_free_irq:
        stex_free_irq(hba);
+out_free_wq:
+       destroy_workqueue(hba->work_q);
 out_ccb_free:
        kfree(hba->ccb);
 out_pci_free:
@@ -1669,6 +1765,8 @@ static void stex_hba_free(struct st_hba *hba)
 {
        stex_free_irq(hba);
 
+       destroy_workqueue(hba->work_q);
+
        iounmap(hba->mmio_base);
 
        pci_release_regions(hba->pdev);
index 45374d6..2b38f6a 100644 (file)
@@ -1864,7 +1864,7 @@ static pci_ers_result_t sym2_io_slot_dump(struct pci_dev *pdev)
  *
  * This routine is similar to sym_set_workarounds(), except
  * that, at this point, we already know that the device was
- * succesfully intialized at least once before, and so most
+ * successfully intialized at least once before, and so most
  * of the steps taken there are un-needed here.
  */
 static void sym2_reset_workarounds(struct pci_dev *pdev)
index 297deb8..a7bc8b7 100644 (file)
@@ -2692,7 +2692,7 @@ static void sym_int_ma (struct sym_hcb *np)
         *  we force a SIR_NEGO_PROTO interrupt (it is a hack that avoids 
         *  bloat for such a should_not_happen situation).
         *  In all other situation, we reset the BUS.
-        *  Are these assumptions reasonnable ? (Wait and see ...)
+        *  Are these assumptions reasonable ? (Wait and see ...)
         */
 unexpected_phase:
        dsp -= 8;
index 053e63c..5a80cba 100644 (file)
@@ -54,7 +54,7 @@
  *
  *    SYM_OPT_LIMIT_COMMAND_REORDERING
  *        When this option is set, the driver tries to limit tagged 
- *        command reordering to some reasonnable value.
+ *        command reordering to some reasonable value.
  *        (set for Linux)
  */
 #if 0
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
new file mode 100644 (file)
index 0000000..d2604c8
--- /dev/null
@@ -0,0 +1,1407 @@
+/*
+ * Linux driver for VMware's para-virtualized SCSI HBA.
+ *
+ * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained by: Alok N Kataria <akataria@vmware.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "vmw_pvscsi.h"
+
+#define PVSCSI_LINUX_DRIVER_DESC "VMware PVSCSI driver"
+
+MODULE_DESCRIPTION(PVSCSI_LINUX_DRIVER_DESC);
+MODULE_AUTHOR("VMware, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PVSCSI_DRIVER_VERSION_STRING);
+
+#define PVSCSI_DEFAULT_NUM_PAGES_PER_RING      8
+#define PVSCSI_DEFAULT_NUM_PAGES_MSG_RING      1
+#define PVSCSI_DEFAULT_QUEUE_DEPTH             64
+#define SGL_SIZE                               PAGE_SIZE
+
+struct pvscsi_sg_list {
+       struct PVSCSISGElement sge[PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT];
+};
+
+struct pvscsi_ctx {
+       /*
+        * The index of the context in cmd_map serves as the context ID for a
+        * 1-to-1 mapping completions back to requests.
+        */
+       struct scsi_cmnd        *cmd;
+       struct pvscsi_sg_list   *sgl;
+       struct list_head        list;
+       dma_addr_t              dataPA;
+       dma_addr_t              sensePA;
+       dma_addr_t              sglPA;
+};
+
+struct pvscsi_adapter {
+       char                            *mmioBase;
+       unsigned int                    irq;
+       u8                              rev;
+       bool                            use_msi;
+       bool                            use_msix;
+       bool                            use_msg;
+
+       spinlock_t                      hw_lock;
+
+       struct workqueue_struct         *workqueue;
+       struct work_struct              work;
+
+       struct PVSCSIRingReqDesc        *req_ring;
+       unsigned                        req_pages;
+       unsigned                        req_depth;
+       dma_addr_t                      reqRingPA;
+
+       struct PVSCSIRingCmpDesc        *cmp_ring;
+       unsigned                        cmp_pages;
+       dma_addr_t                      cmpRingPA;
+
+       struct PVSCSIRingMsgDesc        *msg_ring;
+       unsigned                        msg_pages;
+       dma_addr_t                      msgRingPA;
+
+       struct PVSCSIRingsState         *rings_state;
+       dma_addr_t                      ringStatePA;
+
+       struct pci_dev                  *dev;
+       struct Scsi_Host                *host;
+
+       struct list_head                cmd_pool;
+       struct pvscsi_ctx               *cmd_map;
+};
+
+
+/* Command line parameters */
+static int pvscsi_ring_pages     = PVSCSI_DEFAULT_NUM_PAGES_PER_RING;
+static int pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING;
+static int pvscsi_cmd_per_lun    = PVSCSI_DEFAULT_QUEUE_DEPTH;
+static bool pvscsi_disable_msi;
+static bool pvscsi_disable_msix;
+static bool pvscsi_use_msg       = true;
+
+#define PVSCSI_RW (S_IRUSR | S_IWUSR)
+
+module_param_named(ring_pages, pvscsi_ring_pages, int, PVSCSI_RW);
+MODULE_PARM_DESC(ring_pages, "Number of pages per req/cmp ring - (default="
+                __stringify(PVSCSI_DEFAULT_NUM_PAGES_PER_RING) ")");
+
+module_param_named(msg_ring_pages, pvscsi_msg_ring_pages, int, PVSCSI_RW);
+MODULE_PARM_DESC(msg_ring_pages, "Number of pages for the msg ring - (default="
+                __stringify(PVSCSI_DEFAULT_NUM_PAGES_MSG_RING) ")");
+
+module_param_named(cmd_per_lun, pvscsi_cmd_per_lun, int, PVSCSI_RW);
+MODULE_PARM_DESC(cmd_per_lun, "Maximum commands per lun - (default="
+                __stringify(PVSCSI_MAX_REQ_QUEUE_DEPTH) ")");
+
+module_param_named(disable_msi, pvscsi_disable_msi, bool, PVSCSI_RW);
+MODULE_PARM_DESC(disable_msi, "Disable MSI use in driver - (default=0)");
+
+module_param_named(disable_msix, pvscsi_disable_msix, bool, PVSCSI_RW);
+MODULE_PARM_DESC(disable_msix, "Disable MSI-X use in driver - (default=0)");
+
+module_param_named(use_msg, pvscsi_use_msg, bool, PVSCSI_RW);
+MODULE_PARM_DESC(use_msg, "Use msg ring when available - (default=1)");
+
+static const struct pci_device_id pvscsi_pci_tbl[] = {
+       { PCI_VDEVICE(VMWARE, PCI_DEVICE_ID_VMWARE_PVSCSI) },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pvscsi_pci_tbl);
+
+static struct device *
+pvscsi_dev(const struct pvscsi_adapter *adapter)
+{
+       return &(adapter->dev->dev);
+}
+
+static struct pvscsi_ctx *
+pvscsi_find_context(const struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd)
+{
+       struct pvscsi_ctx *ctx, *end;
+
+       end = &adapter->cmd_map[adapter->req_depth];
+       for (ctx = adapter->cmd_map; ctx < end; ctx++)
+               if (ctx->cmd == cmd)
+                       return ctx;
+
+       return NULL;
+}
+
+static struct pvscsi_ctx *
+pvscsi_acquire_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd)
+{
+       struct pvscsi_ctx *ctx;
+
+       if (list_empty(&adapter->cmd_pool))
+               return NULL;
+
+       ctx = list_first_entry(&adapter->cmd_pool, struct pvscsi_ctx, list);
+       ctx->cmd = cmd;
+       list_del(&ctx->list);
+
+       return ctx;
+}
+
+static void pvscsi_release_context(struct pvscsi_adapter *adapter,
+                                  struct pvscsi_ctx *ctx)
+{
+       ctx->cmd = NULL;
+       list_add(&ctx->list, &adapter->cmd_pool);
+}
+
+/*
+ * Map a pvscsi_ctx struct to a context ID field value; we map to a simple
+ * non-zero integer. ctx always points to an entry in cmd_map array, hence
+ * the return value is always >=1.
+ */
+static u64 pvscsi_map_context(const struct pvscsi_adapter *adapter,
+                             const struct pvscsi_ctx *ctx)
+{
+       return ctx - adapter->cmd_map + 1;
+}
+
+static struct pvscsi_ctx *
+pvscsi_get_context(const struct pvscsi_adapter *adapter, u64 context)
+{
+       return &adapter->cmd_map[context - 1];
+}
+
+static void pvscsi_reg_write(const struct pvscsi_adapter *adapter,
+                            u32 offset, u32 val)
+{
+       writel(val, adapter->mmioBase + offset);
+}
+
+static u32 pvscsi_reg_read(const struct pvscsi_adapter *adapter, u32 offset)
+{
+       return readl(adapter->mmioBase + offset);
+}
+
+static u32 pvscsi_read_intr_status(const struct pvscsi_adapter *adapter)
+{
+       return pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_INTR_STATUS);
+}
+
+static void pvscsi_write_intr_status(const struct pvscsi_adapter *adapter,
+                                    u32 val)
+{
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_STATUS, val);
+}
+
+static void pvscsi_unmask_intr(const struct pvscsi_adapter *adapter)
+{
+       u32 intr_bits;
+
+       intr_bits = PVSCSI_INTR_CMPL_MASK;
+       if (adapter->use_msg)
+               intr_bits |= PVSCSI_INTR_MSG_MASK;
+
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, intr_bits);
+}
+
+static void pvscsi_mask_intr(const struct pvscsi_adapter *adapter)
+{
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, 0);
+}
+
+static void pvscsi_write_cmd_desc(const struct pvscsi_adapter *adapter,
+                                 u32 cmd, const void *desc, size_t len)
+{
+       const u32 *ptr = desc;
+       size_t i;
+
+       len /= sizeof(*ptr);
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, cmd);
+       for (i = 0; i < len; i++)
+               pvscsi_reg_write(adapter,
+                                PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]);
+}
+
+static void pvscsi_abort_cmd(const struct pvscsi_adapter *adapter,
+                            const struct pvscsi_ctx *ctx)
+{
+       struct PVSCSICmdDescAbortCmd cmd = { 0 };
+
+       cmd.target = ctx->cmd->device->id;
+       cmd.context = pvscsi_map_context(adapter, ctx);
+
+       pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ABORT_CMD, &cmd, sizeof(cmd));
+}
+
+static void pvscsi_kick_rw_io(const struct pvscsi_adapter *adapter)
+{
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_RW_IO, 0);
+}
+
+static void pvscsi_process_request_ring(const struct pvscsi_adapter *adapter)
+{
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0);
+}
+
+static int scsi_is_rw(unsigned char op)
+{
+       return op == READ_6  || op == WRITE_6 ||
+              op == READ_10 || op == WRITE_10 ||
+              op == READ_12 || op == WRITE_12 ||
+              op == READ_16 || op == WRITE_16;
+}
+
+static void pvscsi_kick_io(const struct pvscsi_adapter *adapter,
+                          unsigned char op)
+{
+       if (scsi_is_rw(op))
+               pvscsi_kick_rw_io(adapter);
+       else
+               pvscsi_process_request_ring(adapter);
+}
+
+static void ll_adapter_reset(const struct pvscsi_adapter *adapter)
+{
+       dev_dbg(pvscsi_dev(adapter), "Adapter Reset on %p\n", adapter);
+
+       pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ADAPTER_RESET, NULL, 0);
+}
+
+static void ll_bus_reset(const struct pvscsi_adapter *adapter)
+{
+       dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter);
+
+       pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0);
+}
+
+static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target)
+{
+       struct PVSCSICmdDescResetDevice cmd = { 0 };
+
+       dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target);
+
+       cmd.target = target;
+
+       pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_DEVICE,
+                             &cmd, sizeof(cmd));
+}
+
+static void pvscsi_create_sg(struct pvscsi_ctx *ctx,
+                            struct scatterlist *sg, unsigned count)
+{
+       unsigned i;
+       struct PVSCSISGElement *sge;
+
+       BUG_ON(count > PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT);
+
+       sge = &ctx->sgl->sge[0];
+       for (i = 0; i < count; i++, sg++) {
+               sge[i].addr   = sg_dma_address(sg);
+               sge[i].length = sg_dma_len(sg);
+               sge[i].flags  = 0;
+       }
+}
+
+/*
+ * Map all data buffers for a command into PCI space and
+ * setup the scatter/gather list if needed.
+ */
+static void pvscsi_map_buffers(struct pvscsi_adapter *adapter,
+                              struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd,
+                              struct PVSCSIRingReqDesc *e)
+{
+       unsigned count;
+       unsigned bufflen = scsi_bufflen(cmd);
+       struct scatterlist *sg;
+
+       e->dataLen = bufflen;
+       e->dataAddr = 0;
+       if (bufflen == 0)
+               return;
+
+       sg = scsi_sglist(cmd);
+       count = scsi_sg_count(cmd);
+       if (count != 0) {
+               int segs = scsi_dma_map(cmd);
+               if (segs > 1) {
+                       pvscsi_create_sg(ctx, sg, segs);
+
+                       e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST;
+                       ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl,
+                                                   SGL_SIZE, PCI_DMA_TODEVICE);
+                       e->dataAddr = ctx->sglPA;
+               } else
+                       e->dataAddr = sg_dma_address(sg);
+       } else {
+               /*
+                * In case there is no S/G list, scsi_sglist points
+                * directly to the buffer.
+                */
+               ctx->dataPA = pci_map_single(adapter->dev, sg, bufflen,
+                                            cmd->sc_data_direction);
+               e->dataAddr = ctx->dataPA;
+       }
+}
+
+static void pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter,
+                                struct pvscsi_ctx *ctx)
+{
+       struct scsi_cmnd *cmd;
+       unsigned bufflen;
+
+       cmd = ctx->cmd;
+       bufflen = scsi_bufflen(cmd);
+
+       if (bufflen != 0) {
+               unsigned count = scsi_sg_count(cmd);
+
+               if (count != 0) {
+                       scsi_dma_unmap(cmd);
+                       if (ctx->sglPA) {
+                               pci_unmap_single(adapter->dev, ctx->sglPA,
+                                                SGL_SIZE, PCI_DMA_TODEVICE);
+                               ctx->sglPA = 0;
+                       }
+               } else
+                       pci_unmap_single(adapter->dev, ctx->dataPA, bufflen,
+                                        cmd->sc_data_direction);
+       }
+       if (cmd->sense_buffer)
+               pci_unmap_single(adapter->dev, ctx->sensePA,
+                                SCSI_SENSE_BUFFERSIZE, PCI_DMA_FROMDEVICE);
+}
+
+static int __devinit pvscsi_allocate_rings(struct pvscsi_adapter *adapter)
+{
+       adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE,
+                                                   &adapter->ringStatePA);
+       if (!adapter->rings_state)
+               return -ENOMEM;
+
+       adapter->req_pages = min(PVSCSI_MAX_NUM_PAGES_REQ_RING,
+                                pvscsi_ring_pages);
+       adapter->req_depth = adapter->req_pages
+                                       * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+       adapter->req_ring = pci_alloc_consistent(adapter->dev,
+                                                adapter->req_pages * PAGE_SIZE,
+                                                &adapter->reqRingPA);
+       if (!adapter->req_ring)
+               return -ENOMEM;
+
+       adapter->cmp_pages = min(PVSCSI_MAX_NUM_PAGES_CMP_RING,
+                                pvscsi_ring_pages);
+       adapter->cmp_ring = pci_alloc_consistent(adapter->dev,
+                                                adapter->cmp_pages * PAGE_SIZE,
+                                                &adapter->cmpRingPA);
+       if (!adapter->cmp_ring)
+               return -ENOMEM;
+
+       BUG_ON(!IS_ALIGNED(adapter->ringStatePA, PAGE_SIZE));
+       BUG_ON(!IS_ALIGNED(adapter->reqRingPA, PAGE_SIZE));
+       BUG_ON(!IS_ALIGNED(adapter->cmpRingPA, PAGE_SIZE));
+
+       if (!adapter->use_msg)
+               return 0;
+
+       adapter->msg_pages = min(PVSCSI_MAX_NUM_PAGES_MSG_RING,
+                                pvscsi_msg_ring_pages);
+       adapter->msg_ring = pci_alloc_consistent(adapter->dev,
+                                                adapter->msg_pages * PAGE_SIZE,
+                                                &adapter->msgRingPA);
+       if (!adapter->msg_ring)
+               return -ENOMEM;
+       BUG_ON(!IS_ALIGNED(adapter->msgRingPA, PAGE_SIZE));
+
+       return 0;
+}
+
+static void pvscsi_setup_all_rings(const struct pvscsi_adapter *adapter)
+{
+       struct PVSCSICmdDescSetupRings cmd = { 0 };
+       dma_addr_t base;
+       unsigned i;
+
+       cmd.ringsStatePPN   = adapter->ringStatePA >> PAGE_SHIFT;
+       cmd.reqRingNumPages = adapter->req_pages;
+       cmd.cmpRingNumPages = adapter->cmp_pages;
+
+       base = adapter->reqRingPA;
+       for (i = 0; i < adapter->req_pages; i++) {
+               cmd.reqRingPPNs[i] = base >> PAGE_SHIFT;
+               base += PAGE_SIZE;
+       }
+
+       base = adapter->cmpRingPA;
+       for (i = 0; i < adapter->cmp_pages; i++) {
+               cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT;
+               base += PAGE_SIZE;
+       }
+
+       memset(adapter->rings_state, 0, PAGE_SIZE);
+       memset(adapter->req_ring, 0, adapter->req_pages * PAGE_SIZE);
+       memset(adapter->cmp_ring, 0, adapter->cmp_pages * PAGE_SIZE);
+
+       pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_RINGS,
+                             &cmd, sizeof(cmd));
+
+       if (adapter->use_msg) {
+               struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 };
+
+               cmd_msg.numPages = adapter->msg_pages;
+
+               base = adapter->msgRingPA;
+               for (i = 0; i < adapter->msg_pages; i++) {
+                       cmd_msg.ringPPNs[i] = base >> PAGE_SHIFT;
+                       base += PAGE_SIZE;
+               }
+               memset(adapter->msg_ring, 0, adapter->msg_pages * PAGE_SIZE);
+
+               pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_MSG_RING,
+                                     &cmd_msg, sizeof(cmd_msg));
+       }
+}
+
+/*
+ * Pull a completion descriptor off and pass the completion back
+ * to the SCSI mid layer.
+ */
+static void pvscsi_complete_request(struct pvscsi_adapter *adapter,
+                                   const struct PVSCSIRingCmpDesc *e)
+{
+       struct pvscsi_ctx *ctx;
+       struct scsi_cmnd *cmd;
+       u32 btstat = e->hostStatus;
+       u32 sdstat = e->scsiStatus;
+
+       ctx = pvscsi_get_context(adapter, e->context);
+       cmd = ctx->cmd;
+       pvscsi_unmap_buffers(adapter, ctx);
+       pvscsi_release_context(adapter, ctx);
+       cmd->result = 0;
+
+       if (sdstat != SAM_STAT_GOOD &&
+           (btstat == BTSTAT_SUCCESS ||
+            btstat == BTSTAT_LINKED_COMMAND_COMPLETED ||
+            btstat == BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG)) {
+               cmd->result = (DID_OK << 16) | sdstat;
+               if (sdstat == SAM_STAT_CHECK_CONDITION && cmd->sense_buffer)
+                       cmd->result |= (DRIVER_SENSE << 24);
+       } else
+               switch (btstat) {
+               case BTSTAT_SUCCESS:
+               case BTSTAT_LINKED_COMMAND_COMPLETED:
+               case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG:
+                       /* If everything went fine, let's move on..  */
+                       cmd->result = (DID_OK << 16);
+                       break;
+
+               case BTSTAT_DATARUN:
+               case BTSTAT_DATA_UNDERRUN:
+                       /* Report residual data in underruns */
+                       scsi_set_resid(cmd, scsi_bufflen(cmd) - e->dataLen);
+                       cmd->result = (DID_ERROR << 16);
+                       break;
+
+               case BTSTAT_SELTIMEO:
+                       /* Our emulation returns this for non-connected devs */
+                       cmd->result = (DID_BAD_TARGET << 16);
+                       break;
+
+               case BTSTAT_LUNMISMATCH:
+               case BTSTAT_TAGREJECT:
+               case BTSTAT_BADMSG:
+                       cmd->result = (DRIVER_INVALID << 24);
+                       /* fall through */
+
+               case BTSTAT_HAHARDWARE:
+               case BTSTAT_INVPHASE:
+               case BTSTAT_HATIMEOUT:
+               case BTSTAT_NORESPONSE:
+               case BTSTAT_DISCONNECT:
+               case BTSTAT_HASOFTWARE:
+               case BTSTAT_BUSFREE:
+               case BTSTAT_SENSFAILED:
+                       cmd->result |= (DID_ERROR << 16);
+                       break;
+
+               case BTSTAT_SENTRST:
+               case BTSTAT_RECVRST:
+               case BTSTAT_BUSRESET:
+                       cmd->result = (DID_RESET << 16);
+                       break;
+
+               case BTSTAT_ABORTQUEUE:
+                       cmd->result = (DID_ABORT << 16);
+                       break;
+
+               case BTSTAT_SCSIPARITY:
+                       cmd->result = (DID_PARITY << 16);
+                       break;
+
+               default:
+                       cmd->result = (DID_ERROR << 16);
+                       scmd_printk(KERN_DEBUG, cmd,
+                                   "Unknown completion status: 0x%x\n",
+                                   btstat);
+       }
+
+       dev_dbg(&cmd->device->sdev_gendev,
+               "cmd=%p %x ctx=%p result=0x%x status=0x%x,%x\n",
+               cmd, cmd->cmnd[0], ctx, cmd->result, btstat, sdstat);
+
+       cmd->scsi_done(cmd);
+}
+
+/*
+ * barrier usage : Since the PVSCSI device is emulated, there could be cases
+ * where we may want to serialize some accesses between the driver and the
+ * emulation layer. We use compiler barriers instead of the more expensive
+ * memory barriers because PVSCSI is only supported on X86 which has strong
+ * memory access ordering.
+ */
+static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter)
+{
+       struct PVSCSIRingsState *s = adapter->rings_state;
+       struct PVSCSIRingCmpDesc *ring = adapter->cmp_ring;
+       u32 cmp_entries = s->cmpNumEntriesLog2;
+
+       while (s->cmpConsIdx != s->cmpProdIdx) {
+               struct PVSCSIRingCmpDesc *e = ring + (s->cmpConsIdx &
+                                                     MASK(cmp_entries));
+               /*
+                * This barrier() ensures that *e is not dereferenced while
+                * the device emulation still writes data into the slot.
+                * Since the device emulation advances s->cmpProdIdx only after
+                * updating the slot we want to check it first.
+                */
+               barrier();
+               pvscsi_complete_request(adapter, e);
+               /*
+                * This barrier() ensures that compiler doesn't reorder write
+                * to s->cmpConsIdx before the read of (*e) inside
+                * pvscsi_complete_request. Otherwise, device emulation may
+                * overwrite *e before we had a chance to read it.
+                */
+               barrier();
+               s->cmpConsIdx++;
+       }
+}
+
+/*
+ * Translate a Linux SCSI request into a request ring entry.
+ */
+static int pvscsi_queue_ring(struct pvscsi_adapter *adapter,
+                            struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd)
+{
+       struct PVSCSIRingsState *s;
+       struct PVSCSIRingReqDesc *e;
+       struct scsi_device *sdev;
+       u32 req_entries;
+
+       s = adapter->rings_state;
+       sdev = cmd->device;
+       req_entries = s->reqNumEntriesLog2;
+
+       /*
+        * If this condition holds, we might have room on the request ring, but
+        * we might not have room on the completion ring for the response.
+        * However, we have already ruled out this possibility - we would not
+        * have successfully allocated a context if it were true, since we only
+        * have one context per request entry.  Check for it anyway, since it
+        * would be a serious bug.
+        */
+       if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) {
+               scmd_printk(KERN_ERR, cmd, "vmw_pvscsi: "
+                           "ring full: reqProdIdx=%d cmpConsIdx=%d\n",
+                           s->reqProdIdx, s->cmpConsIdx);
+               return -1;
+       }
+
+       e = adapter->req_ring + (s->reqProdIdx & MASK(req_entries));
+
+       e->bus    = sdev->channel;
+       e->target = sdev->id;
+       memset(e->lun, 0, sizeof(e->lun));
+       e->lun[1] = sdev->lun;
+
+       if (cmd->sense_buffer) {
+               ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer,
+                                             SCSI_SENSE_BUFFERSIZE,
+                                             PCI_DMA_FROMDEVICE);
+               e->senseAddr = ctx->sensePA;
+               e->senseLen = SCSI_SENSE_BUFFERSIZE;
+       } else {
+               e->senseLen  = 0;
+               e->senseAddr = 0;
+       }
+       e->cdbLen   = cmd->cmd_len;
+       e->vcpuHint = smp_processor_id();
+       memcpy(e->cdb, cmd->cmnd, e->cdbLen);
+
+       e->tag = SIMPLE_QUEUE_TAG;
+       if (sdev->tagged_supported &&
+           (cmd->tag == HEAD_OF_QUEUE_TAG ||
+            cmd->tag == ORDERED_QUEUE_TAG))
+               e->tag = cmd->tag;
+
+       if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+               e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST;
+       else if (cmd->sc_data_direction == DMA_TO_DEVICE)
+               e->flags = PVSCSI_FLAG_CMD_DIR_TODEVICE;
+       else if (cmd->sc_data_direction == DMA_NONE)
+               e->flags = PVSCSI_FLAG_CMD_DIR_NONE;
+       else
+               e->flags = 0;
+
+       pvscsi_map_buffers(adapter, ctx, cmd, e);
+
+       e->context = pvscsi_map_context(adapter, ctx);
+
+       barrier();
+
+       s->reqProdIdx++;
+
+       return 0;
+}
+
+static int pvscsi_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+{
+       struct Scsi_Host *host = cmd->device->host;
+       struct pvscsi_adapter *adapter = shost_priv(host);
+       struct pvscsi_ctx *ctx;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+
+       ctx = pvscsi_acquire_context(adapter, cmd);
+       if (!ctx || pvscsi_queue_ring(adapter, ctx, cmd) != 0) {
+               if (ctx)
+                       pvscsi_release_context(adapter, ctx);
+               spin_unlock_irqrestore(&adapter->hw_lock, flags);
+               return SCSI_MLQUEUE_HOST_BUSY;
+       }
+
+       cmd->scsi_done = done;
+
+       dev_dbg(&cmd->device->sdev_gendev,
+               "queued cmd %p, ctx %p, op=%x\n", cmd, ctx, cmd->cmnd[0]);
+
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+       pvscsi_kick_io(adapter, cmd->cmnd[0]);
+
+       return 0;
+}
+
+static int pvscsi_abort(struct scsi_cmnd *cmd)
+{
+       struct pvscsi_adapter *adapter = shost_priv(cmd->device->host);
+       struct pvscsi_ctx *ctx;
+       unsigned long flags;
+
+       scmd_printk(KERN_DEBUG, cmd, "task abort on host %u, %p\n",
+                   adapter->host->host_no, cmd);
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+
+       /*
+        * Poll the completion ring first - we might be trying to abort
+        * a command that is waiting to be dispatched in the completion ring.
+        */
+       pvscsi_process_completion_ring(adapter);
+
+       /*
+        * If there is no context for the command, it either already succeeded
+        * or else was never properly issued.  Not our problem.
+        */
+       ctx = pvscsi_find_context(adapter, cmd);
+       if (!ctx) {
+               scmd_printk(KERN_DEBUG, cmd, "Failed to abort cmd %p\n", cmd);
+               goto out;
+       }
+
+       pvscsi_abort_cmd(adapter, ctx);
+
+       pvscsi_process_completion_ring(adapter);
+
+out:
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+       return SUCCESS;
+}
+
+/*
+ * Abort all outstanding requests.  This is only safe to use if the completion
+ * ring will never be walked again or the device has been reset, because it
+ * destroys the 1-1 mapping between context field passed to emulation and our
+ * request structure.
+ */
+static void pvscsi_reset_all(struct pvscsi_adapter *adapter)
+{
+       unsigned i;
+
+       for (i = 0; i < adapter->req_depth; i++) {
+               struct pvscsi_ctx *ctx = &adapter->cmd_map[i];
+               struct scsi_cmnd *cmd = ctx->cmd;
+               if (cmd) {
+                       scmd_printk(KERN_ERR, cmd,
+                                   "Forced reset on cmd %p\n", cmd);
+                       pvscsi_unmap_buffers(adapter, ctx);
+                       pvscsi_release_context(adapter, ctx);
+                       cmd->result = (DID_RESET << 16);
+                       cmd->scsi_done(cmd);
+               }
+       }
+}
+
+static int pvscsi_host_reset(struct scsi_cmnd *cmd)
+{
+       struct Scsi_Host *host = cmd->device->host;
+       struct pvscsi_adapter *adapter = shost_priv(host);
+       unsigned long flags;
+       bool use_msg;
+
+       scmd_printk(KERN_INFO, cmd, "SCSI Host reset\n");
+
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+
+       use_msg = adapter->use_msg;
+
+       if (use_msg) {
+               adapter->use_msg = 0;
+               spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+               /*
+                * Now that we know that the ISR won't add more work on the
+                * workqueue we can safely flush any outstanding work.
+                */
+               flush_workqueue(adapter->workqueue);
+               spin_lock_irqsave(&adapter->hw_lock, flags);
+       }
+
+       /*
+        * We're going to tear down the entire ring structure and set it back
+        * up, so stalling new requests until all completions are flushed and
+        * the rings are back in place.
+        */
+
+       pvscsi_process_request_ring(adapter);
+
+       ll_adapter_reset(adapter);
+
+       /*
+        * Now process any completions.  Note we do this AFTER adapter reset,
+        * which is strange, but stops races where completions get posted
+        * between processing the ring and issuing the reset.  The backend will
+        * not touch the ring memory after reset, so the immediately pre-reset
+        * completion ring state is still valid.
+        */
+       pvscsi_process_completion_ring(adapter);
+
+       pvscsi_reset_all(adapter);
+       adapter->use_msg = use_msg;
+       pvscsi_setup_all_rings(adapter);
+       pvscsi_unmask_intr(adapter);
+
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+       return SUCCESS;
+}
+
+static int pvscsi_bus_reset(struct scsi_cmnd *cmd)
+{
+       struct Scsi_Host *host = cmd->device->host;
+       struct pvscsi_adapter *adapter = shost_priv(host);
+       unsigned long flags;
+
+       scmd_printk(KERN_INFO, cmd, "SCSI Bus reset\n");
+
+       /*
+        * We don't want to queue new requests for this bus after
+        * flushing all pending requests to emulation, since new
+        * requests could then sneak in during this bus reset phase,
+        * so take the lock now.
+        */
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+
+       pvscsi_process_request_ring(adapter);
+       ll_bus_reset(adapter);
+       pvscsi_process_completion_ring(adapter);
+
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+       return SUCCESS;
+}
+
+static int pvscsi_device_reset(struct scsi_cmnd *cmd)
+{
+       struct Scsi_Host *host = cmd->device->host;
+       struct pvscsi_adapter *adapter = shost_priv(host);
+       unsigned long flags;
+
+       scmd_printk(KERN_INFO, cmd, "SCSI device reset on scsi%u:%u\n",
+                   host->host_no, cmd->device->id);
+
+       /*
+        * We don't want to queue new requests for this device after flushing
+        * all pending requests to emulation, since new requests could then
+        * sneak in during this device reset phase, so take the lock now.
+        */
+       spin_lock_irqsave(&adapter->hw_lock, flags);
+
+       pvscsi_process_request_ring(adapter);
+       ll_device_reset(adapter, cmd->device->id);
+       pvscsi_process_completion_ring(adapter);
+
+       spin_unlock_irqrestore(&adapter->hw_lock, flags);
+
+       return SUCCESS;
+}
+
+static struct scsi_host_template pvscsi_template;
+
+static const char *pvscsi_info(struct Scsi_Host *host)
+{
+       struct pvscsi_adapter *adapter = shost_priv(host);
+       static char buf[256];
+
+       sprintf(buf, "VMware PVSCSI storage adapter rev %d, req/cmp/msg rings: "
+               "%u/%u/%u pages, cmd_per_lun=%u", adapter->rev,
+               adapter->req_pages, adapter->cmp_pages, adapter->msg_pages,
+               pvscsi_template.cmd_per_lun);
+
+       return buf;
+}
+
+static struct scsi_host_template pvscsi_template = {
+       .module                         = THIS_MODULE,
+       .name                           = "VMware PVSCSI Host Adapter",
+       .proc_name                      = "vmw_pvscsi",
+       .info                           = pvscsi_info,
+       .queuecommand                   = pvscsi_queue,
+       .this_id                        = -1,
+       .sg_tablesize                   = PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT,
+       .dma_boundary                   = UINT_MAX,
+       .max_sectors                    = 0xffff,
+       .use_clustering                 = ENABLE_CLUSTERING,
+       .eh_abort_handler               = pvscsi_abort,
+       .eh_device_reset_handler        = pvscsi_device_reset,
+       .eh_bus_reset_handler           = pvscsi_bus_reset,
+       .eh_host_reset_handler          = pvscsi_host_reset,
+};
+
+static void pvscsi_process_msg(const struct pvscsi_adapter *adapter,
+                              const struct PVSCSIRingMsgDesc *e)
+{
+       struct PVSCSIRingsState *s = adapter->rings_state;
+       struct Scsi_Host *host = adapter->host;
+       struct scsi_device *sdev;
+
+       printk(KERN_INFO "vmw_pvscsi: msg type: 0x%x - MSG RING: %u/%u (%u) \n",
+              e->type, s->msgProdIdx, s->msgConsIdx, s->msgNumEntriesLog2);
+
+       BUILD_BUG_ON(PVSCSI_MSG_LAST != 2);
+
+       if (e->type == PVSCSI_MSG_DEV_ADDED) {
+               struct PVSCSIMsgDescDevStatusChanged *desc;
+               desc = (struct PVSCSIMsgDescDevStatusChanged *)e;
+
+               printk(KERN_INFO
+                      "vmw_pvscsi: msg: device added at scsi%u:%u:%u\n",
+                      desc->bus, desc->target, desc->lun[1]);
+
+               if (!scsi_host_get(host))
+                       return;
+
+               sdev = scsi_device_lookup(host, desc->bus, desc->target,
+                                         desc->lun[1]);
+               if (sdev) {
+                       printk(KERN_INFO "vmw_pvscsi: device already exists\n");
+                       scsi_device_put(sdev);
+               } else
+                       scsi_add_device(adapter->host, desc->bus,
+                                       desc->target, desc->lun[1]);
+
+               scsi_host_put(host);
+       } else if (e->type == PVSCSI_MSG_DEV_REMOVED) {
+               struct PVSCSIMsgDescDevStatusChanged *desc;
+               desc = (struct PVSCSIMsgDescDevStatusChanged *)e;
+
+               printk(KERN_INFO
+                      "vmw_pvscsi: msg: device removed at scsi%u:%u:%u\n",
+                      desc->bus, desc->target, desc->lun[1]);
+
+               if (!scsi_host_get(host))
+                       return;
+
+               sdev = scsi_device_lookup(host, desc->bus, desc->target,
+                                         desc->lun[1]);
+               if (sdev) {
+                       scsi_remove_device(sdev);
+                       scsi_device_put(sdev);
+               } else
+                       printk(KERN_INFO
+                              "vmw_pvscsi: failed to lookup scsi%u:%u:%u\n",
+                              desc->bus, desc->target, desc->lun[1]);
+
+               scsi_host_put(host);
+       }
+}
+
+static int pvscsi_msg_pending(const struct pvscsi_adapter *adapter)
+{
+       struct PVSCSIRingsState *s = adapter->rings_state;
+
+       return s->msgProdIdx != s->msgConsIdx;
+}
+
+static void pvscsi_process_msg_ring(const struct pvscsi_adapter *adapter)
+{
+       struct PVSCSIRingsState *s = adapter->rings_state;
+       struct PVSCSIRingMsgDesc *ring = adapter->msg_ring;
+       u32 msg_entries = s->msgNumEntriesLog2;
+
+       while (pvscsi_msg_pending(adapter)) {
+               struct PVSCSIRingMsgDesc *e = ring + (s->msgConsIdx &
+                                                     MASK(msg_entries));
+
+               barrier();
+               pvscsi_process_msg(adapter, e);
+               barrier();
+               s->msgConsIdx++;
+       }
+}
+
+static void pvscsi_msg_workqueue_handler(struct work_struct *data)
+{
+       struct pvscsi_adapter *adapter;
+
+       adapter = container_of(data, struct pvscsi_adapter, work);
+
+       pvscsi_process_msg_ring(adapter);
+}
+
+static int pvscsi_setup_msg_workqueue(struct pvscsi_adapter *adapter)
+{
+       char name[32];
+
+       if (!pvscsi_use_msg)
+               return 0;
+
+       pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND,
+                        PVSCSI_CMD_SETUP_MSG_RING);
+
+       if (pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_COMMAND_STATUS) == -1)
+               return 0;
+
+       snprintf(name, sizeof(name),
+                "vmw_pvscsi_wq_%u", adapter->host->host_no);
+
+       adapter->workqueue = create_singlethread_workqueue(name);
+       if (!adapter->workqueue) {
+               printk(KERN_ERR "vmw_pvscsi: failed to create work queue\n");
+               return 0;
+       }
+       INIT_WORK(&adapter->work, pvscsi_msg_workqueue_handler);
+
+       return 1;
+}
+
+static irqreturn_t pvscsi_isr(int irq, void *devp)
+{
+       struct pvscsi_adapter *adapter = devp;
+       int handled;
+
+       if (adapter->use_msi || adapter->use_msix)
+               handled = true;
+       else {
+               u32 val = pvscsi_read_intr_status(adapter);
+               handled = (val & PVSCSI_INTR_ALL_SUPPORTED) != 0;
+               if (handled)
+                       pvscsi_write_intr_status(devp, val);
+       }
+
+       if (handled) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&adapter->hw_lock, flags);
+
+               pvscsi_process_completion_ring(adapter);
+               if (adapter->use_msg && pvscsi_msg_pending(adapter))
+                       queue_work(adapter->workqueue, &adapter->work);
+
+               spin_unlock_irqrestore(&adapter->hw_lock, flags);
+       }
+
+       return IRQ_RETVAL(handled);
+}
+
+static void pvscsi_free_sgls(const struct pvscsi_adapter *adapter)
+{
+       struct pvscsi_ctx *ctx = adapter->cmd_map;
+       unsigned i;
+
+       for (i = 0; i < adapter->req_depth; ++i, ++ctx)
+               free_pages((unsigned long)ctx->sgl, get_order(SGL_SIZE));
+}
+
+static int pvscsi_setup_msix(const struct pvscsi_adapter *adapter, int *irq)
+{
+       struct msix_entry entry = { 0, PVSCSI_VECTOR_COMPLETION };
+       int ret;
+
+       ret = pci_enable_msix(adapter->dev, &entry, 1);
+       if (ret)
+               return ret;
+
+       *irq = entry.vector;
+
+       return 0;
+}
+
+static void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter)
+{
+       if (adapter->irq) {
+               free_irq(adapter->irq, adapter);
+               adapter->irq = 0;
+       }
+       if (adapter->use_msi) {
+               pci_disable_msi(adapter->dev);
+               adapter->use_msi = 0;
+       } else if (adapter->use_msix) {
+               pci_disable_msix(adapter->dev);
+               adapter->use_msix = 0;
+       }
+}
+
+static void pvscsi_release_resources(struct pvscsi_adapter *adapter)
+{
+       pvscsi_shutdown_intr(adapter);
+
+       if (adapter->workqueue)
+               destroy_workqueue(adapter->workqueue);
+
+       if (adapter->mmioBase)
+               pci_iounmap(adapter->dev, adapter->mmioBase);
+
+       pci_release_regions(adapter->dev);
+
+       if (adapter->cmd_map) {
+               pvscsi_free_sgls(adapter);
+               kfree(adapter->cmd_map);
+       }
+
+       if (adapter->rings_state)
+               pci_free_consistent(adapter->dev, PAGE_SIZE,
+                                   adapter->rings_state, adapter->ringStatePA);
+
+       if (adapter->req_ring)
+               pci_free_consistent(adapter->dev,
+                                   adapter->req_pages * PAGE_SIZE,
+                                   adapter->req_ring, adapter->reqRingPA);
+
+       if (adapter->cmp_ring)
+               pci_free_consistent(adapter->dev,
+                                   adapter->cmp_pages * PAGE_SIZE,
+                                   adapter->cmp_ring, adapter->cmpRingPA);
+
+       if (adapter->msg_ring)
+               pci_free_consistent(adapter->dev,
+                                   adapter->msg_pages * PAGE_SIZE,
+                                   adapter->msg_ring, adapter->msgRingPA);
+}
+
+/*
+ * Allocate scatter gather lists.
+ *
+ * These are statically allocated.  Trying to be clever was not worth it.
+ *
+ * Dynamic allocation can fail, and we can't go deeep into the memory
+ * allocator, since we're a SCSI driver, and trying too hard to allocate
+ * memory might generate disk I/O.  We also don't want to fail disk I/O
+ * in that case because we can't get an allocation - the I/O could be
+ * trying to swap out data to free memory.  Since that is pathological,
+ * just use a statically allocated scatter list.
+ *
+ */
+static int __devinit pvscsi_allocate_sg(struct pvscsi_adapter *adapter)
+{
+       struct pvscsi_ctx *ctx;
+       int i;
+
+       ctx = adapter->cmd_map;
+       BUILD_BUG_ON(sizeof(struct pvscsi_sg_list) > SGL_SIZE);
+
+       for (i = 0; i < adapter->req_depth; ++i, ++ctx) {
+               ctx->sgl = (void *)__get_free_pages(GFP_KERNEL,
+                                                   get_order(SGL_SIZE));
+               ctx->sglPA = 0;
+               BUG_ON(!IS_ALIGNED(((unsigned long)ctx->sgl), PAGE_SIZE));
+               if (!ctx->sgl) {
+                       for (; i >= 0; --i, --ctx) {
+                               free_pages((unsigned long)ctx->sgl,
+                                          get_order(SGL_SIZE));
+                               ctx->sgl = NULL;
+                       }
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+static int __devinit pvscsi_probe(struct pci_dev *pdev,
+                                 const struct pci_device_id *id)
+{
+       struct pvscsi_adapter *adapter;
+       struct Scsi_Host *host;
+       unsigned int i;
+       unsigned long flags = 0;
+       int error;
+
+       error = -ENODEV;
+
+       if (pci_enable_device(pdev))
+               return error;
+
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0 &&
+           pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
+               printk(KERN_INFO "vmw_pvscsi: using 64bit dma\n");
+       } else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) == 0 &&
+                  pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) == 0) {
+               printk(KERN_INFO "vmw_pvscsi: using 32bit dma\n");
+       } else {
+               printk(KERN_ERR "vmw_pvscsi: failed to set DMA mask\n");
+               goto out_disable_device;
+       }
+
+       pvscsi_template.can_queue =
+               min(PVSCSI_MAX_NUM_PAGES_REQ_RING, pvscsi_ring_pages) *
+               PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE;
+       pvscsi_template.cmd_per_lun =
+               min(pvscsi_template.can_queue, pvscsi_cmd_per_lun);
+       host = scsi_host_alloc(&pvscsi_template, sizeof(struct pvscsi_adapter));
+       if (!host) {
+               printk(KERN_ERR "vmw_pvscsi: failed to allocate host\n");
+               goto out_disable_device;
+       }
+
+       adapter = shost_priv(host);
+       memset(adapter, 0, sizeof(*adapter));
+       adapter->dev  = pdev;
+       adapter->host = host;
+
+       spin_lock_init(&adapter->hw_lock);
+
+       host->max_channel = 0;
+       host->max_id      = 16;
+       host->max_lun     = 1;
+       host->max_cmd_len = 16;
+
+       adapter->rev = pdev->revision;
+
+       if (pci_request_regions(pdev, "vmw_pvscsi")) {
+               printk(KERN_ERR "vmw_pvscsi: pci memory selection failed\n");
+               goto out_free_host;
+       }
+
+       for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+               if ((pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO))
+                       continue;
+
+               if (pci_resource_len(pdev, i) < PVSCSI_MEM_SPACE_SIZE)
+                       continue;
+
+               break;
+       }
+
+       if (i == DEVICE_COUNT_RESOURCE) {
+               printk(KERN_ERR
+                      "vmw_pvscsi: adapter has no suitable MMIO region\n");
+               goto out_release_resources;
+       }
+
+       adapter->mmioBase = pci_iomap(pdev, i, PVSCSI_MEM_SPACE_SIZE);
+
+       if (!adapter->mmioBase) {
+               printk(KERN_ERR
+                      "vmw_pvscsi: can't iomap for BAR %d memsize %lu\n",
+                      i, PVSCSI_MEM_SPACE_SIZE);
+               goto out_release_resources;
+       }
+
+       pci_set_master(pdev);
+       pci_set_drvdata(pdev, host);
+
+       ll_adapter_reset(adapter);
+
+       adapter->use_msg = pvscsi_setup_msg_workqueue(adapter);
+
+       error = pvscsi_allocate_rings(adapter);
+       if (error) {
+               printk(KERN_ERR "vmw_pvscsi: unable to allocate ring memory\n");
+               goto out_release_resources;
+       }
+
+       /*
+        * From this point on we should reset the adapter if anything goes
+        * wrong.
+        */
+       pvscsi_setup_all_rings(adapter);
+
+       adapter->cmd_map = kcalloc(adapter->req_depth,
+                                  sizeof(struct pvscsi_ctx), GFP_KERNEL);
+       if (!adapter->cmd_map) {
+               printk(KERN_ERR "vmw_pvscsi: failed to allocate memory.\n");
+               error = -ENOMEM;
+               goto out_reset_adapter;
+       }
+
+       INIT_LIST_HEAD(&adapter->cmd_pool);
+       for (i = 0; i < adapter->req_depth; i++) {
+               struct pvscsi_ctx *ctx = adapter->cmd_map + i;
+               list_add(&ctx->list, &adapter->cmd_pool);
+       }
+
+       error = pvscsi_allocate_sg(adapter);
+       if (error) {
+               printk(KERN_ERR "vmw_pvscsi: unable to allocate s/g table\n");
+               goto out_reset_adapter;
+       }
+
+       if (!pvscsi_disable_msix &&
+           pvscsi_setup_msix(adapter, &adapter->irq) == 0) {
+               printk(KERN_INFO "vmw_pvscsi: using MSI-X\n");
+               adapter->use_msix = 1;
+       } else if (!pvscsi_disable_msi && pci_enable_msi(pdev) == 0) {
+               printk(KERN_INFO "vmw_pvscsi: using MSI\n");
+               adapter->use_msi = 1;
+               adapter->irq = pdev->irq;
+       } else {
+               printk(KERN_INFO "vmw_pvscsi: using INTx\n");
+               adapter->irq = pdev->irq;
+               flags = IRQF_SHARED;
+       }
+
+       error = request_irq(adapter->irq, pvscsi_isr, flags,
+                           "vmw_pvscsi", adapter);
+       if (error) {
+               printk(KERN_ERR
+                      "vmw_pvscsi: unable to request IRQ: %d\n", error);
+               adapter->irq = 0;
+               goto out_reset_adapter;
+       }
+
+       error = scsi_add_host(host, &pdev->dev);
+       if (error) {
+               printk(KERN_ERR
+                      "vmw_pvscsi: scsi_add_host failed: %d\n", error);
+               goto out_reset_adapter;
+       }
+
+       dev_info(&pdev->dev, "VMware PVSCSI rev %d host #%u\n",
+                adapter->rev, host->host_no);
+
+       pvscsi_unmask_intr(adapter);
+
+       scsi_scan_host(host);
+
+       return 0;
+
+out_reset_adapter:
+       ll_adapter_reset(adapter);
+out_release_resources:
+       pvscsi_release_resources(adapter);
+out_free_host:
+       scsi_host_put(host);
+out_disable_device:
+       pci_set_drvdata(pdev, NULL);
+       pci_disable_device(pdev);
+
+       return error;
+}
+
+static void __pvscsi_shutdown(struct pvscsi_adapter *adapter)
+{
+       pvscsi_mask_intr(adapter);
+
+       if (adapter->workqueue)
+               flush_workqueue(adapter->workqueue);
+
+       pvscsi_shutdown_intr(adapter);
+
+       pvscsi_process_request_ring(adapter);
+       pvscsi_process_completion_ring(adapter);
+       ll_adapter_reset(adapter);
+}
+
+static void pvscsi_shutdown(struct pci_dev *dev)
+{
+       struct Scsi_Host *host = pci_get_drvdata(dev);
+       struct pvscsi_adapter *adapter = shost_priv(host);
+
+       __pvscsi_shutdown(adapter);
+}
+
+static void pvscsi_remove(struct pci_dev *pdev)
+{
+       struct Scsi_Host *host = pci_get_drvdata(pdev);
+       struct pvscsi_adapter *adapter = shost_priv(host);
+
+       scsi_remove_host(host);
+
+       __pvscsi_shutdown(adapter);
+       pvscsi_release_resources(adapter);
+
+       scsi_host_put(host);
+
+       pci_set_drvdata(pdev, NULL);
+       pci_disable_device(pdev);
+}
+
+static struct pci_driver pvscsi_pci_driver = {
+       .name           = "vmw_pvscsi",
+       .id_table       = pvscsi_pci_tbl,
+       .probe          = pvscsi_probe,
+       .remove         = __devexit_p(pvscsi_remove),
+       .shutdown       = pvscsi_shutdown,
+};
+
+static int __init pvscsi_init(void)
+{
+       pr_info("%s - version %s\n",
+               PVSCSI_LINUX_DRIVER_DESC, PVSCSI_DRIVER_VERSION_STRING);
+       return pci_register_driver(&pvscsi_pci_driver);
+}
+
+static void __exit pvscsi_exit(void)
+{
+       pci_unregister_driver(&pvscsi_pci_driver);
+}
+
+module_init(pvscsi_init);
+module_exit(pvscsi_exit);
diff --git a/drivers/scsi/vmw_pvscsi.h b/drivers/scsi/vmw_pvscsi.h
new file mode 100644 (file)
index 0000000..62e36e7
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * VMware PVSCSI header file
+ *
+ * Copyright (C) 2008-2009, VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained by: Alok N Kataria <akataria@vmware.com>
+ *
+ */
+
+#ifndef _VMW_PVSCSI_H_
+#define _VMW_PVSCSI_H_
+
+#include <linux/types.h>
+
+#define PVSCSI_DRIVER_VERSION_STRING   "1.0.1.0-k"
+
+#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
+
+#define MASK(n)        ((1 << (n)) - 1)        /* make an n-bit mask */
+
+#define PCI_VENDOR_ID_VMWARE           0x15AD
+#define PCI_DEVICE_ID_VMWARE_PVSCSI    0x07C0
+
+/*
+ * host adapter status/error codes
+ */
+enum HostBusAdapterStatus {
+   BTSTAT_SUCCESS       = 0x00,  /* CCB complete normally with no errors */
+   BTSTAT_LINKED_COMMAND_COMPLETED           = 0x0a,
+   BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
+   BTSTAT_DATA_UNDERRUN = 0x0c,
+   BTSTAT_SELTIMEO      = 0x11,  /* SCSI selection timeout */
+   BTSTAT_DATARUN       = 0x12,  /* data overrun/underrun */
+   BTSTAT_BUSFREE       = 0x13,  /* unexpected bus free */
+   BTSTAT_INVPHASE      = 0x14,  /* invalid bus phase or sequence requested by target */
+   BTSTAT_LUNMISMATCH   = 0x17,  /* linked CCB has different LUN from first CCB */
+   BTSTAT_SENSFAILED    = 0x1b,  /* auto request sense failed */
+   BTSTAT_TAGREJECT     = 0x1c,  /* SCSI II tagged queueing message rejected by target */
+   BTSTAT_BADMSG        = 0x1d,  /* unsupported message received by the host adapter */
+   BTSTAT_HAHARDWARE    = 0x20,  /* host adapter hardware failed */
+   BTSTAT_NORESPONSE    = 0x21,  /* target did not respond to SCSI ATN, sent a SCSI RST */
+   BTSTAT_SENTRST       = 0x22,  /* host adapter asserted a SCSI RST */
+   BTSTAT_RECVRST       = 0x23,  /* other SCSI devices asserted a SCSI RST */
+   BTSTAT_DISCONNECT    = 0x24,  /* target device reconnected improperly (w/o tag) */
+   BTSTAT_BUSRESET      = 0x25,  /* host adapter issued BUS device reset */
+   BTSTAT_ABORTQUEUE    = 0x26,  /* abort queue generated */
+   BTSTAT_HASOFTWARE    = 0x27,  /* host adapter software error */
+   BTSTAT_HATIMEOUT     = 0x30,  /* host adapter hardware timeout error */
+   BTSTAT_SCSIPARITY    = 0x34,  /* SCSI parity error detected */
+};
+
+/*
+ * Register offsets.
+ *
+ * These registers are accessible both via i/o space and mm i/o.
+ */
+
+enum PVSCSIRegOffset {
+       PVSCSI_REG_OFFSET_COMMAND        =    0x0,
+       PVSCSI_REG_OFFSET_COMMAND_DATA   =    0x4,
+       PVSCSI_REG_OFFSET_COMMAND_STATUS =    0x8,
+       PVSCSI_REG_OFFSET_LAST_STS_0     =  0x100,
+       PVSCSI_REG_OFFSET_LAST_STS_1     =  0x104,
+       PVSCSI_REG_OFFSET_LAST_STS_2     =  0x108,
+       PVSCSI_REG_OFFSET_LAST_STS_3     =  0x10c,
+       PVSCSI_REG_OFFSET_INTR_STATUS    = 0x100c,
+       PVSCSI_REG_OFFSET_INTR_MASK      = 0x2010,
+       PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
+       PVSCSI_REG_OFFSET_DEBUG          = 0x3018,
+       PVSCSI_REG_OFFSET_KICK_RW_IO     = 0x4018,
+};
+
+/*
+ * Virtual h/w commands.
+ */
+
+enum PVSCSICommands {
+       PVSCSI_CMD_FIRST             = 0, /* has to be first */
+
+       PVSCSI_CMD_ADAPTER_RESET     = 1,
+       PVSCSI_CMD_ISSUE_SCSI        = 2,
+       PVSCSI_CMD_SETUP_RINGS       = 3,
+       PVSCSI_CMD_RESET_BUS         = 4,
+       PVSCSI_CMD_RESET_DEVICE      = 5,
+       PVSCSI_CMD_ABORT_CMD         = 6,
+       PVSCSI_CMD_CONFIG            = 7,
+       PVSCSI_CMD_SETUP_MSG_RING    = 8,
+       PVSCSI_CMD_DEVICE_UNPLUG     = 9,
+
+       PVSCSI_CMD_LAST              = 10  /* has to be last */
+};
+
+/*
+ * Command descriptor for PVSCSI_CMD_RESET_DEVICE --
+ */
+
+struct PVSCSICmdDescResetDevice {
+       u32     target;
+       u8      lun[8];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_ABORT_CMD --
+ *
+ * - currently does not support specifying the LUN.
+ * - _pad should be 0.
+ */
+
+struct PVSCSICmdDescAbortCmd {
+       u64     context;
+       u32     target;
+       u32     _pad;
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_RINGS --
+ *
+ * Notes:
+ * - reqRingNumPages and cmpRingNumPages need to be power of two.
+ * - reqRingNumPages and cmpRingNumPages need to be different from 0,
+ * - reqRingNumPages and cmpRingNumPages need to be inferior to
+ *   PVSCSI_SETUP_RINGS_MAX_NUM_PAGES.
+ */
+
+#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES        32
+struct PVSCSICmdDescSetupRings {
+       u32     reqRingNumPages;
+       u32     cmpRingNumPages;
+       u64     ringsStatePPN;
+       u64     reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+       u64     cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING --
+ *
+ * Notes:
+ * - this command was not supported in the initial revision of the h/w
+ *   interface. Before using it, you need to check that it is supported by
+ *   writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then
+ *   immediately after read the 'command status' register:
+ *       * a value of -1 means that the cmd is NOT supported,
+ *       * a value != -1 means that the cmd IS supported.
+ *   If it's supported the 'command status' register should return:
+ *      sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(u32).
+ * - this command should be issued _after_ the usual SETUP_RINGS so that the
+ *   RingsState page is already setup. If not, the command is a nop.
+ * - numPages needs to be a power of two,
+ * - numPages needs to be different from 0,
+ * - _pad should be zero.
+ */
+
+#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES  16
+
+struct PVSCSICmdDescSetupMsgRing {
+       u32     numPages;
+       u32     _pad;
+       u64     ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
+} __packed;
+
+enum PVSCSIMsgType {
+       PVSCSI_MSG_DEV_ADDED          = 0,
+       PVSCSI_MSG_DEV_REMOVED        = 1,
+       PVSCSI_MSG_LAST               = 2,
+};
+
+/*
+ * Msg descriptor.
+ *
+ * sizeof(struct PVSCSIRingMsgDesc) == 128.
+ *
+ * - type is of type enum PVSCSIMsgType.
+ * - the content of args depend on the type of event being delivered.
+ */
+
+struct PVSCSIRingMsgDesc {
+       u32     type;
+       u32     args[31];
+} __packed;
+
+struct PVSCSIMsgDescDevStatusChanged {
+       u32     type;  /* PVSCSI_MSG_DEV _ADDED / _REMOVED */
+       u32     bus;
+       u32     target;
+       u8      lun[8];
+       u32     pad[27];
+} __packed;
+
+/*
+ * Rings state.
+ *
+ * - the fields:
+ *    . msgProdIdx,
+ *    . msgConsIdx,
+ *    . msgNumEntriesLog2,
+ *   .. are only used once the SETUP_MSG_RING cmd has been issued.
+ * - '_pad' helps to ensure that the msg related fields are on their own
+ *   cache-line.
+ */
+
+struct PVSCSIRingsState {
+       u32     reqProdIdx;
+       u32     reqConsIdx;
+       u32     reqNumEntriesLog2;
+
+       u32     cmpProdIdx;
+       u32     cmpConsIdx;
+       u32     cmpNumEntriesLog2;
+
+       u8      _pad[104];
+
+       u32     msgProdIdx;
+       u32     msgConsIdx;
+       u32     msgNumEntriesLog2;
+} __packed;
+
+/*
+ * Request descriptor.
+ *
+ * sizeof(RingReqDesc) = 128
+ *
+ * - context: is a unique identifier of a command. It could normally be any
+ *   64bit value, however we currently store it in the serialNumber variable
+ *   of struct SCSI_Command, so we have the following restrictions due to the
+ *   way this field is handled in the vmkernel storage stack:
+ *    * this value can't be 0,
+ *    * the upper 32bit need to be 0 since serialNumber is as a u32.
+ *   Currently tracked as PR 292060.
+ * - dataLen: contains the total number of bytes that need to be transferred.
+ * - dataAddr:
+ *   * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first
+ *     s/g table segment, each s/g segment is entirely contained on a single
+ *     page of physical memory,
+ *   * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of
+ *     the buffer used for the DMA transfer,
+ * - flags:
+ *   * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above,
+ *   * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved,
+ *   * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory,
+ *   * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device,
+ *   * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than
+ *     16bytes. To be specified.
+ * - vcpuHint: vcpuId of the processor that will be most likely waiting for the
+ *   completion of the i/o. For guest OSes that use lowest priority message
+ *   delivery mode (such as windows), we use this "hint" to deliver the
+ *   completion action to the proper vcpu. For now, we can use the vcpuId of
+ *   the processor that initiated the i/o as a likely candidate for the vcpu
+ *   that will be waiting for the completion..
+ * - bus should be 0: we currently only support bus 0 for now.
+ * - unused should be zero'd.
+ */
+
+#define PVSCSI_FLAG_CMD_WITH_SG_LIST        (1 << 0)
+#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB     (1 << 1)
+#define PVSCSI_FLAG_CMD_DIR_NONE            (1 << 2)
+#define PVSCSI_FLAG_CMD_DIR_TOHOST          (1 << 3)
+#define PVSCSI_FLAG_CMD_DIR_TODEVICE        (1 << 4)
+
+struct PVSCSIRingReqDesc {
+       u64     context;
+       u64     dataAddr;
+       u64     dataLen;
+       u64     senseAddr;
+       u32     senseLen;
+       u32     flags;
+       u8      cdb[16];
+       u8      cdbLen;
+       u8      lun[8];
+       u8      tag;
+       u8      bus;
+       u8      target;
+       u8      vcpuHint;
+       u8      unused[59];
+} __packed;
+
+/*
+ * Scatter-gather list management.
+ *
+ * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the
+ * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g
+ * table segment.
+ *
+ * - each segment of the s/g table contain a succession of struct
+ *   PVSCSISGElement.
+ * - each segment is entirely contained on a single physical page of memory.
+ * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in
+ *   PVSCSISGElement.flags and in this case:
+ *     * addr is the PA of the next s/g segment,
+ *     * length is undefined, assumed to be 0.
+ */
+
+struct PVSCSISGElement {
+       u64     addr;
+       u32     length;
+       u32     flags;
+} __packed;
+
+/*
+ * Completion descriptor.
+ *
+ * sizeof(RingCmpDesc) = 32
+ *
+ * - context: identifier of the command. The same thing that was specified
+ *   under "context" as part of struct RingReqDesc at initiation time,
+ * - dataLen: number of bytes transferred for the actual i/o operation,
+ * - senseLen: number of bytes written into the sense buffer,
+ * - hostStatus: adapter status,
+ * - scsiStatus: device status,
+ * - _pad should be zero.
+ */
+
+struct PVSCSIRingCmpDesc {
+       u64     context;
+       u64     dataLen;
+       u32     senseLen;
+       u16     hostStatus;
+       u16     scsiStatus;
+       u32     _pad[2];
+} __packed;
+
+/*
+ * Interrupt status / IRQ bits.
+ */
+
+#define PVSCSI_INTR_CMPL_0                 (1 << 0)
+#define PVSCSI_INTR_CMPL_1                 (1 << 1)
+#define PVSCSI_INTR_CMPL_MASK              MASK(2)
+
+#define PVSCSI_INTR_MSG_0                  (1 << 2)
+#define PVSCSI_INTR_MSG_1                  (1 << 3)
+#define PVSCSI_INTR_MSG_MASK               (MASK(2) << 2)
+
+#define PVSCSI_INTR_ALL_SUPPORTED          MASK(4)
+
+/*
+ * Number of MSI-X vectors supported.
+ */
+#define PVSCSI_MAX_INTRS        24
+
+/*
+ * Enumeration of supported MSI-X vectors
+ */
+#define PVSCSI_VECTOR_COMPLETION   0
+
+/*
+ * Misc constants for the rings.
+ */
+
+#define PVSCSI_MAX_NUM_PAGES_REQ_RING   PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_CMP_RING   PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_MSG_RING   PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES
+
+#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \
+                               (PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc))
+
+#define PVSCSI_MAX_REQ_QUEUE_DEPTH \
+       (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE)
+
+#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES     1
+#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES        2
+#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES     2
+#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES        2
+
+enum PVSCSIMemSpace {
+       PVSCSI_MEM_SPACE_COMMAND_PAGE           = 0,
+       PVSCSI_MEM_SPACE_INTR_STATUS_PAGE       = 1,
+       PVSCSI_MEM_SPACE_MISC_PAGE              = 2,
+       PVSCSI_MEM_SPACE_KICK_IO_PAGE           = 4,
+       PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE        = 6,
+       PVSCSI_MEM_SPACE_MSIX_PBA_PAGE          = 7,
+};
+
+#define PVSCSI_MEM_SPACE_NUM_PAGES \
+       (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES +       \
+        PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES +   \
+        PVSCSI_MEM_SPACE_MISC_NUM_PAGES +          \
+        PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES +       \
+        PVSCSI_MEM_SPACE_MSIX_NUM_PAGES)
+
+#define PVSCSI_MEM_SPACE_SIZE        (PVSCSI_MEM_SPACE_NUM_PAGES * PAGE_SIZE)
+
+#endif /* _VMW_PVSCSI_H_ */
index 093610b..2f6e9d8 100644 (file)
  *
  * 2003/02/12 - Christoph Hellwig <hch@infradead.org>
  *
- * Cleaned up host template defintion
+ * Cleaned up host template definition
  * Removed now obsolete wd7000.h
  */
 
index d71dfe3..36ede02 100644 (file)
@@ -361,9 +361,9 @@ static const struct pnp_device_id pnp_dev_table[] = {
        {       "LTS0001",              0       },
        /* Rockwell's (PORALiNK) 33600 INT PNP */
        {       "WCI0003",              0       },
-       /* Unkown PnP modems */
+       /* Unknown PnP modems */
        {       "PNPCXXX",              UNKNOWN_DEV     },
-       /* More unkown PnP modems */
+       /* More unknown PnP modems */
        {       "PNPDXXX",              UNKNOWN_DEV     },
        {       "",                     0       }
 };
index 50943ff..9ff47db 100644 (file)
@@ -996,7 +996,7 @@ config SERIAL_IP22_ZILOG_CONSOLE
 
 config SERIAL_SH_SCI
        tristate "SuperH SCI(F) serial port support"
-       depends on SUPERH || H8300
+       depends on HAVE_CLK && (SUPERH || H8300)
        select SERIAL_CORE
 
 config SERIAL_SH_SCI_NR_UARTS
index b443824..7bb5fee 100644 (file)
@@ -602,7 +602,7 @@ static int __devinit mcf_probe(struct platform_device *pdev)
 
 /****************************************************************************/
 
-static int mcf_remove(struct platform_device *pdev)
+static int __devexit mcf_remove(struct platform_device *pdev)
 {
        struct uart_port *port;
        int i;
index 570b0d9..f6e77f1 100644 (file)
@@ -73,7 +73,7 @@ static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap)
 }
 
 /*
- * Register acessors. Note that we don't need to enforce a recovery
+ * Register accessors. Note that we don't need to enforce a recovery
  * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip,
  * though if we try to use this driver on older machines, we might have
  * to add it back
index 6498bd1..ff38dbd 100644 (file)
@@ -50,7 +50,6 @@
 #include <linux/list.h>
 
 #ifdef CONFIG_SUPERH
-#include <asm/clock.h>
 #include <asm/sh_bios.h>
 #endif
 
@@ -79,22 +78,18 @@ struct sci_port {
        struct timer_list       break_timer;
        int                     break_flag;
 
-#ifdef CONFIG_HAVE_CLK
        /* Interface clock */
        struct clk              *iclk;
        /* Data clock */
        struct clk              *dclk;
-#endif
+
        struct list_head        node;
 };
 
 struct sh_sci_priv {
        spinlock_t lock;
        struct list_head ports;
-
-#ifdef CONFIG_HAVE_CLK
        struct notifier_block clk_nb;
-#endif
 };
 
 /* Function prototypes */
@@ -156,32 +151,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
 }
 #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
 
-#if defined(__H8300S__)
-enum { sci_disable, sci_enable };
-
-static void h8300_sci_config(struct uart_port *port, unsigned int ctrl)
-{
-       volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL;
-       int ch = (port->mapbase  - SMR0) >> 3;
-       unsigned char mask = 1 << (ch+1);
-
-       if (ctrl == sci_disable)
-               *mstpcrl |= mask;
-       else
-               *mstpcrl &= ~mask;
-}
-
-static void h8300_sci_enable(struct uart_port *port)
-{
-       h8300_sci_config(port, sci_enable);
-}
-
-static void h8300_sci_disable(struct uart_port *port)
-{
-       h8300_sci_config(port, sci_disable);
-}
-#endif
-
 #if defined(__H8300H__) || defined(__H8300S__)
 static void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
@@ -733,7 +702,6 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
        return ret;
 }
 
-#ifdef CONFIG_HAVE_CLK
 /*
  * Here we define a transistion notifier so that we can update all of our
  * ports' baud rate when the peripheral clock changes.
@@ -751,7 +719,6 @@ static int sci_notifier(struct notifier_block *self,
                spin_lock_irqsave(&priv->lock, flags);
                list_for_each_entry(sci_port, &priv->ports, node)
                        sci_port->port.uartclk = clk_get_rate(sci_port->dclk);
-
                spin_unlock_irqrestore(&priv->lock, flags);
        }
 
@@ -778,7 +745,6 @@ static void sci_clk_disable(struct uart_port *port)
 
        clk_disable(sci_port->dclk);
 }
-#endif
 
 static int sci_request_irq(struct sci_port *port)
 {
@@ -833,8 +799,8 @@ static void sci_free_irq(struct sci_port *port)
 
 static unsigned int sci_tx_empty(struct uart_port *port)
 {
-       /* Can't detect */
-       return TIOCSER_TEMT;
+       unsigned short status = sci_in(port, SCxSR);
+       return status & SCxSR_TEND(port) ? TIOCSER_TEMT : 0;
 }
 
 static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -1077,21 +1043,10 @@ static void __devinit sci_init_single(struct platform_device *dev,
        sci_port->port.iotype   = UPIO_MEM;
        sci_port->port.line     = index;
        sci_port->port.fifosize = 1;
-
-#if defined(__H8300H__) || defined(__H8300S__)
-#ifdef __H8300S__
-       sci_port->enable        = h8300_sci_enable;
-       sci_port->disable       = h8300_sci_disable;
-#endif
-       sci_port->port.uartclk  = CONFIG_CPU_CLOCK;
-#elif defined(CONFIG_HAVE_CLK)
        sci_port->iclk          = p->clk ? clk_get(&dev->dev, p->clk) : NULL;
        sci_port->dclk          = clk_get(&dev->dev, "peripheral_clk");
        sci_port->enable        = sci_clk_enable;
        sci_port->disable       = sci_clk_disable;
-#else
-#error "Need a valid uartclk"
-#endif
 
        sci_port->break_timer.data = (unsigned long)sci_port;
        sci_port->break_timer.function = sci_break_timer;
@@ -1106,7 +1061,6 @@ static void __devinit sci_init_single(struct platform_device *dev,
        sci_port->type          = sci_port->port.type = p->type;
 
        memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs));
-
 }
 
 #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
@@ -1239,14 +1193,11 @@ static int sci_remove(struct platform_device *dev)
        struct sci_port *p;
        unsigned long flags;
 
-#ifdef CONFIG_HAVE_CLK
        cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
 
        spin_lock_irqsave(&priv->lock, flags);
        list_for_each_entry(p, &priv->ports, node)
                uart_remove_one_port(&sci_uart_driver, &p->port);
-
        spin_unlock_irqrestore(&priv->lock, flags);
 
        kfree(priv);
@@ -1307,10 +1258,8 @@ static int __devinit sci_probe(struct platform_device *dev)
        spin_lock_init(&priv->lock);
        platform_set_drvdata(dev, priv);
 
-#ifdef CONFIG_HAVE_CLK
        priv->clk_nb.notifier_call = sci_notifier;
        cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
 
        if (dev->id != -1) {
                ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]);
@@ -1370,7 +1319,7 @@ static struct dev_pm_ops sci_dev_pm_ops = {
 
 static struct platform_driver sci_driver = {
        .probe          = sci_probe,
-       .remove         = __devexit_p(sci_remove),
+       .remove         = sci_remove,
        .driver         = {
                .name   = "sh-sci",
                .owner  = THIS_MODULE,
index 3e2fcf9..a32094e 100644 (file)
@@ -1,5 +1,5 @@
 #include <linux/serial_core.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/gpio.h>
 
 #if defined(CONFIG_H83007) || defined(CONFIG_H83068)
index 0c08f28..46de564 100644 (file)
@@ -313,7 +313,7 @@ static void qe_uart_stop_tx(struct uart_port *port)
  * This function will attempt to stuff of all the characters from the
  * kernel's transmit buffer into TX BDs.
  *
- * A return value of non-zero indicates that it sucessfully stuffed all
+ * A return value of non-zero indicates that it successfully stuffed all
  * characters from the kernel buffer.
  *
  * A return value of zero indicates that there are still characters in the
index 6a025ce..4956bf1 100644 (file)
@@ -3,4 +3,5 @@
 #
 obj-$(CONFIG_SUPERHYWAY)       += superhyway/
 obj-$(CONFIG_MAPLE)            += maple/
+obj-$(CONFIG_GENERIC_GPIO)     += pfc.o
 obj-y                          += intc.o
index 559b5fe..a7e5c2e 100644 (file)
@@ -2,6 +2,7 @@
  * Shared interrupt handling code for IPR and INTC2 types of IRQs.
  *
  * Copyright (C) 2007, 2008 Magnus Damm
+ * Copyright (C) 2009 Paul Mundt
  *
  * Based on intc2.c and ipr.c
  *
@@ -24,6 +25,7 @@
 #include <linux/sysdev.h>
 #include <linux/list.h>
 #include <linux/topology.h>
+#include <linux/bitmap.h>
 
 #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
        ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -59,6 +61,20 @@ struct intc_desc_int {
 
 static LIST_HEAD(intc_list);
 
+/*
+ * The intc_irq_map provides a global map of bound IRQ vectors for a
+ * given platform. Allocation of IRQs are either static through the CPU
+ * vector map, or dynamic in the case of board mux vectors or MSI.
+ *
+ * As this is a central point for all IRQ controllers on the system,
+ * each of the available sources are mapped out here. This combined with
+ * sparseirq makes it quite trivial to keep the vector map tightly packed
+ * when dynamically creating IRQs, as well as tying in to otherwise
+ * unused irq_desc positions in the sparse array.
+ */
+static DECLARE_BITMAP(intc_irq_map, NR_IRQS);
+static DEFINE_SPINLOCK(vector_lock);
+
 #ifdef CONFIG_SMP
 #define IS_SMP(x) x.smp
 #define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
@@ -70,9 +86,7 @@ static LIST_HEAD(intc_list);
 #endif
 
 static unsigned int intc_prio_level[NR_IRQS]; /* for now */
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
 static unsigned long ack_handle[NR_IRQS];
-#endif
 
 static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
 {
@@ -250,7 +264,6 @@ static int intc_set_wake(unsigned int irq, unsigned int on)
        return 0; /* allow wakeup, but setup hardware in intc_suspend() */
 }
 
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
 static void intc_mask_ack(unsigned int irq)
 {
        struct intc_desc_int *d = get_intc_desc(irq);
@@ -282,7 +295,6 @@ static void intc_mask_ack(unsigned int irq)
                }
        }
 }
-#endif
 
 static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
                                             unsigned int nr_hp,
@@ -501,7 +513,6 @@ static unsigned int __init intc_prio_data(struct intc_desc *desc,
        return 0;
 }
 
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
 static unsigned int __init intc_ack_data(struct intc_desc *desc,
                                          struct intc_desc_int *d,
                                          intc_enum enum_id)
@@ -533,7 +544,6 @@ static unsigned int __init intc_ack_data(struct intc_desc *desc,
 
        return 0;
 }
-#endif
 
 static unsigned int __init intc_sense_data(struct intc_desc *desc,
                                           struct intc_desc_int *d,
@@ -572,6 +582,11 @@ static void __init intc_register_irq(struct intc_desc *desc,
        struct intc_handle_int *hp;
        unsigned int data[2], primary;
 
+       /*
+        * Register the IRQ position with the global IRQ map
+        */
+       set_bit(irq, intc_irq_map);
+
        /* Prefer single interrupt source bitmap over other combinations:
         * 1. bitmap, single interrupt source
         * 2. priority, single interrupt source
@@ -641,10 +656,8 @@ static void __init intc_register_irq(struct intc_desc *desc,
        /* irq should be disabled by default */
        d->chip.mask(irq);
 
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
        if (desc->ack_regs)
                ack_handle[irq] = intc_ack_data(desc, d, enum_id);
-#endif
 }
 
 static unsigned int __init save_reg(struct intc_desc_int *d,
@@ -681,10 +694,8 @@ void __init register_intc_controller(struct intc_desc *desc)
        d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0;
        d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0;
        d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0;
-
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
        d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
-#endif
+
        d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
 #ifdef CONFIG_SMP
        d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
@@ -727,14 +738,12 @@ void __init register_intc_controller(struct intc_desc *desc)
        d->chip.set_type = intc_set_sense;
        d->chip.set_wake = intc_set_wake;
 
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
        if (desc->ack_regs) {
                for (i = 0; i < desc->nr_ack_regs; i++)
                        k += save_reg(d, k, desc->ack_regs[i].set_reg, 0);
 
                d->chip.mask_ack = intc_mask_ack;
        }
-#endif
 
        BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
 
@@ -856,5 +865,91 @@ static int __init register_intc_sysdevs(void)
 
        return error;
 }
-
 device_initcall(register_intc_sysdevs);
+
+/*
+ * Dynamic IRQ allocation and deallocation
+ */
+static unsigned int create_irq_on_node(unsigned int irq_want, int node)
+{
+       unsigned int irq = 0, new;
+       unsigned long flags;
+       struct irq_desc *desc;
+
+       spin_lock_irqsave(&vector_lock, flags);
+
+       /*
+        * First try the wanted IRQ, then scan.
+        */
+       if (test_and_set_bit(irq_want, intc_irq_map)) {
+               new = find_first_zero_bit(intc_irq_map, nr_irqs);
+               if (unlikely(new == nr_irqs))
+                       goto out_unlock;
+
+               desc = irq_to_desc_alloc_node(new, node);
+               if (unlikely(!desc)) {
+                       pr_info("can't get irq_desc for %d\n", new);
+                       goto out_unlock;
+               }
+
+               desc = move_irq_desc(desc, node);
+               __set_bit(new, intc_irq_map);
+               irq = new;
+       }
+
+out_unlock:
+       spin_unlock_irqrestore(&vector_lock, flags);
+
+       if (irq > 0)
+               dynamic_irq_init(irq);
+
+       return irq;
+}
+
+int create_irq(void)
+{
+       int nid = cpu_to_node(smp_processor_id());
+       int irq;
+
+       irq = create_irq_on_node(NR_IRQS_LEGACY, nid);
+       if (irq == 0)
+               irq = -1;
+
+       return irq;
+}
+
+void destroy_irq(unsigned int irq)
+{
+       unsigned long flags;
+
+       dynamic_irq_cleanup(irq);
+
+       spin_lock_irqsave(&vector_lock, flags);
+       __clear_bit(irq, intc_irq_map);
+       spin_unlock_irqrestore(&vector_lock, flags);
+}
+
+int reserve_irq_vector(unsigned int irq)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&vector_lock, flags);
+       if (test_and_set_bit(irq, intc_irq_map))
+               ret = -EBUSY;
+       spin_unlock_irqrestore(&vector_lock, flags);
+
+       return ret;
+}
+
+void reserve_irq_legacy(void)
+{
+       unsigned long flags;
+       int i, j;
+
+       spin_lock_irqsave(&vector_lock, flags);
+       j = find_first_bit(intc_irq_map, nr_irqs);
+       for (i = 0; i < j; i++)
+               __set_bit(i, intc_irq_map);
+       spin_unlock_irqrestore(&vector_lock, flags);
+}
index 93c20e1..4e8f57d 100644 (file)
@@ -106,7 +106,7 @@ static void maple_dma_reset(void)
        * max delay is 11
        */
        ctrl_outl(MAPLE_2MBPS | MAPLE_TIMEOUT(0xFFFF), MAPLE_SPEED);
-       ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR);
+       ctrl_outl(virt_to_phys(maple_sendbuf), MAPLE_DMAADDR);
        ctrl_outl(1, MAPLE_ENABLE);
 }
 
@@ -258,7 +258,7 @@ static void maple_build_block(struct mapleq *mq)
        maple_lastptr = maple_sendptr;
 
        *maple_sendptr++ = (port << 16) | len | 0x80000000;
-       *maple_sendptr++ = PHYSADDR(mq->recvbuf->buf);
+       *maple_sendptr++ = virt_to_phys(mq->recvbuf->buf);
        *maple_sendptr++ =
            mq->command | (to << 8) | (from << 16) | (len << 24);
        while (len-- > 0)
similarity index 94%
rename from arch/sh/kernel/gpio.c
rename to drivers/sh/pfc.c
index d22e5af..841ed50 100644 (file)
@@ -7,7 +7,6 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
-
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
@@ -35,11 +34,11 @@ static unsigned long gpio_read_raw_reg(unsigned long reg,
 {
        switch (reg_width) {
        case 8:
-               return ctrl_inb(reg);
+               return __raw_readb(reg);
        case 16:
-               return ctrl_inw(reg);
+               return __raw_readw(reg);
        case 32:
-               return ctrl_inl(reg);
+               return __raw_readl(reg);
        }
 
        BUG();
@@ -52,13 +51,13 @@ static void gpio_write_raw_reg(unsigned long reg,
 {
        switch (reg_width) {
        case 8:
-               ctrl_outb(data, reg);
+               __raw_writeb(data, reg);
                return;
        case 16:
-               ctrl_outw(data, reg);
+               __raw_writew(data, reg);
                return;
        case 32:
-               ctrl_outl(data, reg);
+               __raw_writel(data, reg);
                return;
        }
 
@@ -72,11 +71,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr,
 
        pos = dr->reg_width - (in_pos + 1);
 
-#ifdef DEBUG
-       pr_info("write_bit addr = %lx, value = %ld, pos = %ld, "
-               "r_width = %ld\n",
-               dr->reg, !!value, pos, dr->reg_width);
-#endif
+       pr_debug("write_bit addr = %lx, value = %ld, pos = %ld, "
+                "r_width = %ld\n",
+                dr->reg, !!value, pos, dr->reg_width);
 
        if (value)
                set_bit(pos, &dr->reg_shadow);
@@ -95,11 +92,9 @@ static int gpio_read_reg(unsigned long reg, unsigned long reg_width,
        mask = (1 << field_width) - 1;
        pos = reg_width - ((in_pos + 1) * field_width);
 
-#ifdef DEBUG
-       pr_info("read_reg: addr = %lx, pos = %ld, "
-               "r_width = %ld, f_width = %ld\n",
-               reg, pos, reg_width, field_width);
-#endif
+       pr_debug("read_reg: addr = %lx, pos = %ld, "
+                "r_width = %ld, f_width = %ld\n",
+                reg, pos, reg_width, field_width);
 
        data = gpio_read_raw_reg(reg, reg_width);
        return (data >> pos) & mask;
@@ -114,24 +109,22 @@ static void gpio_write_reg(unsigned long reg, unsigned long reg_width,
        mask = (1 << field_width) - 1;
        pos = reg_width - ((in_pos + 1) * field_width);
 
-#ifdef DEBUG
-       pr_info("write_reg addr = %lx, value = %ld, pos = %ld, "
-               "r_width = %ld, f_width = %ld\n",
-               reg, value, pos, reg_width, field_width);
-#endif
+       pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, "
+                "r_width = %ld, f_width = %ld\n",
+                reg, value, pos, reg_width, field_width);
 
        mask = ~(mask << pos);
        value = value << pos;
 
        switch (reg_width) {
        case 8:
-               ctrl_outb((ctrl_inb(reg) & mask) | value, reg);
+               __raw_writeb((__raw_readb(reg) & mask) | value, reg);
                break;
        case 16:
-               ctrl_outw((ctrl_inw(reg) & mask) | value, reg);
+               __raw_writew((__raw_readw(reg) & mask) | value, reg);
                break;
        case 32:
-               ctrl_outl((ctrl_inl(reg) & mask) | value, reg);
+               __raw_writel((__raw_readl(reg) & mask) | value, reg);
                break;
        }
 }
index 815a650..20d7322 100644 (file)
@@ -41,7 +41,7 @@
  * This supports acccess to SPI devices using normal userspace I/O calls.
  * Note that while traditional UNIX/POSIX I/O semantics are half duplex,
  * and often mask message boundaries, full SPI support requires full duplex
- * transfers.  There are several kinds of of internal message boundaries to
+ * transfers.  There are several kinds of internal message boundaries to
  * handle chipselect management and other protocol options.
  *
  * SPI has a character major number assigned.  We allocate minor numbers
index 1301caa..d37b7ed 100644 (file)
@@ -5,7 +5,7 @@
 
 obj-$(CONFIG_VIDEO_GO7007) += go7007.o
 obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
-obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o
+obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o
 obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o
 obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o
 obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o
@@ -17,7 +17,7 @@ obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o
 go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \
                snd-go7007.o
 
-s2250-objs += s2250-board.o s2250-loader.o
+s2250-objs += s2250-board.o
 
 # Uncomment when the saa7134 patches get into upstream
 #ifneq ($(CONFIG_VIDEO_SAA7134),)
index 472f4bb..fb1345f 100644 (file)
@@ -49,7 +49,7 @@ int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data)
        go->hpi_ops->read_interrupt(go);
        if (wait_event_timeout(go->interrupt_waitq,
                                go->interrupt_available, 5*HZ) < 0) {
-               v4l2_err(go->video_dev, "timeout waiting for read interrupt\n");
+               v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n");
                return -1;
        }
        if (!go->interrupt_available)
@@ -193,7 +193,8 @@ int go7007_reset_encoder(struct go7007 *go)
 static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
                           int id, int addr)
 {
-       struct i2c_board_info info;
+       struct go7007 *go = i2c_get_adapdata(adapter);
+       struct v4l2_device *v4l2_dev = &go->v4l2_dev;
        char *modname;
 
        switch (id) {
@@ -219,20 +220,16 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type,
                modname = "wis-ov7640";
                break;
        case I2C_DRIVERID_S2250:
-               modname = "s2250-board";
+               modname = "s2250";
                break;
        default:
                modname = NULL;
                break;
        }
-       if (modname != NULL)
-               request_module(modname);
 
-       memset(&info, 0, sizeof(struct i2c_board_info));
-       info.addr = addr;
-       strlcpy(info.type, type, I2C_NAME_SIZE);
-       if (!i2c_new_device(adapter, &info))
+       if (v4l2_i2c_new_subdev(v4l2_dev, adapter, modname, type, addr, NULL))
                return 0;
+
        if (modname != NULL)
                printk(KERN_INFO
                        "go7007: probing for module %s failed\n", modname);
@@ -262,6 +259,11 @@ int go7007_register_encoder(struct go7007 *go)
        if (ret < 0)
                return -1;
 
+       /* v4l2 init must happen before i2c subdevs */
+       ret = go7007_v4l2_init(go);
+       if (ret < 0)
+               return ret;
+
        if (!go->i2c_adapter_online &&
                        go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
                if (go7007_i2c_init(go) < 0)
@@ -282,7 +284,7 @@ int go7007_register_encoder(struct go7007 *go)
                go->audio_enabled = 1;
                go7007_snd_init(go);
        }
-       return go7007_v4l2_init(go);
+       return 0;
 }
 EXPORT_SYMBOL(go7007_register_encoder);
 
@@ -315,7 +317,7 @@ int go7007_start_encoder(struct go7007 *go)
 
        if (go7007_send_firmware(go, fw, fw_len) < 0 ||
                        go7007_read_interrupt(go, &intr_val, &intr_data) < 0) {
-               v4l2_err(go->video_dev, "error transferring firmware\n");
+               v4l2_err(&go->v4l2_dev, "error transferring firmware\n");
                rv = -1;
                goto start_error;
        }
@@ -324,7 +326,7 @@ int go7007_start_encoder(struct go7007 *go)
        go->parse_length = 0;
        go->seen_frame = 0;
        if (go7007_stream_start(go) < 0) {
-               v4l2_err(go->video_dev, "error starting stream transfer\n");
+               v4l2_err(&go->v4l2_dev, "error starting stream transfer\n");
                rv = -1;
                goto start_error;
        }
@@ -420,7 +422,7 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length)
        for (i = 0; i < length; ++i) {
                if (go->active_buf != NULL &&
                            go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) {
-                       v4l2_info(go->video_dev, "dropping oversized frame\n");
+                       v4l2_info(&go->v4l2_dev, "dropping oversized frame\n");
                        go->active_buf->offset -= go->active_buf->bytesused;
                        go->active_buf->bytesused = 0;
                        go->active_buf->modet_active = 0;
@@ -668,7 +670,7 @@ void go7007_remove(struct go7007 *go)
                if (i2c_del_adapter(&go->i2c_adapter) == 0)
                        go->i2c_adapter_online = 0;
                else
-                       v4l2_err(go->video_dev,
+                       v4l2_err(&go->v4l2_dev,
                                "error removing I2C adapter!\n");
        }
 
index ce9307e..b58c394 100644 (file)
@@ -21,6 +21,8 @@
  * user-space applications.
  */
 
+#include <media/v4l2-device.h>
+
 struct go7007;
 
 /* IDs to activate board-specific support code */
@@ -167,6 +169,7 @@ struct go7007 {
        int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */
        char name[64];
        struct video_device *video_dev;
+       struct v4l2_device v4l2_dev;
        int ref_count;
        enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status;
        spinlock_t spinlock;
@@ -240,6 +243,11 @@ struct go7007 {
        unsigned short interrupt_data;
 };
 
+static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev)
+{
+       return container_of(v4l2_dev, struct go7007, v4l2_dev);
+}
+
 /* All of these must be called with the hpi_lock mutex held! */
 #define go7007_interface_reset(go) \
                        ((go)->hpi_ops->interface_reset(go))
index ecaa3c9..1e89dc0 100644 (file)
@@ -425,7 +425,7 @@ static struct go7007_usb_board board_sensoray_2250 = {
                .num_i2c_devs    = 1,
                .i2c_devs        = {
                        {
-                               .type   = "s2250_board",
+                               .type   = "s2250",
                                .id     = I2C_DRIVERID_S2250,
                                .addr   = 0x43,
                        },
@@ -1057,7 +1057,7 @@ static int go7007_usb_probe(struct usb_interface *intf,
                        usb_rcvintpipe(usb->usbdev, 4),
                        usb->intr_urb->transfer_buffer, 2*sizeof(u16),
                        go7007_usb_readinterrupt_complete, go, 8);
-       usb_set_intfdata(intf, go);
+       usb_set_intfdata(intf, &go->v4l2_dev);
 
        /* Boot the GO7007 */
        if (go7007_boot_encoder(go, go->board_info->flags &
@@ -1233,7 +1233,7 @@ allocfail:
 
 static void go7007_usb_disconnect(struct usb_interface *intf)
 {
-       struct go7007 *go = usb_get_intfdata(intf);
+       struct go7007 *go = to_go7007(usb_get_intfdata(intf));
        struct go7007_usb *usb = go->hpi_context;
        struct urb *vurb, *aurb;
        int i;
index 4bd353a..b18d8e2 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/videodev2.h>
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
 #include <linux/i2c.h>
 #include <linux/mutex.h>
 #include <linux/uaccess.h>
@@ -46,6 +47,9 @@
 #define        V4L2_MPEG_VIDEO_ENCODING_MPEG_4   3
 #endif
 
+#define call_all(dev, o, f, args...) \
+       v4l2_device_call_until_err(dev, 0, o, f, ##args)
+
 static void deactivate_buffer(struct go7007_buffer *gobuf)
 {
        int i;
@@ -247,19 +251,23 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
                go->modet_map[i] = 0;
 
        if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
-               struct video_decoder_resolution res;
+               struct v4l2_format res;
+
+               if (fmt != NULL) {
+                       res = *fmt;
+               } else {
+                       res.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                       res.fmt.pix.width = width;
+               }
 
-               res.width = width;
                if (height > sensor_height / 2) {
-                       res.height = height / 2;
+                       res.fmt.pix.height = height / 2;
                        go->encoder_v_halve = 0;
                } else {
-                       res.height = height;
+                       res.fmt.pix.height = height;
                        go->encoder_v_halve = 1;
                }
-               if (go->i2c_adapter_online)
-                       i2c_clients_command(&go->i2c_adapter,
-                                       DECODER_SET_RESOLUTION, &res);
+               call_all(&go->v4l2_dev, video, s_fmt, &res);
        } else {
                if (width <= sensor_width / 4) {
                        go->encoder_h_halve = 1;
@@ -385,7 +393,7 @@ static int clip_to_modet_map(struct go7007 *go, int region,
 }
 #endif
 
-static int mpeg_queryctrl(struct v4l2_queryctrl *ctrl)
+static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl)
 {
        static const u32 mpeg_ctrls[] = {
                V4L2_CID_MPEG_CLASS,
@@ -973,51 +981,35 @@ static int vidioc_queryctrl(struct file *file, void *priv,
                           struct v4l2_queryctrl *query)
 {
        struct go7007 *go = ((struct go7007_file *) priv)->go;
+       int id = query->id;
 
-       if (!go->i2c_adapter_online)
-               return -EIO;
-
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query);
+       if (0 == call_all(&go->v4l2_dev, core, queryctrl, query))
+               return 0;
 
-       return (!query->name[0]) ? mpeg_queryctrl(query) : 0;
+       query->id = id;
+       return mpeg_query_ctrl(query);
 }
 
 static int vidioc_g_ctrl(struct file *file, void *priv,
                                struct v4l2_control *ctrl)
 {
        struct go7007 *go = ((struct go7007_file *) priv)->go;
-       struct v4l2_queryctrl query;
 
-       if (!go->i2c_adapter_online)
-               return -EIO;
-
-       memset(&query, 0, sizeof(query));
-       query.id = ctrl->id;
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
-       if (query.name[0] == 0)
-               return mpeg_g_ctrl(ctrl, go);
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, ctrl);
+       if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl))
+               return 0;
 
-       return 0;
+       return mpeg_g_ctrl(ctrl, go);
 }
 
 static int vidioc_s_ctrl(struct file *file, void *priv,
                                struct v4l2_control *ctrl)
 {
        struct go7007 *go = ((struct go7007_file *) priv)->go;
-       struct v4l2_queryctrl query;
 
-       if (!go->i2c_adapter_online)
-               return -EIO;
-
-       memset(&query, 0, sizeof(query));
-       query.id = ctrl->id;
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
-       if (query.name[0] == 0)
-               return mpeg_s_ctrl(ctrl, go);
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, ctrl);
+       if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl))
+               return 0;
 
-       return 0;
+       return mpeg_s_ctrl(ctrl, go);
 }
 
 static int vidioc_g_parm(struct file *filp, void *priv,
@@ -1135,8 +1127,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
        if (go->streaming)
                return -EBUSY;
 
-       if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
-                       *std != 0)
+       if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0)
                return -EINVAL;
 
        if (*std == 0)
@@ -1146,9 +1137,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
                        go->input == go->board_info->num_inputs - 1) {
                if (!go->i2c_adapter_online)
                        return -EIO;
-               i2c_clients_command(&go->i2c_adapter,
-                                       VIDIOC_S_STD, std);
-               if (!*std) /* hack to indicate EINVAL from tuner */
+               if (call_all(&go->v4l2_dev, core, s_std, *std) < 0)
                        return -EINVAL;
        }
 
@@ -1164,9 +1153,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
        } else
                return -EINVAL;
 
-       if (go->i2c_adapter_online)
-               i2c_clients_command(&go->i2c_adapter,
-                                       VIDIOC_S_STD, std);
+       call_all(&go->v4l2_dev, core, s_std, *std);
        set_capture_size(go, NULL, 0);
 
        return 0;
@@ -1180,7 +1167,7 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
                        go->input == go->board_info->num_inputs - 1) {
                if (!go->i2c_adapter_online)
                        return -EIO;
-               i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYSTD, std);
+               return call_all(&go->v4l2_dev, video, querystd, std);
        } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
                *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
        else
@@ -1238,14 +1225,8 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
                return -EBUSY;
 
        go->input = input;
-       if (go->i2c_adapter_online) {
-               i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT,
-                       &go->board_info->inputs[input].video_input);
-               i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO,
-                       &go->board_info->inputs[input].audio_input);
-       }
 
-       return 0;
+       return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0);
 }
 
 static int vidioc_g_tuner(struct file *file, void *priv,
@@ -1260,10 +1241,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
        if (!go->i2c_adapter_online)
                return -EIO;
 
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, t);
-
-       t->index = 0;
-       return 0;
+       return call_all(&go->v4l2_dev, tuner, g_tuner, t);
 }
 
 static int vidioc_s_tuner(struct file *file, void *priv,
@@ -1287,9 +1265,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
                break;
        }
 
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, t);
-
-       return 0;
+       return call_all(&go->v4l2_dev, tuner, s_tuner, t);
 }
 
 static int vidioc_g_frequency(struct file *file, void *priv,
@@ -1303,8 +1279,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
                return -EIO;
 
        f->type = V4L2_TUNER_ANALOG_TV;
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, f);
-       return 0;
+
+       return call_all(&go->v4l2_dev, tuner, g_frequency, f);
 }
 
 static int vidioc_s_frequency(struct file *file, void *priv,
@@ -1317,9 +1293,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
        if (!go->i2c_adapter_online)
                return -EIO;
 
-       i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, f);
-
-       return 0;
+       return call_all(&go->v4l2_dev, tuner, s_frequency, f);
 }
 
 static int vidioc_cropcap(struct file *file, void *priv,
@@ -1827,7 +1801,7 @@ int go7007_v4l2_init(struct go7007 *go)
        go->video_dev = video_device_alloc();
        if (go->video_dev == NULL)
                return -ENOMEM;
-       memcpy(go->video_dev, &go7007_template, sizeof(go7007_template));
+       *go->video_dev = go7007_template;
        go->video_dev->parent = go->dev;
        rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1);
        if (rv < 0) {
@@ -1835,6 +1809,12 @@ int go7007_v4l2_init(struct go7007 *go)
                go->video_dev = NULL;
                return rv;
        }
+       rv = v4l2_device_register(go->dev, &go->v4l2_dev);
+       if (rv < 0) {
+               video_device_release(go->video_dev);
+               go->video_dev = NULL;
+               return rv;
+       }
        video_set_drvdata(go->video_dev, go);
        ++go->ref_count;
        printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
@@ -1858,4 +1838,5 @@ void go7007_v4l2_remove(struct go7007 *go)
        mutex_unlock(&go->hw_lock);
        if (go->video_dev)
                video_unregister_device(go->video_dev);
+       v4l2_device_unregister(&go->v4l2_dev);
 }
index f4a6541..8cf7f27 100644 (file)
 #include <linux/usb.h>
 #include <linux/i2c.h>
 #include <linux/videodev2.h>
+#include <media/v4l2-device.h>
 #include <media/v4l2-common.h>
-#include "s2250-loader.h"
+#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-subdev.h>
 #include "go7007-priv.h"
-#include "wis-i2c.h"
+
+MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver");
+MODULE_LICENSE("GPL v2");
 
 #define TLV320_ADDRESS      0x34
 #define VPX322_ADDR_ANALOGCONTROL1     0x02
@@ -112,6 +116,7 @@ static u16 vid_regs_fp_pal[] =
 };
 
 struct s2250 {
+       struct v4l2_subdev sd;
        v4l2_std_id std;
        int input;
        int brightness;
@@ -123,6 +128,11 @@ struct s2250 {
        struct i2c_client *audio;
 };
 
+static inline struct s2250 *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct s2250, sd);
+}
+
 /* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
 static int go7007_usb_vendor_request(struct go7007 *go, u16 request,
        u16 value, u16 index, void *transfer_buffer, int length, int in)
@@ -306,253 +316,262 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs)
 }
 
 
-static int s2250_command(struct i2c_client *client,
-                        unsigned int cmd, void *arg)
+/* ------------------------------------------------------------------------- */
+
+static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+                                u32 config)
 {
-       struct s2250 *dec = i2c_get_clientdata(client);
+       struct s2250 *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int vidsys;
+
+       vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
+       if (input == 0) {
+               /* composite */
+               write_reg_fp(client, 0x20, 0x020 | vidsys);
+               write_reg_fp(client, 0x21, 0x662);
+               write_reg_fp(client, 0x140, 0x060);
+       } else if (input == 1) {
+               /* S-Video */
+               write_reg_fp(client, 0x20, 0x040 | vidsys);
+               write_reg_fp(client, 0x21, 0x666);
+               write_reg_fp(client, 0x140, 0x060);
+       } else {
+               return -EINVAL;
+       }
+       state->input = input;
+       return 0;
+}
 
-       switch (cmd) {
-       case VIDIOC_S_INPUT:
-       {
-               int vidsys;
-               int *input = arg;
-
-               vidsys = (dec->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
-               if (*input == 0) {
-                       /* composite */
-                       write_reg_fp(client, 0x20, 0x020 | vidsys);
-                       write_reg_fp(client, 0x21, 0x662);
-                       write_reg_fp(client, 0x140, 0x060);
-               } else {
-                       /* S-Video */
-                       write_reg_fp(client, 0x20, 0x040 | vidsys);
-                       write_reg_fp(client, 0x21, 0x666);
-                       write_reg_fp(client, 0x140, 0x060);
-               }
-               dec->input = *input;
+static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+       struct s2250 *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       u16 vidsource;
+
+       vidsource = (state->input == 1) ? 0x040 : 0x020;
+       switch (norm) {
+       case V4L2_STD_NTSC:
+               write_regs_fp(client, vid_regs_fp);
+               write_reg_fp(client, 0x20, vidsource | 1);
                break;
-       }
-       case VIDIOC_S_STD:
-       {
-               v4l2_std_id *std = arg;
-               u16 vidsource;
-
-               vidsource = (dec->input == 1) ? 0x040 : 0x020;
-               dec->std = *std;
-               switch (dec->std) {
-               case V4L2_STD_NTSC:
-                       write_regs_fp(client, vid_regs_fp);
-                       write_reg_fp(client, 0x20, vidsource | 1);
-                       break;
-               case V4L2_STD_PAL:
-                       write_regs_fp(client, vid_regs_fp);
-                       write_regs_fp(client, vid_regs_fp_pal);
-                       write_reg_fp(client, 0x20, vidsource);
-                       break;
-               default:
-                       return -EINVAL;
-               }
+       case V4L2_STD_PAL:
+               write_regs_fp(client, vid_regs_fp);
+               write_regs_fp(client, vid_regs_fp_pal);
+               write_reg_fp(client, 0x20, vidsource);
                break;
+       default:
+               return -EINVAL;
        }
-       case VIDIOC_QUERYCTRL:
-       {
-               struct v4l2_queryctrl *ctrl = arg;
-               static const u32 user_ctrls[] = {
-                       V4L2_CID_BRIGHTNESS,
-                       V4L2_CID_CONTRAST,
-                       V4L2_CID_SATURATION,
-                       V4L2_CID_HUE,
-                       0
-               };
-               static const u32 *ctrl_classes[] = {
-                       user_ctrls,
-                       NULL
-               };
-
-               ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
-               switch (ctrl->id) {
-               case V4L2_CID_BRIGHTNESS:
-                       v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
-                       break;
-               case V4L2_CID_CONTRAST:
-                       v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
-                       break;
-               case V4L2_CID_SATURATION:
-                       v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
-                       break;
-               case V4L2_CID_HUE:
-                       v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0);
-                       break;
-               default:
-                       ctrl->name[0] = '\0';
-                       return -EINVAL;
-               }
-               break;
+       state->std = norm;
+       return 0;
+}
+
+static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query)
+{
+       switch (query->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+       case V4L2_CID_CONTRAST:
+               return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+       case V4L2_CID_SATURATION:
+               return v4l2_ctrl_query_fill(query, 0, 100, 1, 50);
+       case V4L2_CID_HUE:
+               return v4l2_ctrl_query_fill(query, -50, 50, 1, 0);
+       default:
+               return -EINVAL;
        }
-       case VIDIOC_S_CTRL:
-       {
-               struct v4l2_control *ctrl = arg;
-               int value1;
-               u16 oldvalue;
-
-               switch (ctrl->id) {
-               case V4L2_CID_BRIGHTNESS:
-                       if (ctrl->value > 100)
-                               dec->brightness = 100;
-                       else if (ctrl->value < 0)
-                               dec->brightness = 0;
-                       else
-                               dec->brightness = ctrl->value;
-                       value1 = (dec->brightness - 50) * 255 / 100;
-                       read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
-                       write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
-                                    value1 | (oldvalue & ~0xff));
-                       read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
-                       write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
-                                    value1 | (oldvalue & ~0xff));
-                       write_reg_fp(client, 0x140, 0x60);
-                       break;
-               case V4L2_CID_CONTRAST:
-                       if (ctrl->value > 100)
-                               dec->contrast = 100;
-                       else if (ctrl->value < 0)
-                               dec->contrast = 0;
-                       else
-                               dec->contrast = ctrl->value;
-                       value1 = dec->contrast * 0x40 / 100;
-                       if (value1 > 0x3f)
-                               value1 = 0x3f; /* max */
-                       read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
-                       write_reg_fp(client, VPX322_ADDR_CONTRAST0,
-                                    value1 | (oldvalue & ~0x3f));
-                       read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
-                       write_reg_fp(client, VPX322_ADDR_CONTRAST1,
-                                    value1 | (oldvalue & ~0x3f));
-                       write_reg_fp(client, 0x140, 0x60);
-                       break;
-               case V4L2_CID_SATURATION:
-                       if (ctrl->value > 127)
-                               dec->saturation = 127;
-                       else if (ctrl->value < 0)
-                               dec->saturation = 0;
-                       else
-                               dec->saturation = ctrl->value;
-
-                       value1 = dec->saturation * 4140 / 100;
-                       if (value1 > 4094)
-                               value1 = 4094;
-                       write_reg_fp(client, VPX322_ADDR_SAT, value1);
-                       break;
-               case V4L2_CID_HUE:
-                       if (ctrl->value > 50)
-                               dec->hue = 50;
-                       else if (ctrl->value < -50)
-                               dec->hue = -50;
-                       else
-                               dec->hue = ctrl->value;
-                       /* clamp the hue range */
-                       value1 = dec->hue * 280 / 50;
-                       write_reg_fp(client, VPX322_ADDR_HUE, value1);
-                       break;
-               }
+       return 0;
+}
+
+static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct s2250 *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int value1;
+       u16 oldvalue;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               if (ctrl->value > 100)
+                       state->brightness = 100;
+               else if (ctrl->value < 0)
+                       state->brightness = 0;
+               else
+                       state->brightness = ctrl->value;
+               value1 = (state->brightness - 50) * 255 / 100;
+               read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
+               write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
+                            value1 | (oldvalue & ~0xff));
+               read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
+               write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
+                            value1 | (oldvalue & ~0xff));
+               write_reg_fp(client, 0x140, 0x60);
                break;
-       }
-       case VIDIOC_G_CTRL:
-       {
-               struct v4l2_control *ctrl = arg;
-
-               switch (ctrl->id) {
-               case V4L2_CID_BRIGHTNESS:
-                       ctrl->value = dec->brightness;
-                       break;
-               case V4L2_CID_CONTRAST:
-                       ctrl->value = dec->contrast;
-                       break;
-               case V4L2_CID_SATURATION:
-                       ctrl->value = dec->saturation;
-                       break;
-               case V4L2_CID_HUE:
-                       ctrl->value = dec->hue;
-                       break;
-               }
+       case V4L2_CID_CONTRAST:
+               if (ctrl->value > 100)
+                       state->contrast = 100;
+               else if (ctrl->value < 0)
+                       state->contrast = 0;
+               else
+                       state->contrast = ctrl->value;
+               value1 = state->contrast * 0x40 / 100;
+               if (value1 > 0x3f)
+                       value1 = 0x3f; /* max */
+               read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
+               write_reg_fp(client, VPX322_ADDR_CONTRAST0,
+                            value1 | (oldvalue & ~0x3f));
+               read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
+               write_reg_fp(client, VPX322_ADDR_CONTRAST1,
+                            value1 | (oldvalue & ~0x3f));
+               write_reg_fp(client, 0x140, 0x60);
                break;
+       case V4L2_CID_SATURATION:
+               if (ctrl->value > 100)
+                       state->saturation = 100;
+               else if (ctrl->value < 0)
+                       state->saturation = 0;
+               else
+                       state->saturation = ctrl->value;
+               value1 = state->saturation * 4140 / 100;
+               if (value1 > 4094)
+                       value1 = 4094;
+               write_reg_fp(client, VPX322_ADDR_SAT, value1);
+               break;
+       case V4L2_CID_HUE:
+               if (ctrl->value > 50)
+                       state->hue = 50;
+               else if (ctrl->value < -50)
+                       state->hue = -50;
+               else
+                       state->hue = ctrl->value;
+               /* clamp the hue range */
+               value1 = state->hue * 280 / 50;
+               write_reg_fp(client, VPX322_ADDR_HUE, value1);
+               break;
+       default:
+               return -EINVAL;
        }
-       case VIDIOC_S_FMT:
-       {
-               struct v4l2_format *fmt = arg;
-               if (fmt->fmt.pix.height < 640) {
-                       write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400);
-                       write_reg_fp(client, 0x140, 0x060);
-               } else {
-                       write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400);
-                       write_reg_fp(client, 0x140, 0x060);
-               }
-               return 0;
-       }
-       case VIDIOC_G_AUDIO:
-       {
-               struct v4l2_audio *audio = arg;
+       return 0;
+}
 
-               memset(audio, 0, sizeof(*audio));
-               audio->index = dec->audio_input;
-               /* fall through */
-       }
-       case VIDIOC_ENUMAUDIO:
-       {
-               struct v4l2_audio *audio = arg;
-
-               switch (audio->index) {
-               case 0:
-                       strcpy(audio->name, "Line In");
-                       break;
-               case 1:
-                       strcpy(audio->name, "Mic");
-                       break;
-               case 2:
-                       strcpy(audio->name, "Mic Boost");
-                       break;
-               default:
-                       audio->name[0] = '\0';
-                       return 0;
-               }
-               audio->capability = V4L2_AUDCAP_STEREO;
-               audio->mode = 0;
-               return 0;
+static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+       struct s2250 *state = to_state(sd);
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               ctrl->value = state->brightness;
+               break;
+       case V4L2_CID_CONTRAST:
+               ctrl->value = state->contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               ctrl->value = state->saturation;
+               break;
+       case V4L2_CID_HUE:
+               ctrl->value = state->hue;
+               break;
+       default:
+               return -EINVAL;
        }
-       case VIDIOC_S_AUDIO:
-       {
-               struct v4l2_audio *audio = arg;
-
-               switch (audio->index) {
-               case 0:
-                       write_reg(dec->audio, 0x08, 0x02); /* Line In */
-                       break;
-               case 1:
-                       write_reg(dec->audio, 0x08, 0x04); /* Mic */
-                       break;
-               case 2:
-                       write_reg(dec->audio, 0x08, 0x05); /* Mic Boost */
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               dec->audio_input = audio->index;
-               return 0;
+       return 0;
+}
+
+static int s2250_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+       struct s2250 *state = to_state(sd);
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       if (fmt->fmt.pix.height < 640) {
+               write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
+               write_reg_fp(client, 0x140, 0x060);
+       } else {
+               write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400);
+               write_reg_fp(client, 0x140, 0x060);
        }
+       return 0;
+}
 
-       default:
-               printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd);
+static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output,
+                                u32 config)
+{
+       struct s2250 *state = to_state(sd);
+
+       switch (input) {
+       case 0:
+               write_reg(state->audio, 0x08, 0x02); /* Line In */
+               break;
+       case 1:
+               write_reg(state->audio, 0x08, 0x04); /* Mic */
                break;
+       case 2:
+               write_reg(state->audio, 0x08, 0x05); /* Mic Boost */
+               break;
+       default:
+               return -EINVAL;
        }
+       state->audio_input = input;
+       return 0;
+}
+
+
+static int s2250_log_status(struct v4l2_subdev *sd)
+{
+       struct s2250 *state = to_state(sd);
+
+       v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" :
+                                       state->std == V4L2_STD_PAL ? "PAL" :
+                                       state->std == V4L2_STD_SECAM ? "SECAM" :
+                                       "unknown");
+       v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" :
+                                       state->input == 1 ? "S-video" :
+                                       "error");
+       v4l2_info(sd, "Brightness: %d\n", state->brightness);
+       v4l2_info(sd, "Contrast: %d\n", state->contrast);
+       v4l2_info(sd, "Saturation: %d\n", state->saturation);
+       v4l2_info(sd, "Hue: %d\n", state->hue); return 0;
+       v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" :
+                                       state->audio_input == 1 ? "Mic" :
+                                       state->audio_input == 2 ? "Mic Boost" :
+                                       "error");
        return 0;
 }
 
+/* --------------------------------------------------------------------------*/
+
+static const struct v4l2_subdev_core_ops s2250_core_ops = {
+       .log_status = s2250_log_status,
+       .g_ctrl = s2250_g_ctrl,
+       .s_ctrl = s2250_s_ctrl,
+       .queryctrl = s2250_queryctrl,
+       .s_std = s2250_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops s2250_audio_ops = {
+       .s_routing = s2250_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops s2250_video_ops = {
+       .s_routing = s2250_s_video_routing,
+       .s_fmt = s2250_s_fmt,
+};
+
+static const struct v4l2_subdev_ops s2250_ops = {
+       .core = &s2250_core_ops,
+       .audio = &s2250_audio_ops,
+       .video = &s2250_video_ops,
+};
+
+/* --------------------------------------------------------------------------*/
+
 static int s2250_probe(struct i2c_client *client,
                       const struct i2c_device_id *id)
 {
        struct i2c_client *audio;
        struct i2c_adapter *adapter = client->adapter;
-       struct s2250 *dec;
+       struct s2250 *state;
+       struct v4l2_subdev *sd;
        u8 *data;
        struct go7007 *go = i2c_get_adapdata(adapter);
        struct go7007_usb *usb = go->hpi_context;
@@ -561,30 +580,31 @@ static int s2250_probe(struct i2c_client *client,
        if (audio == NULL)
                return -ENOMEM;
 
-       dec = kmalloc(sizeof(struct s2250), GFP_KERNEL);
-       if (dec == NULL) {
+       state = kmalloc(sizeof(struct s2250), GFP_KERNEL);
+       if (state == NULL) {
                i2c_unregister_device(audio);
                return -ENOMEM;
        }
 
-       dec->std = V4L2_STD_NTSC;
-       dec->brightness = 50;
-       dec->contrast = 50;
-       dec->saturation = 50;
-       dec->hue = 0;
-       dec->audio = audio;
-       i2c_set_clientdata(client, dec);
+       sd = &state->sd;
+       v4l2_i2c_subdev_init(sd, client, &s2250_ops);
+
+       v4l2_info(sd, "initializing %s at address 0x%x on %s\n",
+              "Sensoray 2250/2251", client->addr, client->adapter->name);
 
-       printk(KERN_DEBUG
-              "s2250: initializing video decoder on %s\n",
-              adapter->name);
+       state->std = V4L2_STD_NTSC;
+       state->brightness = 50;
+       state->contrast = 50;
+       state->saturation = 50;
+       state->hue = 0;
+       state->audio = audio;
 
        /* initialize the audio */
        if (write_regs(audio, aud_regs) < 0) {
                printk(KERN_ERR
                       "s2250: error initializing audio\n");
                i2c_unregister_device(audio);
-               kfree(dec);
+               kfree(state);
                return 0;
        }
 
@@ -592,14 +612,14 @@ static int s2250_probe(struct i2c_client *client,
                printk(KERN_ERR
                       "s2250: error initializing decoder\n");
                i2c_unregister_device(audio);
-               kfree(dec);
+               kfree(state);
                return 0;
        }
        if (write_regs_fp(client, vid_regs_fp) < 0) {
                printk(KERN_ERR
                       "s2250: error initializing decoder\n");
                i2c_unregister_device(audio);
-               kfree(dec);
+               kfree(state);
                return 0;
        }
        /* set default channel */
@@ -609,7 +629,7 @@ static int s2250_probe(struct i2c_client *client,
        write_reg_fp(client, 0x140, 0x060);
 
        /* set default audio input */
-       dec->audio_input = 0;
+       state->audio_input = 0;
        write_reg(client, 0x08, 0x02); /* Line In */
 
        if (mutex_lock_interruptible(&usb->i2c_lock) == 0) {
@@ -634,60 +654,28 @@ static int s2250_probe(struct i2c_client *client,
                mutex_unlock(&usb->i2c_lock);
        }
 
-       printk("s2250: initialized successfully\n");
+       v4l2_info(sd, "initialized successfully\n");
        return 0;
 }
 
 static int s2250_remove(struct i2c_client *client)
 {
-       struct s2250 *dec = i2c_get_clientdata(client);
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
 
-       i2c_set_clientdata(client, NULL);
-       i2c_unregister_device(dec->audio);
-       kfree(dec);
+       v4l2_device_unregister_subdev(sd);
+       kfree(to_state(sd));
        return 0;
 }
 
 static struct i2c_device_id s2250_id[] = {
-       { "s2250_board", 0 },
+       { "s2250", 0 },
        { }
 };
+MODULE_DEVICE_TABLE(i2c, s2250_id);
 
-static struct i2c_driver s2250_driver = {
-       .driver = {
-               .name   = "Sensoray 2250 board driver",
-       },
-       .probe          = s2250_probe,
-       .remove         = s2250_remove,
-       .command        = s2250_command,
-       .id_table       = s2250_id,
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+       .name = "s2250",
+       .probe = s2250_probe,
+       .remove = s2250_remove,
+       .id_table = s2250_id,
 };
-
-static int __init s2250_init(void)
-{
-       int r;
-
-       r = s2250loader_init();
-       if (r < 0)
-               return r;
-
-       r = i2c_add_driver(&s2250_driver);
-       if (r < 0)
-               s2250loader_cleanup();
-
-       return r;
-}
-
-static void __exit s2250_cleanup(void)
-{
-       i2c_del_driver(&s2250_driver);
-
-       s2250loader_cleanup();
-}
-
-module_init(s2250_init);
-module_exit(s2250_cleanup);
-
-MODULE_AUTHOR("");
-MODULE_DESCRIPTION("Board driver for Sensoryray 2250");
-MODULE_LICENSE("GPL v2");
index d7bf829..c152ab9 100644 (file)
@@ -162,7 +162,7 @@ static struct usb_driver s2250loader_driver = {
        .id_table       = s2250loader_ids,
 };
 
-int s2250loader_init(void)
+static int __init s2250loader_init(void)
 {
        int r;
        unsigned i = 0;
@@ -179,11 +179,15 @@ int s2250loader_init(void)
        printk(KERN_INFO "s2250loader_init: driver registered\n");
        return 0;
 }
-EXPORT_SYMBOL(s2250loader_init);
+module_init(s2250loader_init);
 
-void s2250loader_cleanup(void)
+static void __exit s2250loader_cleanup(void)
 {
        printk(KERN_INFO "s2250loader_cleanup\n");
        usb_deregister(&s2250loader_driver);
 }
-EXPORT_SYMBOL(s2250loader_cleanup);
+module_exit(s2250loader_cleanup);
+
+MODULE_AUTHOR("");
+MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251");
+MODULE_LICENSE("GPL v2");
index a35bd5d..60b7d1c 100644 (file)
@@ -20,7 +20,7 @@
 #include "../oal_dt.h"
 
 /***** Section 1 : Tunable Parameters *****/
-/* The defintions in this section are tunabel parameters */
+/* The definitions in this section are tunabel parameters */
 
 /* Maximum number of BSS that could be scaned */
 #define ZM_MAX_BSS                          128
index 84ac433..cdcaef5 100644 (file)
@@ -17,7 +17,7 @@
 /*  Module Name : zdcompat.h                                            */
 /*                                                                      */
 /*  Abstract                                                            */
-/*     This module contains function defintion for compatibility.       */
+/*     This module contains function definition for compatibility.      */
 /*                                                                      */
 /*  NOTES                                                               */
 /*     Platform dependent.                                              */
index 8216d7e..35ed60b 100644 (file)
@@ -521,7 +521,7 @@ typedef struct r8180_priv
        //u32 NumTxOkInPeriod;   //YJ,del,080828
        u8   TxPollingTimes;
 
-       bool    bApBufOurFrame;// TRUE if AP buffer our unicast data , we will keep eAwake untill receive data or timeout.
+       bool    bApBufOurFrame;// TRUE if AP buffer our unicast data , we will keep eAwake until receive data or timeout.
        u8      WaitBufDataBcnCount;
        u8      WaitBufDataTimeOut;
 
index 96c7cfa..acf644f 100644 (file)
@@ -38,7 +38,7 @@
 // 2. 0x800/0x900/0xA00/0xC00/0xD00/0xE00
 // 3. RF register 0x00-2E
 // 4. Bit Mask for BB/RF register
-// 5. Other defintion for BB/RF R/W
+// 5. Other definition for BB/RF R/W
 //
 
 
index 33918fd..10c702b 100644 (file)
@@ -3987,7 +3987,7 @@ wavelan_interrupt(int             irq,
 #endif
 
   /* Prevent reentrancy. We need to do that because we may have
-   * multiple interrupt handler running concurently.
+   * multiple interrupt handler running concurrently.
    * It is safe because interrupts are disabled before aquiring
    * the spinlock. */
   spin_lock(&lp->spinlock);
index 40de151..e89304c 100644 (file)
@@ -4190,7 +4190,7 @@ static void ixj_aec_start(IXJ *j, int level)
                                ixj_WriteDSPCommand(0x1224, j);
 
                        ixj_WriteDSPCommand(0xE014, j);
-                       ixj_WriteDSPCommand(0x0003, j); /* Lock threashold at 3dB */
+                       ixj_WriteDSPCommand(0x0003, j); /* Lock threshold at 3dB */
 
                        ixj_WriteDSPCommand(0xE338, j); /* Set Echo Suppresser Attenuation to 0dB */
 
@@ -4235,7 +4235,7 @@ static void ixj_aec_start(IXJ *j, int level)
                                ixj_WriteDSPCommand(0x1224, j);
 
                        ixj_WriteDSPCommand(0xE014, j);
-                       ixj_WriteDSPCommand(0x0003, j); /* Lock threashold at 3dB */
+                       ixj_WriteDSPCommand(0x0003, j); /* Lock threshold at 3dB */
 
                        ixj_WriteDSPCommand(0xE338, j); /* Set Echo Suppresser Attenuation to 0dB */
 
index d171b56..bba4d3e 100644 (file)
@@ -1958,7 +1958,7 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr)
                goto bad1;
 
        /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to
-        * the first MEMACESS cmv. Ignore it...
+        * the first MEMACCESS cmv. Ignore it...
         */
        if (cmv->bFunction != dsc->function) {
                if (UEA_CHIP_VERSION(sc) == ADI930
index 2473cf0..b4bd241 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * drivers/usb/class/usbtmc.c - USB Test & Measurment class driver
+ * drivers/usb/class/usbtmc.c - USB Test & Measurement class driver
  *
  * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany
  * Copyright (C) 2008 Novell, Inc.
index da718e8..e80f1af 100644 (file)
@@ -1890,7 +1890,7 @@ static void cancel_async_set_config(struct usb_device *udev)
  * routine gets around the normal restrictions by using a work thread to
  * submit the change-config request.
  *
- * Returns 0 if the request was succesfully queued, error code otherwise.
+ * Returns 0 if the request was successfully queued, error code otherwise.
  * The caller has no way to know whether the queued request will eventually
  * succeed.
  */
index 7953948..4e36578 100644 (file)
@@ -432,7 +432,7 @@ static void acm_disable(struct usb_function *f)
  * @length: size of data
  * Context: irqs blocked, acm->lock held, acm_notify_req non-null
  *
- * Returns zero on sucess or a negative errno.
+ * Returns zero on success or a negative errno.
  *
  * See section 6.3.5 of the CDC 1.1 specification for information
  * about the only notification we issue:  SerialState change.
index 1937d8c..adda120 100644 (file)
@@ -1524,7 +1524,7 @@ static int pxa_udc_get_frame(struct usb_gadget *_gadget)
  * pxa_udc_wakeup - Force udc device out of suspend
  * @_gadget: usb gadget
  *
- * Returns 0 if succesfull, error code otherwise
+ * Returns 0 if successfull, error code otherwise
  */
 static int pxa_udc_wakeup(struct usb_gadget *_gadget)
 {
index f5f5601..d8f4aaa 100644 (file)
@@ -210,7 +210,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
        if (error) {
                ehci_halt(ehci);
                ehci_to_hcd(ehci)->state = HC_STATE_HALT;
-               ehci_err(ehci, "force halt; handhake %p %08x %08x -> %d\n",
+               ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
                        ptr, mask, done, error);
        }
 
index 62a226b..00a2985 100644 (file)
@@ -627,7 +627,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd)
 
 
 /*
- * Process normal completions(error or sucess) and clean the schedule.
+ * Process normal completions(error or success) and clean the schedule.
  *
  * This is the main path for handing urbs back to drivers. The only other patth
  * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning
index 9ec7fd5..9579cf4 100644 (file)
@@ -111,7 +111,7 @@ struct aes_ccm_b1 {
  *
  * CCM uses Ax blocks to generate a keystream with which the MIC and
  * the message's payload are encoded. A0 always encrypts/decrypts the
- * MIC. Ax (x>0) are used for the sucesive payload blocks.
+ * MIC. Ax (x>0) are used for the successive payload blocks.
  *
  * The x is the counter, and is increased for each block.
  */
index 613a5fc..489b478 100644 (file)
@@ -558,7 +558,7 @@ static void wa_seg_dto_cb(struct urb *urb)
 /*
  * Callback for the segment request
  *
- * If succesful transition state (unless already transitioned or
+ * If successful transition state (unless already transitioned or
  * outbound transfer); otherwise, take a note of the error, mark this
  * segment done and try completion.
  *
@@ -1364,7 +1364,7 @@ segment_aborted:
 /*
  * Callback for the IN data phase
  *
- * If succesful transition state; otherwise, take a note of the
+ * If successful transition state; otherwise, take a note of the
  * error, mark this segment done and try completion.
  *
  * Note we don't access until we are sure that the transfer hasn't
index c7080d4..0bb665a 100644 (file)
@@ -229,7 +229,7 @@ void i1480_usb_neep_cb(struct urb *urb)
  * will verify it.
  *
  * Set i1480->evt_result with the result of getting the event or its
- * size (if succesful).
+ * size (if successful).
  *
  * Delivers the data directly to i1480->evt_buf
  */
index 0af8916..78510a1 100644 (file)
@@ -150,7 +150,7 @@ void uwb_rc_neh_put(struct uwb_rc_neh *neh)
  *      0xff is invalid, so they must not be used. Initialization
  *      fills up those two in the bitmap so they are not allocated.
  *
- * We spread the allocation around to reduce the posiblity of two
+ * We spread the allocation around to reduce the possibility of two
  * consecutive opened @neh's getting the same context ID assigned (to
  * avoid surprises with late events that timed out long time ago). So
  * first we search from where @rc->ctx_roll is, if not found, we
index 86a853b..7350ed6 100644 (file)
@@ -282,7 +282,7 @@ EXPORT_SYMBOL_GPL(wlp_receive_frame);
  *         and transmission will be done by the calling function.
  * @dst:   On return this will contain the device address to which the
  *         frame is destined.
- * @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer,
+ * @returns: 0 on success no tx : WLP header successfully applied to skb buffer,
  *                                calling function can proceed with tx
  *           1 on success with tx : WLP will take over transmission of this
  *                                  frame
index 6b89eb5..bb5fbed 100644 (file)
@@ -2066,6 +2066,7 @@ config XEN_FBDEV_FRONTEND
        select FB_SYS_IMAGEBLIT
        select FB_SYS_FOPS
        select FB_DEFERRED_IO
+       select XEN_XENBUS_FRONTEND
        default y
        help
          This driver implements the front-end of the Xen virtual
@@ -2164,6 +2165,7 @@ config FB_BROADSHEET
          a bridge adapter.
 
 source "drivers/video/omap/Kconfig"
+source "drivers/video/omap2/Kconfig"
 
 source "drivers/video/backlight/Kconfig"
 source "drivers/video/display/Kconfig"
index 80232e1..0f8da33 100644 (file)
@@ -124,6 +124,7 @@ obj-$(CONFIG_FB_SM501)            += sm501fb.o
 obj-$(CONFIG_FB_XILINX)           += xilinxfb.o
 obj-$(CONFIG_FB_SH_MOBILE_LCDC)          += sh_mobile_lcdcfb.o
 obj-$(CONFIG_FB_OMAP)             += omap/
+obj-y                             += omap2/
 obj-$(CONFIG_XEN_FBDEV_FRONTEND)  += xen-fbfront.o
 obj-$(CONFIG_FB_CARMINE)          += carminefb.o
 obj-$(CONFIG_FB_MB862XX)         += mb862xx/
index d5e8010..3d886c6 100644 (file)
@@ -964,7 +964,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
        if (sinfo->atmel_lcdfb_power_control)
                sinfo->atmel_lcdfb_power_control(1);
 
-       dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n",
+       dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %d\n",
                       info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base);
 
        return 0;
index 913b4a4..1ddeb4c 100644 (file)
@@ -3276,7 +3276,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base)
                                txtformat = "24 bit interface";
                                break;
                        default:
-                               txtformat = "unkown format";
+                               txtformat = "unknown format";
                        }
                } else {
                        switch (format & 7) {
@@ -3299,7 +3299,7 @@ static void __devinit aty_init_lcd(struct atyfb_par *par, u32 bios_base)
                                txtformat = "262144 colours (FDPI-2 mode)";
                                break;
                        default:
-                               txtformat = "unkown format";
+                               txtformat = "unknown format";
                        }
                }
                PRINTKI("%s%s %s monitor detected: %s\n",
index 505c082..2cf7ba5 100644 (file)
@@ -158,7 +158,7 @@ static int atmel_pwm_bl_probe(struct platform_device *pdev)
                        goto err_free_pwm;
                }
 
-               /* Turn display off by defatult. */
+               /* Turn display off by default. */
                retval = gpio_direction_output(pwmbl->gpio_on,
                                0 ^ pdata->on_active_low);
                if (retval)
index 50ec17d..fa32b94 100644 (file)
@@ -177,7 +177,7 @@ static int __devinit tosa_lcd_probe(struct spi_device *spi)
        if (!data)
                return -ENOMEM;
 
-       data->is_vga = true; /* defaut to VGA mode */
+       data->is_vga = true; /* default to VGA mode */
 
        /*
         * bits_per_word cannot be configured in platform data
index 509cb92..df9ccb9 100644 (file)
@@ -470,7 +470,7 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev)
        par->read_reg = broadsheet_read_reg;
        init_waitqueue_head(&par->waitq);
 
-       info->flags = FBINFO_FLAG_DEFAULT;
+       info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 
        info->fbdefio = &broadsheetfb_defio;
        fb_deferred_io_init(info);
index 857b366..6468a29 100644 (file)
@@ -436,7 +436,7 @@ sti_init_glob_cfg(struct sti_struct *sti,
                            (offs < PCI_BASE_ADDRESS_0 ||
                             offs > PCI_BASE_ADDRESS_5)) {
                                printk (KERN_WARNING
-                                       "STI pci region maping for region %d (%02x) can't be mapped\n",
+                                       "STI pci region mapping for region %d (%02x) can't be mapped\n",
                                        i,sti->rm_entry[i]);
                                continue;
                        }
index c27ab1e..44ce908 100644 (file)
@@ -71,7 +71,7 @@ int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
 {
        struct fb_info *info = file->private_data;
 
-       /* Skip if deferred io is complied-in but disabled on this fbdev */
+       /* Skip if deferred io is compiled-in but disabled on this fbdev */
        if (!info->fbdefio)
                return 0;
 
@@ -144,7 +144,9 @@ static const struct address_space_operations fb_deferred_io_aops = {
 static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
        vma->vm_ops = &fb_deferred_io_vm_ops;
-       vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND );
+       vma->vm_flags |= ( VM_RESERVED | VM_DONTEXPAND );
+       if (!(info->flags & FBINFO_VIRTFB))
+               vma->vm_flags |= VM_IO;
        vma->vm_private_data = info;
        return 0;
 }
index f67db42..695fa01 100644 (file)
@@ -701,7 +701,7 @@ static int gbefb_set_par(struct fb_info *info)
           blocks of 512x128, 256x128 or 128x128 pixels, respectively for 8bit,
           16bit and 32 bit modes (64 kB). They cover the screen with partial
           tiles on the right and/or bottom of the screen if needed.
-          For exemple in 640x480 8 bit mode the mapping is:
+          For example in 640x480 8 bit mode the mapping is:
 
           <-------- 640 ----->
           <---- 512 ----><128|384 offscreen>
index 0b4bffb..f9d77ad 100644 (file)
@@ -253,7 +253,7 @@ static int __devinit hecubafb_probe(struct platform_device *dev)
        par->send_command = apollo_send_command;
        par->send_data = apollo_send_data;
 
-       info->flags = FBINFO_FLAG_DEFAULT;
+       info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 
        info->fbdefio = &hecubafb_defio;
        fb_deferred_io_init(info);
index df1f757..661bfd2 100644 (file)
@@ -700,7 +700,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        if (retval < 0)
                goto err_free_irq;
 
-       info->flags = FBINFO_FLAG_DEFAULT;
+       info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 
        info->fbdefio = &metronomefb_defio;
        fb_deferred_io_init(info);
index 551e3e9..455c605 100644 (file)
@@ -1,6 +1,7 @@
 config FB_OMAP
        tristate "OMAP frame buffer support (EXPERIMENTAL)"
-       depends on FB && ARCH_OMAP
+       depends on FB && ARCH_OMAP && (OMAP2_DSS = "n")
+
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
@@ -72,7 +73,7 @@ config FB_OMAP_LCD_MIPID
 
 config FB_OMAP_BOOTLOADER_INIT
        bool "Check bootloader initialization"
-       depends on FB_OMAP
+       depends on FB_OMAP || FB_OMAP2
        help
          Say Y here if you want to enable checking if the bootloader has
          already initialized the display controller. In this case the
index f5d75f2..2ffb34a 100644 (file)
@@ -27,9 +27,9 @@
 #include <linux/clk.h>
 
 #include <plat/dma.h>
-#include <plat/omapfb.h>
 #include <plat/blizzard.h>
 
+#include "omapfb.h"
 #include "dispc.h"
 
 #define MODULE_NAME                            "blizzard"
index 7c833db..c7c6455 100644 (file)
 #include <linux/vmalloc.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/platform_device.h>
 
 #include <plat/sram.h>
-#include <plat/omapfb.h>
 #include <plat/board.h>
 
+#include "omapfb.h"
 #include "dispc.h"
 
 #define MODULE_NAME                    "dispc"
@@ -188,6 +189,11 @@ static struct {
        struct omapfb_color_key color_key;
 } dispc;
 
+static struct platform_device omapdss_device = {
+       .name           = "omapdss",
+       .id             = -1,
+};
+
 static void enable_lcd_clocks(int enable);
 
 static void inline dispc_write_reg(int idx, u32 val)
@@ -914,20 +920,20 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
 
 static int get_dss_clocks(void)
 {
-       dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick");
+       dispc.dss_ick = clk_get(&omapdss_device.dev, "ick");
        if (IS_ERR(dispc.dss_ick)) {
                dev_err(dispc.fbdev->dev, "can't get ick\n");
                return PTR_ERR(dispc.dss_ick);
        }
 
-       dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck");
+       dispc.dss1_fck = clk_get(&omapdss_device.dev, "dss1_fck");
        if (IS_ERR(dispc.dss1_fck)) {
                dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
                clk_put(dispc.dss_ick);
                return PTR_ERR(dispc.dss1_fck);
        }
 
-       dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck");
+       dispc.dss_54m_fck = clk_get(&omapdss_device.dev, "tv_fck");
        if (IS_ERR(dispc.dss_54m_fck)) {
                dev_err(dispc.fbdev->dev, "can't get tv_fck\n");
                clk_put(dispc.dss_ick);
@@ -1379,6 +1385,12 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
        int skip_init = 0;
        int i;
 
+       r = platform_device_register(&omapdss_device);
+       if (r) {
+               dev_err(fbdev->dev, "can't register omapdss device\n");
+               return r;
+       }
+
        memset(&dispc, 0, sizeof(dispc));
 
        dispc.base = ioremap(DISPC_BASE, SZ_1K);
@@ -1522,6 +1534,7 @@ static void omap_dispc_cleanup(void)
        free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
        put_dss_clocks();
        iounmap(dispc.base);
+       platform_device_unregister(&omapdss_device);
 }
 
 const struct lcd_ctrl omap2_int_ctrl = {
index 17a975e..0016f77 100644 (file)
 #include <linux/fb.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
+#include <linux/interrupt.h>
 
 #include <plat/dma.h>
-#include <plat/omapfb.h>
 #include <plat/hwa742.h>
+#include "omapfb.h"
 
 #define HWA742_REV_CODE_REG       0x0
 #define HWA742_CONFIG_REG         0x2
index fea7fee..760645d 100644 (file)
 #include <linux/i2c/twl4030.h>
 
 #include <plat/mux.h>
-#include <plat/omapfb.h>
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #define SDP2430_LCD_PANEL_BACKLIGHT_GPIO       91
 #define SDP2430_LCD_PANEL_ENABLE_GPIO          154
 #define SDP3430_LCD_PANEL_BACKLIGHT_GPIO       24
index 3d52772..567db6a 100644 (file)
@@ -27,7 +27,8 @@
 
 #include <plat/board-ams-delta.h>
 #include <mach/hardware.h>
-#include <plat/omapfb.h>
+
+#include "omapfb.h"
 
 #define AMS_DELTA_DEFAULT_CONTRAST     112
 
@@ -123,12 +124,12 @@ struct platform_driver ams_delta_panel_driver = {
        },
 };
 
-static int ams_delta_panel_drv_init(void)
+static int __init ams_delta_panel_drv_init(void)
 {
        return platform_driver_register(&ams_delta_panel_driver);
 }
 
-static void ams_delta_panel_drv_cleanup(void)
+static void __exit ams_delta_panel_drv_cleanup(void)
 {
        platform_driver_unregister(&ams_delta_panel_driver);
 }
index 4c5cefc..2be94eb 100644 (file)
@@ -26,7 +26,8 @@
 
 #include <mach/gpio.h>
 #include <plat/mux.h>
-#include <plat/omapfb.h>
+
+#include "omapfb.h"
 
 /* #define USE_35INCH_LCD 1 */
 
index 240b4fb..8df6887 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/i2c/tps65010.h>
 
 #include <mach/gpio.h>
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 #define MODULE_NAME    "omapfb-lcd_h3"
 
index 720625d..03a06a9 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
 {
index 2e0c81e..a9007c5 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int htcherald_panel_init(struct lcd_panel *panel,
                                        struct omapfb_device *fbdev)
index aafe9b4..3271f16 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/io.h>
 
 #include <plat/fpga.h>
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int innovator1510_panel_init(struct lcd_panel *panel,
                                    struct omapfb_device *fbdev)
index 0de3382..9fff86f 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/platform_device.h>
 
 #include <mach/gpio.h>
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 #define MODULE_NAME    "omapfb-lcd_h3"
 
index 6a260df..5bb7f6f 100644 (file)
 
 #include <mach/gpio.h>
 #include <plat/mux.h>
-#include <plat/omapfb.h>
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #define LCD_PANEL_BACKLIGHT_GPIO       (15 + OMAP_MAX_GPIO_LINES)
 #define LCD_PANEL_ENABLE_GPIO          (7 + OMAP_MAX_GPIO_LINES)
 
index 2162eb0..abe1c76 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/spi/spi.h>
 
-#include <plat/omapfb.h>
 #include <plat/lcd_mipid.h>
 
+#include "omapfb.h"
+
 #define MIPID_MODULE_NAME              "lcd_mipid"
 
 #define MIPID_CMD_READ_DISP_ID         0x04
@@ -607,7 +608,7 @@ static struct spi_driver mipid_spi_driver = {
        .remove = __devexit_p(mipid_spi_remove),
 };
 
-static int mipid_drv_init(void)
+static int __init mipid_drv_init(void)
 {
        spi_register_driver(&mipid_spi_driver);
 
@@ -615,7 +616,7 @@ static int mipid_drv_init(void)
 }
 module_init(mipid_drv_init);
 
-static void mipid_drv_cleanup(void)
+static void __exit mipid_drv_cleanup(void)
 {
        spi_unregister_driver(&mipid_spi_driver);
 }
index e1a38ab..006c2fe 100644 (file)
 #include <linux/i2c/twl4030.h>
 
 #include <plat/mux.h>
-#include <plat/omapfb.h>
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #define LCD_PANEL_ENABLE_GPIO  154
 #define LCD_PANEL_LR           128
 #define LCD_PANEL_UD           129
index ccec084..fc503d8 100644 (file)
 #include <linux/i2c/twl4030.h>
 
 #include <plat/mux.h>
-#include <plat/omapfb.h>
+#include <plat/mux.h>
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #define LCD_PANEL_ENABLE_GPIO       170
 
 static int omap3beagle_panel_init(struct lcd_panel *panel,
index 556eb31..ae2edc4 100644 (file)
 #include <linux/i2c/twl4030.h>
 
 #include <plat/mux.h>
-#include <plat/omapfb.h>
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #define LCD_PANEL_ENABLE_GPIO       153
 #define LCD_PANEL_LR                2
 #define LCD_PANEL_UD                3
index bb21d7d..b87e8b8 100644 (file)
@@ -25,7 +25,7 @@
 
 #include <mach/gpio.h>
 #include <plat/mux.h>
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
 {
index b0f86e5..56ee192 100644 (file)
 
 #include <mach/gpio.h>
 #include <plat/mux.h>
-#include <plat/omapfb.h>
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #define LCD_ENABLE       144
 
 static int overo_panel_init(struct lcd_panel *panel,
index d302896..4cb3017 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/io.h>
 
 #include <plat/fpga.h>
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int palmte_panel_init(struct lcd_panel *panel,
                                struct omapfb_device *fbdev)
index 557424f..ff0e6d7 100644 (file)
@@ -30,7 +30,7 @@ GPIO13 - screen blanking
 #include <linux/io.h>
 
 #include <mach/gpio.h>
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int palmtt_panel_init(struct lcd_panel *panel,
        struct omapfb_device *fbdev)
index 5f4b5b2..2334e56 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 
-#include <plat/omapfb.h>
+#include "omapfb.h"
 
 static int palmz71_panel_init(struct lcd_panel *panel,
                              struct omapfb_device *fbdev)
index 5f32caf..b831e1d 100644 (file)
 #include <linux/clk.h>
 
 #include <plat/dma.h>
-#include <plat/omapfb.h>
 
 #include <asm/mach-types.h>
 
+#include "omapfb.h"
+
 #include "lcdc.h"
 
 #define MODULE_NAME                    "lcdc"
similarity index 60%
rename from arch/arm/plat-omap/include/plat/omapfb.h
rename to drivers/video/omap/omapfb.h
index bfef7ab..46e4714 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * File: arch/arm/plat-omap/include/mach/omapfb.h
+ * File: drivers/video/omap/omapfb.h
  *
  * Framebuffer driver for TI OMAP boards
  *
 #ifndef __OMAPFB_H
 #define __OMAPFB_H
 
-#include <asm/ioctl.h>
-#include <asm/types.h>
-
-/* IOCTL commands. */
-
-#define OMAP_IOW(num, dtype)   _IOW('O', num, dtype)
-#define OMAP_IOR(num, dtype)   _IOR('O', num, dtype)
-#define OMAP_IOWR(num, dtype)  _IOWR('O', num, dtype)
-#define OMAP_IO(num)           _IO('O', num)
-
-#define OMAPFB_MIRROR          OMAP_IOW(31, int)
-#define OMAPFB_SYNC_GFX                OMAP_IO(37)
-#define OMAPFB_VSYNC           OMAP_IO(38)
-#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int)
-#define OMAPFB_GET_CAPS                OMAP_IOR(42, struct omapfb_caps)
-#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int)
-#define OMAPFB_LCD_TEST                OMAP_IOW(45, int)
-#define OMAPFB_CTRL_TEST       OMAP_IOW(46, int)
-#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old)
-#define OMAPFB_SET_COLOR_KEY   OMAP_IOW(50, struct omapfb_color_key)
-#define OMAPFB_GET_COLOR_KEY   OMAP_IOW(51, struct omapfb_color_key)
-#define OMAPFB_SETUP_PLANE     OMAP_IOW(52, struct omapfb_plane_info)
-#define OMAPFB_QUERY_PLANE     OMAP_IOW(53, struct omapfb_plane_info)
-#define OMAPFB_UPDATE_WINDOW   OMAP_IOW(54, struct omapfb_update_window)
-#define OMAPFB_SETUP_MEM       OMAP_IOW(55, struct omapfb_mem_info)
-#define OMAPFB_QUERY_MEM       OMAP_IOW(56, struct omapfb_mem_info)
-
-#define OMAPFB_CAPS_GENERIC_MASK       0x00000fff
-#define OMAPFB_CAPS_LCDC_MASK          0x00fff000
-#define OMAPFB_CAPS_PANEL_MASK         0xff000000
-
-#define OMAPFB_CAPS_MANUAL_UPDATE      0x00001000
-#define OMAPFB_CAPS_TEARSYNC           0x00002000
-#define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000
-#define OMAPFB_CAPS_PLANE_SCALE                0x00008000
-#define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE        0x00010000
-#define OMAPFB_CAPS_WINDOW_SCALE       0x00020000
-#define OMAPFB_CAPS_WINDOW_OVERLAY     0x00040000
-#define OMAPFB_CAPS_WINDOW_ROTATE      0x00080000
-#define OMAPFB_CAPS_SET_BACKLIGHT      0x01000000
-
-/* Values from DSP must map to lower 16-bits */
-#define OMAPFB_FORMAT_MASK             0x00ff
-#define OMAPFB_FORMAT_FLAG_DOUBLE      0x0100
-#define OMAPFB_FORMAT_FLAG_TEARSYNC    0x0200
-#define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400
-#define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY      0x0800
-#define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY     0x1000
-
-#define OMAPFB_EVENT_READY     1
-#define OMAPFB_EVENT_DISABLED  2
-
-#define OMAPFB_MEMTYPE_SDRAM           0
-#define OMAPFB_MEMTYPE_SRAM            1
-#define OMAPFB_MEMTYPE_MAX             1
-
-enum omapfb_color_format {
-       OMAPFB_COLOR_RGB565 = 0,
-       OMAPFB_COLOR_YUV422,
-       OMAPFB_COLOR_YUV420,
-       OMAPFB_COLOR_CLUT_8BPP,
-       OMAPFB_COLOR_CLUT_4BPP,
-       OMAPFB_COLOR_CLUT_2BPP,
-       OMAPFB_COLOR_CLUT_1BPP,
-       OMAPFB_COLOR_RGB444,
-       OMAPFB_COLOR_YUY422,
-};
-
-struct omapfb_update_window {
-       __u32 x, y;
-       __u32 width, height;
-       __u32 format;
-       __u32 out_x, out_y;
-       __u32 out_width, out_height;
-       __u32 reserved[8];
-};
-
-struct omapfb_update_window_old {
-       __u32 x, y;
-       __u32 width, height;
-       __u32 format;
-};
-
-enum omapfb_plane {
-       OMAPFB_PLANE_GFX = 0,
-       OMAPFB_PLANE_VID1,
-       OMAPFB_PLANE_VID2,
-};
-
-enum omapfb_channel_out {
-       OMAPFB_CHANNEL_OUT_LCD = 0,
-       OMAPFB_CHANNEL_OUT_DIGIT,
-};
-
-struct omapfb_plane_info {
-       __u32 pos_x;
-       __u32 pos_y;
-       __u8  enabled;
-       __u8  channel_out;
-       __u8  mirror;
-       __u8  reserved1;
-       __u32 out_width;
-       __u32 out_height;
-       __u32 reserved2[12];
-};
-
-struct omapfb_mem_info {
-       __u32 size;
-       __u8  type;
-       __u8  reserved[3];
-};
-
-struct omapfb_caps {
-       __u32 ctrl;
-       __u32 plane_color;
-       __u32 wnd_color;
-};
-
-enum omapfb_color_key_type {
-       OMAPFB_COLOR_KEY_DISABLED = 0,
-       OMAPFB_COLOR_KEY_GFX_DST,
-       OMAPFB_COLOR_KEY_VID_SRC,
-};
-
-struct omapfb_color_key {
-       __u8  channel_out;
-       __u32 background;
-       __u32 trans_key;
-       __u8  key_type;
-};
-
-enum omapfb_update_mode {
-       OMAPFB_UPDATE_DISABLED = 0,
-       OMAPFB_AUTO_UPDATE,
-       OMAPFB_MANUAL_UPDATE
-};
-
-#ifdef __KERNEL__
-
-#include <linux/completion.h>
-#include <linux/interrupt.h>
 #include <linux/fb.h>
 #include <linux/mutex.h>
+#include <linux/omapfb.h>
 
-#include <plat/board.h>
+#define OMAPFB_EVENT_READY     1
+#define OMAPFB_EVENT_DISABLED  2
 
 #define OMAP_LCDC_INV_VSYNC             0x0001
 #define OMAP_LCDC_INV_HSYNC             0x0002
@@ -184,12 +45,6 @@ enum omapfb_update_mode {
 #define OMAPFB_PLANE_XRES_MIN          8
 #define OMAPFB_PLANE_YRES_MIN          8
 
-#ifdef CONFIG_ARCH_OMAP1
-#define OMAPFB_PLANE_NUM               1
-#else
-#define OMAPFB_PLANE_NUM               3
-#endif
-
 struct omapfb_device;
 
 struct lcd_panel {
@@ -256,7 +111,7 @@ struct lcd_ctrl_extif {
        void (*read_data)       (void *buf, unsigned int len);
        void (*write_data)      (const void *buf, unsigned int len);
        void (*transfer_area)   (int width, int height,
-                                void (callback)(void * data), void *data);
+                                void (callback)(void *data), void *data);
        int  (*setup_tearsync)  (unsigned pin_cnt,
                                 unsigned hs_pulse_time, unsigned vs_pulse_time,
                                 int hs_pol_inv, int vs_pol_inv, int div);
@@ -275,20 +130,6 @@ typedef int (*omapfb_notifier_callback_t)(struct notifier_block *,
                                          unsigned long event,
                                          void *fbi);
 
-struct omapfb_mem_region {
-       u32             paddr;
-       void __iomem    *vaddr;
-       unsigned long   size;
-       u8              type;           /* OMAPFB_PLANE_MEM_* */
-       unsigned        alloc:1;        /* allocated by the driver */
-       unsigned        map:1;          /* kernel mapped by the driver */
-};
-
-struct omapfb_mem_desc {
-       int                             region_cnt;
-       struct omapfb_mem_region        region[OMAPFB_PLANE_NUM];
-};
-
 struct lcd_ctrl {
        const char      *name;
        void            *data;
@@ -331,9 +172,9 @@ struct lcd_ctrl {
 };
 
 enum omapfb_state {
-       OMAPFB_DISABLED = 0,
-       OMAPFB_SUSPENDED= 99,
-       OMAPFB_ACTIVE   = 100
+       OMAPFB_DISABLED         = 0,
+       OMAPFB_SUSPENDED        = 99,
+       OMAPFB_ACTIVE           = 100
 };
 
 struct omapfb_plane_struct {
@@ -345,8 +186,8 @@ struct omapfb_plane_struct {
 
 struct omapfb_device {
        int                     state;
-       int                     ext_lcdc;               /* Using external
-                                                           LCD controller */
+       int                     ext_lcdc;               /* Using external
+                                                          LCD controller */
        struct mutex            rqueue_mutex;
 
        int                     palette_size;
@@ -364,19 +205,12 @@ struct omapfb_device {
        struct fb_info                  *fb_info[OMAPFB_PLANE_NUM];
 };
 
-struct omapfb_platform_data {
-       struct omap_lcd_config          lcd;
-       struct omapfb_mem_desc          mem_desc;
-       void                            *ctrl_platform_data;
-};
-
 #ifdef CONFIG_ARCH_OMAP1
 extern struct lcd_ctrl omap1_lcd_ctrl;
 #else
 extern struct lcd_ctrl omap2_disp_ctrl;
 #endif
 
-extern void omapfb_reserve_sdram(void);
 extern void omapfb_register_panel(struct lcd_panel *panel);
 extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
 extern void omapfb_notify_clients(struct omapfb_device *fbdev,
@@ -390,9 +224,4 @@ extern int  omapfb_update_window_async(struct fb_info *fbi,
                                       void (*callback)(void *),
                                       void *callback_data);
 
-/* in arch/arm/plat-omap/fb.c */
-extern void omapfb_set_ctrl_platform_data(void *pdata);
-
-#endif /* __KERNEL__ */
-
 #endif /* __OMAPFB_H */
index f900a43..c7f59a5 100644 (file)
@@ -29,8 +29,8 @@
 #include <linux/uaccess.h>
 
 #include <plat/dma.h>
-#include <plat/omapfb.h>
 
+#include "omapfb.h"
 #include "lcdc.h"
 #include "dispc.h"
 
index c90fa39..fed7b1b 100644 (file)
@@ -27,8 +27,7 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <plat/omapfb.h>
-
+#include "omapfb.h"
 #include "dispc.h"
 
 /* To work around an RFBI transfer rate limitation */
index 79dc84f..8fb7c70 100644 (file)
 #include <linux/clk.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/interrupt.h>
 
 #include <plat/dma.h>
-#include <plat/omapfb.h>
 
+#include "omapfb.h"
 #include "lcdc.h"
 
 #define MODULE_NAME            "omapfb-sossi"
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
new file mode 100644 (file)
index 0000000..d877c36
--- /dev/null
@@ -0,0 +1,9 @@
+config OMAP2_VRAM
+       bool
+
+config OMAP2_VRFB
+       bool
+
+source "drivers/video/omap2/dss/Kconfig"
+source "drivers/video/omap2/omapfb/Kconfig"
+source "drivers/video/omap2/displays/Kconfig"
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
new file mode 100644 (file)
index 0000000..d853d05
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_OMAP2_VRAM) += vram.o
+obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
+
+obj-y += dss/
+obj-y += omapfb/
+obj-y += displays/
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
new file mode 100644 (file)
index 0000000..b12a59c
--- /dev/null
@@ -0,0 +1,22 @@
+menu "OMAP2/3 Display Device Drivers"
+        depends on OMAP2_DSS
+
+config PANEL_GENERIC
+        tristate "Generic Panel"
+        help
+         Generic panel driver.
+         Used for DVI output for Beagle and OMAP3 SDP.
+
+config PANEL_SHARP_LS037V7DW01
+        tristate "Sharp LS037V7DW01 LCD Panel"
+        depends on OMAP2_DSS
+        help
+          LCD Panel used in TI's SDP3430 and EVM boards
+
+config PANEL_TAAL
+        tristate "Taal DSI Panel"
+        depends on OMAP2_DSS_DSI
+        help
+          Taal DSI command mode panel from TPO.
+
+endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
new file mode 100644 (file)
index 0000000..9556464
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o
+obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
+
+obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c
new file mode 100644 (file)
index 0000000..eb48d1a
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Generic panel support
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <plat/display.h>
+
+static struct omap_video_timings generic_panel_timings = {
+       /* 640 x 480 @ 60 Hz  Reduced blanking VESA CVT 0.31M3-R */
+       .x_res          = 640,
+       .y_res          = 480,
+       .pixel_clock    = 23500,
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+       .vfp            = 3,
+       .vsw            = 4,
+       .vbp            = 7,
+};
+
+static int generic_panel_probe(struct omap_dss_device *dssdev)
+{
+       dssdev->panel.config = OMAP_DSS_LCD_TFT;
+       dssdev->panel.timings = generic_panel_timings;
+
+       return 0;
+}
+
+static void generic_panel_remove(struct omap_dss_device *dssdev)
+{
+}
+
+static int generic_panel_enable(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       if (dssdev->platform_enable)
+               r = dssdev->platform_enable(dssdev);
+
+       return r;
+}
+
+static void generic_panel_disable(struct omap_dss_device *dssdev)
+{
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+}
+
+static int generic_panel_suspend(struct omap_dss_device *dssdev)
+{
+       generic_panel_disable(dssdev);
+       return 0;
+}
+
+static int generic_panel_resume(struct omap_dss_device *dssdev)
+{
+       return generic_panel_enable(dssdev);
+}
+
+static struct omap_dss_driver generic_driver = {
+       .probe          = generic_panel_probe,
+       .remove         = generic_panel_remove,
+
+       .enable         = generic_panel_enable,
+       .disable        = generic_panel_disable,
+       .suspend        = generic_panel_suspend,
+       .resume         = generic_panel_resume,
+
+       .driver         = {
+               .name   = "generic_panel",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init generic_panel_drv_init(void)
+{
+       return omap_dss_register_driver(&generic_driver);
+}
+
+static void __exit generic_panel_drv_exit(void)
+{
+       omap_dss_unregister_driver(&generic_driver);
+}
+
+module_init(generic_panel_drv_init);
+module_exit(generic_panel_drv_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
new file mode 100644 (file)
index 0000000..bbe880b
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * LCD panel driver for Sharp LS037V7DW01
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+
+#include <plat/display.h>
+
+struct sharp_data {
+       /* XXX This regulator should actually be in SDP board file, not here,
+        * as it doesn't actually power the LCD, but something else that
+        * affects the output to LCD (I think. Somebody clarify). It doesn't do
+        * harm here, as SDP is the only board using this currently */
+       struct regulator *vdvi_reg;
+};
+
+static struct omap_video_timings sharp_ls_timings = {
+       .x_res = 480,
+       .y_res = 640,
+
+       .pixel_clock    = 19200,
+
+       .hsw            = 2,
+       .hfp            = 1,
+       .hbp            = 28,
+
+       .vsw            = 1,
+       .vfp            = 1,
+       .vbp            = 1,
+};
+
+static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
+{
+       struct sharp_data *sd;
+
+       dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
+               OMAP_DSS_LCD_IHS;
+       dssdev->panel.acb = 0x28;
+       dssdev->panel.timings = sharp_ls_timings;
+
+       sd = kzalloc(sizeof(*sd), GFP_KERNEL);
+       if (!sd)
+               return -ENOMEM;
+
+       dev_set_drvdata(&dssdev->dev, sd);
+
+       sd->vdvi_reg = regulator_get(&dssdev->dev, "vdvi");
+       if (IS_ERR(sd->vdvi_reg)) {
+               kfree(sd);
+               pr_err("failed to get VDVI regulator\n");
+               return PTR_ERR(sd->vdvi_reg);
+       }
+
+       return 0;
+}
+
+static void sharp_ls_panel_remove(struct omap_dss_device *dssdev)
+{
+       struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
+
+       regulator_put(sd->vdvi_reg);
+
+       kfree(sd);
+}
+
+static int sharp_ls_panel_enable(struct omap_dss_device *dssdev)
+{
+       struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
+       int r = 0;
+
+       /* wait couple of vsyncs until enabling the LCD */
+       msleep(50);
+
+       regulator_enable(sd->vdvi_reg);
+
+       if (dssdev->platform_enable)
+               r = dssdev->platform_enable(dssdev);
+
+       return r;
+}
+
+static void sharp_ls_panel_disable(struct omap_dss_device *dssdev)
+{
+       struct sharp_data *sd = dev_get_drvdata(&dssdev->dev);
+
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+
+       regulator_disable(sd->vdvi_reg);
+
+       /* wait at least 5 vsyncs after disabling the LCD */
+
+       msleep(100);
+}
+
+static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev)
+{
+       sharp_ls_panel_disable(dssdev);
+       return 0;
+}
+
+static int sharp_ls_panel_resume(struct omap_dss_device *dssdev)
+{
+       return sharp_ls_panel_enable(dssdev);
+}
+
+static struct omap_dss_driver sharp_ls_driver = {
+       .probe          = sharp_ls_panel_probe,
+       .remove         = sharp_ls_panel_remove,
+
+       .enable         = sharp_ls_panel_enable,
+       .disable        = sharp_ls_panel_disable,
+       .suspend        = sharp_ls_panel_suspend,
+       .resume         = sharp_ls_panel_resume,
+
+       .driver         = {
+               .name   = "sharp_ls_panel",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init sharp_ls_panel_drv_init(void)
+{
+       return omap_dss_register_driver(&sharp_ls_driver);
+}
+
+static void __exit sharp_ls_panel_drv_exit(void)
+{
+       omap_dss_unregister_driver(&sharp_ls_driver);
+}
+
+module_init(sharp_ls_panel_drv_init);
+module_exit(sharp_ls_panel_drv_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
new file mode 100644 (file)
index 0000000..1f01dfc
--- /dev/null
@@ -0,0 +1,1003 @@
+/*
+ * Taal DSI command mode panel
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+
+#include <plat/display.h>
+
+/* DSI Virtual channel. Hardcoded for now. */
+#define TCH 0
+
+#define DCS_READ_NUM_ERRORS    0x05
+#define DCS_READ_POWER_MODE    0x0a
+#define DCS_READ_MADCTL                0x0b
+#define DCS_READ_PIXEL_FORMAT  0x0c
+#define DCS_RDDSDR             0x0f
+#define DCS_SLEEP_IN           0x10
+#define DCS_SLEEP_OUT          0x11
+#define DCS_DISPLAY_OFF                0x28
+#define DCS_DISPLAY_ON         0x29
+#define DCS_COLUMN_ADDR                0x2a
+#define DCS_PAGE_ADDR          0x2b
+#define DCS_MEMORY_WRITE       0x2c
+#define DCS_TEAR_OFF           0x34
+#define DCS_TEAR_ON            0x35
+#define DCS_MEM_ACC_CTRL       0x36
+#define DCS_PIXEL_FORMAT       0x3a
+#define DCS_BRIGHTNESS         0x51
+#define DCS_CTRL_DISPLAY       0x53
+#define DCS_WRITE_CABC         0x55
+#define DCS_READ_CABC          0x56
+#define DCS_GET_ID1            0xda
+#define DCS_GET_ID2            0xdb
+#define DCS_GET_ID3            0xdc
+
+/* #define TAAL_USE_ESD_CHECK */
+#define TAAL_ESD_CHECK_PERIOD  msecs_to_jiffies(5000)
+
+struct taal_data {
+       struct backlight_device *bldev;
+
+       unsigned long   hw_guard_end;   /* next value of jiffies when we can
+                                        * issue the next sleep in/out command
+                                        */
+       unsigned long   hw_guard_wait;  /* max guard time in jiffies */
+
+       struct omap_dss_device *dssdev;
+
+       bool enabled;
+       u8 rotate;
+       bool mirror;
+
+       bool te_enabled;
+       bool use_ext_te;
+       struct completion te_completion;
+
+       bool use_dsi_bl;
+
+       bool cabc_broken;
+       unsigned cabc_mode;
+
+       bool intro_printed;
+
+       struct workqueue_struct *esd_wq;
+       struct delayed_work esd_work;
+};
+
+static void taal_esd_work(struct work_struct *work);
+
+static void hw_guard_start(struct taal_data *td, int guard_msec)
+{
+       td->hw_guard_wait = msecs_to_jiffies(guard_msec);
+       td->hw_guard_end = jiffies + td->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct taal_data *td)
+{
+       unsigned long wait = td->hw_guard_end - jiffies;
+
+       if ((long)wait > 0 && wait <= td->hw_guard_wait) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(wait);
+       }
+}
+
+static int taal_dcs_read_1(u8 dcs_cmd, u8 *data)
+{
+       int r;
+       u8 buf[1];
+
+       r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1);
+
+       if (r < 0)
+               return r;
+
+       *data = buf[0];
+
+       return 0;
+}
+
+static int taal_dcs_write_0(u8 dcs_cmd)
+{
+       return dsi_vc_dcs_write(TCH, &dcs_cmd, 1);
+}
+
+static int taal_dcs_write_1(u8 dcs_cmd, u8 param)
+{
+       u8 buf[2];
+       buf[0] = dcs_cmd;
+       buf[1] = param;
+       return dsi_vc_dcs_write(TCH, buf, 2);
+}
+
+static int taal_sleep_in(struct taal_data *td)
+
+{
+       u8 cmd;
+       int r;
+
+       hw_guard_wait(td);
+
+       cmd = DCS_SLEEP_IN;
+       r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1);
+       if (r)
+               return r;
+
+       hw_guard_start(td, 120);
+
+       msleep(5);
+
+       return 0;
+}
+
+static int taal_sleep_out(struct taal_data *td)
+{
+       int r;
+
+       hw_guard_wait(td);
+
+       r = taal_dcs_write_0(DCS_SLEEP_OUT);
+       if (r)
+               return r;
+
+       hw_guard_start(td, 120);
+
+       msleep(5);
+
+       return 0;
+}
+
+static int taal_get_id(u8 *id1, u8 *id2, u8 *id3)
+{
+       int r;
+
+       r = taal_dcs_read_1(DCS_GET_ID1, id1);
+       if (r)
+               return r;
+       r = taal_dcs_read_1(DCS_GET_ID2, id2);
+       if (r)
+               return r;
+       r = taal_dcs_read_1(DCS_GET_ID3, id3);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int taal_set_addr_mode(u8 rotate, bool mirror)
+{
+       int r;
+       u8 mode;
+       int b5, b6, b7;
+
+       r = taal_dcs_read_1(DCS_READ_MADCTL, &mode);
+       if (r)
+               return r;
+
+       switch (rotate) {
+       default:
+       case 0:
+               b7 = 0;
+               b6 = 0;
+               b5 = 0;
+               break;
+       case 1:
+               b7 = 0;
+               b6 = 1;
+               b5 = 1;
+               break;
+       case 2:
+               b7 = 1;
+               b6 = 1;
+               b5 = 0;
+               break;
+       case 3:
+               b7 = 1;
+               b6 = 0;
+               b5 = 1;
+               break;
+       }
+
+       if (mirror)
+               b6 = !b6;
+
+       mode &= ~((1<<7) | (1<<6) | (1<<5));
+       mode |= (b7 << 7) | (b6 << 6) | (b5 << 5);
+
+       return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode);
+}
+
+static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h)
+{
+       int r;
+       u16 x1 = x;
+       u16 x2 = x + w - 1;
+       u16 y1 = y;
+       u16 y2 = y + h - 1;
+
+       u8 buf[5];
+       buf[0] = DCS_COLUMN_ADDR;
+       buf[1] = (x1 >> 8) & 0xff;
+       buf[2] = (x1 >> 0) & 0xff;
+       buf[3] = (x2 >> 8) & 0xff;
+       buf[4] = (x2 >> 0) & 0xff;
+
+       r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
+       if (r)
+               return r;
+
+       buf[0] = DCS_PAGE_ADDR;
+       buf[1] = (y1 >> 8) & 0xff;
+       buf[2] = (y1 >> 0) & 0xff;
+       buf[3] = (y2 >> 8) & 0xff;
+       buf[4] = (y2 >> 0) & 0xff;
+
+       r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf));
+       if (r)
+               return r;
+
+       dsi_vc_send_bta_sync(TCH);
+
+       return r;
+}
+
+static int taal_bl_update_status(struct backlight_device *dev)
+{
+       struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int r;
+       int level;
+
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               level = dev->props.brightness;
+       else
+               level = 0;
+
+       dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
+
+       if (td->use_dsi_bl) {
+               if (td->enabled) {
+                       dsi_bus_lock();
+                       r = taal_dcs_write_1(DCS_BRIGHTNESS, level);
+                       dsi_bus_unlock();
+                       if (r)
+                               return r;
+               }
+       } else {
+               if (!dssdev->set_backlight)
+                       return -EINVAL;
+
+               r = dssdev->set_backlight(dssdev, level);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+static int taal_bl_get_intensity(struct backlight_device *dev)
+{
+       if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+                       dev->props.power == FB_BLANK_UNBLANK)
+               return dev->props.brightness;
+
+       return 0;
+}
+
+static struct backlight_ops taal_bl_ops = {
+       .get_brightness = taal_bl_get_intensity,
+       .update_status  = taal_bl_update_status,
+};
+
+static void taal_get_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       *timings = dssdev->panel.timings;
+}
+
+static void taal_get_resolution(struct omap_dss_device *dssdev,
+               u16 *xres, u16 *yres)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+
+       if (td->rotate == 0 || td->rotate == 2) {
+               *xres = dssdev->panel.timings.x_res;
+               *yres = dssdev->panel.timings.y_res;
+       } else {
+               *yres = dssdev->panel.timings.x_res;
+               *xres = dssdev->panel.timings.y_res;
+       }
+}
+
+static irqreturn_t taal_te_isr(int irq, void *data)
+{
+       struct omap_dss_device *dssdev = data;
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+
+       complete_all(&td->te_completion);
+
+       return IRQ_HANDLED;
+}
+
+static ssize_t taal_num_errors_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       u8 errors;
+       int r;
+
+       if (td->enabled) {
+               dsi_bus_lock();
+               r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors);
+               dsi_bus_unlock();
+       } else {
+               r = -ENODEV;
+       }
+
+       if (r)
+               return r;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", errors);
+}
+
+static ssize_t taal_hw_revision_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       u8 id1, id2, id3;
+       int r;
+
+       if (td->enabled) {
+               dsi_bus_lock();
+               r = taal_get_id(&id1, &id2, &id3);
+               dsi_bus_unlock();
+       } else {
+               r = -ENODEV;
+       }
+
+       if (r)
+               return r;
+
+       return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
+}
+
+static const char *cabc_modes[] = {
+       "off",          /* used also always when CABC is not supported */
+       "ui",
+       "still-image",
+       "moving-image",
+};
+
+static ssize_t show_cabc_mode(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       const char *mode_str;
+       int mode;
+       int len;
+
+       mode = td->cabc_mode;
+
+       mode_str = "unknown";
+       if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
+               mode_str = cabc_modes[mode];
+       len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
+
+       return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
+}
+
+static ssize_t store_cabc_mode(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
+               if (sysfs_streq(cabc_modes[i], buf))
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(cabc_modes))
+               return -EINVAL;
+
+       if (td->enabled) {
+               dsi_bus_lock();
+               if (!td->cabc_broken)
+                       taal_dcs_write_1(DCS_WRITE_CABC, i);
+               dsi_bus_unlock();
+       }
+
+       td->cabc_mode = i;
+
+       return count;
+}
+
+static ssize_t show_cabc_available_modes(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       int len;
+       int i;
+
+       for (i = 0, len = 0;
+            len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
+               len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
+                       i ? " " : "", cabc_modes[i],
+                       i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
+
+       return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
+}
+
+static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL);
+static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL);
+static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
+               show_cabc_mode, store_cabc_mode);
+static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
+               show_cabc_available_modes, NULL);
+
+static struct attribute *taal_attrs[] = {
+       &dev_attr_num_dsi_errors.attr,
+       &dev_attr_hw_revision.attr,
+       &dev_attr_cabc_mode.attr,
+       &dev_attr_cabc_available_modes.attr,
+       NULL,
+};
+
+static struct attribute_group taal_attr_group = {
+       .attrs = taal_attrs,
+};
+
+static int taal_probe(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td;
+       struct backlight_device *bldev;
+       int r;
+
+       const struct omap_video_timings taal_panel_timings = {
+               .x_res          = 864,
+               .y_res          = 480,
+       };
+
+       dev_dbg(&dssdev->dev, "probe\n");
+
+       dssdev->panel.config = OMAP_DSS_LCD_TFT;
+       dssdev->panel.timings = taal_panel_timings;
+       dssdev->ctrl.pixel_size = 24;
+
+       td = kzalloc(sizeof(*td), GFP_KERNEL);
+       if (!td) {
+               r = -ENOMEM;
+               goto err0;
+       }
+       td->dssdev = dssdev;
+
+       td->esd_wq = create_singlethread_workqueue("taal_esd");
+       if (td->esd_wq == NULL) {
+               dev_err(&dssdev->dev, "can't create ESD workqueue\n");
+               r = -ENOMEM;
+               goto err2;
+       }
+       INIT_DELAYED_WORK_DEFERRABLE(&td->esd_work, taal_esd_work);
+
+       dev_set_drvdata(&dssdev->dev, td);
+
+       dssdev->get_timings = taal_get_timings;
+       dssdev->get_resolution = taal_get_resolution;
+
+       /* if no platform set_backlight() defined, presume DSI backlight
+        * control */
+       if (!dssdev->set_backlight)
+               td->use_dsi_bl = true;
+
+       bldev = backlight_device_register("taal", &dssdev->dev, dssdev,
+                       &taal_bl_ops);
+       if (IS_ERR(bldev)) {
+               r = PTR_ERR(bldev);
+               goto err1;
+       }
+
+       td->bldev = bldev;
+
+       bldev->props.fb_blank = FB_BLANK_UNBLANK;
+       bldev->props.power = FB_BLANK_UNBLANK;
+       if (td->use_dsi_bl) {
+               bldev->props.max_brightness = 255;
+               bldev->props.brightness = 255;
+       } else {
+               bldev->props.max_brightness = 127;
+               bldev->props.brightness = 127;
+       }
+
+       taal_bl_update_status(bldev);
+
+       if (dssdev->phy.dsi.ext_te) {
+               int gpio = dssdev->phy.dsi.ext_te_gpio;
+
+               r = gpio_request(gpio, "taal irq");
+               if (r) {
+                       dev_err(&dssdev->dev, "GPIO request failed\n");
+                       goto err3;
+               }
+
+               gpio_direction_input(gpio);
+
+               r = request_irq(gpio_to_irq(gpio), taal_te_isr,
+                               IRQF_DISABLED | IRQF_TRIGGER_RISING,
+                               "taal vsync", dssdev);
+
+               if (r) {
+                       dev_err(&dssdev->dev, "IRQ request failed\n");
+                       gpio_free(gpio);
+                       goto err3;
+               }
+
+               init_completion(&td->te_completion);
+
+               td->use_ext_te = true;
+       }
+
+       r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to create sysfs files\n");
+               goto err4;
+       }
+
+       return 0;
+err4:
+       if (td->use_ext_te) {
+               int gpio = dssdev->phy.dsi.ext_te_gpio;
+               free_irq(gpio_to_irq(gpio), dssdev);
+               gpio_free(gpio);
+       }
+err3:
+       backlight_device_unregister(bldev);
+err2:
+       cancel_delayed_work_sync(&td->esd_work);
+       destroy_workqueue(td->esd_wq);
+err1:
+       kfree(td);
+err0:
+       return r;
+}
+
+static void taal_remove(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct backlight_device *bldev;
+
+       dev_dbg(&dssdev->dev, "remove\n");
+
+       sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group);
+
+       if (td->use_ext_te) {
+               int gpio = dssdev->phy.dsi.ext_te_gpio;
+               free_irq(gpio_to_irq(gpio), dssdev);
+               gpio_free(gpio);
+       }
+
+       bldev = td->bldev;
+       bldev->props.power = FB_BLANK_POWERDOWN;
+       taal_bl_update_status(bldev);
+       backlight_device_unregister(bldev);
+
+       cancel_delayed_work_sync(&td->esd_work);
+       destroy_workqueue(td->esd_wq);
+
+       kfree(td);
+}
+
+static int taal_enable(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       u8 id1, id2, id3;
+       int r;
+
+       dev_dbg(&dssdev->dev, "enable\n");
+
+       if (dssdev->platform_enable) {
+               r = dssdev->platform_enable(dssdev);
+               if (r)
+                       return r;
+       }
+
+       /* it seems we have to wait a bit until taal is ready */
+       msleep(5);
+
+       r = taal_sleep_out(td);
+       if (r)
+               goto err;
+
+       r = taal_get_id(&id1, &id2, &id3);
+       if (r)
+               goto err;
+
+       /* on early revisions CABC is broken */
+       if (id2 == 0x00 || id2 == 0xff || id2 == 0x81)
+               td->cabc_broken = true;
+
+       taal_dcs_write_1(DCS_BRIGHTNESS, 0xff);
+       taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */
+
+       taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */
+
+       taal_set_addr_mode(td->rotate, td->mirror);
+       if (!td->cabc_broken)
+               taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode);
+
+       taal_dcs_write_0(DCS_DISPLAY_ON);
+
+#ifdef TAAL_USE_ESD_CHECK
+       queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
+#endif
+
+       td->enabled = 1;
+
+       if (!td->intro_printed) {
+               dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n",
+                               id1, id2, id3);
+               if (td->cabc_broken)
+                       dev_info(&dssdev->dev,
+                                       "old Taal version, CABC disabled\n");
+               td->intro_printed = true;
+       }
+
+       return 0;
+err:
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+
+       return r;
+}
+
+static void taal_disable(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+
+       dev_dbg(&dssdev->dev, "disable\n");
+
+       cancel_delayed_work(&td->esd_work);
+
+       taal_dcs_write_0(DCS_DISPLAY_OFF);
+       taal_sleep_in(td);
+
+       /* wait a bit so that the message goes through */
+       msleep(10);
+
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+
+       td->enabled = 0;
+}
+
+static int taal_suspend(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct backlight_device *bldev = td->bldev;
+
+       bldev->props.power = FB_BLANK_POWERDOWN;
+       taal_bl_update_status(bldev);
+
+       return 0;
+}
+
+static int taal_resume(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       struct backlight_device *bldev = td->bldev;
+
+       bldev->props.power = FB_BLANK_UNBLANK;
+       taal_bl_update_status(bldev);
+
+       return 0;
+}
+
+static void taal_setup_update(struct omap_dss_device *dssdev,
+                                   u16 x, u16 y, u16 w, u16 h)
+{
+       taal_set_update_window(x, y, w, h);
+}
+
+static int taal_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       td->te_enabled = enable;
+
+       if (enable)
+               r = taal_dcs_write_1(DCS_TEAR_ON, 0);
+       else
+               r = taal_dcs_write_0(DCS_TEAR_OFF);
+
+       return r;
+}
+
+static int taal_wait_te(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       long wait = msecs_to_jiffies(500);
+
+       if (!td->use_ext_te || !td->te_enabled)
+               return 0;
+
+       INIT_COMPLETION(td->te_completion);
+       wait = wait_for_completion_timeout(&td->te_completion, wait);
+       if (wait == 0) {
+               dev_err(&dssdev->dev, "timeout waiting TE\n");
+               return -ETIME;
+       }
+
+       return 0;
+}
+
+static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       dev_dbg(&dssdev->dev, "rotate %d\n", rotate);
+
+       if (td->enabled) {
+               r = taal_set_addr_mode(rotate, td->mirror);
+
+               if (r)
+                       return r;
+       }
+
+       td->rotate = rotate;
+
+       return 0;
+}
+
+static u8 taal_get_rotate(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       return td->rotate;
+}
+
+static int taal_mirror(struct omap_dss_device *dssdev, bool enable)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       int r;
+
+       dev_dbg(&dssdev->dev, "mirror %d\n", enable);
+
+       if (td->enabled) {
+               r = taal_set_addr_mode(td->rotate, enable);
+
+               if (r)
+                       return r;
+       }
+
+       td->mirror = enable;
+
+       return 0;
+}
+
+static bool taal_get_mirror(struct omap_dss_device *dssdev)
+{
+       struct taal_data *td = dev_get_drvdata(&dssdev->dev);
+       return td->mirror;
+}
+
+static int taal_run_test(struct omap_dss_device *dssdev, int test_num)
+{
+       u8 id1, id2, id3;
+       int r;
+
+       r = taal_dcs_read_1(DCS_GET_ID1, &id1);
+       if (r)
+               return r;
+       r = taal_dcs_read_1(DCS_GET_ID2, &id2);
+       if (r)
+               return r;
+       r = taal_dcs_read_1(DCS_GET_ID3, &id3);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int taal_memory_read(struct omap_dss_device *dssdev,
+               void *buf, size_t size,
+               u16 x, u16 y, u16 w, u16 h)
+{
+       int r;
+       int first = 1;
+       int plen;
+       unsigned buf_used = 0;
+
+       if (size < w * h * 3)
+               return -ENOMEM;
+
+       size = min(w * h * 3,
+                       dssdev->panel.timings.x_res *
+                       dssdev->panel.timings.y_res * 3);
+
+       /* plen 1 or 2 goes into short packet. until checksum error is fixed,
+        * use short packets. plen 32 works, but bigger packets seem to cause
+        * an error. */
+       if (size % 2)
+               plen = 1;
+       else
+               plen = 2;
+
+       taal_setup_update(dssdev, x, y, w, h);
+
+       r = dsi_vc_set_max_rx_packet_size(TCH, plen);
+       if (r)
+               return r;
+
+       while (buf_used < size) {
+               u8 dcs_cmd = first ? 0x2e : 0x3e;
+               first = 0;
+
+               r = dsi_vc_dcs_read(TCH, dcs_cmd,
+                               buf + buf_used, size - buf_used);
+
+               if (r < 0) {
+                       dev_err(&dssdev->dev, "read error\n");
+                       goto err;
+               }
+
+               buf_used += r;
+
+               if (r < plen) {
+                       dev_err(&dssdev->dev, "short read\n");
+                       break;
+               }
+
+               if (signal_pending(current)) {
+                       dev_err(&dssdev->dev, "signal pending, "
+                                       "aborting memory read\n");
+                       r = -ERESTARTSYS;
+                       goto err;
+               }
+       }
+
+       r = buf_used;
+
+err:
+       dsi_vc_set_max_rx_packet_size(TCH, 1);
+
+       return r;
+}
+
+static void taal_esd_work(struct work_struct *work)
+{
+       struct taal_data *td = container_of(work, struct taal_data,
+                       esd_work.work);
+       struct omap_dss_device *dssdev = td->dssdev;
+       u8 state1, state2;
+       int r;
+
+       if (!td->enabled)
+               return;
+
+       dsi_bus_lock();
+
+       r = taal_dcs_read_1(DCS_RDDSDR, &state1);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to read Taal status\n");
+               goto err;
+       }
+
+       /* Run self diagnostics */
+       r = taal_sleep_out(td);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to run Taal self-diagnostics\n");
+               goto err;
+       }
+
+       r = taal_dcs_read_1(DCS_RDDSDR, &state2);
+       if (r) {
+               dev_err(&dssdev->dev, "failed to read Taal status\n");
+               goto err;
+       }
+
+       /* Each sleep out command will trigger a self diagnostic and flip
+        * Bit6 if the test passes.
+        */
+       if (!((state1 ^ state2) & (1 << 6))) {
+               dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
+               goto err;
+       }
+       /* Self-diagnostics result is also shown on TE GPIO line. We need
+        * to re-enable TE after self diagnostics */
+       if (td->use_ext_te && td->te_enabled)
+               taal_enable_te(dssdev, true);
+
+       dsi_bus_unlock();
+
+       queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
+
+       return;
+err:
+       dev_err(&dssdev->dev, "performing LCD reset\n");
+
+       taal_disable(dssdev);
+       taal_enable(dssdev);
+
+       dsi_bus_unlock();
+
+       queue_delayed_work(td->esd_wq, &td->esd_work, TAAL_ESD_CHECK_PERIOD);
+}
+
+static struct omap_dss_driver taal_driver = {
+       .probe          = taal_probe,
+       .remove         = taal_remove,
+
+       .enable         = taal_enable,
+       .disable        = taal_disable,
+       .suspend        = taal_suspend,
+       .resume         = taal_resume,
+
+       .setup_update   = taal_setup_update,
+       .enable_te      = taal_enable_te,
+       .wait_for_te    = taal_wait_te,
+       .set_rotate     = taal_rotate,
+       .get_rotate     = taal_get_rotate,
+       .set_mirror     = taal_mirror,
+       .get_mirror     = taal_get_mirror,
+       .run_test       = taal_run_test,
+       .memory_read    = taal_memory_read,
+
+       .driver         = {
+               .name   = "taal",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init taal_init(void)
+{
+       omap_dss_register_driver(&taal_driver);
+
+       return 0;
+}
+
+static void __exit taal_exit(void)
+{
+       omap_dss_unregister_driver(&taal_driver);
+}
+
+module_init(taal_init);
+module_exit(taal_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("Taal Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
new file mode 100644 (file)
index 0000000..71d8dec
--- /dev/null
@@ -0,0 +1,89 @@
+menuconfig OMAP2_DSS
+        tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)"
+        depends on ARCH_OMAP2 || ARCH_OMAP3
+        help
+          OMAP2/3 Display Subsystem support.
+
+if OMAP2_DSS
+
+config OMAP2_VRAM_SIZE
+       int "VRAM size (MB)"
+       range 0 32
+       default 0
+       help
+         The amount of SDRAM to reserve at boot time for video RAM use.
+         This VRAM will be used by omapfb and other drivers that need
+         large continuous RAM area for video use.
+
+         You can also set this with "vram=<bytes>" kernel argument, or
+         in the board file.
+
+config OMAP2_DSS_DEBUG_SUPPORT
+        bool "Debug support"
+       default y
+       help
+         This enables debug messages. You need to enable printing
+         with 'debug' module parameter.
+
+config OMAP2_DSS_RFBI
+       bool "RFBI support"
+        default n
+       help
+         MIPI DBI, or RFBI (Remote Framebuffer Interface), support.
+
+config OMAP2_DSS_VENC
+       bool "VENC support"
+        default y
+       help
+         OMAP Video Encoder support.
+
+config OMAP2_DSS_SDI
+       bool "SDI support"
+       depends on ARCH_OMAP3
+        default n
+       help
+         SDI (Serial Display Interface) support.
+
+config OMAP2_DSS_DSI
+       bool "DSI support"
+       depends on ARCH_OMAP3
+        default n
+       help
+         MIPI DSI support.
+
+config OMAP2_DSS_USE_DSI_PLL
+       bool "Use DSI PLL for PCLK (EXPERIMENTAL)"
+       default n
+       depends on OMAP2_DSS_DSI
+       help
+         Use DSI PLL to generate pixel clock.  Currently only for DPI output.
+         DSI PLL can be used to generate higher and more precise pixel clocks.
+
+config OMAP2_DSS_FAKE_VSYNC
+       bool "Fake VSYNC irq from manual update displays"
+       default n
+       help
+         If this is selected, DSI will generate a fake DISPC VSYNC interrupt
+         when DSI has sent a frame. This is only needed with DSI or RFBI
+         displays using manual mode, and you want VSYNC to, for example,
+         time animation.
+
+config OMAP2_DSS_MIN_FCK_PER_PCK
+       int "Minimum FCK/PCK ratio (for scaling)"
+       range 0 32
+       default 0
+       help
+         This can be used to adjust the minimum FCK/PCK ratio.
+
+         With this you can make sure that DISPC FCK is at least
+         n x PCK. Video plane scaling requires higher FCK than
+         normally.
+
+         If this is set to 0, there's no extra constraint on the
+         DISPC FCK. However, the FCK will at minimum be
+         2xPCK (if active matrix) or 3xPCK (if passive matrix).
+
+         Max FCK is 173MHz, so this doesn't work if your PCK
+         is very high.
+
+endif
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
new file mode 100644 (file)
index 0000000..980c72c
--- /dev/null
@@ -0,0 +1,6 @@
+obj-$(CONFIG_OMAP2_DSS) += omapdss.o
+omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o
+omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
+omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
+omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
+omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
new file mode 100644 (file)
index 0000000..29497a0
--- /dev/null
@@ -0,0 +1,919 @@
+/*
+ * linux/drivers/video/omap2/dss/core.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "CORE"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/device.h>
+
+#include <plat/display.h>
+#include <plat/clock.h>
+
+#include "dss.h"
+
+static struct {
+       struct platform_device *pdev;
+       int             ctx_id;
+
+       struct clk      *dss_ick;
+       struct clk      *dss1_fck;
+       struct clk      *dss2_fck;
+       struct clk      *dss_54m_fck;
+       struct clk      *dss_96m_fck;
+       unsigned        num_clks_enabled;
+} core;
+
+static void dss_clk_enable_all_no_ctx(void);
+static void dss_clk_disable_all_no_ctx(void);
+static void dss_clk_enable_no_ctx(enum dss_clock clks);
+static void dss_clk_disable_no_ctx(enum dss_clock clks);
+
+static char *def_disp_name;
+module_param_named(def_disp, def_disp_name, charp, 0);
+MODULE_PARM_DESC(def_disp_name, "default display name");
+
+#ifdef DEBUG
+unsigned int dss_debug;
+module_param_named(debug, dss_debug, bool, 0644);
+#endif
+
+/* CONTEXT */
+static int dss_get_ctx_id(void)
+{
+       struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
+       int r;
+
+       if (!pdata->get_last_off_on_transaction_id)
+               return 0;
+       r = pdata->get_last_off_on_transaction_id(&core.pdev->dev);
+       if (r < 0) {
+               dev_err(&core.pdev->dev, "getting transaction ID failed, "
+                               "will force context restore\n");
+               r = -1;
+       }
+       return r;
+}
+
+int dss_need_ctx_restore(void)
+{
+       int id = dss_get_ctx_id();
+
+       if (id < 0 || id != core.ctx_id) {
+               DSSDBG("ctx id %d -> id %d\n",
+                               core.ctx_id, id);
+               core.ctx_id = id;
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+static void save_all_ctx(void)
+{
+       DSSDBG("save context\n");
+
+       dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dss_save_context();
+       dispc_save_context();
+#ifdef CONFIG_OMAP2_DSS_DSI
+       dsi_save_context();
+#endif
+
+       dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+static void restore_all_ctx(void)
+{
+       DSSDBG("restore context\n");
+
+       dss_clk_enable_all_no_ctx();
+
+       dss_restore_context();
+       dispc_restore_context();
+#ifdef CONFIG_OMAP2_DSS_DSI
+       dsi_restore_context();
+#endif
+
+       dss_clk_disable_all_no_ctx();
+}
+
+/* CLOCKS */
+static void core_dump_clocks(struct seq_file *s)
+{
+       int i;
+       struct clk *clocks[5] = {
+               core.dss_ick,
+               core.dss1_fck,
+               core.dss2_fck,
+               core.dss_54m_fck,
+               core.dss_96m_fck
+       };
+
+       seq_printf(s, "- CORE -\n");
+
+       seq_printf(s, "internal clk count\t\t%u\n", core.num_clks_enabled);
+
+       for (i = 0; i < 5; i++) {
+               if (!clocks[i])
+                       continue;
+               seq_printf(s, "%-15s\t%lu\t%d\n",
+                               clocks[i]->name,
+                               clk_get_rate(clocks[i]),
+                               clocks[i]->usecount);
+       }
+}
+
+static int dss_get_clock(struct clk **clock, const char *clk_name)
+{
+       struct clk *clk;
+
+       clk = clk_get(&core.pdev->dev, clk_name);
+
+       if (IS_ERR(clk)) {
+               DSSERR("can't get clock %s", clk_name);
+               return PTR_ERR(clk);
+       }
+
+       *clock = clk;
+
+       DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
+
+       return 0;
+}
+
+static int dss_get_clocks(void)
+{
+       int r;
+
+       core.dss_ick = NULL;
+       core.dss1_fck = NULL;
+       core.dss2_fck = NULL;
+       core.dss_54m_fck = NULL;
+       core.dss_96m_fck = NULL;
+
+       r = dss_get_clock(&core.dss_ick, "ick");
+       if (r)
+               goto err;
+
+       r = dss_get_clock(&core.dss1_fck, "dss1_fck");
+       if (r)
+               goto err;
+
+       r = dss_get_clock(&core.dss2_fck, "dss2_fck");
+       if (r)
+               goto err;
+
+       r = dss_get_clock(&core.dss_54m_fck, "tv_fck");
+       if (r)
+               goto err;
+
+       r = dss_get_clock(&core.dss_96m_fck, "video_fck");
+       if (r)
+               goto err;
+
+       return 0;
+
+err:
+       if (core.dss_ick)
+               clk_put(core.dss_ick);
+       if (core.dss1_fck)
+               clk_put(core.dss1_fck);
+       if (core.dss2_fck)
+               clk_put(core.dss2_fck);
+       if (core.dss_54m_fck)
+               clk_put(core.dss_54m_fck);
+       if (core.dss_96m_fck)
+               clk_put(core.dss_96m_fck);
+
+       return r;
+}
+
+static void dss_put_clocks(void)
+{
+       if (core.dss_96m_fck)
+               clk_put(core.dss_96m_fck);
+       clk_put(core.dss_54m_fck);
+       clk_put(core.dss1_fck);
+       clk_put(core.dss2_fck);
+       clk_put(core.dss_ick);
+}
+
+unsigned long dss_clk_get_rate(enum dss_clock clk)
+{
+       switch (clk) {
+       case DSS_CLK_ICK:
+               return clk_get_rate(core.dss_ick);
+       case DSS_CLK_FCK1:
+               return clk_get_rate(core.dss1_fck);
+       case DSS_CLK_FCK2:
+               return clk_get_rate(core.dss2_fck);
+       case DSS_CLK_54M:
+               return clk_get_rate(core.dss_54m_fck);
+       case DSS_CLK_96M:
+               return clk_get_rate(core.dss_96m_fck);
+       }
+
+       BUG();
+       return 0;
+}
+
+static unsigned count_clk_bits(enum dss_clock clks)
+{
+       unsigned num_clks = 0;
+
+       if (clks & DSS_CLK_ICK)
+               ++num_clks;
+       if (clks & DSS_CLK_FCK1)
+               ++num_clks;
+       if (clks & DSS_CLK_FCK2)
+               ++num_clks;
+       if (clks & DSS_CLK_54M)
+               ++num_clks;
+       if (clks & DSS_CLK_96M)
+               ++num_clks;
+
+       return num_clks;
+}
+
+static void dss_clk_enable_no_ctx(enum dss_clock clks)
+{
+       unsigned num_clks = count_clk_bits(clks);
+
+       if (clks & DSS_CLK_ICK)
+               clk_enable(core.dss_ick);
+       if (clks & DSS_CLK_FCK1)
+               clk_enable(core.dss1_fck);
+       if (clks & DSS_CLK_FCK2)
+               clk_enable(core.dss2_fck);
+       if (clks & DSS_CLK_54M)
+               clk_enable(core.dss_54m_fck);
+       if (clks & DSS_CLK_96M)
+               clk_enable(core.dss_96m_fck);
+
+       core.num_clks_enabled += num_clks;
+}
+
+void dss_clk_enable(enum dss_clock clks)
+{
+       dss_clk_enable_no_ctx(clks);
+
+       if (cpu_is_omap34xx() && dss_need_ctx_restore())
+               restore_all_ctx();
+}
+
+static void dss_clk_disable_no_ctx(enum dss_clock clks)
+{
+       unsigned num_clks = count_clk_bits(clks);
+
+       if (clks & DSS_CLK_ICK)
+               clk_disable(core.dss_ick);
+       if (clks & DSS_CLK_FCK1)
+               clk_disable(core.dss1_fck);
+       if (clks & DSS_CLK_FCK2)
+               clk_disable(core.dss2_fck);
+       if (clks & DSS_CLK_54M)
+               clk_disable(core.dss_54m_fck);
+       if (clks & DSS_CLK_96M)
+               clk_disable(core.dss_96m_fck);
+
+       core.num_clks_enabled -= num_clks;
+}
+
+void dss_clk_disable(enum dss_clock clks)
+{
+       if (cpu_is_omap34xx()) {
+               unsigned num_clks = count_clk_bits(clks);
+
+               BUG_ON(core.num_clks_enabled < num_clks);
+
+               if (core.num_clks_enabled == num_clks)
+                       save_all_ctx();
+       }
+
+       dss_clk_disable_no_ctx(clks);
+}
+
+static void dss_clk_enable_all_no_ctx(void)
+{
+       enum dss_clock clks;
+
+       clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+       if (cpu_is_omap34xx())
+               clks |= DSS_CLK_96M;
+       dss_clk_enable_no_ctx(clks);
+}
+
+static void dss_clk_disable_all_no_ctx(void)
+{
+       enum dss_clock clks;
+
+       clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+       if (cpu_is_omap34xx())
+               clks |= DSS_CLK_96M;
+       dss_clk_disable_no_ctx(clks);
+}
+
+static void dss_clk_disable_all(void)
+{
+       enum dss_clock clks;
+
+       clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+       if (cpu_is_omap34xx())
+               clks |= DSS_CLK_96M;
+       dss_clk_disable(clks);
+}
+
+/* DEBUGFS */
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+static void dss_debug_dump_clocks(struct seq_file *s)
+{
+       core_dump_clocks(s);
+       dss_dump_clocks(s);
+       dispc_dump_clocks(s);
+#ifdef CONFIG_OMAP2_DSS_DSI
+       dsi_dump_clocks(s);
+#endif
+}
+
+static int dss_debug_show(struct seq_file *s, void *unused)
+{
+       void (*func)(struct seq_file *) = s->private;
+       func(s);
+       return 0;
+}
+
+static int dss_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dss_debug_show, inode->i_private);
+}
+
+static const struct file_operations dss_debug_fops = {
+       .open           = dss_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *dss_debugfs_dir;
+
+static int dss_initialize_debugfs(void)
+{
+       dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
+       if (IS_ERR(dss_debugfs_dir)) {
+               int err = PTR_ERR(dss_debugfs_dir);
+               dss_debugfs_dir = NULL;
+               return err;
+       }
+
+       debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
+                       &dss_debug_dump_clocks, &dss_debug_fops);
+
+       debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir,
+                       &dss_dump_regs, &dss_debug_fops);
+       debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir,
+                       &dispc_dump_regs, &dss_debug_fops);
+#ifdef CONFIG_OMAP2_DSS_RFBI
+       debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir,
+                       &rfbi_dump_regs, &dss_debug_fops);
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+       debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir,
+                       &dsi_dump_regs, &dss_debug_fops);
+#endif
+#ifdef CONFIG_OMAP2_DSS_VENC
+       debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir,
+                       &venc_dump_regs, &dss_debug_fops);
+#endif
+       return 0;
+}
+
+static void dss_uninitialize_debugfs(void)
+{
+       if (dss_debugfs_dir)
+               debugfs_remove_recursive(dss_debugfs_dir);
+}
+#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
+
+/* PLATFORM DEVICE */
+static int omap_dss_probe(struct platform_device *pdev)
+{
+       struct omap_dss_board_info *pdata = pdev->dev.platform_data;
+       int skip_init = 0;
+       int r;
+       int i;
+
+       core.pdev = pdev;
+
+       dss_init_overlay_managers(pdev);
+       dss_init_overlays(pdev);
+
+       r = dss_get_clocks();
+       if (r)
+               goto fail0;
+
+       dss_clk_enable_all_no_ctx();
+
+       core.ctx_id = dss_get_ctx_id();
+       DSSDBG("initial ctx id %u\n", core.ctx_id);
+
+#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
+       /* DISPC_CONTROL */
+       if (omap_readl(0x48050440) & 1) /* LCD enabled? */
+               skip_init = 1;
+#endif
+
+       r = dss_init(skip_init);
+       if (r) {
+               DSSERR("Failed to initialize DSS\n");
+               goto fail0;
+       }
+
+#ifdef CONFIG_OMAP2_DSS_RFBI
+       r = rfbi_init();
+       if (r) {
+               DSSERR("Failed to initialize rfbi\n");
+               goto fail0;
+       }
+#endif
+
+       r = dpi_init();
+       if (r) {
+               DSSERR("Failed to initialize dpi\n");
+               goto fail0;
+       }
+
+       r = dispc_init();
+       if (r) {
+               DSSERR("Failed to initialize dispc\n");
+               goto fail0;
+       }
+#ifdef CONFIG_OMAP2_DSS_VENC
+       r = venc_init(pdev);
+       if (r) {
+               DSSERR("Failed to initialize venc\n");
+               goto fail0;
+       }
+#endif
+       if (cpu_is_omap34xx()) {
+#ifdef CONFIG_OMAP2_DSS_SDI
+               r = sdi_init(skip_init);
+               if (r) {
+                       DSSERR("Failed to initialize SDI\n");
+                       goto fail0;
+               }
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+               r = dsi_init(pdev);
+               if (r) {
+                       DSSERR("Failed to initialize DSI\n");
+                       goto fail0;
+               }
+#endif
+       }
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+       r = dss_initialize_debugfs();
+       if (r)
+               goto fail0;
+#endif
+
+       for (i = 0; i < pdata->num_devices; ++i) {
+               struct omap_dss_device *dssdev = pdata->devices[i];
+
+               r = omap_dss_register_device(dssdev);
+               if (r)
+                       DSSERR("device reg failed %d\n", i);
+
+               if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0)
+                       pdata->default_device = dssdev;
+       }
+
+       dss_clk_disable_all();
+
+       return 0;
+
+       /* XXX fail correctly */
+fail0:
+       return r;
+}
+
+static int omap_dss_remove(struct platform_device *pdev)
+{
+       struct omap_dss_board_info *pdata = pdev->dev.platform_data;
+       int i;
+       int c;
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+       dss_uninitialize_debugfs();
+#endif
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+       venc_exit();
+#endif
+       dispc_exit();
+       dpi_exit();
+#ifdef CONFIG_OMAP2_DSS_RFBI
+       rfbi_exit();
+#endif
+       if (cpu_is_omap34xx()) {
+#ifdef CONFIG_OMAP2_DSS_DSI
+               dsi_exit();
+#endif
+#ifdef CONFIG_OMAP2_DSS_SDI
+               sdi_exit();
+#endif
+       }
+
+       dss_exit();
+
+       /* these should be removed at some point */
+       c = core.dss_ick->usecount;
+       if (c > 0) {
+               DSSERR("warning: dss_ick usecount %d, disabling\n", c);
+               while (c-- > 0)
+                       clk_disable(core.dss_ick);
+       }
+
+       c = core.dss1_fck->usecount;
+       if (c > 0) {
+               DSSERR("warning: dss1_fck usecount %d, disabling\n", c);
+               while (c-- > 0)
+                       clk_disable(core.dss1_fck);
+       }
+
+       c = core.dss2_fck->usecount;
+       if (c > 0) {
+               DSSERR("warning: dss2_fck usecount %d, disabling\n", c);
+               while (c-- > 0)
+                       clk_disable(core.dss2_fck);
+       }
+
+       c = core.dss_54m_fck->usecount;
+       if (c > 0) {
+               DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c);
+               while (c-- > 0)
+                       clk_disable(core.dss_54m_fck);
+       }
+
+       if (core.dss_96m_fck) {
+               c = core.dss_96m_fck->usecount;
+               if (c > 0) {
+                       DSSERR("warning: dss_96m_fck usecount %d, disabling\n",
+                                       c);
+                       while (c-- > 0)
+                               clk_disable(core.dss_96m_fck);
+               }
+       }
+
+       dss_put_clocks();
+
+       dss_uninit_overlays(pdev);
+       dss_uninit_overlay_managers(pdev);
+
+       for (i = 0; i < pdata->num_devices; ++i)
+               omap_dss_unregister_device(pdata->devices[i]);
+
+       return 0;
+}
+
+static void omap_dss_shutdown(struct platform_device *pdev)
+{
+       DSSDBG("shutdown\n");
+       dss_disable_all_devices();
+}
+
+static int omap_dss_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       DSSDBG("suspend %d\n", state.event);
+
+       return dss_suspend_all_devices();
+}
+
+static int omap_dss_resume(struct platform_device *pdev)
+{
+       DSSDBG("resume\n");
+
+       return dss_resume_all_devices();
+}
+
+static struct platform_driver omap_dss_driver = {
+       .probe          = omap_dss_probe,
+       .remove         = omap_dss_remove,
+       .shutdown       = omap_dss_shutdown,
+       .suspend        = omap_dss_suspend,
+       .resume         = omap_dss_resume,
+       .driver         = {
+               .name   = "omapdss",
+               .owner  = THIS_MODULE,
+       },
+};
+
+/* BUS */
+static int dss_bus_match(struct device *dev, struct device_driver *driver)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+
+       DSSDBG("bus_match. dev %s/%s, drv %s\n",
+                       dev_name(dev), dssdev->driver_name, driver->name);
+
+       return strcmp(dssdev->driver_name, driver->name) == 0;
+}
+
+static ssize_t device_name_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       dssdev->name ?
+                       dssdev->name : "");
+}
+
+static struct device_attribute default_dev_attrs[] = {
+       __ATTR(name, S_IRUGO, device_name_show, NULL),
+       __ATTR_NULL,
+};
+
+static ssize_t driver_name_show(struct device_driver *drv, char *buf)
+{
+       struct omap_dss_driver *dssdrv = to_dss_driver(drv);
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       dssdrv->driver.name ?
+                       dssdrv->driver.name : "");
+}
+static struct driver_attribute default_drv_attrs[] = {
+       __ATTR(name, S_IRUGO, driver_name_show, NULL),
+       __ATTR_NULL,
+};
+
+static struct bus_type dss_bus_type = {
+       .name = "omapdss",
+       .match = dss_bus_match,
+       .dev_attrs = default_dev_attrs,
+       .drv_attrs = default_drv_attrs,
+};
+
+static void dss_bus_release(struct device *dev)
+{
+       DSSDBG("bus_release\n");
+}
+
+static struct device dss_bus = {
+       .release = dss_bus_release,
+};
+
+struct bus_type *dss_get_bus(void)
+{
+       return &dss_bus_type;
+}
+
+/* DRIVER */
+static int dss_driver_probe(struct device *dev)
+{
+       int r;
+       struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
+       bool force;
+
+       DSSDBG("driver_probe: dev %s/%s, drv %s\n",
+                               dev_name(dev), dssdev->driver_name,
+                               dssdrv->driver.name);
+
+       dss_init_device(core.pdev, dssdev);
+
+       /* skip this if the device is behind a ctrl */
+       if (!dssdev->panel.ctrl) {
+               force = pdata->default_device == dssdev;
+               dss_recheck_connections(dssdev, force);
+       }
+
+       r = dssdrv->probe(dssdev);
+
+       if (r) {
+               DSSERR("driver probe failed: %d\n", r);
+               return r;
+       }
+
+       DSSDBG("probe done for device %s\n", dev_name(dev));
+
+       dssdev->driver = dssdrv;
+
+       return 0;
+}
+
+static int dss_driver_remove(struct device *dev)
+{
+       struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+
+       DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev),
+                       dssdev->driver_name);
+
+       dssdrv->remove(dssdev);
+
+       dss_uninit_device(core.pdev, dssdev);
+
+       dssdev->driver = NULL;
+
+       return 0;
+}
+
+int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
+{
+       dssdriver->driver.bus = &dss_bus_type;
+       dssdriver->driver.probe = dss_driver_probe;
+       dssdriver->driver.remove = dss_driver_remove;
+       return driver_register(&dssdriver->driver);
+}
+EXPORT_SYMBOL(omap_dss_register_driver);
+
+void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver)
+{
+       driver_unregister(&dssdriver->driver);
+}
+EXPORT_SYMBOL(omap_dss_unregister_driver);
+
+/* DEVICE */
+static void reset_device(struct device *dev, int check)
+{
+       u8 *dev_p = (u8 *)dev;
+       u8 *dev_end = dev_p + sizeof(*dev);
+       void *saved_pdata;
+
+       saved_pdata = dev->platform_data;
+       if (check) {
+               /*
+                * Check if there is any other setting than platform_data
+                * in struct device; warn that these will be reset by our
+                * init.
+                */
+               dev->platform_data = NULL;
+               while (dev_p < dev_end) {
+                       if (*dev_p) {
+                               WARN("%s: struct device fields will be "
+                                               "discarded\n",
+                                    __func__);
+                               break;
+                       }
+                       dev_p++;
+               }
+       }
+       memset(dev, 0, sizeof(*dev));
+       dev->platform_data = saved_pdata;
+}
+
+
+static void omap_dss_dev_release(struct device *dev)
+{
+       reset_device(dev, 0);
+}
+
+int omap_dss_register_device(struct omap_dss_device *dssdev)
+{
+       static int dev_num;
+       static int panel_num;
+       int r;
+
+       WARN_ON(!dssdev->driver_name);
+
+       reset_device(&dssdev->dev, 1);
+       dssdev->dev.bus = &dss_bus_type;
+       dssdev->dev.parent = &dss_bus;
+       dssdev->dev.release = omap_dss_dev_release;
+       dev_set_name(&dssdev->dev, "display%d", dev_num++);
+       r = device_register(&dssdev->dev);
+       if (r)
+               return r;
+
+       if (dssdev->ctrl.panel) {
+               struct omap_dss_device *panel = dssdev->ctrl.panel;
+
+               panel->panel.ctrl = dssdev;
+
+               reset_device(&panel->dev, 1);
+               panel->dev.bus = &dss_bus_type;
+               panel->dev.parent = &dssdev->dev;
+               panel->dev.release = omap_dss_dev_release;
+               dev_set_name(&panel->dev, "panel%d", panel_num++);
+               r = device_register(&panel->dev);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+void omap_dss_unregister_device(struct omap_dss_device *dssdev)
+{
+       device_unregister(&dssdev->dev);
+
+       if (dssdev->ctrl.panel) {
+               struct omap_dss_device *panel = dssdev->ctrl.panel;
+               device_unregister(&panel->dev);
+       }
+}
+
+/* BUS */
+static int omap_dss_bus_register(void)
+{
+       int r;
+
+       r = bus_register(&dss_bus_type);
+       if (r) {
+               DSSERR("bus register failed\n");
+               return r;
+       }
+
+       dev_set_name(&dss_bus, "omapdss");
+       r = device_register(&dss_bus);
+       if (r) {
+               DSSERR("bus driver register failed\n");
+               bus_unregister(&dss_bus_type);
+               return r;
+       }
+
+       return 0;
+}
+
+/* INIT */
+
+#ifdef CONFIG_OMAP2_DSS_MODULE
+static void omap_dss_bus_unregister(void)
+{
+       device_unregister(&dss_bus);
+
+       bus_unregister(&dss_bus_type);
+}
+
+static int __init omap_dss_init(void)
+{
+       int r;
+
+       r = omap_dss_bus_register();
+       if (r)
+               return r;
+
+       r = platform_driver_register(&omap_dss_driver);
+       if (r) {
+               omap_dss_bus_unregister();
+               return r;
+       }
+
+       return 0;
+}
+
+static void __exit omap_dss_exit(void)
+{
+       platform_driver_unregister(&omap_dss_driver);
+
+       omap_dss_bus_unregister();
+}
+
+module_init(omap_dss_init);
+module_exit(omap_dss_exit);
+#else
+static int __init omap_dss_init(void)
+{
+       return omap_dss_bus_register();
+}
+
+static int __init omap_dss_init2(void)
+{
+       return platform_driver_register(&omap_dss_driver);
+}
+
+core_initcall(omap_dss_init);
+device_initcall(omap_dss_init2);
+#endif
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
+MODULE_LICENSE("GPL v2");
+
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
new file mode 100644 (file)
index 0000000..6dabf4b
--- /dev/null
@@ -0,0 +1,3091 @@
+/*
+ * linux/drivers/video/omap2/dss/dispc.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DISPC"
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+
+#include <plat/sram.h>
+#include <plat/clock.h>
+
+#include <plat/display.h>
+
+#include "dss.h"
+
+/* DISPC */
+#define DISPC_BASE                     0x48050400
+
+#define DISPC_SZ_REGS                  SZ_1K
+
+struct dispc_reg { u16 idx; };
+
+#define DISPC_REG(idx)                 ((const struct dispc_reg) { idx })
+
+/* DISPC common */
+#define DISPC_REVISION                 DISPC_REG(0x0000)
+#define DISPC_SYSCONFIG                        DISPC_REG(0x0010)
+#define DISPC_SYSSTATUS                        DISPC_REG(0x0014)
+#define DISPC_IRQSTATUS                        DISPC_REG(0x0018)
+#define DISPC_IRQENABLE                        DISPC_REG(0x001C)
+#define DISPC_CONTROL                  DISPC_REG(0x0040)
+#define DISPC_CONFIG                   DISPC_REG(0x0044)
+#define DISPC_CAPABLE                  DISPC_REG(0x0048)
+#define DISPC_DEFAULT_COLOR0           DISPC_REG(0x004C)
+#define DISPC_DEFAULT_COLOR1           DISPC_REG(0x0050)
+#define DISPC_TRANS_COLOR0             DISPC_REG(0x0054)
+#define DISPC_TRANS_COLOR1             DISPC_REG(0x0058)
+#define DISPC_LINE_STATUS              DISPC_REG(0x005C)
+#define DISPC_LINE_NUMBER              DISPC_REG(0x0060)
+#define DISPC_TIMING_H                 DISPC_REG(0x0064)
+#define DISPC_TIMING_V                 DISPC_REG(0x0068)
+#define DISPC_POL_FREQ                 DISPC_REG(0x006C)
+#define DISPC_DIVISOR                  DISPC_REG(0x0070)
+#define DISPC_GLOBAL_ALPHA             DISPC_REG(0x0074)
+#define DISPC_SIZE_DIG                 DISPC_REG(0x0078)
+#define DISPC_SIZE_LCD                 DISPC_REG(0x007C)
+
+/* DISPC GFX plane */
+#define DISPC_GFX_BA0                  DISPC_REG(0x0080)
+#define DISPC_GFX_BA1                  DISPC_REG(0x0084)
+#define DISPC_GFX_POSITION             DISPC_REG(0x0088)
+#define DISPC_GFX_SIZE                 DISPC_REG(0x008C)
+#define DISPC_GFX_ATTRIBUTES           DISPC_REG(0x00A0)
+#define DISPC_GFX_FIFO_THRESHOLD       DISPC_REG(0x00A4)
+#define DISPC_GFX_FIFO_SIZE_STATUS     DISPC_REG(0x00A8)
+#define DISPC_GFX_ROW_INC              DISPC_REG(0x00AC)
+#define DISPC_GFX_PIXEL_INC            DISPC_REG(0x00B0)
+#define DISPC_GFX_WINDOW_SKIP          DISPC_REG(0x00B4)
+#define DISPC_GFX_TABLE_BA             DISPC_REG(0x00B8)
+
+#define DISPC_DATA_CYCLE1              DISPC_REG(0x01D4)
+#define DISPC_DATA_CYCLE2              DISPC_REG(0x01D8)
+#define DISPC_DATA_CYCLE3              DISPC_REG(0x01DC)
+
+#define DISPC_CPR_COEF_R               DISPC_REG(0x0220)
+#define DISPC_CPR_COEF_G               DISPC_REG(0x0224)
+#define DISPC_CPR_COEF_B               DISPC_REG(0x0228)
+
+#define DISPC_GFX_PRELOAD              DISPC_REG(0x022C)
+
+/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */
+#define DISPC_VID_REG(n, idx)          DISPC_REG(0x00BC + (n)*0x90 + idx)
+
+#define DISPC_VID_BA0(n)               DISPC_VID_REG(n, 0x0000)
+#define DISPC_VID_BA1(n)               DISPC_VID_REG(n, 0x0004)
+#define DISPC_VID_POSITION(n)          DISPC_VID_REG(n, 0x0008)
+#define DISPC_VID_SIZE(n)              DISPC_VID_REG(n, 0x000C)
+#define DISPC_VID_ATTRIBUTES(n)                DISPC_VID_REG(n, 0x0010)
+#define DISPC_VID_FIFO_THRESHOLD(n)    DISPC_VID_REG(n, 0x0014)
+#define DISPC_VID_FIFO_SIZE_STATUS(n)  DISPC_VID_REG(n, 0x0018)
+#define DISPC_VID_ROW_INC(n)           DISPC_VID_REG(n, 0x001C)
+#define DISPC_VID_PIXEL_INC(n)         DISPC_VID_REG(n, 0x0020)
+#define DISPC_VID_FIR(n)               DISPC_VID_REG(n, 0x0024)
+#define DISPC_VID_PICTURE_SIZE(n)      DISPC_VID_REG(n, 0x0028)
+#define DISPC_VID_ACCU0(n)             DISPC_VID_REG(n, 0x002C)
+#define DISPC_VID_ACCU1(n)             DISPC_VID_REG(n, 0x0030)
+
+/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
+#define DISPC_VID_FIR_COEF_H(n, i)     DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8)
+/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
+#define DISPC_VID_FIR_COEF_HV(n, i)    DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8)
+/* coef index i = {0, 1, 2, 3, 4} */
+#define DISPC_VID_CONV_COEF(n, i)      DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4)
+/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */
+#define DISPC_VID_FIR_COEF_V(n, i)     DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4)
+
+#define DISPC_VID_PRELOAD(n)           DISPC_REG(0x230 + (n)*0x04)
+
+
+#define DISPC_IRQ_MASK_ERROR            (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
+                                        DISPC_IRQ_OCP_ERR | \
+                                        DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
+                                        DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
+                                        DISPC_IRQ_SYNC_LOST | \
+                                        DISPC_IRQ_SYNC_LOST_DIGIT)
+
+#define DISPC_MAX_NR_ISRS              8
+
+struct omap_dispc_isr_data {
+       omap_dispc_isr_t        isr;
+       void                    *arg;
+       u32                     mask;
+};
+
+#define REG_GET(idx, start, end) \
+       FLD_GET(dispc_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end)                              \
+       dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end))
+
+static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES,
+       DISPC_VID_ATTRIBUTES(0),
+       DISPC_VID_ATTRIBUTES(1) };
+
+static struct {
+       void __iomem    *base;
+
+       u32     fifo_size[3];
+
+       spinlock_t irq_lock;
+       u32 irq_error_mask;
+       struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
+       u32 error_irqs;
+       struct work_struct error_work;
+
+       u32             ctx[DISPC_SZ_REGS / sizeof(u32)];
+} dispc;
+
+static void _omap_dispc_set_irqs(void);
+
+static inline void dispc_write_reg(const struct dispc_reg idx, u32 val)
+{
+       __raw_writel(val, dispc.base + idx.idx);
+}
+
+static inline u32 dispc_read_reg(const struct dispc_reg idx)
+{
+       return __raw_readl(dispc.base + idx.idx);
+}
+
+#define SR(reg) \
+       dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
+#define RR(reg) \
+       dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)])
+
+void dispc_save_context(void)
+{
+       if (cpu_is_omap24xx())
+               return;
+
+       SR(SYSCONFIG);
+       SR(IRQENABLE);
+       SR(CONTROL);
+       SR(CONFIG);
+       SR(DEFAULT_COLOR0);
+       SR(DEFAULT_COLOR1);
+       SR(TRANS_COLOR0);
+       SR(TRANS_COLOR1);
+       SR(LINE_NUMBER);
+       SR(TIMING_H);
+       SR(TIMING_V);
+       SR(POL_FREQ);
+       SR(DIVISOR);
+       SR(GLOBAL_ALPHA);
+       SR(SIZE_DIG);
+       SR(SIZE_LCD);
+
+       SR(GFX_BA0);
+       SR(GFX_BA1);
+       SR(GFX_POSITION);
+       SR(GFX_SIZE);
+       SR(GFX_ATTRIBUTES);
+       SR(GFX_FIFO_THRESHOLD);
+       SR(GFX_ROW_INC);
+       SR(GFX_PIXEL_INC);
+       SR(GFX_WINDOW_SKIP);
+       SR(GFX_TABLE_BA);
+
+       SR(DATA_CYCLE1);
+       SR(DATA_CYCLE2);
+       SR(DATA_CYCLE3);
+
+       SR(CPR_COEF_R);
+       SR(CPR_COEF_G);
+       SR(CPR_COEF_B);
+
+       SR(GFX_PRELOAD);
+
+       /* VID1 */
+       SR(VID_BA0(0));
+       SR(VID_BA1(0));
+       SR(VID_POSITION(0));
+       SR(VID_SIZE(0));
+       SR(VID_ATTRIBUTES(0));
+       SR(VID_FIFO_THRESHOLD(0));
+       SR(VID_ROW_INC(0));
+       SR(VID_PIXEL_INC(0));
+       SR(VID_FIR(0));
+       SR(VID_PICTURE_SIZE(0));
+       SR(VID_ACCU0(0));
+       SR(VID_ACCU1(0));
+
+       SR(VID_FIR_COEF_H(0, 0));
+       SR(VID_FIR_COEF_H(0, 1));
+       SR(VID_FIR_COEF_H(0, 2));
+       SR(VID_FIR_COEF_H(0, 3));
+       SR(VID_FIR_COEF_H(0, 4));
+       SR(VID_FIR_COEF_H(0, 5));
+       SR(VID_FIR_COEF_H(0, 6));
+       SR(VID_FIR_COEF_H(0, 7));
+
+       SR(VID_FIR_COEF_HV(0, 0));
+       SR(VID_FIR_COEF_HV(0, 1));
+       SR(VID_FIR_COEF_HV(0, 2));
+       SR(VID_FIR_COEF_HV(0, 3));
+       SR(VID_FIR_COEF_HV(0, 4));
+       SR(VID_FIR_COEF_HV(0, 5));
+       SR(VID_FIR_COEF_HV(0, 6));
+       SR(VID_FIR_COEF_HV(0, 7));
+
+       SR(VID_CONV_COEF(0, 0));
+       SR(VID_CONV_COEF(0, 1));
+       SR(VID_CONV_COEF(0, 2));
+       SR(VID_CONV_COEF(0, 3));
+       SR(VID_CONV_COEF(0, 4));
+
+       SR(VID_FIR_COEF_V(0, 0));
+       SR(VID_FIR_COEF_V(0, 1));
+       SR(VID_FIR_COEF_V(0, 2));
+       SR(VID_FIR_COEF_V(0, 3));
+       SR(VID_FIR_COEF_V(0, 4));
+       SR(VID_FIR_COEF_V(0, 5));
+       SR(VID_FIR_COEF_V(0, 6));
+       SR(VID_FIR_COEF_V(0, 7));
+
+       SR(VID_PRELOAD(0));
+
+       /* VID2 */
+       SR(VID_BA0(1));
+       SR(VID_BA1(1));
+       SR(VID_POSITION(1));
+       SR(VID_SIZE(1));
+       SR(VID_ATTRIBUTES(1));
+       SR(VID_FIFO_THRESHOLD(1));
+       SR(VID_ROW_INC(1));
+       SR(VID_PIXEL_INC(1));
+       SR(VID_FIR(1));
+       SR(VID_PICTURE_SIZE(1));
+       SR(VID_ACCU0(1));
+       SR(VID_ACCU1(1));
+
+       SR(VID_FIR_COEF_H(1, 0));
+       SR(VID_FIR_COEF_H(1, 1));
+       SR(VID_FIR_COEF_H(1, 2));
+       SR(VID_FIR_COEF_H(1, 3));
+       SR(VID_FIR_COEF_H(1, 4));
+       SR(VID_FIR_COEF_H(1, 5));
+       SR(VID_FIR_COEF_H(1, 6));
+       SR(VID_FIR_COEF_H(1, 7));
+
+       SR(VID_FIR_COEF_HV(1, 0));
+       SR(VID_FIR_COEF_HV(1, 1));
+       SR(VID_FIR_COEF_HV(1, 2));
+       SR(VID_FIR_COEF_HV(1, 3));
+       SR(VID_FIR_COEF_HV(1, 4));
+       SR(VID_FIR_COEF_HV(1, 5));
+       SR(VID_FIR_COEF_HV(1, 6));
+       SR(VID_FIR_COEF_HV(1, 7));
+
+       SR(VID_CONV_COEF(1, 0));
+       SR(VID_CONV_COEF(1, 1));
+       SR(VID_CONV_COEF(1, 2));
+       SR(VID_CONV_COEF(1, 3));
+       SR(VID_CONV_COEF(1, 4));
+
+       SR(VID_FIR_COEF_V(1, 0));
+       SR(VID_FIR_COEF_V(1, 1));
+       SR(VID_FIR_COEF_V(1, 2));
+       SR(VID_FIR_COEF_V(1, 3));
+       SR(VID_FIR_COEF_V(1, 4));
+       SR(VID_FIR_COEF_V(1, 5));
+       SR(VID_FIR_COEF_V(1, 6));
+       SR(VID_FIR_COEF_V(1, 7));
+
+       SR(VID_PRELOAD(1));
+}
+
+void dispc_restore_context(void)
+{
+       RR(SYSCONFIG);
+       RR(IRQENABLE);
+       /*RR(CONTROL);*/
+       RR(CONFIG);
+       RR(DEFAULT_COLOR0);
+       RR(DEFAULT_COLOR1);
+       RR(TRANS_COLOR0);
+       RR(TRANS_COLOR1);
+       RR(LINE_NUMBER);
+       RR(TIMING_H);
+       RR(TIMING_V);
+       RR(POL_FREQ);
+       RR(DIVISOR);
+       RR(GLOBAL_ALPHA);
+       RR(SIZE_DIG);
+       RR(SIZE_LCD);
+
+       RR(GFX_BA0);
+       RR(GFX_BA1);
+       RR(GFX_POSITION);
+       RR(GFX_SIZE);
+       RR(GFX_ATTRIBUTES);
+       RR(GFX_FIFO_THRESHOLD);
+       RR(GFX_ROW_INC);
+       RR(GFX_PIXEL_INC);
+       RR(GFX_WINDOW_SKIP);
+       RR(GFX_TABLE_BA);
+
+       RR(DATA_CYCLE1);
+       RR(DATA_CYCLE2);
+       RR(DATA_CYCLE3);
+
+       RR(CPR_COEF_R);
+       RR(CPR_COEF_G);
+       RR(CPR_COEF_B);
+
+       RR(GFX_PRELOAD);
+
+       /* VID1 */
+       RR(VID_BA0(0));
+       RR(VID_BA1(0));
+       RR(VID_POSITION(0));
+       RR(VID_SIZE(0));
+       RR(VID_ATTRIBUTES(0));
+       RR(VID_FIFO_THRESHOLD(0));
+       RR(VID_ROW_INC(0));
+       RR(VID_PIXEL_INC(0));
+       RR(VID_FIR(0));
+       RR(VID_PICTURE_SIZE(0));
+       RR(VID_ACCU0(0));
+       RR(VID_ACCU1(0));
+
+       RR(VID_FIR_COEF_H(0, 0));
+       RR(VID_FIR_COEF_H(0, 1));
+       RR(VID_FIR_COEF_H(0, 2));
+       RR(VID_FIR_COEF_H(0, 3));
+       RR(VID_FIR_COEF_H(0, 4));
+       RR(VID_FIR_COEF_H(0, 5));
+       RR(VID_FIR_COEF_H(0, 6));
+       RR(VID_FIR_COEF_H(0, 7));
+
+       RR(VID_FIR_COEF_HV(0, 0));
+       RR(VID_FIR_COEF_HV(0, 1));
+       RR(VID_FIR_COEF_HV(0, 2));
+       RR(VID_FIR_COEF_HV(0, 3));
+       RR(VID_FIR_COEF_HV(0, 4));
+       RR(VID_FIR_COEF_HV(0, 5));
+       RR(VID_FIR_COEF_HV(0, 6));
+       RR(VID_FIR_COEF_HV(0, 7));
+
+       RR(VID_CONV_COEF(0, 0));
+       RR(VID_CONV_COEF(0, 1));
+       RR(VID_CONV_COEF(0, 2));
+       RR(VID_CONV_COEF(0, 3));
+       RR(VID_CONV_COEF(0, 4));
+
+       RR(VID_FIR_COEF_V(0, 0));
+       RR(VID_FIR_COEF_V(0, 1));
+       RR(VID_FIR_COEF_V(0, 2));
+       RR(VID_FIR_COEF_V(0, 3));
+       RR(VID_FIR_COEF_V(0, 4));
+       RR(VID_FIR_COEF_V(0, 5));
+       RR(VID_FIR_COEF_V(0, 6));
+       RR(VID_FIR_COEF_V(0, 7));
+
+       RR(VID_PRELOAD(0));
+
+       /* VID2 */
+       RR(VID_BA0(1));
+       RR(VID_BA1(1));
+       RR(VID_POSITION(1));
+       RR(VID_SIZE(1));
+       RR(VID_ATTRIBUTES(1));
+       RR(VID_FIFO_THRESHOLD(1));
+       RR(VID_ROW_INC(1));
+       RR(VID_PIXEL_INC(1));
+       RR(VID_FIR(1));
+       RR(VID_PICTURE_SIZE(1));
+       RR(VID_ACCU0(1));
+       RR(VID_ACCU1(1));
+
+       RR(VID_FIR_COEF_H(1, 0));
+       RR(VID_FIR_COEF_H(1, 1));
+       RR(VID_FIR_COEF_H(1, 2));
+       RR(VID_FIR_COEF_H(1, 3));
+       RR(VID_FIR_COEF_H(1, 4));
+       RR(VID_FIR_COEF_H(1, 5));
+       RR(VID_FIR_COEF_H(1, 6));
+       RR(VID_FIR_COEF_H(1, 7));
+
+       RR(VID_FIR_COEF_HV(1, 0));
+       RR(VID_FIR_COEF_HV(1, 1));
+       RR(VID_FIR_COEF_HV(1, 2));
+       RR(VID_FIR_COEF_HV(1, 3));
+       RR(VID_FIR_COEF_HV(1, 4));
+       RR(VID_FIR_COEF_HV(1, 5));
+       RR(VID_FIR_COEF_HV(1, 6));
+       RR(VID_FIR_COEF_HV(1, 7));
+
+       RR(VID_CONV_COEF(1, 0));
+       RR(VID_CONV_COEF(1, 1));
+       RR(VID_CONV_COEF(1, 2));
+       RR(VID_CONV_COEF(1, 3));
+       RR(VID_CONV_COEF(1, 4));
+
+       RR(VID_FIR_COEF_V(1, 0));
+       RR(VID_FIR_COEF_V(1, 1));
+       RR(VID_FIR_COEF_V(1, 2));
+       RR(VID_FIR_COEF_V(1, 3));
+       RR(VID_FIR_COEF_V(1, 4));
+       RR(VID_FIR_COEF_V(1, 5));
+       RR(VID_FIR_COEF_V(1, 6));
+       RR(VID_FIR_COEF_V(1, 7));
+
+       RR(VID_PRELOAD(1));
+
+       /* enable last, because LCD & DIGIT enable are here */
+       RR(CONTROL);
+}
+
+#undef SR
+#undef RR
+
+static inline void enable_clocks(bool enable)
+{
+       if (enable)
+               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       else
+               dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+bool dispc_go_busy(enum omap_channel channel)
+{
+       int bit;
+
+       if (channel == OMAP_DSS_CHANNEL_LCD)
+               bit = 5; /* GOLCD */
+       else
+               bit = 6; /* GODIGIT */
+
+       return REG_GET(DISPC_CONTROL, bit, bit) == 1;
+}
+
+void dispc_go(enum omap_channel channel)
+{
+       int bit;
+
+       enable_clocks(1);
+
+       if (channel == OMAP_DSS_CHANNEL_LCD)
+               bit = 0; /* LCDENABLE */
+       else
+               bit = 1; /* DIGITALENABLE */
+
+       /* if the channel is not enabled, we don't need GO */
+       if (REG_GET(DISPC_CONTROL, bit, bit) == 0)
+               goto end;
+
+       if (channel == OMAP_DSS_CHANNEL_LCD)
+               bit = 5; /* GOLCD */
+       else
+               bit = 6; /* GODIGIT */
+
+       if (REG_GET(DISPC_CONTROL, bit, bit) == 1) {
+               DSSERR("GO bit not down for channel %d\n", channel);
+               goto end;
+       }
+
+       DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT");
+
+       REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
+end:
+       enable_clocks(0);
+}
+
+static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value)
+{
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value);
+}
+
+static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value)
+{
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value);
+}
+
+static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value)
+{
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value);
+}
+
+static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
+               int vscaleup, int five_taps)
+{
+       /* Coefficients for horizontal up-sampling */
+       static const u32 coef_hup[8] = {
+               0x00800000,
+               0x0D7CF800,
+               0x1E70F5FF,
+               0x335FF5FE,
+               0xF74949F7,
+               0xF55F33FB,
+               0xF5701EFE,
+               0xF87C0DFF,
+       };
+
+       /* Coefficients for horizontal down-sampling */
+       static const u32 coef_hdown[8] = {
+               0x24382400,
+               0x28371FFE,
+               0x2C361BFB,
+               0x303516F9,
+               0x11343311,
+               0x1635300C,
+               0x1B362C08,
+               0x1F372804,
+       };
+
+       /* Coefficients for horizontal and vertical up-sampling */
+       static const u32 coef_hvup[2][8] = {
+               {
+               0x00800000,
+               0x037B02FF,
+               0x0C6F05FE,
+               0x205907FB,
+               0x00404000,
+               0x075920FE,
+               0x056F0CFF,
+               0x027B0300,
+               },
+               {
+               0x00800000,
+               0x0D7CF8FF,
+               0x1E70F5FE,
+               0x335FF5FB,
+               0xF7404000,
+               0xF55F33FE,
+               0xF5701EFF,
+               0xF87C0D00,
+               },
+       };
+
+       /* Coefficients for horizontal and vertical down-sampling */
+       static const u32 coef_hvdown[2][8] = {
+               {
+               0x24382400,
+               0x28391F04,
+               0x2D381B08,
+               0x3237170C,
+               0x123737F7,
+               0x173732F9,
+               0x1B382DFB,
+               0x1F3928FE,
+               },
+               {
+               0x24382400,
+               0x28371F04,
+               0x2C361B08,
+               0x3035160C,
+               0x113433F7,
+               0x163530F9,
+               0x1B362CFB,
+               0x1F3728FE,
+               },
+       };
+
+       /* Coefficients for vertical up-sampling */
+       static const u32 coef_vup[8] = {
+               0x00000000,
+               0x0000FF00,
+               0x0000FEFF,
+               0x0000FBFE,
+               0x000000F7,
+               0x0000FEFB,
+               0x0000FFFE,
+               0x000000FF,
+       };
+
+
+       /* Coefficients for vertical down-sampling */
+       static const u32 coef_vdown[8] = {
+               0x00000000,
+               0x000004FE,
+               0x000008FB,
+               0x00000CF9,
+               0x0000F711,
+               0x0000F90C,
+               0x0000FB08,
+               0x0000FE04,
+       };
+
+       const u32 *h_coef;
+       const u32 *hv_coef;
+       const u32 *hv_coef_mod;
+       const u32 *v_coef;
+       int i;
+
+       if (hscaleup)
+               h_coef = coef_hup;
+       else
+               h_coef = coef_hdown;
+
+       if (vscaleup) {
+               hv_coef = coef_hvup[five_taps];
+               v_coef = coef_vup;
+
+               if (hscaleup)
+                       hv_coef_mod = NULL;
+               else
+                       hv_coef_mod = coef_hvdown[five_taps];
+       } else {
+               hv_coef = coef_hvdown[five_taps];
+               v_coef = coef_vdown;
+
+               if (hscaleup)
+                       hv_coef_mod = coef_hvup[five_taps];
+               else
+                       hv_coef_mod = NULL;
+       }
+
+       for (i = 0; i < 8; i++) {
+               u32 h, hv;
+
+               h = h_coef[i];
+
+               hv = hv_coef[i];
+
+               if (hv_coef_mod) {
+                       hv &= 0xffffff00;
+                       hv |= (hv_coef_mod[i] & 0xff);
+               }
+
+               _dispc_write_firh_reg(plane, i, h);
+               _dispc_write_firhv_reg(plane, i, hv);
+       }
+
+       if (!five_taps)
+               return;
+
+       for (i = 0; i < 8; i++) {
+               u32 v;
+               v = v_coef[i];
+               _dispc_write_firv_reg(plane, i, v);
+       }
+}
+
+static void _dispc_setup_color_conv_coef(void)
+{
+       const struct color_conv_coef {
+               int  ry,  rcr,  rcb,   gy,  gcr,  gcb,   by,  bcr,  bcb;
+               int  full_range;
+       }  ctbl_bt601_5 = {
+               298,  409,    0,  298, -208, -100,  298,    0,  517, 0,
+       };
+
+       const struct color_conv_coef *ct;
+
+#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))
+
+       ct = &ctbl_bt601_5;
+
+       dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry));
+       dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy,  ct->rcb));
+       dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr));
+       dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by));
+       dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0,       ct->bcb));
+
+       dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry));
+       dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy,  ct->rcb));
+       dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr));
+       dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by));
+       dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0,       ct->bcb));
+
+#undef CVAL
+
+       REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11);
+       REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11);
+}
+
+
+static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr)
+{
+       const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0,
+               DISPC_VID_BA0(0),
+               DISPC_VID_BA0(1) };
+
+       dispc_write_reg(ba0_reg[plane], paddr);
+}
+
+static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr)
+{
+       const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1,
+                                     DISPC_VID_BA1(0),
+                                     DISPC_VID_BA1(1) };
+
+       dispc_write_reg(ba1_reg[plane], paddr);
+}
+
+static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y)
+{
+       const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION,
+                                     DISPC_VID_POSITION(0),
+                                     DISPC_VID_POSITION(1) };
+
+       u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
+       dispc_write_reg(pos_reg[plane], val);
+}
+
+static void _dispc_set_pic_size(enum omap_plane plane, int width, int height)
+{
+       const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE,
+                                     DISPC_VID_PICTURE_SIZE(0),
+                                     DISPC_VID_PICTURE_SIZE(1) };
+       u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+       dispc_write_reg(siz_reg[plane], val);
+}
+
+static void _dispc_set_vid_size(enum omap_plane plane, int width, int height)
+{
+       u32 val;
+       const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0),
+                                     DISPC_VID_SIZE(1) };
+
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+       dispc_write_reg(vsi_reg[plane-1], val);
+}
+
+static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
+{
+
+       BUG_ON(plane == OMAP_DSS_VIDEO1);
+
+       if (cpu_is_omap24xx())
+               return;
+
+       if (plane == OMAP_DSS_GFX)
+               REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0);
+       else if (plane == OMAP_DSS_VIDEO2)
+               REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16);
+}
+
+static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc)
+{
+       const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC,
+                                    DISPC_VID_PIXEL_INC(0),
+                                    DISPC_VID_PIXEL_INC(1) };
+
+       dispc_write_reg(ri_reg[plane], inc);
+}
+
+static void _dispc_set_row_inc(enum omap_plane plane, s32 inc)
+{
+       const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC,
+                                    DISPC_VID_ROW_INC(0),
+                                    DISPC_VID_ROW_INC(1) };
+
+       dispc_write_reg(ri_reg[plane], inc);
+}
+
+static void _dispc_set_color_mode(enum omap_plane plane,
+               enum omap_color_mode color_mode)
+{
+       u32 m = 0;
+
+       switch (color_mode) {
+       case OMAP_DSS_COLOR_CLUT1:
+               m = 0x0; break;
+       case OMAP_DSS_COLOR_CLUT2:
+               m = 0x1; break;
+       case OMAP_DSS_COLOR_CLUT4:
+               m = 0x2; break;
+       case OMAP_DSS_COLOR_CLUT8:
+               m = 0x3; break;
+       case OMAP_DSS_COLOR_RGB12U:
+               m = 0x4; break;
+       case OMAP_DSS_COLOR_ARGB16:
+               m = 0x5; break;
+       case OMAP_DSS_COLOR_RGB16:
+               m = 0x6; break;
+       case OMAP_DSS_COLOR_RGB24U:
+               m = 0x8; break;
+       case OMAP_DSS_COLOR_RGB24P:
+               m = 0x9; break;
+       case OMAP_DSS_COLOR_YUV2:
+               m = 0xa; break;
+       case OMAP_DSS_COLOR_UYVY:
+               m = 0xb; break;
+       case OMAP_DSS_COLOR_ARGB32:
+               m = 0xc; break;
+       case OMAP_DSS_COLOR_RGBA32:
+               m = 0xd; break;
+       case OMAP_DSS_COLOR_RGBX32:
+               m = 0xe; break;
+       default:
+               BUG(); break;
+       }
+
+       REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1);
+}
+
+static void _dispc_set_channel_out(enum omap_plane plane,
+               enum omap_channel channel)
+{
+       int shift;
+       u32 val;
+
+       switch (plane) {
+       case OMAP_DSS_GFX:
+               shift = 8;
+               break;
+       case OMAP_DSS_VIDEO1:
+       case OMAP_DSS_VIDEO2:
+               shift = 16;
+               break;
+       default:
+               BUG();
+               return;
+       }
+
+       val = dispc_read_reg(dispc_reg_att[plane]);
+       val = FLD_MOD(val, channel, shift, shift);
+       dispc_write_reg(dispc_reg_att[plane], val);
+}
+
+void dispc_set_burst_size(enum omap_plane plane,
+               enum omap_burst_size burst_size)
+{
+       int shift;
+       u32 val;
+
+       enable_clocks(1);
+
+       switch (plane) {
+       case OMAP_DSS_GFX:
+               shift = 6;
+               break;
+       case OMAP_DSS_VIDEO1:
+       case OMAP_DSS_VIDEO2:
+               shift = 14;
+               break;
+       default:
+               BUG();
+               return;
+       }
+
+       val = dispc_read_reg(dispc_reg_att[plane]);
+       val = FLD_MOD(val, burst_size, shift+1, shift);
+       dispc_write_reg(dispc_reg_att[plane], val);
+
+       enable_clocks(0);
+}
+
+static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
+{
+       u32 val;
+
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       val = dispc_read_reg(dispc_reg_att[plane]);
+       val = FLD_MOD(val, enable, 9, 9);
+       dispc_write_reg(dispc_reg_att[plane], val);
+}
+
+void dispc_enable_replication(enum omap_plane plane, bool enable)
+{
+       int bit;
+
+       if (plane == OMAP_DSS_GFX)
+               bit = 5;
+       else
+               bit = 10;
+
+       enable_clocks(1);
+       REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit);
+       enable_clocks(0);
+}
+
+void dispc_set_lcd_size(u16 width, u16 height)
+{
+       u32 val;
+       BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
+       val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+       enable_clocks(1);
+       dispc_write_reg(DISPC_SIZE_LCD, val);
+       enable_clocks(0);
+}
+
+void dispc_set_digit_size(u16 width, u16 height)
+{
+       u32 val;
+       BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
+       val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
+       enable_clocks(1);
+       dispc_write_reg(DISPC_SIZE_DIG, val);
+       enable_clocks(0);
+}
+
+static void dispc_read_plane_fifo_sizes(void)
+{
+       const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS,
+                                     DISPC_VID_FIFO_SIZE_STATUS(0),
+                                     DISPC_VID_FIFO_SIZE_STATUS(1) };
+       u32 size;
+       int plane;
+
+       enable_clocks(1);
+
+       for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
+               if (cpu_is_omap24xx())
+                       size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0);
+               else if (cpu_is_omap34xx())
+                       size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0);
+               else
+                       BUG();
+
+               dispc.fifo_size[plane] = size;
+       }
+
+       enable_clocks(0);
+}
+
+u32 dispc_get_plane_fifo_size(enum omap_plane plane)
+{
+       return dispc.fifo_size[plane];
+}
+
+void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
+{
+       const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
+                                      DISPC_VID_FIFO_THRESHOLD(0),
+                                      DISPC_VID_FIFO_THRESHOLD(1) };
+       enable_clocks(1);
+
+       DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
+                       plane,
+                       REG_GET(ftrs_reg[plane], 11, 0),
+                       REG_GET(ftrs_reg[plane], 27, 16),
+                       low, high);
+
+       if (cpu_is_omap24xx())
+               dispc_write_reg(ftrs_reg[plane],
+                               FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0));
+       else
+               dispc_write_reg(ftrs_reg[plane],
+                               FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0));
+
+       enable_clocks(0);
+}
+
+void dispc_enable_fifomerge(bool enable)
+{
+       enable_clocks(1);
+
+       DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
+       REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
+
+       enable_clocks(0);
+}
+
+static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc)
+{
+       u32 val;
+       const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0),
+                                     DISPC_VID_FIR(1) };
+
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       if (cpu_is_omap24xx())
+               val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0);
+       else
+               val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
+       dispc_write_reg(fir_reg[plane-1], val);
+}
+
+static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
+{
+       u32 val;
+       const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0),
+                                     DISPC_VID_ACCU0(1) };
+
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0);
+       dispc_write_reg(ac0_reg[plane-1], val);
+}
+
+static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
+{
+       u32 val;
+       const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0),
+                                     DISPC_VID_ACCU1(1) };
+
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0);
+       dispc_write_reg(ac1_reg[plane-1], val);
+}
+
+
+static void _dispc_set_scaling(enum omap_plane plane,
+               u16 orig_width, u16 orig_height,
+               u16 out_width, u16 out_height,
+               bool ilace, bool five_taps,
+               bool fieldmode)
+{
+       int fir_hinc;
+       int fir_vinc;
+       int hscaleup, vscaleup;
+       int accu0 = 0;
+       int accu1 = 0;
+       u32 l;
+
+       BUG_ON(plane == OMAP_DSS_GFX);
+
+       hscaleup = orig_width <= out_width;
+       vscaleup = orig_height <= out_height;
+
+       _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps);
+
+       if (!orig_width || orig_width == out_width)
+               fir_hinc = 0;
+       else
+               fir_hinc = 1024 * orig_width / out_width;
+
+       if (!orig_height || orig_height == out_height)
+               fir_vinc = 0;
+       else
+               fir_vinc = 1024 * orig_height / out_height;
+
+       _dispc_set_fir(plane, fir_hinc, fir_vinc);
+
+       l = dispc_read_reg(dispc_reg_att[plane]);
+       l &= ~((0x0f << 5) | (0x3 << 21));
+
+       l |= fir_hinc ? (1 << 5) : 0;
+       l |= fir_vinc ? (1 << 6) : 0;
+
+       l |= hscaleup ? 0 : (1 << 7);
+       l |= vscaleup ? 0 : (1 << 8);
+
+       l |= five_taps ? (1 << 21) : 0;
+       l |= five_taps ? (1 << 22) : 0;
+
+       dispc_write_reg(dispc_reg_att[plane], l);
+
+       /*
+        * field 0 = even field = bottom field
+        * field 1 = odd field = top field
+        */
+       if (ilace && !fieldmode) {
+               accu1 = 0;
+               accu0 = (fir_vinc / 2) & 0x3ff;
+               if (accu0 >= 1024/2) {
+                       accu1 = 1024/2;
+                       accu0 -= accu1;
+               }
+       }
+
+       _dispc_set_vid_accu0(plane, 0, accu0);
+       _dispc_set_vid_accu1(plane, 0, accu1);
+}
+
+static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation,
+               bool mirroring, enum omap_color_mode color_mode)
+{
+       if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+                       color_mode == OMAP_DSS_COLOR_UYVY) {
+               int vidrot = 0;
+
+               if (mirroring) {
+                       switch (rotation) {
+                       case OMAP_DSS_ROT_0:
+                               vidrot = 2;
+                               break;
+                       case OMAP_DSS_ROT_90:
+                               vidrot = 1;
+                               break;
+                       case OMAP_DSS_ROT_180:
+                               vidrot = 0;
+                               break;
+                       case OMAP_DSS_ROT_270:
+                               vidrot = 3;
+                               break;
+                       }
+               } else {
+                       switch (rotation) {
+                       case OMAP_DSS_ROT_0:
+                               vidrot = 0;
+                               break;
+                       case OMAP_DSS_ROT_90:
+                               vidrot = 1;
+                               break;
+                       case OMAP_DSS_ROT_180:
+                               vidrot = 2;
+                               break;
+                       case OMAP_DSS_ROT_270:
+                               vidrot = 3;
+                               break;
+                       }
+               }
+
+               REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12);
+
+               if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270)
+                       REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18);
+               else
+                       REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18);
+       } else {
+               REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12);
+               REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18);
+       }
+}
+
+static int color_mode_to_bpp(enum omap_color_mode color_mode)
+{
+       switch (color_mode) {
+       case OMAP_DSS_COLOR_CLUT1:
+               return 1;
+       case OMAP_DSS_COLOR_CLUT2:
+               return 2;
+       case OMAP_DSS_COLOR_CLUT4:
+               return 4;
+       case OMAP_DSS_COLOR_CLUT8:
+               return 8;
+       case OMAP_DSS_COLOR_RGB12U:
+       case OMAP_DSS_COLOR_RGB16:
+       case OMAP_DSS_COLOR_ARGB16:
+       case OMAP_DSS_COLOR_YUV2:
+       case OMAP_DSS_COLOR_UYVY:
+               return 16;
+       case OMAP_DSS_COLOR_RGB24P:
+               return 24;
+       case OMAP_DSS_COLOR_RGB24U:
+       case OMAP_DSS_COLOR_ARGB32:
+       case OMAP_DSS_COLOR_RGBA32:
+       case OMAP_DSS_COLOR_RGBX32:
+               return 32;
+       default:
+               BUG();
+       }
+}
+
+static s32 pixinc(int pixels, u8 ps)
+{
+       if (pixels == 1)
+               return 1;
+       else if (pixels > 1)
+               return 1 + (pixels - 1) * ps;
+       else if (pixels < 0)
+               return 1 - (-pixels + 1) * ps;
+       else
+               BUG();
+}
+
+static void calc_vrfb_rotation_offset(u8 rotation, bool mirror,
+               u16 screen_width,
+               u16 width, u16 height,
+               enum omap_color_mode color_mode, bool fieldmode,
+               unsigned int field_offset,
+               unsigned *offset0, unsigned *offset1,
+               s32 *row_inc, s32 *pix_inc)
+{
+       u8 ps;
+
+       /* FIXME CLUT formats */
+       switch (color_mode) {
+       case OMAP_DSS_COLOR_CLUT1:
+       case OMAP_DSS_COLOR_CLUT2:
+       case OMAP_DSS_COLOR_CLUT4:
+       case OMAP_DSS_COLOR_CLUT8:
+               BUG();
+               return;
+       case OMAP_DSS_COLOR_YUV2:
+       case OMAP_DSS_COLOR_UYVY:
+               ps = 4;
+               break;
+       default:
+               ps = color_mode_to_bpp(color_mode) / 8;
+               break;
+       }
+
+       DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
+                       width, height);
+
+       /*
+        * field 0 = even field = bottom field
+        * field 1 = odd field = top field
+        */
+       switch (rotation + mirror * 4) {
+       case OMAP_DSS_ROT_0:
+       case OMAP_DSS_ROT_180:
+               /*
+                * If the pixel format is YUV or UYVY divide the width
+                * of the image by 2 for 0 and 180 degree rotation.
+                */
+               if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+                       color_mode == OMAP_DSS_COLOR_UYVY)
+                       width = width >> 1;
+       case OMAP_DSS_ROT_90:
+       case OMAP_DSS_ROT_270:
+               *offset1 = 0;
+               if (field_offset)
+                       *offset0 = field_offset * screen_width * ps;
+               else
+                       *offset0 = 0;
+
+               *row_inc = pixinc(1 + (screen_width - width) +
+                               (fieldmode ? screen_width : 0),
+                               ps);
+               *pix_inc = pixinc(1, ps);
+               break;
+
+       case OMAP_DSS_ROT_0 + 4:
+       case OMAP_DSS_ROT_180 + 4:
+               /* If the pixel format is YUV or UYVY divide the width
+                * of the image by 2  for 0 degree and 180 degree
+                */
+               if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+                       color_mode == OMAP_DSS_COLOR_UYVY)
+                       width = width >> 1;
+       case OMAP_DSS_ROT_90 + 4:
+       case OMAP_DSS_ROT_270 + 4:
+               *offset1 = 0;
+               if (field_offset)
+                       *offset0 = field_offset * screen_width * ps;
+               else
+                       *offset0 = 0;
+               *row_inc = pixinc(1 - (screen_width + width) -
+                               (fieldmode ? screen_width : 0),
+                               ps);
+               *pix_inc = pixinc(1, ps);
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+static void calc_dma_rotation_offset(u8 rotation, bool mirror,
+               u16 screen_width,
+               u16 width, u16 height,
+               enum omap_color_mode color_mode, bool fieldmode,
+               unsigned int field_offset,
+               unsigned *offset0, unsigned *offset1,
+               s32 *row_inc, s32 *pix_inc)
+{
+       u8 ps;
+       u16 fbw, fbh;
+
+       /* FIXME CLUT formats */
+       switch (color_mode) {
+       case OMAP_DSS_COLOR_CLUT1:
+       case OMAP_DSS_COLOR_CLUT2:
+       case OMAP_DSS_COLOR_CLUT4:
+       case OMAP_DSS_COLOR_CLUT8:
+               BUG();
+               return;
+       default:
+               ps = color_mode_to_bpp(color_mode) / 8;
+               break;
+       }
+
+       DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
+                       width, height);
+
+       /* width & height are overlay sizes, convert to fb sizes */
+
+       if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) {
+               fbw = width;
+               fbh = height;
+       } else {
+               fbw = height;
+               fbh = width;
+       }
+
+       /*
+        * field 0 = even field = bottom field
+        * field 1 = odd field = top field
+        */
+       switch (rotation + mirror * 4) {
+       case OMAP_DSS_ROT_0:
+               *offset1 = 0;
+               if (field_offset)
+                       *offset0 = *offset1 + field_offset * screen_width * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(1 + (screen_width - fbw) +
+                               (fieldmode ? screen_width : 0),
+                               ps);
+               *pix_inc = pixinc(1, ps);
+               break;
+       case OMAP_DSS_ROT_90:
+               *offset1 = screen_width * (fbh - 1) * ps;
+               if (field_offset)
+                       *offset0 = *offset1 + field_offset * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(screen_width * (fbh - 1) + 1 +
+                               (fieldmode ? 1 : 0), ps);
+               *pix_inc = pixinc(-screen_width, ps);
+               break;
+       case OMAP_DSS_ROT_180:
+               *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
+               if (field_offset)
+                       *offset0 = *offset1 - field_offset * screen_width * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(-1 -
+                               (screen_width - fbw) -
+                               (fieldmode ? screen_width : 0),
+                               ps);
+               *pix_inc = pixinc(-1, ps);
+               break;
+       case OMAP_DSS_ROT_270:
+               *offset1 = (fbw - 1) * ps;
+               if (field_offset)
+                       *offset0 = *offset1 - field_offset * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(-screen_width * (fbh - 1) - 1 -
+                               (fieldmode ? 1 : 0), ps);
+               *pix_inc = pixinc(screen_width, ps);
+               break;
+
+       /* mirroring */
+       case OMAP_DSS_ROT_0 + 4:
+               *offset1 = (fbw - 1) * ps;
+               if (field_offset)
+                       *offset0 = *offset1 + field_offset * screen_width * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(screen_width * 2 - 1 +
+                               (fieldmode ? screen_width : 0),
+                               ps);
+               *pix_inc = pixinc(-1, ps);
+               break;
+
+       case OMAP_DSS_ROT_90 + 4:
+               *offset1 = 0;
+               if (field_offset)
+                       *offset0 = *offset1 + field_offset * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(-screen_width * (fbh - 1) + 1 +
+                               (fieldmode ? 1 : 0),
+                               ps);
+               *pix_inc = pixinc(screen_width, ps);
+               break;
+
+       case OMAP_DSS_ROT_180 + 4:
+               *offset1 = screen_width * (fbh - 1) * ps;
+               if (field_offset)
+                       *offset0 = *offset1 - field_offset * screen_width * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(1 - screen_width * 2 -
+                               (fieldmode ? screen_width : 0),
+                               ps);
+               *pix_inc = pixinc(1, ps);
+               break;
+
+       case OMAP_DSS_ROT_270 + 4:
+               *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
+               if (field_offset)
+                       *offset0 = *offset1 - field_offset * ps;
+               else
+                       *offset0 = *offset1;
+               *row_inc = pixinc(screen_width * (fbh - 1) - 1 -
+                               (fieldmode ? 1 : 0),
+                               ps);
+               *pix_inc = pixinc(-screen_width, ps);
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+static unsigned long calc_fclk_five_taps(u16 width, u16 height,
+               u16 out_width, u16 out_height, enum omap_color_mode color_mode)
+{
+       u32 fclk = 0;
+       /* FIXME venc pclk? */
+       u64 tmp, pclk = dispc_pclk_rate();
+
+       if (height > out_height) {
+               /* FIXME get real display PPL */
+               unsigned int ppl = 800;
+
+               tmp = pclk * height * out_width;
+               do_div(tmp, 2 * out_height * ppl);
+               fclk = tmp;
+
+               if (height > 2 * out_height && ppl != out_width) {
+                       tmp = pclk * (height - 2 * out_height) * out_width;
+                       do_div(tmp, 2 * out_height * (ppl - out_width));
+                       fclk = max(fclk, (u32) tmp);
+               }
+       }
+
+       if (width > out_width) {
+               tmp = pclk * width;
+               do_div(tmp, out_width);
+               fclk = max(fclk, (u32) tmp);
+
+               if (color_mode == OMAP_DSS_COLOR_RGB24U)
+                       fclk <<= 1;
+       }
+
+       return fclk;
+}
+
+static unsigned long calc_fclk(u16 width, u16 height,
+               u16 out_width, u16 out_height)
+{
+       unsigned int hf, vf;
+
+       /*
+        * FIXME how to determine the 'A' factor
+        * for the no downscaling case ?
+        */
+
+       if (width > 3 * out_width)
+               hf = 4;
+       else if (width > 2 * out_width)
+               hf = 3;
+       else if (width > out_width)
+               hf = 2;
+       else
+               hf = 1;
+
+       if (height > out_height)
+               vf = 2;
+       else
+               vf = 1;
+
+       /* FIXME venc pclk? */
+       return dispc_pclk_rate() * vf * hf;
+}
+
+void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out)
+{
+       enable_clocks(1);
+       _dispc_set_channel_out(plane, channel_out);
+       enable_clocks(0);
+}
+
+static int _dispc_setup_plane(enum omap_plane plane,
+               u32 paddr, u16 screen_width,
+               u16 pos_x, u16 pos_y,
+               u16 width, u16 height,
+               u16 out_width, u16 out_height,
+               enum omap_color_mode color_mode,
+               bool ilace,
+               enum omap_dss_rotation_type rotation_type,
+               u8 rotation, int mirror,
+               u8 global_alpha)
+{
+       const int maxdownscale = cpu_is_omap34xx() ? 4 : 2;
+       bool five_taps = 0;
+       bool fieldmode = 0;
+       int cconv = 0;
+       unsigned offset0, offset1;
+       s32 row_inc;
+       s32 pix_inc;
+       u16 frame_height = height;
+       unsigned int field_offset = 0;
+
+       if (paddr == 0)
+               return -EINVAL;
+
+       if (ilace && height == out_height)
+               fieldmode = 1;
+
+       if (ilace) {
+               if (fieldmode)
+                       height /= 2;
+               pos_y /= 2;
+               out_height /= 2;
+
+               DSSDBG("adjusting for ilace: height %d, pos_y %d, "
+                               "out_height %d\n",
+                               height, pos_y, out_height);
+       }
+
+       if (plane == OMAP_DSS_GFX) {
+               if (width != out_width || height != out_height)
+                       return -EINVAL;
+
+               switch (color_mode) {
+               case OMAP_DSS_COLOR_ARGB16:
+               case OMAP_DSS_COLOR_ARGB32:
+               case OMAP_DSS_COLOR_RGBA32:
+               case OMAP_DSS_COLOR_RGBX32:
+                       if (cpu_is_omap24xx())
+                               return -EINVAL;
+                       /* fall through */
+               case OMAP_DSS_COLOR_RGB12U:
+               case OMAP_DSS_COLOR_RGB16:
+               case OMAP_DSS_COLOR_RGB24P:
+               case OMAP_DSS_COLOR_RGB24U:
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+       } else {
+               /* video plane */
+
+               unsigned long fclk = 0;
+
+               if (out_width < width / maxdownscale ||
+                  out_width > width * 8)
+                       return -EINVAL;
+
+               if (out_height < height / maxdownscale ||
+                  out_height > height * 8)
+                       return -EINVAL;
+
+               switch (color_mode) {
+               case OMAP_DSS_COLOR_RGBX32:
+               case OMAP_DSS_COLOR_RGB12U:
+                       if (cpu_is_omap24xx())
+                               return -EINVAL;
+                       /* fall through */
+               case OMAP_DSS_COLOR_RGB16:
+               case OMAP_DSS_COLOR_RGB24P:
+               case OMAP_DSS_COLOR_RGB24U:
+                       break;
+
+               case OMAP_DSS_COLOR_ARGB16:
+               case OMAP_DSS_COLOR_ARGB32:
+               case OMAP_DSS_COLOR_RGBA32:
+                       if (cpu_is_omap24xx())
+                               return -EINVAL;
+                       if (plane == OMAP_DSS_VIDEO1)
+                               return -EINVAL;
+                       break;
+
+               case OMAP_DSS_COLOR_YUV2:
+               case OMAP_DSS_COLOR_UYVY:
+                       cconv = 1;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+
+               /* Must use 5-tap filter? */
+               five_taps = height > out_height * 2;
+
+               if (!five_taps) {
+                       fclk = calc_fclk(width, height,
+                                       out_width, out_height);
+
+                       /* Try 5-tap filter if 3-tap fclk is too high */
+                       if (cpu_is_omap34xx() && height > out_height &&
+                                       fclk > dispc_fclk_rate())
+                               five_taps = true;
+               }
+
+               if (width > (2048 >> five_taps)) {
+                       DSSERR("failed to set up scaling, fclk too low\n");
+                       return -EINVAL;
+               }
+
+               if (five_taps)
+                       fclk = calc_fclk_five_taps(width, height,
+                                       out_width, out_height, color_mode);
+
+               DSSDBG("required fclk rate = %lu Hz\n", fclk);
+               DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
+
+               if (fclk > dispc_fclk_rate()) {
+                       DSSERR("failed to set up scaling, "
+                                       "required fclk rate = %lu Hz, "
+                                       "current fclk rate = %lu Hz\n",
+                                       fclk, dispc_fclk_rate());
+                       return -EINVAL;
+               }
+       }
+
+       if (ilace && !fieldmode) {
+               /*
+                * when downscaling the bottom field may have to start several
+                * source lines below the top field. Unfortunately ACCUI
+                * registers will only hold the fractional part of the offset
+                * so the integer part must be added to the base address of the
+                * bottom field.
+                */
+               if (!height || height == out_height)
+                       field_offset = 0;
+               else
+                       field_offset = height / out_height / 2;
+       }
+
+       /* Fields are independent but interleaved in memory. */
+       if (fieldmode)
+               field_offset = 1;
+
+       if (rotation_type == OMAP_DSS_ROT_DMA)
+               calc_dma_rotation_offset(rotation, mirror,
+                               screen_width, width, frame_height, color_mode,
+                               fieldmode, field_offset,
+                               &offset0, &offset1, &row_inc, &pix_inc);
+       else
+               calc_vrfb_rotation_offset(rotation, mirror,
+                               screen_width, width, frame_height, color_mode,
+                               fieldmode, field_offset,
+                               &offset0, &offset1, &row_inc, &pix_inc);
+
+       DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
+                       offset0, offset1, row_inc, pix_inc);
+
+       _dispc_set_color_mode(plane, color_mode);
+
+       _dispc_set_plane_ba0(plane, paddr + offset0);
+       _dispc_set_plane_ba1(plane, paddr + offset1);
+
+       _dispc_set_row_inc(plane, row_inc);
+       _dispc_set_pix_inc(plane, pix_inc);
+
+       DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height,
+                       out_width, out_height);
+
+       _dispc_set_plane_pos(plane, pos_x, pos_y);
+
+       _dispc_set_pic_size(plane, width, height);
+
+       if (plane != OMAP_DSS_GFX) {
+               _dispc_set_scaling(plane, width, height,
+                                  out_width, out_height,
+                                  ilace, five_taps, fieldmode);
+               _dispc_set_vid_size(plane, out_width, out_height);
+               _dispc_set_vid_color_conv(plane, cconv);
+       }
+
+       _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode);
+
+       if (plane != OMAP_DSS_VIDEO1)
+               _dispc_setup_global_alpha(plane, global_alpha);
+
+       return 0;
+}
+
+static void _dispc_enable_plane(enum omap_plane plane, bool enable)
+{
+       REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0);
+}
+
+static void dispc_disable_isr(void *data, u32 mask)
+{
+       struct completion *compl = data;
+       complete(compl);
+}
+
+static void _enable_lcd_out(bool enable)
+{
+       REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
+}
+
+void dispc_enable_lcd_out(bool enable)
+{
+       struct completion frame_done_completion;
+       bool is_on;
+       int r;
+
+       enable_clocks(1);
+
+       /* When we disable LCD output, we need to wait until frame is done.
+        * Otherwise the DSS is still working, and turning off the clocks
+        * prevents DSS from going to OFF mode */
+       is_on = REG_GET(DISPC_CONTROL, 0, 0);
+
+       if (!enable && is_on) {
+               init_completion(&frame_done_completion);
+
+               r = omap_dispc_register_isr(dispc_disable_isr,
+                               &frame_done_completion,
+                               DISPC_IRQ_FRAMEDONE);
+
+               if (r)
+                       DSSERR("failed to register FRAMEDONE isr\n");
+       }
+
+       _enable_lcd_out(enable);
+
+       if (!enable && is_on) {
+               if (!wait_for_completion_timeout(&frame_done_completion,
+                                       msecs_to_jiffies(100)))
+                       DSSERR("timeout waiting for FRAME DONE\n");
+
+               r = omap_dispc_unregister_isr(dispc_disable_isr,
+                               &frame_done_completion,
+                               DISPC_IRQ_FRAMEDONE);
+
+               if (r)
+                       DSSERR("failed to unregister FRAMEDONE isr\n");
+       }
+
+       enable_clocks(0);
+}
+
+static void _enable_digit_out(bool enable)
+{
+       REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
+}
+
+void dispc_enable_digit_out(bool enable)
+{
+       struct completion frame_done_completion;
+       int r;
+
+       enable_clocks(1);
+
+       if (REG_GET(DISPC_CONTROL, 1, 1) == enable) {
+               enable_clocks(0);
+               return;
+       }
+
+       if (enable) {
+               unsigned long flags;
+               /* When we enable digit output, we'll get an extra digit
+                * sync lost interrupt, that we need to ignore */
+               spin_lock_irqsave(&dispc.irq_lock, flags);
+               dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
+               _omap_dispc_set_irqs();
+               spin_unlock_irqrestore(&dispc.irq_lock, flags);
+       }
+
+       /* When we disable digit output, we need to wait until fields are done.
+        * Otherwise the DSS is still working, and turning off the clocks
+        * prevents DSS from going to OFF mode. And when enabling, we need to
+        * wait for the extra sync losts */
+       init_completion(&frame_done_completion);
+
+       r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion,
+                       DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+       if (r)
+               DSSERR("failed to register EVSYNC isr\n");
+
+       _enable_digit_out(enable);
+
+       /* XXX I understand from TRM that we should only wait for the
+        * current field to complete. But it seems we have to wait
+        * for both fields */
+       if (!wait_for_completion_timeout(&frame_done_completion,
+                               msecs_to_jiffies(100)))
+               DSSERR("timeout waiting for EVSYNC\n");
+
+       if (!wait_for_completion_timeout(&frame_done_completion,
+                               msecs_to_jiffies(100)))
+               DSSERR("timeout waiting for EVSYNC\n");
+
+       r = omap_dispc_unregister_isr(dispc_disable_isr,
+                       &frame_done_completion,
+                       DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+       if (r)
+               DSSERR("failed to unregister EVSYNC isr\n");
+
+       if (enable) {
+               unsigned long flags;
+               spin_lock_irqsave(&dispc.irq_lock, flags);
+               dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
+               dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
+               _omap_dispc_set_irqs();
+               spin_unlock_irqrestore(&dispc.irq_lock, flags);
+       }
+
+       enable_clocks(0);
+}
+
+void dispc_lcd_enable_signal_polarity(bool act_high)
+{
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29);
+       enable_clocks(0);
+}
+
+void dispc_lcd_enable_signal(bool enable)
+{
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28);
+       enable_clocks(0);
+}
+
+void dispc_pck_free_enable(bool enable)
+{
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
+       enable_clocks(0);
+}
+
+void dispc_enable_fifohandcheck(bool enable)
+{
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
+       enable_clocks(0);
+}
+
+
+void dispc_set_lcd_display_type(enum omap_lcd_display_type type)
+{
+       int mode;
+
+       switch (type) {
+       case OMAP_DSS_LCD_DISPLAY_STN:
+               mode = 0;
+               break;
+
+       case OMAP_DSS_LCD_DISPLAY_TFT:
+               mode = 1;
+               break;
+
+       default:
+               BUG();
+               return;
+       }
+
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
+       enable_clocks(0);
+}
+
+void dispc_set_loadmode(enum omap_dss_load_mode mode)
+{
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
+       enable_clocks(0);
+}
+
+
+void dispc_set_default_color(enum omap_channel channel, u32 color)
+{
+       const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0,
+                               DISPC_DEFAULT_COLOR1 };
+
+       enable_clocks(1);
+       dispc_write_reg(def_reg[channel], color);
+       enable_clocks(0);
+}
+
+u32 dispc_get_default_color(enum omap_channel channel)
+{
+       const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0,
+                               DISPC_DEFAULT_COLOR1 };
+       u32 l;
+
+       BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT &&
+              channel != OMAP_DSS_CHANNEL_LCD);
+
+       enable_clocks(1);
+       l = dispc_read_reg(def_reg[channel]);
+       enable_clocks(0);
+
+       return l;
+}
+
+void dispc_set_trans_key(enum omap_channel ch,
+               enum omap_dss_trans_key_type type,
+               u32 trans_key)
+{
+       const struct dispc_reg tr_reg[] = {
+               DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 };
+
+       enable_clocks(1);
+       if (ch == OMAP_DSS_CHANNEL_LCD)
+               REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
+       else /* OMAP_DSS_CHANNEL_DIGIT */
+               REG_FLD_MOD(DISPC_CONFIG, type, 13, 13);
+
+       dispc_write_reg(tr_reg[ch], trans_key);
+       enable_clocks(0);
+}
+
+void dispc_get_trans_key(enum omap_channel ch,
+               enum omap_dss_trans_key_type *type,
+               u32 *trans_key)
+{
+       const struct dispc_reg tr_reg[] = {
+               DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 };
+
+       enable_clocks(1);
+       if (type) {
+               if (ch == OMAP_DSS_CHANNEL_LCD)
+                       *type = REG_GET(DISPC_CONFIG, 11, 11);
+               else if (ch == OMAP_DSS_CHANNEL_DIGIT)
+                       *type = REG_GET(DISPC_CONFIG, 13, 13);
+               else
+                       BUG();
+       }
+
+       if (trans_key)
+               *trans_key = dispc_read_reg(tr_reg[ch]);
+       enable_clocks(0);
+}
+
+void dispc_enable_trans_key(enum omap_channel ch, bool enable)
+{
+       enable_clocks(1);
+       if (ch == OMAP_DSS_CHANNEL_LCD)
+               REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
+       else /* OMAP_DSS_CHANNEL_DIGIT */
+               REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
+       enable_clocks(0);
+}
+void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
+{
+       if (cpu_is_omap24xx())
+               return;
+
+       enable_clocks(1);
+       if (ch == OMAP_DSS_CHANNEL_LCD)
+               REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
+       else /* OMAP_DSS_CHANNEL_DIGIT */
+               REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
+       enable_clocks(0);
+}
+bool dispc_alpha_blending_enabled(enum omap_channel ch)
+{
+       bool enabled;
+
+       if (cpu_is_omap24xx())
+               return false;
+
+       enable_clocks(1);
+       if (ch == OMAP_DSS_CHANNEL_LCD)
+               enabled = REG_GET(DISPC_CONFIG, 18, 18);
+       else if (ch == OMAP_DSS_CHANNEL_DIGIT)
+               enabled = REG_GET(DISPC_CONFIG, 18, 18);
+       else
+               BUG();
+       enable_clocks(0);
+
+       return enabled;
+
+}
+
+
+bool dispc_trans_key_enabled(enum omap_channel ch)
+{
+       bool enabled;
+
+       enable_clocks(1);
+       if (ch == OMAP_DSS_CHANNEL_LCD)
+               enabled = REG_GET(DISPC_CONFIG, 10, 10);
+       else if (ch == OMAP_DSS_CHANNEL_DIGIT)
+               enabled = REG_GET(DISPC_CONFIG, 12, 12);
+       else
+               BUG();
+       enable_clocks(0);
+
+       return enabled;
+}
+
+
+void dispc_set_tft_data_lines(u8 data_lines)
+{
+       int code;
+
+       switch (data_lines) {
+       case 12:
+               code = 0;
+               break;
+       case 16:
+               code = 1;
+               break;
+       case 18:
+               code = 2;
+               break;
+       case 24:
+               code = 3;
+               break;
+       default:
+               BUG();
+               return;
+       }
+
+       enable_clocks(1);
+       REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
+       enable_clocks(0);
+}
+
+void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode)
+{
+       u32 l;
+       int stallmode;
+       int gpout0 = 1;
+       int gpout1;
+
+       switch (mode) {
+       case OMAP_DSS_PARALLELMODE_BYPASS:
+               stallmode = 0;
+               gpout1 = 1;
+               break;
+
+       case OMAP_DSS_PARALLELMODE_RFBI:
+               stallmode = 1;
+               gpout1 = 0;
+               break;
+
+       case OMAP_DSS_PARALLELMODE_DSI:
+               stallmode = 1;
+               gpout1 = 1;
+               break;
+
+       default:
+               BUG();
+               return;
+       }
+
+       enable_clocks(1);
+
+       l = dispc_read_reg(DISPC_CONTROL);
+
+       l = FLD_MOD(l, stallmode, 11, 11);
+       l = FLD_MOD(l, gpout0, 15, 15);
+       l = FLD_MOD(l, gpout1, 16, 16);
+
+       dispc_write_reg(DISPC_CONTROL, l);
+
+       enable_clocks(0);
+}
+
+static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
+               int vsw, int vfp, int vbp)
+{
+       if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
+               if (hsw < 1 || hsw > 64 ||
+                               hfp < 1 || hfp > 256 ||
+                               hbp < 1 || hbp > 256 ||
+                               vsw < 1 || vsw > 64 ||
+                               vfp < 0 || vfp > 255 ||
+                               vbp < 0 || vbp > 255)
+                       return false;
+       } else {
+               if (hsw < 1 || hsw > 256 ||
+                               hfp < 1 || hfp > 4096 ||
+                               hbp < 1 || hbp > 4096 ||
+                               vsw < 1 || vsw > 256 ||
+                               vfp < 0 || vfp > 4095 ||
+                               vbp < 0 || vbp > 4095)
+                       return false;
+       }
+
+       return true;
+}
+
+bool dispc_lcd_timings_ok(struct omap_video_timings *timings)
+{
+       return _dispc_lcd_timings_ok(timings->hsw, timings->hfp,
+                       timings->hbp, timings->vsw,
+                       timings->vfp, timings->vbp);
+}
+
+static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp,
+                                  int vsw, int vfp, int vbp)
+{
+       u32 timing_h, timing_v;
+
+       if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
+               timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) |
+                       FLD_VAL(hbp-1, 27, 20);
+
+               timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) |
+                       FLD_VAL(vbp, 27, 20);
+       } else {
+               timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) |
+                       FLD_VAL(hbp-1, 31, 20);
+
+               timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) |
+                       FLD_VAL(vbp, 31, 20);
+       }
+
+       enable_clocks(1);
+       dispc_write_reg(DISPC_TIMING_H, timing_h);
+       dispc_write_reg(DISPC_TIMING_V, timing_v);
+       enable_clocks(0);
+}
+
+/* change name to mode? */
+void dispc_set_lcd_timings(struct omap_video_timings *timings)
+{
+       unsigned xtot, ytot;
+       unsigned long ht, vt;
+
+       if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp,
+                               timings->hbp, timings->vsw,
+                               timings->vfp, timings->vbp))
+               BUG();
+
+       _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp,
+                       timings->vsw, timings->vfp, timings->vbp);
+
+       dispc_set_lcd_size(timings->x_res, timings->y_res);
+
+       xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp;
+       ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp;
+
+       ht = (timings->pixel_clock * 1000) / xtot;
+       vt = (timings->pixel_clock * 1000) / xtot / ytot;
+
+       DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res);
+       DSSDBG("pck %u\n", timings->pixel_clock);
+       DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
+                       timings->hsw, timings->hfp, timings->hbp,
+                       timings->vsw, timings->vfp, timings->vbp);
+
+       DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
+}
+
+static void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div)
+{
+       BUG_ON(lck_div < 1);
+       BUG_ON(pck_div < 2);
+
+       enable_clocks(1);
+       dispc_write_reg(DISPC_DIVISOR,
+                       FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
+       enable_clocks(0);
+}
+
+static void dispc_get_lcd_divisor(int *lck_div, int *pck_div)
+{
+       u32 l;
+       l = dispc_read_reg(DISPC_DIVISOR);
+       *lck_div = FLD_GET(l, 23, 16);
+       *pck_div = FLD_GET(l, 7, 0);
+}
+
+unsigned long dispc_fclk_rate(void)
+{
+       unsigned long r = 0;
+
+       if (dss_get_dispc_clk_source() == 0)
+               r = dss_clk_get_rate(DSS_CLK_FCK1);
+       else
+#ifdef CONFIG_OMAP2_DSS_DSI
+               r = dsi_get_dsi1_pll_rate();
+#else
+       BUG();
+#endif
+       return r;
+}
+
+unsigned long dispc_lclk_rate(void)
+{
+       int lcd;
+       unsigned long r;
+       u32 l;
+
+       l = dispc_read_reg(DISPC_DIVISOR);
+
+       lcd = FLD_GET(l, 23, 16);
+
+       r = dispc_fclk_rate();
+
+       return r / lcd;
+}
+
+unsigned long dispc_pclk_rate(void)
+{
+       int lcd, pcd;
+       unsigned long r;
+       u32 l;
+
+       l = dispc_read_reg(DISPC_DIVISOR);
+
+       lcd = FLD_GET(l, 23, 16);
+       pcd = FLD_GET(l, 7, 0);
+
+       r = dispc_fclk_rate();
+
+       return r / lcd / pcd;
+}
+
+void dispc_dump_clocks(struct seq_file *s)
+{
+       int lcd, pcd;
+
+       enable_clocks(1);
+
+       dispc_get_lcd_divisor(&lcd, &pcd);
+
+       seq_printf(s, "- DISPC -\n");
+
+       seq_printf(s, "dispc fclk source = %s\n",
+                       dss_get_dispc_clk_source() == 0 ?
+                       "dss1_alwon_fclk" : "dsi1_pll_fclk");
+
+       seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate());
+       seq_printf(s, "lck\t\t%-16lulck div\t%u\n", dispc_lclk_rate(), lcd);
+       seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(), pcd);
+
+       enable_clocks(0);
+}
+
+void dispc_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r))
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       DUMPREG(DISPC_REVISION);
+       DUMPREG(DISPC_SYSCONFIG);
+       DUMPREG(DISPC_SYSSTATUS);
+       DUMPREG(DISPC_IRQSTATUS);
+       DUMPREG(DISPC_IRQENABLE);
+       DUMPREG(DISPC_CONTROL);
+       DUMPREG(DISPC_CONFIG);
+       DUMPREG(DISPC_CAPABLE);
+       DUMPREG(DISPC_DEFAULT_COLOR0);
+       DUMPREG(DISPC_DEFAULT_COLOR1);
+       DUMPREG(DISPC_TRANS_COLOR0);
+       DUMPREG(DISPC_TRANS_COLOR1);
+       DUMPREG(DISPC_LINE_STATUS);
+       DUMPREG(DISPC_LINE_NUMBER);
+       DUMPREG(DISPC_TIMING_H);
+       DUMPREG(DISPC_TIMING_V);
+       DUMPREG(DISPC_POL_FREQ);
+       DUMPREG(DISPC_DIVISOR);
+       DUMPREG(DISPC_GLOBAL_ALPHA);
+       DUMPREG(DISPC_SIZE_DIG);
+       DUMPREG(DISPC_SIZE_LCD);
+
+       DUMPREG(DISPC_GFX_BA0);
+       DUMPREG(DISPC_GFX_BA1);
+       DUMPREG(DISPC_GFX_POSITION);
+       DUMPREG(DISPC_GFX_SIZE);
+       DUMPREG(DISPC_GFX_ATTRIBUTES);
+       DUMPREG(DISPC_GFX_FIFO_THRESHOLD);
+       DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS);
+       DUMPREG(DISPC_GFX_ROW_INC);
+       DUMPREG(DISPC_GFX_PIXEL_INC);
+       DUMPREG(DISPC_GFX_WINDOW_SKIP);
+       DUMPREG(DISPC_GFX_TABLE_BA);
+
+       DUMPREG(DISPC_DATA_CYCLE1);
+       DUMPREG(DISPC_DATA_CYCLE2);
+       DUMPREG(DISPC_DATA_CYCLE3);
+
+       DUMPREG(DISPC_CPR_COEF_R);
+       DUMPREG(DISPC_CPR_COEF_G);
+       DUMPREG(DISPC_CPR_COEF_B);
+
+       DUMPREG(DISPC_GFX_PRELOAD);
+
+       DUMPREG(DISPC_VID_BA0(0));
+       DUMPREG(DISPC_VID_BA1(0));
+       DUMPREG(DISPC_VID_POSITION(0));
+       DUMPREG(DISPC_VID_SIZE(0));
+       DUMPREG(DISPC_VID_ATTRIBUTES(0));
+       DUMPREG(DISPC_VID_FIFO_THRESHOLD(0));
+       DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0));
+       DUMPREG(DISPC_VID_ROW_INC(0));
+       DUMPREG(DISPC_VID_PIXEL_INC(0));
+       DUMPREG(DISPC_VID_FIR(0));
+       DUMPREG(DISPC_VID_PICTURE_SIZE(0));
+       DUMPREG(DISPC_VID_ACCU0(0));
+       DUMPREG(DISPC_VID_ACCU1(0));
+
+       DUMPREG(DISPC_VID_BA0(1));
+       DUMPREG(DISPC_VID_BA1(1));
+       DUMPREG(DISPC_VID_POSITION(1));
+       DUMPREG(DISPC_VID_SIZE(1));
+       DUMPREG(DISPC_VID_ATTRIBUTES(1));
+       DUMPREG(DISPC_VID_FIFO_THRESHOLD(1));
+       DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1));
+       DUMPREG(DISPC_VID_ROW_INC(1));
+       DUMPREG(DISPC_VID_PIXEL_INC(1));
+       DUMPREG(DISPC_VID_FIR(1));
+       DUMPREG(DISPC_VID_PICTURE_SIZE(1));
+       DUMPREG(DISPC_VID_ACCU0(1));
+       DUMPREG(DISPC_VID_ACCU1(1));
+
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 0));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 1));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 2));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 3));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 5));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 6));
+       DUMPREG(DISPC_VID_FIR_COEF_H(0, 7));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7));
+       DUMPREG(DISPC_VID_CONV_COEF(0, 0));
+       DUMPREG(DISPC_VID_CONV_COEF(0, 1));
+       DUMPREG(DISPC_VID_CONV_COEF(0, 2));
+       DUMPREG(DISPC_VID_CONV_COEF(0, 3));
+       DUMPREG(DISPC_VID_CONV_COEF(0, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 0));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 1));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 2));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 3));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 5));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 6));
+       DUMPREG(DISPC_VID_FIR_COEF_V(0, 7));
+
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 0));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 1));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 2));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 3));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 5));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 6));
+       DUMPREG(DISPC_VID_FIR_COEF_H(1, 7));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6));
+       DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7));
+       DUMPREG(DISPC_VID_CONV_COEF(1, 0));
+       DUMPREG(DISPC_VID_CONV_COEF(1, 1));
+       DUMPREG(DISPC_VID_CONV_COEF(1, 2));
+       DUMPREG(DISPC_VID_CONV_COEF(1, 3));
+       DUMPREG(DISPC_VID_CONV_COEF(1, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 0));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 1));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 2));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 3));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 4));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 5));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 6));
+       DUMPREG(DISPC_VID_FIR_COEF_V(1, 7));
+
+       DUMPREG(DISPC_VID_PRELOAD(0));
+       DUMPREG(DISPC_VID_PRELOAD(1));
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc,
+                               bool ihs, bool ivs, u8 acbi, u8 acb)
+{
+       u32 l = 0;
+
+       DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n",
+                       onoff, rf, ieo, ipc, ihs, ivs, acbi, acb);
+
+       l |= FLD_VAL(onoff, 17, 17);
+       l |= FLD_VAL(rf, 16, 16);
+       l |= FLD_VAL(ieo, 15, 15);
+       l |= FLD_VAL(ipc, 14, 14);
+       l |= FLD_VAL(ihs, 13, 13);
+       l |= FLD_VAL(ivs, 12, 12);
+       l |= FLD_VAL(acbi, 11, 8);
+       l |= FLD_VAL(acb, 7, 0);
+
+       enable_clocks(1);
+       dispc_write_reg(DISPC_POL_FREQ, l);
+       enable_clocks(0);
+}
+
+void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb)
+{
+       _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0,
+                       (config & OMAP_DSS_LCD_RF) != 0,
+                       (config & OMAP_DSS_LCD_IEO) != 0,
+                       (config & OMAP_DSS_LCD_IPC) != 0,
+                       (config & OMAP_DSS_LCD_IHS) != 0,
+                       (config & OMAP_DSS_LCD_IVS) != 0,
+                       acbi, acb);
+}
+
+/* with fck as input clock rate, find dispc dividers that produce req_pck */
+void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+               struct dispc_clock_info *cinfo)
+{
+       u16 pcd_min = is_tft ? 2 : 3;
+       unsigned long best_pck;
+       u16 best_ld, cur_ld;
+       u16 best_pd, cur_pd;
+
+       best_pck = 0;
+       best_ld = 0;
+       best_pd = 0;
+
+       for (cur_ld = 1; cur_ld <= 255; ++cur_ld) {
+               unsigned long lck = fck / cur_ld;
+
+               for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) {
+                       unsigned long pck = lck / cur_pd;
+                       long old_delta = abs(best_pck - req_pck);
+                       long new_delta = abs(pck - req_pck);
+
+                       if (best_pck == 0 || new_delta < old_delta) {
+                               best_pck = pck;
+                               best_ld = cur_ld;
+                               best_pd = cur_pd;
+
+                               if (pck == req_pck)
+                                       goto found;
+                       }
+
+                       if (pck < req_pck)
+                               break;
+               }
+
+               if (lck / pcd_min < req_pck)
+                       break;
+       }
+
+found:
+       cinfo->lck_div = best_ld;
+       cinfo->pck_div = best_pd;
+       cinfo->lck = fck / cinfo->lck_div;
+       cinfo->pck = cinfo->lck / cinfo->pck_div;
+}
+
+/* calculate clock rates using dividers in cinfo */
+int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
+               struct dispc_clock_info *cinfo)
+{
+       if (cinfo->lck_div > 255 || cinfo->lck_div == 0)
+               return -EINVAL;
+       if (cinfo->pck_div < 2 || cinfo->pck_div > 255)
+               return -EINVAL;
+
+       cinfo->lck = dispc_fclk_rate / cinfo->lck_div;
+       cinfo->pck = cinfo->lck / cinfo->pck_div;
+
+       return 0;
+}
+
+int dispc_set_clock_div(struct dispc_clock_info *cinfo)
+{
+       DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
+       DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);
+
+       dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div);
+
+       return 0;
+}
+
+int dispc_get_clock_div(struct dispc_clock_info *cinfo)
+{
+       unsigned long fck;
+
+       fck = dispc_fclk_rate();
+
+       cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16);
+       cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0);
+
+       cinfo->lck = fck / cinfo->lck_div;
+       cinfo->pck = cinfo->lck / cinfo->pck_div;
+
+       return 0;
+}
+
+/* dispc.irq_lock has to be locked by the caller */
+static void _omap_dispc_set_irqs(void)
+{
+       u32 mask;
+       u32 old_mask;
+       int i;
+       struct omap_dispc_isr_data *isr_data;
+
+       mask = dispc.irq_error_mask;
+
+       for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+               isr_data = &dispc.registered_isr[i];
+
+               if (isr_data->isr == NULL)
+                       continue;
+
+               mask |= isr_data->mask;
+       }
+
+       enable_clocks(1);
+
+       old_mask = dispc_read_reg(DISPC_IRQENABLE);
+       /* clear the irqstatus for newly enabled irqs */
+       dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask);
+
+       dispc_write_reg(DISPC_IRQENABLE, mask);
+
+       enable_clocks(0);
+}
+
+int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
+{
+       int i;
+       int ret;
+       unsigned long flags;
+       struct omap_dispc_isr_data *isr_data;
+
+       if (isr == NULL)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dispc.irq_lock, flags);
+
+       /* check for duplicate entry */
+       for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+               isr_data = &dispc.registered_isr[i];
+               if (isr_data->isr == isr && isr_data->arg == arg &&
+                               isr_data->mask == mask) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
+       isr_data = NULL;
+       ret = -EBUSY;
+
+       for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+               isr_data = &dispc.registered_isr[i];
+
+               if (isr_data->isr != NULL)
+                       continue;
+
+               isr_data->isr = isr;
+               isr_data->arg = arg;
+               isr_data->mask = mask;
+               ret = 0;
+
+               break;
+       }
+
+       _omap_dispc_set_irqs();
+
+       spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+       return 0;
+err:
+       spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(omap_dispc_register_isr);
+
+int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
+{
+       int i;
+       unsigned long flags;
+       int ret = -EINVAL;
+       struct omap_dispc_isr_data *isr_data;
+
+       spin_lock_irqsave(&dispc.irq_lock, flags);
+
+       for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+               isr_data = &dispc.registered_isr[i];
+               if (isr_data->isr != isr || isr_data->arg != arg ||
+                               isr_data->mask != mask)
+                       continue;
+
+               /* found the correct isr */
+
+               isr_data->isr = NULL;
+               isr_data->arg = NULL;
+               isr_data->mask = 0;
+
+               ret = 0;
+               break;
+       }
+
+       if (ret == 0)
+               _omap_dispc_set_irqs();
+
+       spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(omap_dispc_unregister_isr);
+
+#ifdef DEBUG
+static void print_irq_status(u32 status)
+{
+       if ((status & dispc.irq_error_mask) == 0)
+               return;
+
+       printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status);
+
+#define PIS(x) \
+       if (status & DISPC_IRQ_##x) \
+               printk(#x " ");
+       PIS(GFX_FIFO_UNDERFLOW);
+       PIS(OCP_ERR);
+       PIS(VID1_FIFO_UNDERFLOW);
+       PIS(VID2_FIFO_UNDERFLOW);
+       PIS(SYNC_LOST);
+       PIS(SYNC_LOST_DIGIT);
+#undef PIS
+
+       printk("\n");
+}
+#endif
+
+/* Called from dss.c. Note that we don't touch clocks here,
+ * but we presume they are on because we got an IRQ. However,
+ * an irq handler may turn the clocks off, so we may not have
+ * clock later in the function. */
+void dispc_irq_handler(void)
+{
+       int i;
+       u32 irqstatus;
+       u32 handledirqs = 0;
+       u32 unhandled_errors;
+       struct omap_dispc_isr_data *isr_data;
+       struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
+
+       spin_lock(&dispc.irq_lock);
+
+       irqstatus = dispc_read_reg(DISPC_IRQSTATUS);
+
+#ifdef DEBUG
+       if (dss_debug)
+               print_irq_status(irqstatus);
+#endif
+       /* Ack the interrupt. Do it here before clocks are possibly turned
+        * off */
+       dispc_write_reg(DISPC_IRQSTATUS, irqstatus);
+       /* flush posted write */
+       dispc_read_reg(DISPC_IRQSTATUS);
+
+       /* make a copy and unlock, so that isrs can unregister
+        * themselves */
+       memcpy(registered_isr, dispc.registered_isr,
+                       sizeof(registered_isr));
+
+       spin_unlock(&dispc.irq_lock);
+
+       for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+               isr_data = &registered_isr[i];
+
+               if (!isr_data->isr)
+                       continue;
+
+               if (isr_data->mask & irqstatus) {
+                       isr_data->isr(isr_data->arg, irqstatus);
+                       handledirqs |= isr_data->mask;
+               }
+       }
+
+       spin_lock(&dispc.irq_lock);
+
+       unhandled_errors = irqstatus & ~handledirqs & dispc.irq_error_mask;
+
+       if (unhandled_errors) {
+               dispc.error_irqs |= unhandled_errors;
+
+               dispc.irq_error_mask &= ~unhandled_errors;
+               _omap_dispc_set_irqs();
+
+               schedule_work(&dispc.error_work);
+       }
+
+       spin_unlock(&dispc.irq_lock);
+}
+
+static void dispc_error_worker(struct work_struct *work)
+{
+       int i;
+       u32 errors;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dispc.irq_lock, flags);
+       errors = dispc.error_irqs;
+       dispc.error_irqs = 0;
+       spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+       if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) {
+               DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n");
+               for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+                       struct omap_overlay *ovl;
+                       ovl = omap_dss_get_overlay(i);
+
+                       if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                               continue;
+
+                       if (ovl->id == 0) {
+                               dispc_enable_plane(ovl->id, 0);
+                               dispc_go(ovl->manager->id);
+                               mdelay(50);
+                               break;
+                       }
+               }
+       }
+
+       if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) {
+               DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n");
+               for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+                       struct omap_overlay *ovl;
+                       ovl = omap_dss_get_overlay(i);
+
+                       if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                               continue;
+
+                       if (ovl->id == 1) {
+                               dispc_enable_plane(ovl->id, 0);
+                               dispc_go(ovl->manager->id);
+                               mdelay(50);
+                               break;
+                       }
+               }
+       }
+
+       if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) {
+               DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n");
+               for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+                       struct omap_overlay *ovl;
+                       ovl = omap_dss_get_overlay(i);
+
+                       if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                               continue;
+
+                       if (ovl->id == 2) {
+                               dispc_enable_plane(ovl->id, 0);
+                               dispc_go(ovl->manager->id);
+                               mdelay(50);
+                               break;
+                       }
+               }
+       }
+
+       if (errors & DISPC_IRQ_SYNC_LOST) {
+               struct omap_overlay_manager *manager = NULL;
+               bool enable = false;
+
+               DSSERR("SYNC_LOST, disabling LCD\n");
+
+               for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+                       struct omap_overlay_manager *mgr;
+                       mgr = omap_dss_get_overlay_manager(i);
+
+                       if (mgr->id == OMAP_DSS_CHANNEL_LCD) {
+                               manager = mgr;
+                               enable = mgr->device->state ==
+                                               OMAP_DSS_DISPLAY_ACTIVE;
+                               mgr->device->disable(mgr->device);
+                               break;
+                       }
+               }
+
+               if (manager) {
+                       for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+                               struct omap_overlay *ovl;
+                               ovl = omap_dss_get_overlay(i);
+
+                               if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                                       continue;
+
+                               if (ovl->id != 0 && ovl->manager == manager)
+                                       dispc_enable_plane(ovl->id, 0);
+                       }
+
+                       dispc_go(manager->id);
+                       mdelay(50);
+                       if (enable)
+                               manager->device->enable(manager->device);
+               }
+       }
+
+       if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) {
+               struct omap_overlay_manager *manager = NULL;
+               bool enable = false;
+
+               DSSERR("SYNC_LOST_DIGIT, disabling TV\n");
+
+               for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+                       struct omap_overlay_manager *mgr;
+                       mgr = omap_dss_get_overlay_manager(i);
+
+                       if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) {
+                               manager = mgr;
+                               enable = mgr->device->state ==
+                                               OMAP_DSS_DISPLAY_ACTIVE;
+                               mgr->device->disable(mgr->device);
+                               break;
+                       }
+               }
+
+               if (manager) {
+                       for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+                               struct omap_overlay *ovl;
+                               ovl = omap_dss_get_overlay(i);
+
+                               if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                                       continue;
+
+                               if (ovl->id != 0 && ovl->manager == manager)
+                                       dispc_enable_plane(ovl->id, 0);
+                       }
+
+                       dispc_go(manager->id);
+                       mdelay(50);
+                       if (enable)
+                               manager->device->enable(manager->device);
+               }
+       }
+
+       if (errors & DISPC_IRQ_OCP_ERR) {
+               DSSERR("OCP_ERR\n");
+               for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+                       struct omap_overlay_manager *mgr;
+                       mgr = omap_dss_get_overlay_manager(i);
+
+                       if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC)
+                               mgr->device->disable(mgr->device);
+               }
+       }
+
+       spin_lock_irqsave(&dispc.irq_lock, flags);
+       dispc.irq_error_mask |= errors;
+       _omap_dispc_set_irqs();
+       spin_unlock_irqrestore(&dispc.irq_lock, flags);
+}
+
+int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout)
+{
+       void dispc_irq_wait_handler(void *data, u32 mask)
+       {
+               complete((struct completion *)data);
+       }
+
+       int r;
+       DECLARE_COMPLETION_ONSTACK(completion);
+
+       r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
+                       irqmask);
+
+       if (r)
+               return r;
+
+       timeout = wait_for_completion_timeout(&completion, timeout);
+
+       omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
+
+       if (timeout == 0)
+               return -ETIMEDOUT;
+
+       if (timeout == -ERESTARTSYS)
+               return -ERESTARTSYS;
+
+       return 0;
+}
+
+int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
+               unsigned long timeout)
+{
+       void dispc_irq_wait_handler(void *data, u32 mask)
+       {
+               complete((struct completion *)data);
+       }
+
+       int r;
+       DECLARE_COMPLETION_ONSTACK(completion);
+
+       r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
+                       irqmask);
+
+       if (r)
+               return r;
+
+       timeout = wait_for_completion_interruptible_timeout(&completion,
+                       timeout);
+
+       omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
+
+       if (timeout == 0)
+               return -ETIMEDOUT;
+
+       if (timeout == -ERESTARTSYS)
+               return -ERESTARTSYS;
+
+       return 0;
+}
+
+#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+void dispc_fake_vsync_irq(void)
+{
+       u32 irqstatus = DISPC_IRQ_VSYNC;
+       int i;
+
+       local_irq_disable();
+
+       for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
+               struct omap_dispc_isr_data *isr_data;
+               isr_data = &dispc.registered_isr[i];
+
+               if (!isr_data->isr)
+                       continue;
+
+               if (isr_data->mask & irqstatus)
+                       isr_data->isr(isr_data->arg, irqstatus);
+       }
+
+       local_irq_enable();
+}
+#endif
+
+static void _omap_dispc_initialize_irq(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dispc.irq_lock, flags);
+
+       memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr));
+
+       dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
+
+       /* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
+        * so clear it */
+       dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS));
+
+       _omap_dispc_set_irqs();
+
+       spin_unlock_irqrestore(&dispc.irq_lock, flags);
+}
+
+void dispc_enable_sidle(void)
+{
+       REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3);  /* SIDLEMODE: smart idle */
+}
+
+void dispc_disable_sidle(void)
+{
+       REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3);  /* SIDLEMODE: no idle */
+}
+
+static void _omap_dispc_initial_config(void)
+{
+       u32 l;
+
+       l = dispc_read_reg(DISPC_SYSCONFIG);
+       l = FLD_MOD(l, 2, 13, 12);      /* MIDLEMODE: smart standby */
+       l = FLD_MOD(l, 2, 4, 3);        /* SIDLEMODE: smart idle */
+       l = FLD_MOD(l, 1, 2, 2);        /* ENWAKEUP */
+       l = FLD_MOD(l, 1, 0, 0);        /* AUTOIDLE */
+       dispc_write_reg(DISPC_SYSCONFIG, l);
+
+       /* FUNCGATED */
+       REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
+
+       /* L3 firewall setting: enable access to OCM RAM */
+       /* XXX this should be somewhere in plat-omap */
+       if (cpu_is_omap24xx())
+               __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0));
+
+       _dispc_setup_color_conv_coef();
+
+       dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
+
+       dispc_read_plane_fifo_sizes();
+}
+
+int dispc_init(void)
+{
+       u32 rev;
+
+       spin_lock_init(&dispc.irq_lock);
+
+       INIT_WORK(&dispc.error_work, dispc_error_worker);
+
+       dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS);
+       if (!dispc.base) {
+               DSSERR("can't ioremap DISPC\n");
+               return -ENOMEM;
+       }
+
+       enable_clocks(1);
+
+       _omap_dispc_initial_config();
+
+       _omap_dispc_initialize_irq();
+
+       dispc_save_context();
+
+       rev = dispc_read_reg(DISPC_REVISION);
+       printk(KERN_INFO "OMAP DISPC rev %d.%d\n",
+              FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+       enable_clocks(0);
+
+       return 0;
+}
+
+void dispc_exit(void)
+{
+       iounmap(dispc.base);
+}
+
+int dispc_enable_plane(enum omap_plane plane, bool enable)
+{
+       DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
+
+       enable_clocks(1);
+       _dispc_enable_plane(plane, enable);
+       enable_clocks(0);
+
+       return 0;
+}
+
+int dispc_setup_plane(enum omap_plane plane,
+                      u32 paddr, u16 screen_width,
+                      u16 pos_x, u16 pos_y,
+                      u16 width, u16 height,
+                      u16 out_width, u16 out_height,
+                      enum omap_color_mode color_mode,
+                      bool ilace,
+                      enum omap_dss_rotation_type rotation_type,
+                      u8 rotation, bool mirror, u8 global_alpha)
+{
+       int r = 0;
+
+       DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> "
+              "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n",
+              plane, paddr, screen_width, pos_x, pos_y,
+              width, height,
+              out_width, out_height,
+              ilace, color_mode,
+              rotation, mirror);
+
+       enable_clocks(1);
+
+       r = _dispc_setup_plane(plane,
+                          paddr, screen_width,
+                          pos_x, pos_y,
+                          width, height,
+                          out_width, out_height,
+                          color_mode, ilace,
+                          rotation_type,
+                          rotation, mirror,
+                          global_alpha);
+
+       enable_clocks(0);
+
+       return r;
+}
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
new file mode 100644 (file)
index 0000000..3b92b84
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ * linux/drivers/video/omap2/dss/display.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DISPLAY"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+static LIST_HEAD(display_list);
+
+static ssize_t display_enabled_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
+}
+
+static ssize_t display_enabled_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       bool enabled, r;
+
+       enabled = simple_strtoul(buf, NULL, 10);
+
+       if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
+               if (enabled) {
+                       r = dssdev->enable(dssdev);
+                       if (r)
+                               return r;
+               } else {
+                       dssdev->disable(dssdev);
+               }
+       }
+
+       return size;
+}
+
+static ssize_t display_upd_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO;
+       if (dssdev->get_update_mode)
+               mode = dssdev->get_update_mode(dssdev);
+       return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t display_upd_mode_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       int val, r;
+       enum omap_dss_update_mode mode;
+
+       val = simple_strtoul(buf, NULL, 10);
+
+       switch (val) {
+       case OMAP_DSS_UPDATE_DISABLED:
+       case OMAP_DSS_UPDATE_AUTO:
+       case OMAP_DSS_UPDATE_MANUAL:
+               mode = (enum omap_dss_update_mode)val;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       r = dssdev->set_update_mode(dssdev, mode);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t display_tear_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       dssdev->get_te ? dssdev->get_te(dssdev) : 0);
+}
+
+static ssize_t display_tear_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       unsigned long te;
+       int r;
+
+       if (!dssdev->enable_te || !dssdev->get_te)
+               return -ENOENT;
+
+       te = simple_strtoul(buf, NULL, 0);
+
+       r = dssdev->enable_te(dssdev, te);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t display_timings_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_video_timings t;
+
+       if (!dssdev->get_timings)
+               return -ENOENT;
+
+       dssdev->get_timings(dssdev, &t);
+
+       return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
+                       t.pixel_clock,
+                       t.x_res, t.hfp, t.hbp, t.hsw,
+                       t.y_res, t.vfp, t.vbp, t.vsw);
+}
+
+static ssize_t display_timings_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       struct omap_video_timings t;
+       int r, found;
+
+       if (!dssdev->set_timings || !dssdev->check_timings)
+               return -ENOENT;
+
+       found = 0;
+#ifdef CONFIG_OMAP2_DSS_VENC
+       if (strncmp("pal", buf, 3) == 0) {
+               t = omap_dss_pal_timings;
+               found = 1;
+       } else if (strncmp("ntsc", buf, 4) == 0) {
+               t = omap_dss_ntsc_timings;
+               found = 1;
+       }
+#endif
+       if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
+                               &t.pixel_clock,
+                               &t.x_res, &t.hfp, &t.hbp, &t.hsw,
+                               &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
+               return -EINVAL;
+
+       r = dssdev->check_timings(dssdev, &t);
+       if (r)
+               return r;
+
+       dssdev->set_timings(dssdev, &t);
+
+       return size;
+}
+
+static ssize_t display_rotate_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       int rotate;
+       if (!dssdev->get_rotate)
+               return -ENOENT;
+       rotate = dssdev->get_rotate(dssdev);
+       return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
+}
+
+static ssize_t display_rotate_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       unsigned long rot;
+       int r;
+
+       if (!dssdev->set_rotate || !dssdev->get_rotate)
+               return -ENOENT;
+
+       rot = simple_strtoul(buf, NULL, 0);
+
+       r = dssdev->set_rotate(dssdev, rot);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t display_mirror_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       int mirror;
+       if (!dssdev->get_mirror)
+               return -ENOENT;
+       mirror = dssdev->get_mirror(dssdev);
+       return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
+}
+
+static ssize_t display_mirror_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       unsigned long mirror;
+       int r;
+
+       if (!dssdev->set_mirror || !dssdev->get_mirror)
+               return -ENOENT;
+
+       mirror = simple_strtoul(buf, NULL, 0);
+
+       r = dssdev->set_mirror(dssdev, mirror);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t display_wss_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       unsigned int wss;
+
+       if (!dssdev->get_wss)
+               return -ENOENT;
+
+       wss = dssdev->get_wss(dssdev);
+
+       return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
+}
+
+static ssize_t display_wss_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       unsigned long wss;
+       int r;
+
+       if (!dssdev->get_wss || !dssdev->set_wss)
+               return -ENOENT;
+
+       if (strict_strtoul(buf, 0, &wss))
+               return -EINVAL;
+
+       if (wss > 0xfffff)
+               return -EINVAL;
+
+       r = dssdev->set_wss(dssdev, wss);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
+               display_enabled_show, display_enabled_store);
+static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR,
+               display_upd_mode_show, display_upd_mode_store);
+static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
+               display_tear_show, display_tear_store);
+static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR,
+               display_timings_show, display_timings_store);
+static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR,
+               display_rotate_show, display_rotate_store);
+static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR,
+               display_mirror_show, display_mirror_store);
+static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,
+               display_wss_show, display_wss_store);
+
+static struct device_attribute *display_sysfs_attrs[] = {
+       &dev_attr_enabled,
+       &dev_attr_update_mode,
+       &dev_attr_tear_elim,
+       &dev_attr_timings,
+       &dev_attr_rotate,
+       &dev_attr_mirror,
+       &dev_attr_wss,
+       NULL
+};
+
+static void default_get_resolution(struct omap_dss_device *dssdev,
+                       u16 *xres, u16 *yres)
+{
+       *xres = dssdev->panel.timings.x_res;
+       *yres = dssdev->panel.timings.y_res;
+}
+
+void default_get_overlay_fifo_thresholds(enum omap_plane plane,
+               u32 fifo_size, enum omap_burst_size *burst_size,
+               u32 *fifo_low, u32 *fifo_high)
+{
+       unsigned burst_size_bytes;
+
+       *burst_size = OMAP_DSS_BURST_16x32;
+       burst_size_bytes = 16 * 32 / 8;
+
+       *fifo_high = fifo_size - 1;
+       *fifo_low = fifo_size - burst_size_bytes;
+}
+
+static int default_wait_vsync(struct omap_dss_device *dssdev)
+{
+       unsigned long timeout = msecs_to_jiffies(500);
+       u32 irq;
+
+       if (dssdev->type == OMAP_DISPLAY_TYPE_VENC)
+               irq = DISPC_IRQ_EVSYNC_ODD;
+       else
+               irq = DISPC_IRQ_VSYNC;
+
+       return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+}
+
+static int default_get_recommended_bpp(struct omap_dss_device *dssdev)
+{
+       if (dssdev->panel.recommended_bpp)
+               return dssdev->panel.recommended_bpp;
+
+       switch (dssdev->type) {
+       case OMAP_DISPLAY_TYPE_DPI:
+               if (dssdev->phy.dpi.data_lines == 24)
+                       return 24;
+               else
+                       return 16;
+
+       case OMAP_DISPLAY_TYPE_DBI:
+       case OMAP_DISPLAY_TYPE_DSI:
+               if (dssdev->ctrl.pixel_size == 24)
+                       return 24;
+               else
+                       return 16;
+       case OMAP_DISPLAY_TYPE_VENC:
+       case OMAP_DISPLAY_TYPE_SDI:
+               return 24;
+               return 24;
+       default:
+               BUG();
+       }
+}
+
+/* Checks if replication logic should be used. Only use for active matrix,
+ * when overlay is in RGB12U or RGB16 mode, and LCD interface is
+ * 18bpp or 24bpp */
+bool dss_use_replication(struct omap_dss_device *dssdev,
+               enum omap_color_mode mode)
+{
+       int bpp;
+
+       if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
+               return false;
+
+       if (dssdev->type == OMAP_DISPLAY_TYPE_DPI &&
+                       (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0)
+               return false;
+
+       switch (dssdev->type) {
+       case OMAP_DISPLAY_TYPE_DPI:
+               bpp = dssdev->phy.dpi.data_lines;
+               break;
+       case OMAP_DISPLAY_TYPE_VENC:
+       case OMAP_DISPLAY_TYPE_SDI:
+               bpp = 24;
+               break;
+       case OMAP_DISPLAY_TYPE_DBI:
+       case OMAP_DISPLAY_TYPE_DSI:
+               bpp = dssdev->ctrl.pixel_size;
+               break;
+       default:
+               BUG();
+       }
+
+       return bpp > 16;
+}
+
+void dss_init_device(struct platform_device *pdev,
+               struct omap_dss_device *dssdev)
+{
+       struct device_attribute *attr;
+       int i;
+       int r;
+
+       switch (dssdev->type) {
+       case OMAP_DISPLAY_TYPE_DPI:
+#ifdef CONFIG_OMAP2_DSS_RFBI
+       case OMAP_DISPLAY_TYPE_DBI:
+#endif
+#ifdef CONFIG_OMAP2_DSS_SDI
+       case OMAP_DISPLAY_TYPE_SDI:
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+       case OMAP_DISPLAY_TYPE_DSI:
+#endif
+#ifdef CONFIG_OMAP2_DSS_VENC
+       case OMAP_DISPLAY_TYPE_VENC:
+#endif
+               break;
+       default:
+               DSSERR("Support for display '%s' not compiled in.\n",
+                               dssdev->name);
+               return;
+       }
+
+       dssdev->get_resolution = default_get_resolution;
+       dssdev->get_recommended_bpp = default_get_recommended_bpp;
+       dssdev->wait_vsync = default_wait_vsync;
+
+       switch (dssdev->type) {
+       case OMAP_DISPLAY_TYPE_DPI:
+               r = dpi_init_display(dssdev);
+               break;
+#ifdef CONFIG_OMAP2_DSS_RFBI
+       case OMAP_DISPLAY_TYPE_DBI:
+               r = rfbi_init_display(dssdev);
+               break;
+#endif
+#ifdef CONFIG_OMAP2_DSS_VENC
+       case OMAP_DISPLAY_TYPE_VENC:
+               r = venc_init_display(dssdev);
+               break;
+#endif
+#ifdef CONFIG_OMAP2_DSS_SDI
+       case OMAP_DISPLAY_TYPE_SDI:
+               r = sdi_init_display(dssdev);
+               break;
+#endif
+#ifdef CONFIG_OMAP2_DSS_DSI
+       case OMAP_DISPLAY_TYPE_DSI:
+               r = dsi_init_display(dssdev);
+               break;
+#endif
+       default:
+               BUG();
+       }
+
+       if (r) {
+               DSSERR("failed to init display %s\n", dssdev->name);
+               return;
+       }
+
+       /* create device sysfs files */
+       i = 0;
+       while ((attr = display_sysfs_attrs[i++]) != NULL) {
+               r = device_create_file(&dssdev->dev, attr);
+               if (r)
+                       DSSERR("failed to create sysfs file\n");
+       }
+
+       /* create display? sysfs links */
+       r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj,
+                       dev_name(&dssdev->dev));
+       if (r)
+               DSSERR("failed to create sysfs display link\n");
+}
+
+void dss_uninit_device(struct platform_device *pdev,
+               struct omap_dss_device *dssdev)
+{
+       struct device_attribute *attr;
+       int i = 0;
+
+       sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev));
+
+       while ((attr = display_sysfs_attrs[i++]) != NULL)
+               device_remove_file(&dssdev->dev, attr);
+
+       if (dssdev->manager)
+               dssdev->manager->unset_device(dssdev->manager);
+}
+
+static int dss_suspend_device(struct device *dev, void *data)
+{
+       int r;
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+               dssdev->activate_after_resume = false;
+               return 0;
+       }
+
+       if (!dssdev->suspend) {
+               DSSERR("display '%s' doesn't implement suspend\n",
+                               dssdev->name);
+               return -ENOSYS;
+       }
+
+       r = dssdev->suspend(dssdev);
+       if (r)
+               return r;
+
+       dssdev->activate_after_resume = true;
+
+       return 0;
+}
+
+int dss_suspend_all_devices(void)
+{
+       int r;
+       struct bus_type *bus = dss_get_bus();
+
+       r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device);
+       if (r) {
+               /* resume all displays that were suspended */
+               dss_resume_all_devices();
+               return r;
+       }
+
+       return 0;
+}
+
+static int dss_resume_device(struct device *dev, void *data)
+{
+       int r;
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+
+       if (dssdev->activate_after_resume && dssdev->resume) {
+               r = dssdev->resume(dssdev);
+               if (r)
+                       return r;
+       }
+
+       dssdev->activate_after_resume = false;
+
+       return 0;
+}
+
+int dss_resume_all_devices(void)
+{
+       struct bus_type *bus = dss_get_bus();
+
+       return bus_for_each_dev(bus, NULL, NULL, dss_resume_device);
+}
+
+static int dss_disable_device(struct device *dev, void *data)
+{
+       struct omap_dss_device *dssdev = to_dss_device(dev);
+       dssdev->disable(dssdev);
+       return 0;
+}
+
+void dss_disable_all_devices(void)
+{
+       struct bus_type *bus = dss_get_bus();
+       bus_for_each_dev(bus, NULL, NULL, dss_disable_device);
+}
+
+
+void omap_dss_get_device(struct omap_dss_device *dssdev)
+{
+       get_device(&dssdev->dev);
+}
+EXPORT_SYMBOL(omap_dss_get_device);
+
+void omap_dss_put_device(struct omap_dss_device *dssdev)
+{
+       put_device(&dssdev->dev);
+}
+EXPORT_SYMBOL(omap_dss_put_device);
+
+/* ref count of the found device is incremented. ref count
+ * of from-device is decremented. */
+struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
+{
+       struct device *dev;
+       struct device *dev_start = NULL;
+       struct omap_dss_device *dssdev = NULL;
+
+       int match(struct device *dev, void *data)
+       {
+               /* skip panels connected to controllers */
+               if (to_dss_device(dev)->panel.ctrl)
+                       return 0;
+
+               return 1;
+       }
+
+       if (from)
+               dev_start = &from->dev;
+       dev = bus_find_device(dss_get_bus(), dev_start, NULL, match);
+       if (dev)
+               dssdev = to_dss_device(dev);
+       if (from)
+               put_device(&from->dev);
+
+       return dssdev;
+}
+EXPORT_SYMBOL(omap_dss_get_next_device);
+
+struct omap_dss_device *omap_dss_find_device(void *data,
+               int (*match)(struct omap_dss_device *dssdev, void *data))
+{
+       struct omap_dss_device *dssdev = NULL;
+
+       while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
+               if (match(dssdev, data))
+                       return dssdev;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(omap_dss_find_device);
+
+int omap_dss_start_device(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       if (!dssdev->driver) {
+               DSSDBG("no driver\n");
+               r = -ENODEV;
+               goto err0;
+       }
+
+       if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) {
+               DSSDBG("no panel driver\n");
+               r = -ENODEV;
+               goto err0;
+       }
+
+       if (!try_module_get(dssdev->dev.driver->owner)) {
+               r = -ENODEV;
+               goto err0;
+       }
+
+       if (dssdev->ctrl.panel) {
+               if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) {
+                       r = -ENODEV;
+                       goto err1;
+               }
+       }
+
+       return 0;
+err1:
+       module_put(dssdev->dev.driver->owner);
+err0:
+       return r;
+}
+EXPORT_SYMBOL(omap_dss_start_device);
+
+void omap_dss_stop_device(struct omap_dss_device *dssdev)
+{
+       if (dssdev->ctrl.panel)
+               module_put(dssdev->ctrl.panel->dev.driver->owner);
+
+       module_put(dssdev->dev.driver->owner);
+}
+EXPORT_SYMBOL(omap_dss_stop_device);
+
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
new file mode 100644 (file)
index 0000000..2d71031
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * linux/drivers/video/omap2/dss/dpi.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DPI"
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+static struct {
+       int update_enabled;
+} dpi;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req,
+               unsigned long *fck, int *lck_div, int *pck_div)
+{
+       struct dsi_clock_info dsi_cinfo;
+       struct dispc_clock_info dispc_cinfo;
+       int r;
+
+       r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo,
+                       &dispc_cinfo);
+       if (r)
+               return r;
+
+       r = dsi_pll_set_clock_div(&dsi_cinfo);
+       if (r)
+               return r;
+
+       dss_select_clk_source(0, 1);
+
+       r = dispc_set_clock_div(&dispc_cinfo);
+       if (r)
+               return r;
+
+       *fck = dsi_cinfo.dsi1_pll_fclk;
+       *lck_div = dispc_cinfo.lck_div;
+       *pck_div = dispc_cinfo.pck_div;
+
+       return 0;
+}
+#else
+static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req,
+               unsigned long *fck, int *lck_div, int *pck_div)
+{
+       struct dss_clock_info dss_cinfo;
+       struct dispc_clock_info dispc_cinfo;
+       int r;
+
+       r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
+       if (r)
+               return r;
+
+       r = dss_set_clock_div(&dss_cinfo);
+       if (r)
+               return r;
+
+       r = dispc_set_clock_div(&dispc_cinfo);
+       if (r)
+               return r;
+
+       *fck = dss_cinfo.fck;
+       *lck_div = dispc_cinfo.lck_div;
+       *pck_div = dispc_cinfo.pck_div;
+
+       return 0;
+}
+#endif
+
+static int dpi_set_mode(struct omap_dss_device *dssdev)
+{
+       struct omap_video_timings *t = &dssdev->panel.timings;
+       int lck_div, pck_div;
+       unsigned long fck;
+       unsigned long pck;
+       bool is_tft;
+       int r = 0;
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
+                       dssdev->panel.acb);
+
+       is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+       r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000,
+                       &fck, &lck_div, &pck_div);
+#else
+       r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000,
+                       &fck, &lck_div, &pck_div);
+#endif
+       if (r)
+               goto err0;
+
+       pck = fck / lck_div / pck_div / 1000;
+
+       if (pck != t->pixel_clock) {
+               DSSWARN("Could not find exact pixel clock. "
+                               "Requested %d kHz, got %lu kHz\n",
+                               t->pixel_clock, pck);
+
+               t->pixel_clock = pck;
+       }
+
+       dispc_set_lcd_timings(t);
+
+err0:
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       return r;
+}
+
+static int dpi_basic_init(struct omap_dss_device *dssdev)
+{
+       bool is_tft;
+
+       is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+
+       dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
+       dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT :
+                       OMAP_DSS_LCD_DISPLAY_STN);
+       dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines);
+
+       return 0;
+}
+
+static int dpi_display_enable(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       r = omap_dss_start_device(dssdev);
+       if (r) {
+               DSSERR("failed to start device\n");
+               goto err0;
+       }
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+               DSSERR("display already enabled\n");
+               r = -EINVAL;
+               goto err1;
+       }
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       r = dpi_basic_init(dssdev);
+       if (r)
+               goto err2;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+       dss_clk_enable(DSS_CLK_FCK2);
+       r = dsi_pll_init(dssdev, 0, 1);
+       if (r)
+               goto err3;
+#endif
+       r = dpi_set_mode(dssdev);
+       if (r)
+               goto err4;
+
+       mdelay(2);
+
+       dispc_enable_lcd_out(1);
+
+       r = dssdev->driver->enable(dssdev);
+       if (r)
+               goto err5;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+
+err5:
+       dispc_enable_lcd_out(0);
+err4:
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+       dsi_pll_uninit();
+err3:
+       dss_clk_disable(DSS_CLK_FCK2);
+#endif
+err2:
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+err1:
+       omap_dss_stop_device(dssdev);
+err0:
+       return r;
+}
+
+static int dpi_display_resume(struct omap_dss_device *dssdev);
+
+static void dpi_display_disable(struct omap_dss_device *dssdev)
+{
+       if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
+               return;
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+               dpi_display_resume(dssdev);
+
+       dssdev->driver->disable(dssdev);
+
+       dispc_enable_lcd_out(0);
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+       dss_select_clk_source(0, 0);
+       dsi_pll_uninit();
+       dss_clk_disable(DSS_CLK_FCK2);
+#endif
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+       omap_dss_stop_device(dssdev);
+}
+
+static int dpi_display_suspend(struct omap_dss_device *dssdev)
+{
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return -EINVAL;
+
+       DSSDBG("dpi_display_suspend\n");
+
+       if (dssdev->driver->suspend)
+               dssdev->driver->suspend(dssdev);
+
+       dispc_enable_lcd_out(0);
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+       return 0;
+}
+
+static int dpi_display_resume(struct omap_dss_device *dssdev)
+{
+       if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
+               return -EINVAL;
+
+       DSSDBG("dpi_display_resume\n");
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dispc_enable_lcd_out(1);
+
+       if (dssdev->driver->resume)
+               dssdev->driver->resume(dssdev);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+}
+
+static void dpi_set_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       DSSDBG("dpi_set_timings\n");
+       dssdev->panel.timings = *timings;
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+               dpi_set_mode(dssdev);
+               dispc_go(OMAP_DSS_CHANNEL_LCD);
+       }
+}
+
+static int dpi_check_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       bool is_tft;
+       int r;
+       int lck_div, pck_div;
+       unsigned long fck;
+       unsigned long pck;
+
+       if (!dispc_lcd_timings_ok(timings))
+               return -EINVAL;
+
+       if (timings->pixel_clock == 0)
+               return -EINVAL;
+
+       is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+
+#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL
+       {
+               struct dsi_clock_info dsi_cinfo;
+               struct dispc_clock_info dispc_cinfo;
+               r = dsi_pll_calc_clock_div_pck(is_tft,
+                               timings->pixel_clock * 1000,
+                               &dsi_cinfo, &dispc_cinfo);
+
+               if (r)
+                       return r;
+
+               fck = dsi_cinfo.dsi1_pll_fclk;
+               lck_div = dispc_cinfo.lck_div;
+               pck_div = dispc_cinfo.pck_div;
+       }
+#else
+       {
+               struct dss_clock_info dss_cinfo;
+               struct dispc_clock_info dispc_cinfo;
+               r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
+                               &dss_cinfo, &dispc_cinfo);
+
+               if (r)
+                       return r;
+
+               fck = dss_cinfo.fck;
+               lck_div = dispc_cinfo.lck_div;
+               pck_div = dispc_cinfo.pck_div;
+       }
+#endif
+
+       pck = fck / lck_div / pck_div / 1000;
+
+       timings->pixel_clock = pck;
+
+       return 0;
+}
+
+static void dpi_get_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       *timings = dssdev->panel.timings;
+}
+
+static int dpi_display_set_update_mode(struct omap_dss_device *dssdev,
+               enum omap_dss_update_mode mode)
+{
+       if (mode == OMAP_DSS_UPDATE_MANUAL)
+               return -EINVAL;
+
+       if (mode == OMAP_DSS_UPDATE_DISABLED) {
+               dispc_enable_lcd_out(0);
+               dpi.update_enabled = 0;
+       } else {
+               dispc_enable_lcd_out(1);
+               dpi.update_enabled = 1;
+       }
+
+       return 0;
+}
+
+static enum omap_dss_update_mode dpi_display_get_update_mode(
+               struct omap_dss_device *dssdev)
+{
+       return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
+               OMAP_DSS_UPDATE_DISABLED;
+}
+
+int dpi_init_display(struct omap_dss_device *dssdev)
+{
+       DSSDBG("init_display\n");
+
+       dssdev->enable = dpi_display_enable;
+       dssdev->disable = dpi_display_disable;
+       dssdev->suspend = dpi_display_suspend;
+       dssdev->resume = dpi_display_resume;
+       dssdev->set_timings = dpi_set_timings;
+       dssdev->check_timings = dpi_check_timings;
+       dssdev->get_timings = dpi_get_timings;
+       dssdev->set_update_mode = dpi_display_set_update_mode;
+       dssdev->get_update_mode = dpi_display_get_update_mode;
+
+       return 0;
+}
+
+int dpi_init(void)
+{
+       return 0;
+}
+
+void dpi_exit(void)
+{
+}
+
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
new file mode 100644 (file)
index 0000000..5936487
--- /dev/null
@@ -0,0 +1,3710 @@
+/*
+ * linux/drivers/video/omap2/dss/dsi.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DSI"
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+
+#include <plat/display.h>
+#include <plat/clock.h>
+
+#include "dss.h"
+
+/*#define VERBOSE_IRQ*/
+#define DSI_CATCH_MISSING_TE
+
+#define DSI_BASE               0x4804FC00
+
+struct dsi_reg { u16 idx; };
+
+#define DSI_REG(idx)           ((const struct dsi_reg) { idx })
+
+#define DSI_SZ_REGS            SZ_1K
+/* DSI Protocol Engine */
+
+#define DSI_REVISION                   DSI_REG(0x0000)
+#define DSI_SYSCONFIG                  DSI_REG(0x0010)
+#define DSI_SYSSTATUS                  DSI_REG(0x0014)
+#define DSI_IRQSTATUS                  DSI_REG(0x0018)
+#define DSI_IRQENABLE                  DSI_REG(0x001C)
+#define DSI_CTRL                       DSI_REG(0x0040)
+#define DSI_COMPLEXIO_CFG1             DSI_REG(0x0048)
+#define DSI_COMPLEXIO_IRQ_STATUS       DSI_REG(0x004C)
+#define DSI_COMPLEXIO_IRQ_ENABLE       DSI_REG(0x0050)
+#define DSI_CLK_CTRL                   DSI_REG(0x0054)
+#define DSI_TIMING1                    DSI_REG(0x0058)
+#define DSI_TIMING2                    DSI_REG(0x005C)
+#define DSI_VM_TIMING1                 DSI_REG(0x0060)
+#define DSI_VM_TIMING2                 DSI_REG(0x0064)
+#define DSI_VM_TIMING3                 DSI_REG(0x0068)
+#define DSI_CLK_TIMING                 DSI_REG(0x006C)
+#define DSI_TX_FIFO_VC_SIZE            DSI_REG(0x0070)
+#define DSI_RX_FIFO_VC_SIZE            DSI_REG(0x0074)
+#define DSI_COMPLEXIO_CFG2             DSI_REG(0x0078)
+#define DSI_RX_FIFO_VC_FULLNESS                DSI_REG(0x007C)
+#define DSI_VM_TIMING4                 DSI_REG(0x0080)
+#define DSI_TX_FIFO_VC_EMPTINESS       DSI_REG(0x0084)
+#define DSI_VM_TIMING5                 DSI_REG(0x0088)
+#define DSI_VM_TIMING6                 DSI_REG(0x008C)
+#define DSI_VM_TIMING7                 DSI_REG(0x0090)
+#define DSI_STOPCLK_TIMING             DSI_REG(0x0094)
+#define DSI_VC_CTRL(n)                 DSI_REG(0x0100 + (n * 0x20))
+#define DSI_VC_TE(n)                   DSI_REG(0x0104 + (n * 0x20))
+#define DSI_VC_LONG_PACKET_HEADER(n)   DSI_REG(0x0108 + (n * 0x20))
+#define DSI_VC_LONG_PACKET_PAYLOAD(n)  DSI_REG(0x010C + (n * 0x20))
+#define DSI_VC_SHORT_PACKET_HEADER(n)  DSI_REG(0x0110 + (n * 0x20))
+#define DSI_VC_IRQSTATUS(n)            DSI_REG(0x0118 + (n * 0x20))
+#define DSI_VC_IRQENABLE(n)            DSI_REG(0x011C + (n * 0x20))
+
+/* DSIPHY_SCP */
+
+#define DSI_DSIPHY_CFG0                        DSI_REG(0x200 + 0x0000)
+#define DSI_DSIPHY_CFG1                        DSI_REG(0x200 + 0x0004)
+#define DSI_DSIPHY_CFG2                        DSI_REG(0x200 + 0x0008)
+#define DSI_DSIPHY_CFG5                        DSI_REG(0x200 + 0x0014)
+
+/* DSI_PLL_CTRL_SCP */
+
+#define DSI_PLL_CONTROL                        DSI_REG(0x300 + 0x0000)
+#define DSI_PLL_STATUS                 DSI_REG(0x300 + 0x0004)
+#define DSI_PLL_GO                     DSI_REG(0x300 + 0x0008)
+#define DSI_PLL_CONFIGURATION1         DSI_REG(0x300 + 0x000C)
+#define DSI_PLL_CONFIGURATION2         DSI_REG(0x300 + 0x0010)
+
+#define REG_GET(idx, start, end) \
+       FLD_GET(dsi_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end) \
+       dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end))
+
+/* Global interrupts */
+#define DSI_IRQ_VC0            (1 << 0)
+#define DSI_IRQ_VC1            (1 << 1)
+#define DSI_IRQ_VC2            (1 << 2)
+#define DSI_IRQ_VC3            (1 << 3)
+#define DSI_IRQ_WAKEUP         (1 << 4)
+#define DSI_IRQ_RESYNC         (1 << 5)
+#define DSI_IRQ_PLL_LOCK       (1 << 7)
+#define DSI_IRQ_PLL_UNLOCK     (1 << 8)
+#define DSI_IRQ_PLL_RECALL     (1 << 9)
+#define DSI_IRQ_COMPLEXIO_ERR  (1 << 10)
+#define DSI_IRQ_HS_TX_TIMEOUT  (1 << 14)
+#define DSI_IRQ_LP_RX_TIMEOUT  (1 << 15)
+#define DSI_IRQ_TE_TRIGGER     (1 << 16)
+#define DSI_IRQ_ACK_TRIGGER    (1 << 17)
+#define DSI_IRQ_SYNC_LOST      (1 << 18)
+#define DSI_IRQ_LDO_POWER_GOOD (1 << 19)
+#define DSI_IRQ_TA_TIMEOUT     (1 << 20)
+#define DSI_IRQ_ERROR_MASK \
+       (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \
+       DSI_IRQ_TA_TIMEOUT)
+#define DSI_IRQ_CHANNEL_MASK   0xf
+
+/* Virtual channel interrupts */
+#define DSI_VC_IRQ_CS          (1 << 0)
+#define DSI_VC_IRQ_ECC_CORR    (1 << 1)
+#define DSI_VC_IRQ_PACKET_SENT (1 << 2)
+#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3)
+#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4)
+#define DSI_VC_IRQ_BTA         (1 << 5)
+#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6)
+#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7)
+#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8)
+#define DSI_VC_IRQ_ERROR_MASK \
+       (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \
+       DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \
+       DSI_VC_IRQ_FIFO_TX_UDF)
+
+/* ComplexIO interrupts */
+#define DSI_CIO_IRQ_ERRSYNCESC1                (1 << 0)
+#define DSI_CIO_IRQ_ERRSYNCESC2                (1 << 1)
+#define DSI_CIO_IRQ_ERRSYNCESC3                (1 << 2)
+#define DSI_CIO_IRQ_ERRESC1            (1 << 5)
+#define DSI_CIO_IRQ_ERRESC2            (1 << 6)
+#define DSI_CIO_IRQ_ERRESC3            (1 << 7)
+#define DSI_CIO_IRQ_ERRCONTROL1                (1 << 10)
+#define DSI_CIO_IRQ_ERRCONTROL2                (1 << 11)
+#define DSI_CIO_IRQ_ERRCONTROL3                (1 << 12)
+#define DSI_CIO_IRQ_STATEULPS1         (1 << 15)
+#define DSI_CIO_IRQ_STATEULPS2         (1 << 16)
+#define DSI_CIO_IRQ_STATEULPS3         (1 << 17)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24)
+#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25)
+#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30)
+#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31)
+
+#define DSI_DT_DCS_SHORT_WRITE_0       0x05
+#define DSI_DT_DCS_SHORT_WRITE_1       0x15
+#define DSI_DT_DCS_READ                        0x06
+#define DSI_DT_SET_MAX_RET_PKG_SIZE    0x37
+#define DSI_DT_NULL_PACKET             0x09
+#define DSI_DT_DCS_LONG_WRITE          0x39
+
+#define DSI_DT_RX_ACK_WITH_ERR         0x02
+#define DSI_DT_RX_DCS_LONG_READ                0x1c
+#define DSI_DT_RX_SHORT_READ_1         0x21
+#define DSI_DT_RX_SHORT_READ_2         0x22
+
+#define FINT_MAX 2100000
+#define FINT_MIN 750000
+#define REGN_MAX (1 << 7)
+#define REGM_MAX ((1 << 11) - 1)
+#define REGM3_MAX (1 << 4)
+#define REGM4_MAX (1 << 4)
+#define LP_DIV_MAX ((1 << 13) - 1)
+
+enum fifo_size {
+       DSI_FIFO_SIZE_0         = 0,
+       DSI_FIFO_SIZE_32        = 1,
+       DSI_FIFO_SIZE_64        = 2,
+       DSI_FIFO_SIZE_96        = 3,
+       DSI_FIFO_SIZE_128       = 4,
+};
+
+enum dsi_vc_mode {
+       DSI_VC_MODE_L4 = 0,
+       DSI_VC_MODE_VP,
+};
+
+struct dsi_update_region {
+       bool dirty;
+       u16 x, y, w, h;
+       struct omap_dss_device *device;
+};
+
+static struct
+{
+       void __iomem    *base;
+
+       struct dsi_clock_info current_cinfo;
+
+       struct regulator *vdds_dsi_reg;
+
+       struct {
+               enum dsi_vc_mode mode;
+               struct omap_dss_device *dssdev;
+               enum fifo_size fifo_size;
+               int dest_per;   /* destination peripheral 0-3 */
+       } vc[4];
+
+       struct mutex lock;
+       struct mutex bus_lock;
+
+       unsigned pll_locked;
+
+       struct completion bta_completion;
+
+       struct task_struct *thread;
+       wait_queue_head_t waitqueue;
+
+       spinlock_t update_lock;
+       bool framedone_received;
+       struct dsi_update_region update_region;
+       struct dsi_update_region active_update_region;
+       struct completion update_completion;
+
+       enum omap_dss_update_mode user_update_mode;
+       enum omap_dss_update_mode update_mode;
+       bool te_enabled;
+       bool use_ext_te;
+
+#ifdef DSI_CATCH_MISSING_TE
+       struct timer_list te_timer;
+#endif
+
+       unsigned long cache_req_pck;
+       unsigned long cache_clk_freq;
+       struct dsi_clock_info cache_cinfo;
+
+       u32             errors;
+       spinlock_t      errors_lock;
+#ifdef DEBUG
+       ktime_t perf_setup_time;
+       ktime_t perf_start_time;
+       ktime_t perf_start_time_auto;
+       int perf_measure_frames;
+#endif
+       int debug_read;
+       int debug_write;
+} dsi;
+
+#ifdef DEBUG
+static unsigned int dsi_perf;
+module_param_named(dsi_perf, dsi_perf, bool, 0644);
+#endif
+
+static inline void dsi_write_reg(const struct dsi_reg idx, u32 val)
+{
+       __raw_writel(val, dsi.base + idx.idx);
+}
+
+static inline u32 dsi_read_reg(const struct dsi_reg idx)
+{
+       return __raw_readl(dsi.base + idx.idx);
+}
+
+
+void dsi_save_context(void)
+{
+}
+
+void dsi_restore_context(void)
+{
+}
+
+void dsi_bus_lock(void)
+{
+       mutex_lock(&dsi.bus_lock);
+}
+EXPORT_SYMBOL(dsi_bus_lock);
+
+void dsi_bus_unlock(void)
+{
+       mutex_unlock(&dsi.bus_lock);
+}
+EXPORT_SYMBOL(dsi_bus_unlock);
+
+static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum,
+               int value)
+{
+       int t = 100000;
+
+       while (REG_GET(idx, bitnum, bitnum) != value) {
+               if (--t == 0)
+                       return !value;
+       }
+
+       return value;
+}
+
+#ifdef DEBUG
+static void dsi_perf_mark_setup(void)
+{
+       dsi.perf_setup_time = ktime_get();
+}
+
+static void dsi_perf_mark_start(void)
+{
+       dsi.perf_start_time = ktime_get();
+}
+
+static void dsi_perf_mark_start_auto(void)
+{
+       dsi.perf_measure_frames = 0;
+       dsi.perf_start_time_auto = ktime_get();
+}
+
+static void dsi_perf_show(const char *name)
+{
+       ktime_t t, setup_time, trans_time;
+       u32 total_bytes;
+       u32 setup_us, trans_us, total_us;
+
+       if (!dsi_perf)
+               return;
+
+       if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED)
+               return;
+
+       t = ktime_get();
+
+       setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time);
+       setup_us = (u32)ktime_to_us(setup_time);
+       if (setup_us == 0)
+               setup_us = 1;
+
+       trans_time = ktime_sub(t, dsi.perf_start_time);
+       trans_us = (u32)ktime_to_us(trans_time);
+       if (trans_us == 0)
+               trans_us = 1;
+
+       total_us = setup_us + trans_us;
+
+       total_bytes = dsi.active_update_region.w *
+               dsi.active_update_region.h *
+               dsi.active_update_region.device->ctrl.pixel_size / 8;
+
+       if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+               static u32 s_total_trans_us, s_total_setup_us;
+               static u32 s_min_trans_us = 0xffffffff, s_min_setup_us;
+               static u32 s_max_trans_us, s_max_setup_us;
+               const int numframes = 100;
+               ktime_t total_time_auto;
+               u32 total_time_auto_us;
+
+               dsi.perf_measure_frames++;
+
+               if (setup_us < s_min_setup_us)
+                       s_min_setup_us = setup_us;
+
+               if (setup_us > s_max_setup_us)
+                       s_max_setup_us = setup_us;
+
+               s_total_setup_us += setup_us;
+
+               if (trans_us < s_min_trans_us)
+                       s_min_trans_us = trans_us;
+
+               if (trans_us > s_max_trans_us)
+                       s_max_trans_us = trans_us;
+
+               s_total_trans_us += trans_us;
+
+               if (dsi.perf_measure_frames < numframes)
+                       return;
+
+               total_time_auto = ktime_sub(t, dsi.perf_start_time_auto);
+               total_time_auto_us = (u32)ktime_to_us(total_time_auto);
+
+               printk(KERN_INFO "DSI(%s): %u fps, setup %u/%u/%u, "
+                               "trans %u/%u/%u\n",
+                               name,
+                               1000 * 1000 * numframes / total_time_auto_us,
+                               s_min_setup_us,
+                               s_max_setup_us,
+                               s_total_setup_us / numframes,
+                               s_min_trans_us,
+                               s_max_trans_us,
+                               s_total_trans_us / numframes);
+
+               s_total_setup_us = 0;
+               s_min_setup_us = 0xffffffff;
+               s_max_setup_us = 0;
+               s_total_trans_us = 0;
+               s_min_trans_us = 0xffffffff;
+               s_max_trans_us = 0;
+               dsi_perf_mark_start_auto();
+       } else {
+               printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), "
+                               "%u bytes, %u kbytes/sec\n",
+                               name,
+                               setup_us,
+                               trans_us,
+                               total_us,
+                               1000*1000 / total_us,
+                               total_bytes,
+                               total_bytes * 1000 / total_us);
+       }
+}
+#else
+#define dsi_perf_mark_setup()
+#define dsi_perf_mark_start()
+#define dsi_perf_mark_start_auto()
+#define dsi_perf_show(x)
+#endif
+
+static void print_irq_status(u32 status)
+{
+#ifndef VERBOSE_IRQ
+       if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0)
+               return;
+#endif
+       printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status);
+
+#define PIS(x) \
+       if (status & DSI_IRQ_##x) \
+               printk(#x " ");
+#ifdef VERBOSE_IRQ
+       PIS(VC0);
+       PIS(VC1);
+       PIS(VC2);
+       PIS(VC3);
+#endif
+       PIS(WAKEUP);
+       PIS(RESYNC);
+       PIS(PLL_LOCK);
+       PIS(PLL_UNLOCK);
+       PIS(PLL_RECALL);
+       PIS(COMPLEXIO_ERR);
+       PIS(HS_TX_TIMEOUT);
+       PIS(LP_RX_TIMEOUT);
+       PIS(TE_TRIGGER);
+       PIS(ACK_TRIGGER);
+       PIS(SYNC_LOST);
+       PIS(LDO_POWER_GOOD);
+       PIS(TA_TIMEOUT);
+#undef PIS
+
+       printk("\n");
+}
+
+static void print_irq_status_vc(int channel, u32 status)
+{
+#ifndef VERBOSE_IRQ
+       if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
+               return;
+#endif
+       printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status);
+
+#define PIS(x) \
+       if (status & DSI_VC_IRQ_##x) \
+               printk(#x " ");
+       PIS(CS);
+       PIS(ECC_CORR);
+#ifdef VERBOSE_IRQ
+       PIS(PACKET_SENT);
+#endif
+       PIS(FIFO_TX_OVF);
+       PIS(FIFO_RX_OVF);
+       PIS(BTA);
+       PIS(ECC_NO_CORR);
+       PIS(FIFO_TX_UDF);
+       PIS(PP_BUSY_CHANGE);
+#undef PIS
+       printk("\n");
+}
+
+static void print_irq_status_cio(u32 status)
+{
+       printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status);
+
+#define PIS(x) \
+       if (status & DSI_CIO_IRQ_##x) \
+               printk(#x " ");
+       PIS(ERRSYNCESC1);
+       PIS(ERRSYNCESC2);
+       PIS(ERRSYNCESC3);
+       PIS(ERRESC1);
+       PIS(ERRESC2);
+       PIS(ERRESC3);
+       PIS(ERRCONTROL1);
+       PIS(ERRCONTROL2);
+       PIS(ERRCONTROL3);
+       PIS(STATEULPS1);
+       PIS(STATEULPS2);
+       PIS(STATEULPS3);
+       PIS(ERRCONTENTIONLP0_1);
+       PIS(ERRCONTENTIONLP1_1);
+       PIS(ERRCONTENTIONLP0_2);
+       PIS(ERRCONTENTIONLP1_2);
+       PIS(ERRCONTENTIONLP0_3);
+       PIS(ERRCONTENTIONLP1_3);
+       PIS(ULPSACTIVENOT_ALL0);
+       PIS(ULPSACTIVENOT_ALL1);
+#undef PIS
+
+       printk("\n");
+}
+
+static int debug_irq;
+
+/* called from dss */
+void dsi_irq_handler(void)
+{
+       u32 irqstatus, vcstatus, ciostatus;
+       int i;
+
+       irqstatus = dsi_read_reg(DSI_IRQSTATUS);
+
+       if (irqstatus & DSI_IRQ_ERROR_MASK) {
+               DSSERR("DSI error, irqstatus %x\n", irqstatus);
+               print_irq_status(irqstatus);
+               spin_lock(&dsi.errors_lock);
+               dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK;
+               spin_unlock(&dsi.errors_lock);
+       } else if (debug_irq) {
+               print_irq_status(irqstatus);
+       }
+
+#ifdef DSI_CATCH_MISSING_TE
+       if (irqstatus & DSI_IRQ_TE_TRIGGER)
+               del_timer(&dsi.te_timer);
+#endif
+
+       for (i = 0; i < 4; ++i) {
+               if ((irqstatus & (1<<i)) == 0)
+                       continue;
+
+               vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i));
+
+               if (vcstatus & DSI_VC_IRQ_BTA)
+                       complete(&dsi.bta_completion);
+
+               if (vcstatus & DSI_VC_IRQ_ERROR_MASK) {
+                       DSSERR("DSI VC(%d) error, vc irqstatus %x\n",
+                                      i, vcstatus);
+                       print_irq_status_vc(i, vcstatus);
+               } else if (debug_irq) {
+                       print_irq_status_vc(i, vcstatus);
+               }
+
+               dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus);
+               /* flush posted write */
+               dsi_read_reg(DSI_VC_IRQSTATUS(i));
+       }
+
+       if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) {
+               ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+
+               dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus);
+               /* flush posted write */
+               dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+
+               DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus);
+               print_irq_status_cio(ciostatus);
+       }
+
+       dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
+       /* flush posted write */
+       dsi_read_reg(DSI_IRQSTATUS);
+}
+
+
+static void _dsi_initialize_irq(void)
+{
+       u32 l;
+       int i;
+
+       /* disable all interrupts */
+       dsi_write_reg(DSI_IRQENABLE, 0);
+       for (i = 0; i < 4; ++i)
+               dsi_write_reg(DSI_VC_IRQENABLE(i), 0);
+       dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0);
+
+       /* clear interrupt status */
+       l = dsi_read_reg(DSI_IRQSTATUS);
+       dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK);
+
+       for (i = 0; i < 4; ++i) {
+               l = dsi_read_reg(DSI_VC_IRQSTATUS(i));
+               dsi_write_reg(DSI_VC_IRQSTATUS(i), l);
+       }
+
+       l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
+       dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l);
+
+       /* enable error irqs */
+       l = DSI_IRQ_ERROR_MASK;
+#ifdef DSI_CATCH_MISSING_TE
+       l |= DSI_IRQ_TE_TRIGGER;
+#endif
+       dsi_write_reg(DSI_IRQENABLE, l);
+
+       l = DSI_VC_IRQ_ERROR_MASK;
+       for (i = 0; i < 4; ++i)
+               dsi_write_reg(DSI_VC_IRQENABLE(i), l);
+
+       /* XXX zonda responds incorrectly, causing control error:
+          Exit from LP-ESC mode to LP11 uses wrong transition states on the
+          data lines LP0 and LN0. */
+       dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE,
+                       -1 & (~DSI_CIO_IRQ_ERRCONTROL2));
+}
+
+static u32 dsi_get_errors(void)
+{
+       unsigned long flags;
+       u32 e;
+       spin_lock_irqsave(&dsi.errors_lock, flags);
+       e = dsi.errors;
+       dsi.errors = 0;
+       spin_unlock_irqrestore(&dsi.errors_lock, flags);
+       return e;
+}
+
+static void dsi_vc_enable_bta_irq(int channel)
+{
+       u32 l;
+
+       dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA);
+
+       l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
+       l |= DSI_VC_IRQ_BTA;
+       dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+}
+
+static void dsi_vc_disable_bta_irq(int channel)
+{
+       u32 l;
+
+       l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
+       l &= ~DSI_VC_IRQ_BTA;
+       dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+}
+
+/* DSI func clock. this could also be DSI2_PLL_FCLK */
+static inline void enable_clocks(bool enable)
+{
+       if (enable)
+               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       else
+               dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+/* source clock for DSI PLL. this could also be PCLKFREE */
+static inline void dsi_enable_pll_clock(bool enable)
+{
+       if (enable)
+               dss_clk_enable(DSS_CLK_FCK2);
+       else
+               dss_clk_disable(DSS_CLK_FCK2);
+
+       if (enable && dsi.pll_locked) {
+               if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1)
+                       DSSERR("cannot lock PLL when enabling clocks\n");
+       }
+}
+
+#ifdef DEBUG
+static void _dsi_print_reset_status(void)
+{
+       u32 l;
+
+       if (!dss_debug)
+               return;
+
+       /* A dummy read using the SCP interface to any DSIPHY register is
+        * required after DSIPHY reset to complete the reset of the DSI complex
+        * I/O. */
+       l = dsi_read_reg(DSI_DSIPHY_CFG5);
+
+       printk(KERN_DEBUG "DSI resets: ");
+
+       l = dsi_read_reg(DSI_PLL_STATUS);
+       printk("PLL (%d) ", FLD_GET(l, 0, 0));
+
+       l = dsi_read_reg(DSI_COMPLEXIO_CFG1);
+       printk("CIO (%d) ", FLD_GET(l, 29, 29));
+
+       l = dsi_read_reg(DSI_DSIPHY_CFG5);
+       printk("PHY (%x, %d, %d, %d)\n",
+                       FLD_GET(l, 28, 26),
+                       FLD_GET(l, 29, 29),
+                       FLD_GET(l, 30, 30),
+                       FLD_GET(l, 31, 31));
+}
+#else
+#define _dsi_print_reset_status()
+#endif
+
+static inline int dsi_if_enable(bool enable)
+{
+       DSSDBG("dsi_if_enable(%d)\n", enable);
+
+       enable = enable ? 1 : 0;
+       REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */
+
+       if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) {
+                       DSSERR("Failed to set dsi_if_enable to %d\n", enable);
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+unsigned long dsi_get_dsi1_pll_rate(void)
+{
+       return dsi.current_cinfo.dsi1_pll_fclk;
+}
+
+static unsigned long dsi_get_dsi2_pll_rate(void)
+{
+       return dsi.current_cinfo.dsi2_pll_fclk;
+}
+
+static unsigned long dsi_get_txbyteclkhs(void)
+{
+       return dsi.current_cinfo.clkin4ddr / 16;
+}
+
+static unsigned long dsi_fclk_rate(void)
+{
+       unsigned long r;
+
+       if (dss_get_dsi_clk_source() == 0) {
+               /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */
+               r = dss_clk_get_rate(DSS_CLK_FCK1);
+       } else {
+               /* DSI FCLK source is DSI2_PLL_FCLK */
+               r = dsi_get_dsi2_pll_rate();
+       }
+
+       return r;
+}
+
+static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev)
+{
+       unsigned long dsi_fclk;
+       unsigned lp_clk_div;
+       unsigned long lp_clk;
+
+       lp_clk_div = dssdev->phy.dsi.div.lp_clk_div;
+
+       if (lp_clk_div == 0 || lp_clk_div > LP_DIV_MAX)
+               return -EINVAL;
+
+       dsi_fclk = dsi_fclk_rate();
+
+       lp_clk = dsi_fclk / 2 / lp_clk_div;
+
+       DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk);
+       dsi.current_cinfo.lp_clk = lp_clk;
+       dsi.current_cinfo.lp_clk_div = lp_clk_div;
+
+       REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0);   /* LP_CLK_DIVISOR */
+
+       REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0,
+                       21, 21);                /* LP_RX_SYNCHRO_ENABLE */
+
+       return 0;
+}
+
+
+enum dsi_pll_power_state {
+       DSI_PLL_POWER_OFF       = 0x0,
+       DSI_PLL_POWER_ON_HSCLK  = 0x1,
+       DSI_PLL_POWER_ON_ALL    = 0x2,
+       DSI_PLL_POWER_ON_DIV    = 0x3,
+};
+
+static int dsi_pll_power(enum dsi_pll_power_state state)
+{
+       int t = 0;
+
+       REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30);       /* PLL_PWR_CMD */
+
+       /* PLL_PWR_STATUS */
+       while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) {
+               udelay(1);
+               if (t++ > 1000) {
+                       DSSERR("Failed to set DSI PLL power mode to %d\n",
+                                       state);
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+/* calculate clock rates using dividers in cinfo */
+static int dsi_calc_clock_rates(struct dsi_clock_info *cinfo)
+{
+       if (cinfo->regn == 0 || cinfo->regn > REGN_MAX)
+               return -EINVAL;
+
+       if (cinfo->regm == 0 || cinfo->regm > REGM_MAX)
+               return -EINVAL;
+
+       if (cinfo->regm3 > REGM3_MAX)
+               return -EINVAL;
+
+       if (cinfo->regm4 > REGM4_MAX)
+               return -EINVAL;
+
+       if (cinfo->use_dss2_fck) {
+               cinfo->clkin = dss_clk_get_rate(DSS_CLK_FCK2);
+               /* XXX it is unclear if highfreq should be used
+                * with DSS2_FCK source also */
+               cinfo->highfreq = 0;
+       } else {
+               cinfo->clkin = dispc_pclk_rate();
+
+               if (cinfo->clkin < 32000000)
+                       cinfo->highfreq = 0;
+               else
+                       cinfo->highfreq = 1;
+       }
+
+       cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1));
+
+       if (cinfo->fint > FINT_MAX || cinfo->fint < FINT_MIN)
+               return -EINVAL;
+
+       cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint;
+
+       if (cinfo->clkin4ddr > 1800 * 1000 * 1000)
+               return -EINVAL;
+
+       if (cinfo->regm3 > 0)
+               cinfo->dsi1_pll_fclk = cinfo->clkin4ddr / cinfo->regm3;
+       else
+               cinfo->dsi1_pll_fclk = 0;
+
+       if (cinfo->regm4 > 0)
+               cinfo->dsi2_pll_fclk = cinfo->clkin4ddr / cinfo->regm4;
+       else
+               cinfo->dsi2_pll_fclk = 0;
+
+       return 0;
+}
+
+int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck,
+               struct dsi_clock_info *dsi_cinfo,
+               struct dispc_clock_info *dispc_cinfo)
+{
+       struct dsi_clock_info cur, best;
+       struct dispc_clock_info best_dispc;
+       int min_fck_per_pck;
+       int match = 0;
+       unsigned long dss_clk_fck2;
+
+       dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2);
+
+       if (req_pck == dsi.cache_req_pck &&
+                       dsi.cache_cinfo.clkin == dss_clk_fck2) {
+               DSSDBG("DSI clock info found from cache\n");
+               *dsi_cinfo = dsi.cache_cinfo;
+               dispc_find_clk_divs(is_tft, req_pck, dsi_cinfo->dsi1_pll_fclk,
+                               dispc_cinfo);
+               return 0;
+       }
+
+       min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
+
+       if (min_fck_per_pck &&
+               req_pck * min_fck_per_pck > DISPC_MAX_FCK) {
+               DSSERR("Requested pixel clock not possible with the current "
+                               "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
+                               "the constraint off.\n");
+               min_fck_per_pck = 0;
+       }
+
+       DSSDBG("dsi_pll_calc\n");
+
+retry:
+       memset(&best, 0, sizeof(best));
+       memset(&best_dispc, 0, sizeof(best_dispc));
+
+       memset(&cur, 0, sizeof(cur));
+       cur.clkin = dss_clk_fck2;
+       cur.use_dss2_fck = 1;
+       cur.highfreq = 0;
+
+       /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */
+       /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */
+       /* To reduce PLL lock time, keep Fint high (around 2 MHz) */
+       for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) {
+               if (cur.highfreq == 0)
+                       cur.fint = cur.clkin / cur.regn;
+               else
+                       cur.fint = cur.clkin / (2 * cur.regn);
+
+               if (cur.fint > FINT_MAX || cur.fint < FINT_MIN)
+                       continue;
+
+               /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */
+               for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) {
+                       unsigned long a, b;
+
+                       a = 2 * cur.regm * (cur.clkin/1000);
+                       b = cur.regn * (cur.highfreq + 1);
+                       cur.clkin4ddr = a / b * 1000;
+
+                       if (cur.clkin4ddr > 1800 * 1000 * 1000)
+                               break;
+
+                       /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3  < 173MHz */
+                       for (cur.regm3 = 1; cur.regm3 < REGM3_MAX;
+                                       ++cur.regm3) {
+                               struct dispc_clock_info cur_dispc;
+                               cur.dsi1_pll_fclk = cur.clkin4ddr / cur.regm3;
+
+                               /* this will narrow down the search a bit,
+                                * but still give pixclocks below what was
+                                * requested */
+                               if (cur.dsi1_pll_fclk  < req_pck)
+                                       break;
+
+                               if (cur.dsi1_pll_fclk > DISPC_MAX_FCK)
+                                       continue;
+
+                               if (min_fck_per_pck &&
+                                       cur.dsi1_pll_fclk <
+                                               req_pck * min_fck_per_pck)
+                                       continue;
+
+                               match = 1;
+
+                               dispc_find_clk_divs(is_tft, req_pck,
+                                               cur.dsi1_pll_fclk,
+                                               &cur_dispc);
+
+                               if (abs(cur_dispc.pck - req_pck) <
+                                               abs(best_dispc.pck - req_pck)) {
+                                       best = cur;
+                                       best_dispc = cur_dispc;
+
+                                       if (cur_dispc.pck == req_pck)
+                                               goto found;
+                               }
+                       }
+               }
+       }
+found:
+       if (!match) {
+               if (min_fck_per_pck) {
+                       DSSERR("Could not find suitable clock settings.\n"
+                                       "Turning FCK/PCK constraint off and"
+                                       "trying again.\n");
+                       min_fck_per_pck = 0;
+                       goto retry;
+               }
+
+               DSSERR("Could not find suitable clock settings.\n");
+
+               return -EINVAL;
+       }
+
+       /* DSI2_PLL_FCLK (regm4) is not used */
+       best.regm4 = 0;
+       best.dsi2_pll_fclk = 0;
+
+       if (dsi_cinfo)
+               *dsi_cinfo = best;
+       if (dispc_cinfo)
+               *dispc_cinfo = best_dispc;
+
+       dsi.cache_req_pck = req_pck;
+       dsi.cache_clk_freq = 0;
+       dsi.cache_cinfo = best;
+
+       return 0;
+}
+
+int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo)
+{
+       int r = 0;
+       u32 l;
+       int f;
+
+       DSSDBGF();
+
+       dsi.current_cinfo.fint = cinfo->fint;
+       dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr;
+       dsi.current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk;
+       dsi.current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk;
+
+       dsi.current_cinfo.regn = cinfo->regn;
+       dsi.current_cinfo.regm = cinfo->regm;
+       dsi.current_cinfo.regm3 = cinfo->regm3;
+       dsi.current_cinfo.regm4 = cinfo->regm4;
+
+       DSSDBG("DSI Fint %ld\n", cinfo->fint);
+
+       DSSDBG("clkin (%s) rate %ld, highfreq %d\n",
+                       cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree",
+                       cinfo->clkin,
+                       cinfo->highfreq);
+
+       /* DSIPHY == CLKIN4DDR */
+       DSSDBG("CLKIN4DDR = 2 * %d / %d * %lu / %d = %lu\n",
+                       cinfo->regm,
+                       cinfo->regn,
+                       cinfo->clkin,
+                       cinfo->highfreq + 1,
+                       cinfo->clkin4ddr);
+
+       DSSDBG("Data rate on 1 DSI lane %ld Mbps\n",
+                       cinfo->clkin4ddr / 1000 / 1000 / 2);
+
+       DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4);
+
+       DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n",
+                       cinfo->regm3, cinfo->dsi1_pll_fclk);
+       DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n",
+                       cinfo->regm4, cinfo->dsi2_pll_fclk);
+
+       REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */
+
+       l = dsi_read_reg(DSI_PLL_CONFIGURATION1);
+       l = FLD_MOD(l, 1, 0, 0);                /* DSI_PLL_STOPMODE */
+       l = FLD_MOD(l, cinfo->regn - 1, 7, 1);  /* DSI_PLL_REGN */
+       l = FLD_MOD(l, cinfo->regm, 18, 8);     /* DSI_PLL_REGM */
+       l = FLD_MOD(l, cinfo->regm3 > 0 ? cinfo->regm3 - 1 : 0,
+                       22, 19);                /* DSI_CLOCK_DIV */
+       l = FLD_MOD(l, cinfo->regm4 > 0 ? cinfo->regm4 - 1 : 0,
+                       26, 23);                /* DSIPROTO_CLOCK_DIV */
+       dsi_write_reg(DSI_PLL_CONFIGURATION1, l);
+
+       BUG_ON(cinfo->fint < 750000 || cinfo->fint > 2100000);
+       if (cinfo->fint < 1000000)
+               f = 0x3;
+       else if (cinfo->fint < 1250000)
+               f = 0x4;
+       else if (cinfo->fint < 1500000)
+               f = 0x5;
+       else if (cinfo->fint < 1750000)
+               f = 0x6;
+       else
+               f = 0x7;
+
+       l = dsi_read_reg(DSI_PLL_CONFIGURATION2);
+       l = FLD_MOD(l, f, 4, 1);                /* DSI_PLL_FREQSEL */
+       l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1,
+                       11, 11);                /* DSI_PLL_CLKSEL */
+       l = FLD_MOD(l, cinfo->highfreq,
+                       12, 12);                /* DSI_PLL_HIGHFREQ */
+       l = FLD_MOD(l, 1, 13, 13);              /* DSI_PLL_REFEN */
+       l = FLD_MOD(l, 0, 14, 14);              /* DSIPHY_CLKINEN */
+       l = FLD_MOD(l, 1, 20, 20);              /* DSI_HSDIVBYPASS */
+       dsi_write_reg(DSI_PLL_CONFIGURATION2, l);
+
+       REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0);       /* DSI_PLL_GO */
+
+       if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) {
+               DSSERR("dsi pll go bit not going down.\n");
+               r = -EIO;
+               goto err;
+       }
+
+       if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) {
+               DSSERR("cannot lock PLL\n");
+               r = -EIO;
+               goto err;
+       }
+
+       dsi.pll_locked = 1;
+
+       l = dsi_read_reg(DSI_PLL_CONFIGURATION2);
+       l = FLD_MOD(l, 0, 0, 0);        /* DSI_PLL_IDLE */
+       l = FLD_MOD(l, 0, 5, 5);        /* DSI_PLL_PLLLPMODE */
+       l = FLD_MOD(l, 0, 6, 6);        /* DSI_PLL_LOWCURRSTBY */
+       l = FLD_MOD(l, 0, 7, 7);        /* DSI_PLL_TIGHTPHASELOCK */
+       l = FLD_MOD(l, 0, 8, 8);        /* DSI_PLL_DRIFTGUARDEN */
+       l = FLD_MOD(l, 0, 10, 9);       /* DSI_PLL_LOCKSEL */
+       l = FLD_MOD(l, 1, 13, 13);      /* DSI_PLL_REFEN */
+       l = FLD_MOD(l, 1, 14, 14);      /* DSIPHY_CLKINEN */
+       l = FLD_MOD(l, 0, 15, 15);      /* DSI_BYPASSEN */
+       l = FLD_MOD(l, 1, 16, 16);      /* DSS_CLOCK_EN */
+       l = FLD_MOD(l, 0, 17, 17);      /* DSS_CLOCK_PWDN */
+       l = FLD_MOD(l, 1, 18, 18);      /* DSI_PROTO_CLOCK_EN */
+       l = FLD_MOD(l, 0, 19, 19);      /* DSI_PROTO_CLOCK_PWDN */
+       l = FLD_MOD(l, 0, 20, 20);      /* DSI_HSDIVBYPASS */
+       dsi_write_reg(DSI_PLL_CONFIGURATION2, l);
+
+       DSSDBG("PLL config done\n");
+err:
+       return r;
+}
+
+int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk,
+               bool enable_hsdiv)
+{
+       int r = 0;
+       enum dsi_pll_power_state pwstate;
+
+       DSSDBG("PLL init\n");
+
+       enable_clocks(1);
+       dsi_enable_pll_clock(1);
+
+       r = regulator_enable(dsi.vdds_dsi_reg);
+       if (r)
+               goto err0;
+
+       /* XXX PLL does not come out of reset without this... */
+       dispc_pck_free_enable(1);
+
+       if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) {
+               DSSERR("PLL not coming out of reset.\n");
+               r = -ENODEV;
+               goto err1;
+       }
+
+       /* XXX ... but if left on, we get problems when planes do not
+        * fill the whole display. No idea about this */
+       dispc_pck_free_enable(0);
+
+       if (enable_hsclk && enable_hsdiv)
+               pwstate = DSI_PLL_POWER_ON_ALL;
+       else if (enable_hsclk)
+               pwstate = DSI_PLL_POWER_ON_HSCLK;
+       else if (enable_hsdiv)
+               pwstate = DSI_PLL_POWER_ON_DIV;
+       else
+               pwstate = DSI_PLL_POWER_OFF;
+
+       r = dsi_pll_power(pwstate);
+
+       if (r)
+               goto err1;
+
+       DSSDBG("PLL init done\n");
+
+       return 0;
+err1:
+       regulator_disable(dsi.vdds_dsi_reg);
+err0:
+       enable_clocks(0);
+       dsi_enable_pll_clock(0);
+       return r;
+}
+
+void dsi_pll_uninit(void)
+{
+       enable_clocks(0);
+       dsi_enable_pll_clock(0);
+
+       dsi.pll_locked = 0;
+       dsi_pll_power(DSI_PLL_POWER_OFF);
+       regulator_disable(dsi.vdds_dsi_reg);
+       DSSDBG("PLL uninit done\n");
+}
+
+void dsi_dump_clocks(struct seq_file *s)
+{
+       int clksel;
+       struct dsi_clock_info *cinfo = &dsi.current_cinfo;
+
+       enable_clocks(1);
+
+       clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11);
+
+       seq_printf(s,   "- DSI PLL -\n");
+
+       seq_printf(s,   "dsi pll source = %s\n",
+                       clksel == 0 ?
+                       "dss2_alwon_fclk" : "pclkfree");
+
+       seq_printf(s,   "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn);
+
+       seq_printf(s,   "CLKIN4DDR\t%-16luregm %u\n",
+                       cinfo->clkin4ddr, cinfo->regm);
+
+       seq_printf(s,   "dsi1_pll_fck\t%-16luregm3 %u\t(%s)\n",
+                       cinfo->dsi1_pll_fclk,
+                       cinfo->regm3,
+                       dss_get_dispc_clk_source() == 0 ? "off" : "on");
+
+       seq_printf(s,   "dsi2_pll_fck\t%-16luregm4 %u\t(%s)\n",
+                       cinfo->dsi2_pll_fclk,
+                       cinfo->regm4,
+                       dss_get_dsi_clk_source() == 0 ? "off" : "on");
+
+       seq_printf(s,   "- DSI -\n");
+
+       seq_printf(s,   "dsi fclk source = %s\n",
+                       dss_get_dsi_clk_source() == 0 ?
+                       "dss1_alwon_fclk" : "dsi2_pll_fclk");
+
+       seq_printf(s,   "DSI_FCLK\t%lu\n", dsi_fclk_rate());
+
+       seq_printf(s,   "DDR_CLK\t\t%lu\n",
+                       cinfo->clkin4ddr / 4);
+
+       seq_printf(s,   "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs());
+
+       seq_printf(s,   "LP_CLK\t\t%lu\n", cinfo->lp_clk);
+
+       seq_printf(s,   "VP_CLK\t\t%lu\n"
+                       "VP_PCLK\t\t%lu\n",
+                       dispc_lclk_rate(),
+                       dispc_pclk_rate());
+
+       enable_clocks(0);
+}
+
+void dsi_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r))
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       DUMPREG(DSI_REVISION);
+       DUMPREG(DSI_SYSCONFIG);
+       DUMPREG(DSI_SYSSTATUS);
+       DUMPREG(DSI_IRQSTATUS);
+       DUMPREG(DSI_IRQENABLE);
+       DUMPREG(DSI_CTRL);
+       DUMPREG(DSI_COMPLEXIO_CFG1);
+       DUMPREG(DSI_COMPLEXIO_IRQ_STATUS);
+       DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE);
+       DUMPREG(DSI_CLK_CTRL);
+       DUMPREG(DSI_TIMING1);
+       DUMPREG(DSI_TIMING2);
+       DUMPREG(DSI_VM_TIMING1);
+       DUMPREG(DSI_VM_TIMING2);
+       DUMPREG(DSI_VM_TIMING3);
+       DUMPREG(DSI_CLK_TIMING);
+       DUMPREG(DSI_TX_FIFO_VC_SIZE);
+       DUMPREG(DSI_RX_FIFO_VC_SIZE);
+       DUMPREG(DSI_COMPLEXIO_CFG2);
+       DUMPREG(DSI_RX_FIFO_VC_FULLNESS);
+       DUMPREG(DSI_VM_TIMING4);
+       DUMPREG(DSI_TX_FIFO_VC_EMPTINESS);
+       DUMPREG(DSI_VM_TIMING5);
+       DUMPREG(DSI_VM_TIMING6);
+       DUMPREG(DSI_VM_TIMING7);
+       DUMPREG(DSI_STOPCLK_TIMING);
+
+       DUMPREG(DSI_VC_CTRL(0));
+       DUMPREG(DSI_VC_TE(0));
+       DUMPREG(DSI_VC_LONG_PACKET_HEADER(0));
+       DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0));
+       DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0));
+       DUMPREG(DSI_VC_IRQSTATUS(0));
+       DUMPREG(DSI_VC_IRQENABLE(0));
+
+       DUMPREG(DSI_VC_CTRL(1));
+       DUMPREG(DSI_VC_TE(1));
+       DUMPREG(DSI_VC_LONG_PACKET_HEADER(1));
+       DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1));
+       DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1));
+       DUMPREG(DSI_VC_IRQSTATUS(1));
+       DUMPREG(DSI_VC_IRQENABLE(1));
+
+       DUMPREG(DSI_VC_CTRL(2));
+       DUMPREG(DSI_VC_TE(2));
+       DUMPREG(DSI_VC_LONG_PACKET_HEADER(2));
+       DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2));
+       DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2));
+       DUMPREG(DSI_VC_IRQSTATUS(2));
+       DUMPREG(DSI_VC_IRQENABLE(2));
+
+       DUMPREG(DSI_VC_CTRL(3));
+       DUMPREG(DSI_VC_TE(3));
+       DUMPREG(DSI_VC_LONG_PACKET_HEADER(3));
+       DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3));
+       DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3));
+       DUMPREG(DSI_VC_IRQSTATUS(3));
+       DUMPREG(DSI_VC_IRQENABLE(3));
+
+       DUMPREG(DSI_DSIPHY_CFG0);
+       DUMPREG(DSI_DSIPHY_CFG1);
+       DUMPREG(DSI_DSIPHY_CFG2);
+       DUMPREG(DSI_DSIPHY_CFG5);
+
+       DUMPREG(DSI_PLL_CONTROL);
+       DUMPREG(DSI_PLL_STATUS);
+       DUMPREG(DSI_PLL_GO);
+       DUMPREG(DSI_PLL_CONFIGURATION1);
+       DUMPREG(DSI_PLL_CONFIGURATION2);
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+enum dsi_complexio_power_state {
+       DSI_COMPLEXIO_POWER_OFF         = 0x0,
+       DSI_COMPLEXIO_POWER_ON          = 0x1,
+       DSI_COMPLEXIO_POWER_ULPS        = 0x2,
+};
+
+static int dsi_complexio_power(enum dsi_complexio_power_state state)
+{
+       int t = 0;
+
+       /* PWR_CMD */
+       REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27);
+
+       /* PWR_STATUS */
+       while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) {
+               udelay(1);
+               if (t++ > 1000) {
+                       DSSERR("failed to set complexio power state to "
+                                       "%d\n", state);
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+static void dsi_complexio_config(struct omap_dss_device *dssdev)
+{
+       u32 r;
+
+       int clk_lane   = dssdev->phy.dsi.clk_lane;
+       int data1_lane = dssdev->phy.dsi.data1_lane;
+       int data2_lane = dssdev->phy.dsi.data2_lane;
+       int clk_pol    = dssdev->phy.dsi.clk_pol;
+       int data1_pol  = dssdev->phy.dsi.data1_pol;
+       int data2_pol  = dssdev->phy.dsi.data2_pol;
+
+       r = dsi_read_reg(DSI_COMPLEXIO_CFG1);
+       r = FLD_MOD(r, clk_lane, 2, 0);
+       r = FLD_MOD(r, clk_pol, 3, 3);
+       r = FLD_MOD(r, data1_lane, 6, 4);
+       r = FLD_MOD(r, data1_pol, 7, 7);
+       r = FLD_MOD(r, data2_lane, 10, 8);
+       r = FLD_MOD(r, data2_pol, 11, 11);
+       dsi_write_reg(DSI_COMPLEXIO_CFG1, r);
+
+       /* The configuration of the DSI complex I/O (number of data lanes,
+          position, differential order) should not be changed while
+          DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for
+          the hardware to take into account a new configuration of the complex
+          I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to
+          follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1,
+          then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set
+          DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the
+          DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the
+          DSI complex I/O configuration is unknown. */
+
+       /*
+       REG_FLD_MOD(DSI_CTRL, 1, 0, 0);
+       REG_FLD_MOD(DSI_CTRL, 0, 0, 0);
+       REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20);
+       REG_FLD_MOD(DSI_CTRL, 1, 0, 0);
+       */
+}
+
+static inline unsigned ns2ddr(unsigned ns)
+{
+       /* convert time in ns to ddr ticks, rounding up */
+       unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4;
+       return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000;
+}
+
+static inline unsigned ddr2ns(unsigned ddr)
+{
+       unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4;
+       return ddr * 1000 * 1000 / (ddr_clk / 1000);
+}
+
+static void dsi_complexio_timings(void)
+{
+       u32 r;
+       u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit;
+       u32 tlpx_half, tclk_trail, tclk_zero;
+       u32 tclk_prepare;
+
+       /* calculate timings */
+
+       /* 1 * DDR_CLK = 2 * UI */
+
+       /* min 40ns + 4*UI      max 85ns + 6*UI */
+       ths_prepare = ns2ddr(70) + 2;
+
+       /* min 145ns + 10*UI */
+       ths_prepare_ths_zero = ns2ddr(175) + 2;
+
+       /* min max(8*UI, 60ns+4*UI) */
+       ths_trail = ns2ddr(60) + 5;
+
+       /* min 100ns */
+       ths_exit = ns2ddr(145);
+
+       /* tlpx min 50n */
+       tlpx_half = ns2ddr(25);
+
+       /* min 60ns */
+       tclk_trail = ns2ddr(60) + 2;
+
+       /* min 38ns, max 95ns */
+       tclk_prepare = ns2ddr(65);
+
+       /* min tclk-prepare + tclk-zero = 300ns */
+       tclk_zero = ns2ddr(260);
+
+       DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n",
+               ths_prepare, ddr2ns(ths_prepare),
+               ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero));
+       DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n",
+                       ths_trail, ddr2ns(ths_trail),
+                       ths_exit, ddr2ns(ths_exit));
+
+       DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), "
+                       "tclk_zero %u (%uns)\n",
+                       tlpx_half, ddr2ns(tlpx_half),
+                       tclk_trail, ddr2ns(tclk_trail),
+                       tclk_zero, ddr2ns(tclk_zero));
+       DSSDBG("tclk_prepare %u (%uns)\n",
+                       tclk_prepare, ddr2ns(tclk_prepare));
+
+       /* program timings */
+
+       r = dsi_read_reg(DSI_DSIPHY_CFG0);
+       r = FLD_MOD(r, ths_prepare, 31, 24);
+       r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16);
+       r = FLD_MOD(r, ths_trail, 15, 8);
+       r = FLD_MOD(r, ths_exit, 7, 0);
+       dsi_write_reg(DSI_DSIPHY_CFG0, r);
+
+       r = dsi_read_reg(DSI_DSIPHY_CFG1);
+       r = FLD_MOD(r, tlpx_half, 22, 16);
+       r = FLD_MOD(r, tclk_trail, 15, 8);
+       r = FLD_MOD(r, tclk_zero, 7, 0);
+       dsi_write_reg(DSI_DSIPHY_CFG1, r);
+
+       r = dsi_read_reg(DSI_DSIPHY_CFG2);
+       r = FLD_MOD(r, tclk_prepare, 7, 0);
+       dsi_write_reg(DSI_DSIPHY_CFG2, r);
+}
+
+
+static int dsi_complexio_init(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       DSSDBG("dsi_complexio_init\n");
+
+       /* CIO_CLK_ICG, enable L3 clk to CIO */
+       REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14);
+
+       /* A dummy read using the SCP interface to any DSIPHY register is
+        * required after DSIPHY reset to complete the reset of the DSI complex
+        * I/O. */
+       dsi_read_reg(DSI_DSIPHY_CFG5);
+
+       if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) {
+               DSSERR("ComplexIO PHY not coming out of reset.\n");
+               r = -ENODEV;
+               goto err;
+       }
+
+       dsi_complexio_config(dssdev);
+
+       r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON);
+
+       if (r)
+               goto err;
+
+       if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) {
+               DSSERR("ComplexIO not coming out of reset.\n");
+               r = -ENODEV;
+               goto err;
+       }
+
+       if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) {
+               DSSERR("ComplexIO LDO power down.\n");
+               r = -ENODEV;
+               goto err;
+       }
+
+       dsi_complexio_timings();
+
+       /*
+          The configuration of the DSI complex I/O (number of data lanes,
+          position, differential order) should not be changed while
+          DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the
+          hardware to recognize a new configuration of the complex I/O (done
+          in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow
+          this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next
+          reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20]
+          LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN
+          bit to 1. If the sequence is not followed, the DSi complex I/O
+          configuration is undetermined.
+          */
+       dsi_if_enable(1);
+       dsi_if_enable(0);
+       REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */
+       dsi_if_enable(1);
+       dsi_if_enable(0);
+
+       DSSDBG("CIO init done\n");
+err:
+       return r;
+}
+
+static void dsi_complexio_uninit(void)
+{
+       dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF);
+}
+
+static int _dsi_wait_reset(void)
+{
+       int i = 0;
+
+       while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) {
+               if (i++ > 5) {
+                       DSSERR("soft reset failed\n");
+                       return -ENODEV;
+               }
+               udelay(1);
+       }
+
+       return 0;
+}
+
+static int _dsi_reset(void)
+{
+       /* Soft reset */
+       REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1);
+       return _dsi_wait_reset();
+}
+
+static void dsi_reset_tx_fifo(int channel)
+{
+       u32 mask;
+       u32 l;
+
+       /* set fifosize of the channel to 0, then return the old size */
+       l = dsi_read_reg(DSI_TX_FIFO_VC_SIZE);
+
+       mask = FLD_MASK((8 * channel) + 7, (8 * channel) + 4);
+       dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l & ~mask);
+
+       dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l);
+}
+
+static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2,
+               enum fifo_size size3, enum fifo_size size4)
+{
+       u32 r = 0;
+       int add = 0;
+       int i;
+
+       dsi.vc[0].fifo_size = size1;
+       dsi.vc[1].fifo_size = size2;
+       dsi.vc[2].fifo_size = size3;
+       dsi.vc[3].fifo_size = size4;
+
+       for (i = 0; i < 4; i++) {
+               u8 v;
+               int size = dsi.vc[i].fifo_size;
+
+               if (add + size > 4) {
+                       DSSERR("Illegal FIFO configuration\n");
+                       BUG();
+               }
+
+               v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
+               r |= v << (8 * i);
+               /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */
+               add += size;
+       }
+
+       dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r);
+}
+
+static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2,
+               enum fifo_size size3, enum fifo_size size4)
+{
+       u32 r = 0;
+       int add = 0;
+       int i;
+
+       dsi.vc[0].fifo_size = size1;
+       dsi.vc[1].fifo_size = size2;
+       dsi.vc[2].fifo_size = size3;
+       dsi.vc[3].fifo_size = size4;
+
+       for (i = 0; i < 4; i++) {
+               u8 v;
+               int size = dsi.vc[i].fifo_size;
+
+               if (add + size > 4) {
+                       DSSERR("Illegal FIFO configuration\n");
+                       BUG();
+               }
+
+               v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4);
+               r |= v << (8 * i);
+               /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */
+               add += size;
+       }
+
+       dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r);
+}
+
+static int dsi_force_tx_stop_mode_io(void)
+{
+       u32 r;
+
+       r = dsi_read_reg(DSI_TIMING1);
+       r = FLD_MOD(r, 1, 15, 15);      /* FORCE_TX_STOP_MODE_IO */
+       dsi_write_reg(DSI_TIMING1, r);
+
+       if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) {
+               DSSERR("TX_STOP bit not going down\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void dsi_vc_print_status(int channel)
+{
+       u32 r;
+
+       r = dsi_read_reg(DSI_VC_CTRL(channel));
+       DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, "
+                       "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ",
+                       channel,
+                       FLD_GET(r, 5, 5),
+                       FLD_GET(r, 6, 6),
+                       FLD_GET(r, 15, 15),
+                       FLD_GET(r, 16, 16),
+                       FLD_GET(r, 20, 20));
+
+       r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS);
+       DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff);
+}
+
+static int dsi_vc_enable(int channel, bool enable)
+{
+       if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
+               DSSDBG("dsi_vc_enable channel %d, enable %d\n",
+                               channel, enable);
+
+       enable = enable ? 1 : 0;
+
+       REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0);
+
+       if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) {
+                       DSSERR("Failed to set dsi_vc_enable to %d\n", enable);
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static void dsi_vc_initial_config(int channel)
+{
+       u32 r;
+
+       DSSDBGF("%d", channel);
+
+       r = dsi_read_reg(DSI_VC_CTRL(channel));
+
+       if (FLD_GET(r, 15, 15)) /* VC_BUSY */
+               DSSERR("VC(%d) busy when trying to configure it!\n",
+                               channel);
+
+       r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */
+       r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN  */
+       r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */
+       r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */
+       r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */
+       r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */
+       r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
+
+       r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
+       r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
+
+       dsi_write_reg(DSI_VC_CTRL(channel), r);
+
+       dsi.vc[channel].mode = DSI_VC_MODE_L4;
+}
+
+static void dsi_vc_config_l4(int channel)
+{
+       if (dsi.vc[channel].mode == DSI_VC_MODE_L4)
+               return;
+
+       DSSDBGF("%d", channel);
+
+       dsi_vc_enable(channel, 0);
+
+       if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */
+               DSSERR("vc(%d) busy when trying to config for L4\n", channel);
+
+       REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */
+
+       dsi_vc_enable(channel, 1);
+
+       dsi.vc[channel].mode = DSI_VC_MODE_L4;
+}
+
+static void dsi_vc_config_vp(int channel)
+{
+       if (dsi.vc[channel].mode == DSI_VC_MODE_VP)
+               return;
+
+       DSSDBGF("%d", channel);
+
+       dsi_vc_enable(channel, 0);
+
+       if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */
+               DSSERR("vc(%d) busy when trying to config for VP\n", channel);
+
+       REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */
+
+       dsi_vc_enable(channel, 1);
+
+       dsi.vc[channel].mode = DSI_VC_MODE_VP;
+}
+
+
+static void dsi_vc_enable_hs(int channel, bool enable)
+{
+       DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable);
+
+       dsi_vc_enable(channel, 0);
+       dsi_if_enable(0);
+
+       REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9);
+
+       dsi_vc_enable(channel, 1);
+       dsi_if_enable(1);
+
+       dsi_force_tx_stop_mode_io();
+}
+
+static void dsi_vc_flush_long_data(int channel)
+{
+       while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+               u32 val;
+               val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+               DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n",
+                               (val >> 0) & 0xff,
+                               (val >> 8) & 0xff,
+                               (val >> 16) & 0xff,
+                               (val >> 24) & 0xff);
+       }
+}
+
+static void dsi_show_rx_ack_with_err(u16 err)
+{
+       DSSERR("\tACK with ERROR (%#x):\n", err);
+       if (err & (1 << 0))
+               DSSERR("\t\tSoT Error\n");
+       if (err & (1 << 1))
+               DSSERR("\t\tSoT Sync Error\n");
+       if (err & (1 << 2))
+               DSSERR("\t\tEoT Sync Error\n");
+       if (err & (1 << 3))
+               DSSERR("\t\tEscape Mode Entry Command Error\n");
+       if (err & (1 << 4))
+               DSSERR("\t\tLP Transmit Sync Error\n");
+       if (err & (1 << 5))
+               DSSERR("\t\tHS Receive Timeout Error\n");
+       if (err & (1 << 6))
+               DSSERR("\t\tFalse Control Error\n");
+       if (err & (1 << 7))
+               DSSERR("\t\t(reserved7)\n");
+       if (err & (1 << 8))
+               DSSERR("\t\tECC Error, single-bit (corrected)\n");
+       if (err & (1 << 9))
+               DSSERR("\t\tECC Error, multi-bit (not corrected)\n");
+       if (err & (1 << 10))
+               DSSERR("\t\tChecksum Error\n");
+       if (err & (1 << 11))
+               DSSERR("\t\tData type not recognized\n");
+       if (err & (1 << 12))
+               DSSERR("\t\tInvalid VC ID\n");
+       if (err & (1 << 13))
+               DSSERR("\t\tInvalid Transmission Length\n");
+       if (err & (1 << 14))
+               DSSERR("\t\t(reserved14)\n");
+       if (err & (1 << 15))
+               DSSERR("\t\tDSI Protocol Violation\n");
+}
+
+static u16 dsi_vc_flush_receive_data(int channel)
+{
+       /* RX_FIFO_NOT_EMPTY */
+       while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+               u32 val;
+               u8 dt;
+               val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+               DSSDBG("\trawval %#08x\n", val);
+               dt = FLD_GET(val, 5, 0);
+               if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+                       u16 err = FLD_GET(val, 23, 8);
+                       dsi_show_rx_ack_with_err(err);
+               } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+                       DSSDBG("\tDCS short response, 1 byte: %#x\n",
+                                       FLD_GET(val, 23, 8));
+               } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+                       DSSDBG("\tDCS short response, 2 byte: %#x\n",
+                                       FLD_GET(val, 23, 8));
+               } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+                       DSSDBG("\tDCS long response, len %d\n",
+                                       FLD_GET(val, 23, 8));
+                       dsi_vc_flush_long_data(channel);
+               } else {
+                       DSSERR("\tunknown datatype 0x%02x\n", dt);
+               }
+       }
+       return 0;
+}
+
+static int dsi_vc_send_bta(int channel)
+{
+       if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO &&
+                       (dsi.debug_write || dsi.debug_read))
+               DSSDBG("dsi_vc_send_bta %d\n", channel);
+
+       WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+       if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {    /* RX_FIFO_NOT_EMPTY */
+               DSSERR("rx fifo not empty when sending BTA, dumping data:\n");
+               dsi_vc_flush_receive_data(channel);
+       }
+
+       REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */
+
+       return 0;
+}
+
+int dsi_vc_send_bta_sync(int channel)
+{
+       int r = 0;
+       u32 err;
+
+       INIT_COMPLETION(dsi.bta_completion);
+
+       dsi_vc_enable_bta_irq(channel);
+
+       r = dsi_vc_send_bta(channel);
+       if (r)
+               goto err;
+
+       if (wait_for_completion_timeout(&dsi.bta_completion,
+                               msecs_to_jiffies(500)) == 0) {
+               DSSERR("Failed to receive BTA\n");
+               r = -EIO;
+               goto err;
+       }
+
+       err = dsi_get_errors();
+       if (err) {
+               DSSERR("Error while sending BTA: %x\n", err);
+               r = -EIO;
+               goto err;
+       }
+err:
+       dsi_vc_disable_bta_irq(channel);
+
+       return r;
+}
+EXPORT_SYMBOL(dsi_vc_send_bta_sync);
+
+static inline void dsi_vc_write_long_header(int channel, u8 data_type,
+               u16 len, u8 ecc)
+{
+       u32 val;
+       u8 data_id;
+
+       WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+       /*data_id = data_type | channel << 6; */
+       data_id = data_type | dsi.vc[channel].dest_per << 6;
+
+       val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) |
+               FLD_VAL(ecc, 31, 24);
+
+       dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val);
+}
+
+static inline void dsi_vc_write_long_payload(int channel,
+               u8 b1, u8 b2, u8 b3, u8 b4)
+{
+       u32 val;
+
+       val = b4 << 24 | b3 << 16 | b2 << 8  | b1 << 0;
+
+/*     DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n",
+                       b1, b2, b3, b4, val); */
+
+       dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val);
+}
+
+static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len,
+               u8 ecc)
+{
+       /*u32 val; */
+       int i;
+       u8 *p;
+       int r = 0;
+       u8 b1, b2, b3, b4;
+
+       if (dsi.debug_write)
+               DSSDBG("dsi_vc_send_long, %d bytes\n", len);
+
+       /* len + header */
+       if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) {
+               DSSERR("unable to send long packet: packet too long.\n");
+               return -EINVAL;
+       }
+
+       dsi_vc_config_l4(channel);
+
+       dsi_vc_write_long_header(channel, data_type, len, ecc);
+
+       /*dsi_vc_print_status(0); */
+
+       p = data;
+       for (i = 0; i < len >> 2; i++) {
+               if (dsi.debug_write)
+                       DSSDBG("\tsending full packet %d\n", i);
+               /*dsi_vc_print_status(0); */
+
+               b1 = *p++;
+               b2 = *p++;
+               b3 = *p++;
+               b4 = *p++;
+
+               dsi_vc_write_long_payload(channel, b1, b2, b3, b4);
+       }
+
+       i = len % 4;
+       if (i) {
+               b1 = 0; b2 = 0; b3 = 0;
+
+               if (dsi.debug_write)
+                       DSSDBG("\tsending remainder bytes %d\n", i);
+
+               switch (i) {
+               case 3:
+                       b1 = *p++;
+                       b2 = *p++;
+                       b3 = *p++;
+                       break;
+               case 2:
+                       b1 = *p++;
+                       b2 = *p++;
+                       break;
+               case 1:
+                       b1 = *p++;
+                       break;
+               }
+
+               dsi_vc_write_long_payload(channel, b1, b2, b3, 0);
+       }
+
+       return r;
+}
+
+static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc)
+{
+       u32 r;
+       u8 data_id;
+
+       WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+       if (dsi.debug_write)
+               DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n",
+                               channel,
+                               data_type, data & 0xff, (data >> 8) & 0xff);
+
+       dsi_vc_config_l4(channel);
+
+       if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) {
+               DSSERR("ERROR FIFO FULL, aborting transfer\n");
+               return -EINVAL;
+       }
+
+       data_id = data_type | channel << 6;
+
+       r = (data_id << 0) | (data << 8) | (ecc << 24);
+
+       dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r);
+
+       return 0;
+}
+
+int dsi_vc_send_null(int channel)
+{
+       u8 nullpkg[] = {0, 0, 0, 0};
+       return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0);
+}
+EXPORT_SYMBOL(dsi_vc_send_null);
+
+int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len)
+{
+       int r;
+
+       BUG_ON(len == 0);
+
+       if (len == 1) {
+               r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0,
+                               data[0], 0);
+       } else if (len == 2) {
+               r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1,
+                               data[0] | (data[1] << 8), 0);
+       } else {
+               /* 0x39 = DCS Long Write */
+               r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE,
+                               data, len, 0);
+       }
+
+       return r;
+}
+EXPORT_SYMBOL(dsi_vc_dcs_write_nosync);
+
+int dsi_vc_dcs_write(int channel, u8 *data, int len)
+{
+       int r;
+
+       r = dsi_vc_dcs_write_nosync(channel, data, len);
+       if (r)
+               return r;
+
+       r = dsi_vc_send_bta_sync(channel);
+
+       return r;
+}
+EXPORT_SYMBOL(dsi_vc_dcs_write);
+
+int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen)
+{
+       u32 val;
+       u8 dt;
+       int r;
+
+       if (dsi.debug_read)
+               DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %u)\n", channel, dcs_cmd);
+
+       r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0);
+       if (r)
+               return r;
+
+       r = dsi_vc_send_bta_sync(channel);
+       if (r)
+               return r;
+
+       /* RX_FIFO_NOT_EMPTY */
+       if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) {
+               DSSERR("RX fifo empty when trying to read.\n");
+               return -EIO;
+       }
+
+       val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+       if (dsi.debug_read)
+               DSSDBG("\theader: %08x\n", val);
+       dt = FLD_GET(val, 5, 0);
+       if (dt == DSI_DT_RX_ACK_WITH_ERR) {
+               u16 err = FLD_GET(val, 23, 8);
+               dsi_show_rx_ack_with_err(err);
+               return -EIO;
+
+       } else if (dt == DSI_DT_RX_SHORT_READ_1) {
+               u8 data = FLD_GET(val, 15, 8);
+               if (dsi.debug_read)
+                       DSSDBG("\tDCS short response, 1 byte: %02x\n", data);
+
+               if (buflen < 1)
+                       return -EIO;
+
+               buf[0] = data;
+
+               return 1;
+       } else if (dt == DSI_DT_RX_SHORT_READ_2) {
+               u16 data = FLD_GET(val, 23, 8);
+               if (dsi.debug_read)
+                       DSSDBG("\tDCS short response, 2 byte: %04x\n", data);
+
+               if (buflen < 2)
+                       return -EIO;
+
+               buf[0] = data & 0xff;
+               buf[1] = (data >> 8) & 0xff;
+
+               return 2;
+       } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+               int w;
+               int len = FLD_GET(val, 23, 8);
+               if (dsi.debug_read)
+                       DSSDBG("\tDCS long response, len %d\n", len);
+
+               if (len > buflen)
+                       return -EIO;
+
+               /* two byte checksum ends the packet, not included in len */
+               for (w = 0; w < len + 2;) {
+                       int b;
+                       val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel));
+                       if (dsi.debug_read)
+                               DSSDBG("\t\t%02x %02x %02x %02x\n",
+                                               (val >> 0) & 0xff,
+                                               (val >> 8) & 0xff,
+                                               (val >> 16) & 0xff,
+                                               (val >> 24) & 0xff);
+
+                       for (b = 0; b < 4; ++b) {
+                               if (w < len)
+                                       buf[w] = (val >> (b * 8)) & 0xff;
+                               /* we discard the 2 byte checksum */
+                               ++w;
+                       }
+               }
+
+               return len;
+
+       } else {
+               DSSERR("\tunknown datatype 0x%02x\n", dt);
+               return -EIO;
+       }
+}
+EXPORT_SYMBOL(dsi_vc_dcs_read);
+
+
+int dsi_vc_set_max_rx_packet_size(int channel, u16 len)
+{
+       int r;
+       r = dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE,
+                       len, 0);
+
+       if (r)
+               return r;
+
+       r = dsi_vc_send_bta_sync(channel);
+
+       return r;
+}
+EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size);
+
+static void dsi_set_lp_rx_timeout(unsigned long ns)
+{
+       u32 r;
+       unsigned x4, x16;
+       unsigned long fck;
+       unsigned long ticks;
+
+       /* ticks in DSI_FCK */
+
+       fck = dsi_fclk_rate();
+       ticks = (fck / 1000 / 1000) * ns / 1000;
+       x4 = 0;
+       x16 = 0;
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
+               x4 = 1;
+               x16 = 0;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+               x4 = 0;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
+               x4 = 1;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               DSSWARN("LP_TX_TO over limit, setting it to max\n");
+               ticks = 0x1fff;
+               x4 = 1;
+               x16 = 1;
+       }
+
+       r = dsi_read_reg(DSI_TIMING2);
+       r = FLD_MOD(r, 1, 15, 15);      /* LP_RX_TO */
+       r = FLD_MOD(r, x16, 14, 14);    /* LP_RX_TO_X16 */
+       r = FLD_MOD(r, x4, 13, 13);     /* LP_RX_TO_X4 */
+       r = FLD_MOD(r, ticks, 12, 0);   /* LP_RX_COUNTER */
+       dsi_write_reg(DSI_TIMING2, r);
+
+       DSSDBG("LP_RX_TO %lu ns (%#lx ticks%s%s)\n",
+                       (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+                       (fck / 1000 / 1000),
+                       ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+}
+
+static void dsi_set_ta_timeout(unsigned long ns)
+{
+       u32 r;
+       unsigned x8, x16;
+       unsigned long fck;
+       unsigned long ticks;
+
+       /* ticks in DSI_FCK */
+       fck = dsi_fclk_rate();
+       ticks = (fck / 1000 / 1000) * ns / 1000;
+       x8 = 0;
+       x16 = 0;
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 8;
+               x8 = 1;
+               x16 = 0;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+               x8 = 0;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / (8 * 16);
+               x8 = 1;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               DSSWARN("TA_TO over limit, setting it to max\n");
+               ticks = 0x1fff;
+               x8 = 1;
+               x16 = 1;
+       }
+
+       r = dsi_read_reg(DSI_TIMING1);
+       r = FLD_MOD(r, 1, 31, 31);      /* TA_TO */
+       r = FLD_MOD(r, x16, 30, 30);    /* TA_TO_X16 */
+       r = FLD_MOD(r, x8, 29, 29);     /* TA_TO_X8 */
+       r = FLD_MOD(r, ticks, 28, 16);  /* TA_TO_COUNTER */
+       dsi_write_reg(DSI_TIMING1, r);
+
+       DSSDBG("TA_TO %lu ns (%#lx ticks%s%s)\n",
+                       (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) /
+                       (fck / 1000 / 1000),
+                       ticks, x8 ? " x8" : "", x16 ? " x16" : "");
+}
+
+static void dsi_set_stop_state_counter(unsigned long ns)
+{
+       u32 r;
+       unsigned x4, x16;
+       unsigned long fck;
+       unsigned long ticks;
+
+       /* ticks in DSI_FCK */
+
+       fck = dsi_fclk_rate();
+       ticks = (fck / 1000 / 1000) * ns / 1000;
+       x4 = 0;
+       x16 = 0;
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
+               x4 = 1;
+               x16 = 0;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+               x4 = 0;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
+               x4 = 1;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               DSSWARN("STOP_STATE_COUNTER_IO over limit, "
+                               "setting it to max\n");
+               ticks = 0x1fff;
+               x4 = 1;
+               x16 = 1;
+       }
+
+       r = dsi_read_reg(DSI_TIMING1);
+       r = FLD_MOD(r, 1, 15, 15);      /* FORCE_TX_STOP_MODE_IO */
+       r = FLD_MOD(r, x16, 14, 14);    /* STOP_STATE_X16_IO */
+       r = FLD_MOD(r, x4, 13, 13);     /* STOP_STATE_X4_IO */
+       r = FLD_MOD(r, ticks, 12, 0);   /* STOP_STATE_COUNTER_IO */
+       dsi_write_reg(DSI_TIMING1, r);
+
+       DSSDBG("STOP_STATE_COUNTER %lu ns (%#lx ticks%s%s)\n",
+                       (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+                       (fck / 1000 / 1000),
+                       ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+}
+
+static void dsi_set_hs_tx_timeout(unsigned long ns)
+{
+       u32 r;
+       unsigned x4, x16;
+       unsigned long fck;
+       unsigned long ticks;
+
+       /* ticks in TxByteClkHS */
+
+       fck = dsi_get_txbyteclkhs();
+       ticks = (fck / 1000 / 1000) * ns / 1000;
+       x4 = 0;
+       x16 = 0;
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 4;
+               x4 = 1;
+               x16 = 0;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / 16;
+               x4 = 0;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               ticks = (fck / 1000 / 1000) * ns / 1000 / (4 * 16);
+               x4 = 1;
+               x16 = 1;
+       }
+
+       if (ticks > 0x1fff) {
+               DSSWARN("HS_TX_TO over limit, setting it to max\n");
+               ticks = 0x1fff;
+               x4 = 1;
+               x16 = 1;
+       }
+
+       r = dsi_read_reg(DSI_TIMING2);
+       r = FLD_MOD(r, 1, 31, 31);      /* HS_TX_TO */
+       r = FLD_MOD(r, x16, 30, 30);    /* HS_TX_TO_X16 */
+       r = FLD_MOD(r, x4, 29, 29);     /* HS_TX_TO_X8 (4 really) */
+       r = FLD_MOD(r, ticks, 28, 16);  /* HS_TX_TO_COUNTER */
+       dsi_write_reg(DSI_TIMING2, r);
+
+       DSSDBG("HS_TX_TO %lu ns (%#lx ticks%s%s)\n",
+                       (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) /
+                       (fck / 1000 / 1000),
+                       ticks, x4 ? " x4" : "", x16 ? " x16" : "");
+}
+static int dsi_proto_config(struct omap_dss_device *dssdev)
+{
+       u32 r;
+       int buswidth = 0;
+
+       dsi_config_tx_fifo(DSI_FIFO_SIZE_128,
+                       DSI_FIFO_SIZE_0,
+                       DSI_FIFO_SIZE_0,
+                       DSI_FIFO_SIZE_0);
+
+       dsi_config_rx_fifo(DSI_FIFO_SIZE_128,
+                       DSI_FIFO_SIZE_0,
+                       DSI_FIFO_SIZE_0,
+                       DSI_FIFO_SIZE_0);
+
+       /* XXX what values for the timeouts? */
+       dsi_set_stop_state_counter(1000);
+       dsi_set_ta_timeout(6400000);
+       dsi_set_lp_rx_timeout(48000);
+       dsi_set_hs_tx_timeout(1000000);
+
+       switch (dssdev->ctrl.pixel_size) {
+       case 16:
+               buswidth = 0;
+               break;
+       case 18:
+               buswidth = 1;
+               break;
+       case 24:
+               buswidth = 2;
+               break;
+       default:
+               BUG();
+       }
+
+       r = dsi_read_reg(DSI_CTRL);
+       r = FLD_MOD(r, 1, 1, 1);        /* CS_RX_EN */
+       r = FLD_MOD(r, 1, 2, 2);        /* ECC_RX_EN */
+       r = FLD_MOD(r, 1, 3, 3);        /* TX_FIFO_ARBITRATION */
+       r = FLD_MOD(r, 1, 4, 4);        /* VP_CLK_RATIO, always 1, see errata*/
+       r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
+       r = FLD_MOD(r, 0, 8, 8);        /* VP_CLK_POL */
+       r = FLD_MOD(r, 2, 13, 12);      /* LINE_BUFFER, 2 lines */
+       r = FLD_MOD(r, 1, 14, 14);      /* TRIGGER_RESET_MODE */
+       r = FLD_MOD(r, 1, 19, 19);      /* EOT_ENABLE */
+       r = FLD_MOD(r, 1, 24, 24);      /* DCS_CMD_ENABLE */
+       r = FLD_MOD(r, 0, 25, 25);      /* DCS_CMD_CODE, 1=start, 0=continue */
+
+       dsi_write_reg(DSI_CTRL, r);
+
+       dsi_vc_initial_config(0);
+
+       /* set all vc targets to peripheral 0 */
+       dsi.vc[0].dest_per = 0;
+       dsi.vc[1].dest_per = 0;
+       dsi.vc[2].dest_per = 0;
+       dsi.vc[3].dest_per = 0;
+
+       return 0;
+}
+
+static void dsi_proto_timings(struct omap_dss_device *dssdev)
+{
+       unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail;
+       unsigned tclk_pre, tclk_post;
+       unsigned ths_prepare, ths_prepare_ths_zero, ths_zero;
+       unsigned ths_trail, ths_exit;
+       unsigned ddr_clk_pre, ddr_clk_post;
+       unsigned enter_hs_mode_lat, exit_hs_mode_lat;
+       unsigned ths_eot;
+       u32 r;
+
+       r = dsi_read_reg(DSI_DSIPHY_CFG0);
+       ths_prepare = FLD_GET(r, 31, 24);
+       ths_prepare_ths_zero = FLD_GET(r, 23, 16);
+       ths_zero = ths_prepare_ths_zero - ths_prepare;
+       ths_trail = FLD_GET(r, 15, 8);
+       ths_exit = FLD_GET(r, 7, 0);
+
+       r = dsi_read_reg(DSI_DSIPHY_CFG1);
+       tlpx = FLD_GET(r, 22, 16) * 2;
+       tclk_trail = FLD_GET(r, 15, 8);
+       tclk_zero = FLD_GET(r, 7, 0);
+
+       r = dsi_read_reg(DSI_DSIPHY_CFG2);
+       tclk_prepare = FLD_GET(r, 7, 0);
+
+       /* min 8*UI */
+       tclk_pre = 20;
+       /* min 60ns + 52*UI */
+       tclk_post = ns2ddr(60) + 26;
+
+       /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */
+       if (dssdev->phy.dsi.data1_lane != 0 &&
+                       dssdev->phy.dsi.data2_lane != 0)
+               ths_eot = 2;
+       else
+               ths_eot = 4;
+
+       ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
+                       4);
+       ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
+
+       BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
+       BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
+
+       r = dsi_read_reg(DSI_CLK_TIMING);
+       r = FLD_MOD(r, ddr_clk_pre, 15, 8);
+       r = FLD_MOD(r, ddr_clk_post, 7, 0);
+       dsi_write_reg(DSI_CLK_TIMING, r);
+
+       DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n",
+                       ddr_clk_pre,
+                       ddr_clk_post);
+
+       enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) +
+               DIV_ROUND_UP(ths_prepare, 4) +
+               DIV_ROUND_UP(ths_zero + 3, 4);
+
+       exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot;
+
+       r = FLD_VAL(enter_hs_mode_lat, 31, 16) |
+               FLD_VAL(exit_hs_mode_lat, 15, 0);
+       dsi_write_reg(DSI_VM_TIMING7, r);
+
+       DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n",
+                       enter_hs_mode_lat, exit_hs_mode_lat);
+}
+
+
+#define DSI_DECL_VARS \
+       int __dsi_cb = 0; u32 __dsi_cv = 0;
+
+#define DSI_FLUSH(ch) \
+       if (__dsi_cb > 0) { \
+               /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \
+               dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \
+               __dsi_cb = __dsi_cv = 0; \
+       }
+
+#define DSI_PUSH(ch, data) \
+       do { \
+               __dsi_cv |= (data) << (__dsi_cb * 8); \
+               /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \
+               if (++__dsi_cb > 3) \
+                       DSI_FLUSH(ch); \
+       } while (0)
+
+static int dsi_update_screen_l4(struct omap_dss_device *dssdev,
+                       int x, int y, int w, int h)
+{
+       /* Note: supports only 24bit colors in 32bit container */
+       int first = 1;
+       int fifo_stalls = 0;
+       int max_dsi_packet_size;
+       int max_data_per_packet;
+       int max_pixels_per_packet;
+       int pixels_left;
+       int bytespp = dssdev->ctrl.pixel_size / 8;
+       int scr_width;
+       u32 __iomem *data;
+       int start_offset;
+       int horiz_inc;
+       int current_x;
+       struct omap_overlay *ovl;
+
+       debug_irq = 0;
+
+       DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n",
+                       x, y, w, h);
+
+       ovl = dssdev->manager->overlays[0];
+
+       if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U)
+               return -EINVAL;
+
+       if (dssdev->ctrl.pixel_size != 24)
+               return -EINVAL;
+
+       scr_width = ovl->info.screen_width;
+       data = ovl->info.vaddr;
+
+       start_offset = scr_width * y + x;
+       horiz_inc = scr_width - w;
+       current_x = x;
+
+       /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes
+        * in fifo */
+
+       /* When using CPU, max long packet size is TX buffer size */
+       max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4;
+
+       /* we seem to get better perf if we divide the tx fifo to half,
+          and while the other half is being sent, we fill the other half
+          max_dsi_packet_size /= 2; */
+
+       max_data_per_packet = max_dsi_packet_size - 4 - 1;
+
+       max_pixels_per_packet = max_data_per_packet / bytespp;
+
+       DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet);
+
+       pixels_left = w * h;
+
+       DSSDBG("total pixels %d\n", pixels_left);
+
+       data += start_offset;
+
+       while (pixels_left > 0) {
+               /* 0x2c = write_memory_start */
+               /* 0x3c = write_memory_continue */
+               u8 dcs_cmd = first ? 0x2c : 0x3c;
+               int pixels;
+               DSI_DECL_VARS;
+               first = 0;
+
+#if 1
+               /* using fifo not empty */
+               /* TX_FIFO_NOT_EMPTY */
+               while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) {
+                       udelay(1);
+                       fifo_stalls++;
+                       if (fifo_stalls > 0xfffff) {
+                               DSSERR("fifo stalls overflow, pixels left %d\n",
+                                               pixels_left);
+                               dsi_if_enable(0);
+                               return -EIO;
+                       }
+               }
+#elif 1
+               /* using fifo emptiness */
+               while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 <
+                               max_dsi_packet_size) {
+                       fifo_stalls++;
+                       if (fifo_stalls > 0xfffff) {
+                               DSSERR("fifo stalls overflow, pixels left %d\n",
+                                              pixels_left);
+                               dsi_if_enable(0);
+                               return -EIO;
+                       }
+               }
+#else
+               while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) {
+                       fifo_stalls++;
+                       if (fifo_stalls > 0xfffff) {
+                               DSSERR("fifo stalls overflow, pixels left %d\n",
+                                              pixels_left);
+                               dsi_if_enable(0);
+                               return -EIO;
+                       }
+               }
+#endif
+               pixels = min(max_pixels_per_packet, pixels_left);
+
+               pixels_left -= pixels;
+
+               dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE,
+                               1 + pixels * bytespp, 0);
+
+               DSI_PUSH(0, dcs_cmd);
+
+               while (pixels-- > 0) {
+                       u32 pix = __raw_readl(data++);
+
+                       DSI_PUSH(0, (pix >> 16) & 0xff);
+                       DSI_PUSH(0, (pix >> 8) & 0xff);
+                       DSI_PUSH(0, (pix >> 0) & 0xff);
+
+                       current_x++;
+                       if (current_x == x+w) {
+                               current_x = x;
+                               data += horiz_inc;
+                       }
+               }
+
+               DSI_FLUSH(0);
+       }
+
+       return 0;
+}
+
+static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
+               u16 x, u16 y, u16 w, u16 h)
+{
+       unsigned bytespp;
+       unsigned bytespl;
+       unsigned bytespf;
+       unsigned total_len;
+       unsigned packet_payload;
+       unsigned packet_len;
+       u32 l;
+       bool use_te_trigger;
+       const unsigned channel = 0;
+       /* line buffer is 1024 x 24bits */
+       /* XXX: for some reason using full buffer size causes considerable TX
+        * slowdown with update sizes that fill the whole buffer */
+       const unsigned line_buf_size = 1023 * 3;
+
+       use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
+
+       if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
+               DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
+                               x, y, w, h);
+
+       bytespp = dssdev->ctrl.pixel_size / 8;
+       bytespl = w * bytespp;
+       bytespf = bytespl * h;
+
+       /* NOTE: packet_payload has to be equal to N * bytespl, where N is
+        * number of lines in a packet.  See errata about VP_CLK_RATIO */
+
+       if (bytespf < line_buf_size)
+               packet_payload = bytespf;
+       else
+               packet_payload = (line_buf_size) / bytespl * bytespl;
+
+       packet_len = packet_payload + 1;        /* 1 byte for DCS cmd */
+       total_len = (bytespf / packet_payload) * packet_len;
+
+       if (bytespf % packet_payload)
+               total_len += (bytespf % packet_payload) + 1;
+
+       if (0)
+               dsi_vc_print_status(1);
+
+       l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */
+       dsi_write_reg(DSI_VC_TE(channel), l);
+
+       dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0);
+
+       if (use_te_trigger)
+               l = FLD_MOD(l, 1, 30, 30); /* TE_EN */
+       else
+               l = FLD_MOD(l, 1, 31, 31); /* TE_START */
+       dsi_write_reg(DSI_VC_TE(channel), l);
+
+       /* We put SIDLEMODE to no-idle for the duration of the transfer,
+        * because DSS interrupts are not capable of waking up the CPU and the
+        * framedone interrupt could be delayed for quite a long time. I think
+        * the same goes for any DSS interrupts, but for some reason I have not
+        * seen the problem anywhere else than here.
+        */
+       dispc_disable_sidle();
+
+       dss_start_update(dssdev);
+
+       if (use_te_trigger) {
+               /* disable LP_RX_TO, so that we can receive TE.  Time to wait
+                * for TE is longer than the timer allows */
+               REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */
+
+               dsi_vc_send_bta(channel);
+
+#ifdef DSI_CATCH_MISSING_TE
+               mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250));
+#endif
+       }
+}
+
+#ifdef DSI_CATCH_MISSING_TE
+static void dsi_te_timeout(unsigned long arg)
+{
+       DSSERR("TE not received for 250ms!\n");
+}
+#endif
+
+static void dsi_framedone_irq_callback(void *data, u32 mask)
+{
+       /* Note: We get FRAMEDONE when DISPC has finished sending pixels and
+        * turns itself off. However, DSI still has the pixels in its buffers,
+        * and is sending the data.
+        */
+
+       /* SIDLEMODE back to smart-idle */
+       dispc_enable_sidle();
+
+       dsi.framedone_received = true;
+       wake_up(&dsi.waitqueue);
+}
+
+static void dsi_set_update_region(struct omap_dss_device *dssdev,
+               u16 x, u16 y, u16 w, u16 h)
+{
+       spin_lock(&dsi.update_lock);
+       if (dsi.update_region.dirty) {
+               dsi.update_region.x = min(x, dsi.update_region.x);
+               dsi.update_region.y = min(y, dsi.update_region.y);
+               dsi.update_region.w = max(w, dsi.update_region.w);
+               dsi.update_region.h = max(h, dsi.update_region.h);
+       } else {
+               dsi.update_region.x = x;
+               dsi.update_region.y = y;
+               dsi.update_region.w = w;
+               dsi.update_region.h = h;
+       }
+
+       dsi.update_region.device = dssdev;
+       dsi.update_region.dirty = true;
+
+       spin_unlock(&dsi.update_lock);
+
+}
+
+static int dsi_set_update_mode(struct omap_dss_device *dssdev,
+               enum omap_dss_update_mode mode)
+{
+       int r = 0;
+       int i;
+
+       WARN_ON(!mutex_is_locked(&dsi.bus_lock));
+
+       if (dsi.update_mode != mode) {
+               dsi.update_mode = mode;
+
+               /* Mark the overlays dirty, and do apply(), so that we get the
+                * overlays configured properly after update mode change. */
+               for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+                       struct omap_overlay *ovl;
+                       ovl = omap_dss_get_overlay(i);
+                       if (ovl->manager == dssdev->manager)
+                               ovl->info_dirty = true;
+               }
+
+               r = dssdev->manager->apply(dssdev->manager);
+
+               if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE &&
+                               mode == OMAP_DSS_UPDATE_AUTO) {
+                       u16 w, h;
+
+                       DSSDBG("starting auto update\n");
+
+                       dssdev->get_resolution(dssdev, &w, &h);
+
+                       dsi_set_update_region(dssdev, 0, 0, w, h);
+
+                       dsi_perf_mark_start_auto();
+
+                       wake_up(&dsi.waitqueue);
+               }
+       }
+
+       return r;
+}
+
+static int dsi_set_te(struct omap_dss_device *dssdev, bool enable)
+{
+       int r;
+       r = dssdev->driver->enable_te(dssdev, enable);
+       /* XXX for some reason, DSI TE breaks if we don't wait here.
+        * Panel bug? Needs more studying */
+       msleep(100);
+       return r;
+}
+
+static void dsi_handle_framedone(void)
+{
+       int r;
+       const int channel = 0;
+       bool use_te_trigger;
+
+       use_te_trigger = dsi.te_enabled && !dsi.use_ext_te;
+
+       if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO)
+               DSSDBG("FRAMEDONE\n");
+
+       if (use_te_trigger) {
+               /* enable LP_RX_TO again after the TE */
+               REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
+       }
+
+       /* Send BTA after the frame. We need this for the TE to work, as TE
+        * trigger is only sent for BTAs without preceding packet. Thus we need
+        * to BTA after the pixel packets so that next BTA will cause TE
+        * trigger.
+        *
+        * This is not needed when TE is not in use, but we do it anyway to
+        * make sure that the transfer has been completed. It would be more
+        * optimal, but more complex, to wait only just before starting next
+        * transfer. */
+       r = dsi_vc_send_bta_sync(channel);
+       if (r)
+               DSSERR("BTA after framedone failed\n");
+
+       /* RX_FIFO_NOT_EMPTY */
+       if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) {
+               DSSERR("Received error during frame transfer:\n");
+               dsi_vc_flush_receive_data(0);
+       }
+
+#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
+       dispc_fake_vsync_irq();
+#endif
+}
+
+static int dsi_update_thread(void *data)
+{
+       unsigned long timeout;
+       struct omap_dss_device *device;
+       u16 x, y, w, h;
+
+       while (1) {
+               bool sched;
+
+               wait_event_interruptible(dsi.waitqueue,
+                               dsi.update_mode == OMAP_DSS_UPDATE_AUTO ||
+                               (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
+                                dsi.update_region.dirty == true) ||
+                               kthread_should_stop());
+
+               if (kthread_should_stop())
+                       break;
+
+               dsi_bus_lock();
+
+               if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED ||
+                               kthread_should_stop()) {
+                       dsi_bus_unlock();
+                       break;
+               }
+
+               dsi_perf_mark_setup();
+
+               if (dsi.update_region.dirty) {
+                       spin_lock(&dsi.update_lock);
+                       dsi.active_update_region = dsi.update_region;
+                       dsi.update_region.dirty = false;
+                       spin_unlock(&dsi.update_lock);
+               }
+
+               device = dsi.active_update_region.device;
+               x = dsi.active_update_region.x;
+               y = dsi.active_update_region.y;
+               w = dsi.active_update_region.w;
+               h = dsi.active_update_region.h;
+
+               if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+
+                       if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL)
+                               dss_setup_partial_planes(device,
+                                               &x, &y, &w, &h);
+
+                       dispc_set_lcd_size(w, h);
+               }
+
+               if (dsi.active_update_region.dirty) {
+                       dsi.active_update_region.dirty = false;
+                       /* XXX TODO we don't need to send the coords, if they
+                        * are the same that are already programmed to the
+                        * panel. That should speed up manual update a bit */
+                       device->driver->setup_update(device, x, y, w, h);
+               }
+
+               dsi_perf_mark_start();
+
+               if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+                       dsi_vc_config_vp(0);
+
+                       if (dsi.te_enabled && dsi.use_ext_te)
+                               device->driver->wait_for_te(device);
+
+                       dsi.framedone_received = false;
+
+                       dsi_update_screen_dispc(device, x, y, w, h);
+
+                       /* wait for framedone */
+                       timeout = msecs_to_jiffies(1000);
+                       wait_event_timeout(dsi.waitqueue,
+                                       dsi.framedone_received == true,
+                                       timeout);
+
+                       if (!dsi.framedone_received) {
+                               DSSERR("framedone timeout\n");
+                               DSSERR("failed update %d,%d %dx%d\n",
+                                               x, y, w, h);
+
+                               dispc_enable_sidle();
+                               dispc_enable_lcd_out(0);
+
+                               dsi_reset_tx_fifo(0);
+                       } else {
+                               dsi_handle_framedone();
+                               dsi_perf_show("DISPC");
+                       }
+               } else {
+                       dsi_update_screen_l4(device, x, y, w, h);
+                       dsi_perf_show("L4");
+               }
+
+               sched = atomic_read(&dsi.bus_lock.count) < 0;
+
+               complete_all(&dsi.update_completion);
+
+               dsi_bus_unlock();
+
+               /* XXX We need to give others chance to get the bus lock. Is
+                * there a better way for this? */
+               if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched)
+                       schedule_timeout_interruptible(1);
+       }
+
+       DSSDBG("update thread exiting\n");
+
+       return 0;
+}
+
+
+
+/* Display funcs */
+
+static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL,
+                       DISPC_IRQ_FRAMEDONE);
+       if (r) {
+               DSSERR("can't get FRAMEDONE irq\n");
+               return r;
+       }
+
+       dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+
+       dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI);
+       dispc_enable_fifohandcheck(1);
+
+       dispc_set_tft_data_lines(dssdev->ctrl.pixel_size);
+
+       {
+               struct omap_video_timings timings = {
+                       .hsw            = 1,
+                       .hfp            = 1,
+                       .hbp            = 1,
+                       .vsw            = 1,
+                       .vfp            = 0,
+                       .vbp            = 0,
+               };
+
+               dispc_set_lcd_timings(&timings);
+       }
+
+       return 0;
+}
+
+static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
+{
+       omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL,
+                       DISPC_IRQ_FRAMEDONE);
+}
+
+static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
+{
+       struct dsi_clock_info cinfo;
+       int r;
+
+       /* we always use DSS2_FCK as input clock */
+       cinfo.use_dss2_fck = true;
+       cinfo.regn  = dssdev->phy.dsi.div.regn;
+       cinfo.regm  = dssdev->phy.dsi.div.regm;
+       cinfo.regm3 = dssdev->phy.dsi.div.regm3;
+       cinfo.regm4 = dssdev->phy.dsi.div.regm4;
+       r = dsi_calc_clock_rates(&cinfo);
+       if (r)
+               return r;
+
+       r = dsi_pll_set_clock_div(&cinfo);
+       if (r) {
+               DSSERR("Failed to set dsi clocks\n");
+               return r;
+       }
+
+       return 0;
+}
+
+static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
+{
+       struct dispc_clock_info dispc_cinfo;
+       int r;
+       unsigned long long fck;
+
+       fck = dsi_get_dsi1_pll_rate();
+
+       dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div;
+       dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div;
+
+       r = dispc_calc_clock_rates(fck, &dispc_cinfo);
+       if (r) {
+               DSSERR("Failed to calc dispc clocks\n");
+               return r;
+       }
+
+       r = dispc_set_clock_div(&dispc_cinfo);
+       if (r) {
+               DSSERR("Failed to set dispc clocks\n");
+               return r;
+       }
+
+       return 0;
+}
+
+static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       _dsi_print_reset_status();
+
+       r = dsi_pll_init(dssdev, true, true);
+       if (r)
+               goto err0;
+
+       r = dsi_configure_dsi_clocks(dssdev);
+       if (r)
+               goto err1;
+
+       dss_select_clk_source(true, true);
+
+       DSSDBG("PLL OK\n");
+
+       r = dsi_configure_dispc_clocks(dssdev);
+       if (r)
+               goto err2;
+
+       r = dsi_complexio_init(dssdev);
+       if (r)
+               goto err2;
+
+       _dsi_print_reset_status();
+
+       dsi_proto_timings(dssdev);
+       dsi_set_lp_clk_divisor(dssdev);
+
+       if (1)
+               _dsi_print_reset_status();
+
+       r = dsi_proto_config(dssdev);
+       if (r)
+               goto err3;
+
+       /* enable interface */
+       dsi_vc_enable(0, 1);
+       dsi_if_enable(1);
+       dsi_force_tx_stop_mode_io();
+
+       if (dssdev->driver->enable) {
+               r = dssdev->driver->enable(dssdev);
+               if (r)
+                       goto err4;
+       }
+
+       /* enable high-speed after initial config */
+       dsi_vc_enable_hs(0, 1);
+
+       return 0;
+err4:
+       dsi_if_enable(0);
+err3:
+       dsi_complexio_uninit();
+err2:
+       dss_select_clk_source(false, false);
+err1:
+       dsi_pll_uninit();
+err0:
+       return r;
+}
+
+static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev)
+{
+       if (dssdev->driver->disable)
+               dssdev->driver->disable(dssdev);
+
+       dss_select_clk_source(false, false);
+       dsi_complexio_uninit();
+       dsi_pll_uninit();
+}
+
+static int dsi_core_init(void)
+{
+       /* Autoidle */
+       REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0);
+
+       /* ENWAKEUP */
+       REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2);
+
+       /* SIDLEMODE smart-idle */
+       REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3);
+
+       _dsi_initialize_irq();
+
+       return 0;
+}
+
+static int dsi_display_enable(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       DSSDBG("dsi_display_enable\n");
+
+       mutex_lock(&dsi.lock);
+       dsi_bus_lock();
+
+       r = omap_dss_start_device(dssdev);
+       if (r) {
+               DSSERR("failed to start device\n");
+               goto err0;
+       }
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+               DSSERR("dssdev already enabled\n");
+               r = -EINVAL;
+               goto err1;
+       }
+
+       enable_clocks(1);
+       dsi_enable_pll_clock(1);
+
+       r = _dsi_reset();
+       if (r)
+               goto err2;
+
+       dsi_core_init();
+
+       r = dsi_display_init_dispc(dssdev);
+       if (r)
+               goto err2;
+
+       r = dsi_display_init_dsi(dssdev);
+       if (r)
+               goto err3;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       dsi.use_ext_te = dssdev->phy.dsi.ext_te;
+       r = dsi_set_te(dssdev, dsi.te_enabled);
+       if (r)
+               goto err4;
+
+       dsi_set_update_mode(dssdev, dsi.user_update_mode);
+
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+
+       return 0;
+
+err4:
+
+       dsi_display_uninit_dsi(dssdev);
+err3:
+       dsi_display_uninit_dispc(dssdev);
+err2:
+       enable_clocks(0);
+       dsi_enable_pll_clock(0);
+err1:
+       omap_dss_stop_device(dssdev);
+err0:
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+       DSSDBG("dsi_display_enable FAILED\n");
+       return r;
+}
+
+static void dsi_display_disable(struct omap_dss_device *dssdev)
+{
+       DSSDBG("dsi_display_disable\n");
+
+       mutex_lock(&dsi.lock);
+       dsi_bus_lock();
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+                       dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+               goto end;
+
+       dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+       dsi_display_uninit_dispc(dssdev);
+
+       dsi_display_uninit_dsi(dssdev);
+
+       enable_clocks(0);
+       dsi_enable_pll_clock(0);
+
+       omap_dss_stop_device(dssdev);
+end:
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+}
+
+static int dsi_display_suspend(struct omap_dss_device *dssdev)
+{
+       DSSDBG("dsi_display_suspend\n");
+
+       mutex_lock(&dsi.lock);
+       dsi_bus_lock();
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
+                       dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+               goto end;
+
+       dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+       dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+       dsi_display_uninit_dispc(dssdev);
+
+       dsi_display_uninit_dsi(dssdev);
+
+       enable_clocks(0);
+       dsi_enable_pll_clock(0);
+end:
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+
+       return 0;
+}
+
+static int dsi_display_resume(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       DSSDBG("dsi_display_resume\n");
+
+       mutex_lock(&dsi.lock);
+       dsi_bus_lock();
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+               DSSERR("dssdev not suspended\n");
+               r = -EINVAL;
+               goto err0;
+       }
+
+       enable_clocks(1);
+       dsi_enable_pll_clock(1);
+
+       r = _dsi_reset();
+       if (r)
+               goto err1;
+
+       dsi_core_init();
+
+       r = dsi_display_init_dispc(dssdev);
+       if (r)
+               goto err1;
+
+       r = dsi_display_init_dsi(dssdev);
+       if (r)
+               goto err2;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       r = dsi_set_te(dssdev, dsi.te_enabled);
+       if (r)
+               goto err2;
+
+       dsi_set_update_mode(dssdev, dsi.user_update_mode);
+
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+
+       return 0;
+
+err2:
+       dsi_display_uninit_dispc(dssdev);
+err1:
+       enable_clocks(0);
+       dsi_enable_pll_clock(0);
+err0:
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+       DSSDBG("dsi_display_resume FAILED\n");
+       return r;
+}
+
+static int dsi_display_update(struct omap_dss_device *dssdev,
+                       u16 x, u16 y, u16 w, u16 h)
+{
+       int r = 0;
+       u16 dw, dh;
+
+       DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h);
+
+       mutex_lock(&dsi.lock);
+
+       if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL)
+               goto end;
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               goto end;
+
+       dssdev->get_resolution(dssdev, &dw, &dh);
+
+       if  (x > dw || y > dh)
+               goto end;
+
+       if (x + w > dw)
+               w = dw - x;
+
+       if (y + h > dh)
+               h = dh - y;
+
+       if (w == 0 || h == 0)
+               goto end;
+
+       if (w == 1) {
+               r = -EINVAL;
+               goto end;
+       }
+
+       dsi_set_update_region(dssdev, x, y, w, h);
+
+       wake_up(&dsi.waitqueue);
+
+end:
+       mutex_unlock(&dsi.lock);
+
+       return r;
+}
+
+static int dsi_display_sync(struct omap_dss_device *dssdev)
+{
+       bool wait;
+
+       DSSDBG("dsi_display_sync()\n");
+
+       mutex_lock(&dsi.lock);
+       dsi_bus_lock();
+
+       if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL &&
+                       dsi.update_region.dirty) {
+               INIT_COMPLETION(dsi.update_completion);
+               wait = true;
+       } else {
+               wait = false;
+       }
+
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+
+       if (wait)
+               wait_for_completion_interruptible(&dsi.update_completion);
+
+       DSSDBG("dsi_display_sync() done\n");
+       return 0;
+}
+
+static int dsi_display_set_update_mode(struct omap_dss_device *dssdev,
+               enum omap_dss_update_mode mode)
+{
+       int r = 0;
+
+       DSSDBGF("%d", mode);
+
+       mutex_lock(&dsi.lock);
+       dsi_bus_lock();
+
+       dsi.user_update_mode = mode;
+       r = dsi_set_update_mode(dssdev, mode);
+
+       dsi_bus_unlock();
+       mutex_unlock(&dsi.lock);
+
+       return r;
+}
+
+static enum omap_dss_update_mode dsi_display_get_update_mode(
+               struct omap_dss_device *dssdev)
+{
+       return dsi.update_mode;
+}
+
+
+static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+       int r = 0;
+
+       DSSDBGF("%d", enable);
+
+       if (!dssdev->driver->enable_te)
+               return -ENOENT;
+
+       dsi_bus_lock();
+
+       dsi.te_enabled = enable;
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               goto end;
+
+       r = dsi_set_te(dssdev, enable);
+end:
+       dsi_bus_unlock();
+
+       return r;
+}
+
+static int dsi_display_get_te(struct omap_dss_device *dssdev)
+{
+       return dsi.te_enabled;
+}
+
+static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate)
+{
+
+       DSSDBGF("%d", rotate);
+
+       if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
+               return -EINVAL;
+
+       dsi_bus_lock();
+       dssdev->driver->set_rotate(dssdev, rotate);
+       if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) {
+               u16 w, h;
+               /* the display dimensions may have changed, so set a new
+                * update region */
+               dssdev->get_resolution(dssdev, &w, &h);
+               dsi_set_update_region(dssdev, 0, 0, w, h);
+       }
+       dsi_bus_unlock();
+
+       return 0;
+}
+
+static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev)
+{
+       if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
+               return 0;
+
+       return dssdev->driver->get_rotate(dssdev);
+}
+
+static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror)
+{
+       DSSDBGF("%d", mirror);
+
+       if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
+               return -EINVAL;
+
+       dsi_bus_lock();
+       dssdev->driver->set_mirror(dssdev, mirror);
+       dsi_bus_unlock();
+
+       return 0;
+}
+
+static bool dsi_display_get_mirror(struct omap_dss_device *dssdev)
+{
+       if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
+               return 0;
+
+       return dssdev->driver->get_mirror(dssdev);
+}
+
+static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num)
+{
+       int r;
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return -EIO;
+
+       DSSDBGF("%d", test_num);
+
+       dsi_bus_lock();
+
+       /* run test first in low speed mode */
+       dsi_vc_enable_hs(0, 0);
+
+       if (dssdev->driver->run_test) {
+               r = dssdev->driver->run_test(dssdev, test_num);
+               if (r)
+                       goto end;
+       }
+
+       /* then in high speed */
+       dsi_vc_enable_hs(0, 1);
+
+       if (dssdev->driver->run_test) {
+               r = dssdev->driver->run_test(dssdev, test_num);
+               if (r)
+                       goto end;
+       }
+
+end:
+       dsi_vc_enable_hs(0, 1);
+
+       dsi_bus_unlock();
+
+       return r;
+}
+
+static int dsi_display_memory_read(struct omap_dss_device *dssdev,
+               void *buf, size_t size,
+               u16 x, u16 y, u16 w, u16 h)
+{
+       int r;
+
+       DSSDBGF("");
+
+       if (!dssdev->driver->memory_read)
+               return -EINVAL;
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return -EIO;
+
+       dsi_bus_lock();
+
+       r = dssdev->driver->memory_read(dssdev, buf, size,
+                       x, y, w, h);
+
+       /* Memory read usually changes the update area. This will
+        * force the next update to re-set the update area */
+       dsi.active_update_region.dirty = true;
+
+       dsi_bus_unlock();
+
+       return r;
+}
+
+void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
+               u32 fifo_size, enum omap_burst_size *burst_size,
+               u32 *fifo_low, u32 *fifo_high)
+{
+       unsigned burst_size_bytes;
+
+       *burst_size = OMAP_DSS_BURST_16x32;
+       burst_size_bytes = 16 * 32 / 8;
+
+       *fifo_high = fifo_size - burst_size_bytes;
+       *fifo_low = fifo_size - burst_size_bytes * 8;
+}
+
+int dsi_init_display(struct omap_dss_device *dssdev)
+{
+       DSSDBG("DSI init\n");
+
+       dssdev->enable = dsi_display_enable;
+       dssdev->disable = dsi_display_disable;
+       dssdev->suspend = dsi_display_suspend;
+       dssdev->resume = dsi_display_resume;
+       dssdev->update = dsi_display_update;
+       dssdev->sync = dsi_display_sync;
+       dssdev->set_update_mode = dsi_display_set_update_mode;
+       dssdev->get_update_mode = dsi_display_get_update_mode;
+       dssdev->enable_te = dsi_display_enable_te;
+       dssdev->get_te = dsi_display_get_te;
+
+       dssdev->get_rotate = dsi_display_get_rotate;
+       dssdev->set_rotate = dsi_display_set_rotate;
+
+       dssdev->get_mirror = dsi_display_get_mirror;
+       dssdev->set_mirror = dsi_display_set_mirror;
+
+       dssdev->run_test = dsi_display_run_test;
+       dssdev->memory_read = dsi_display_memory_read;
+
+       /* XXX these should be figured out dynamically */
+       dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
+               OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+
+       dsi.vc[0].dssdev = dssdev;
+       dsi.vc[1].dssdev = dssdev;
+
+       return 0;
+}
+
+int dsi_init(struct platform_device *pdev)
+{
+       u32 rev;
+       int r;
+       struct sched_param param = {
+               .sched_priority = MAX_USER_RT_PRIO-1
+       };
+
+       spin_lock_init(&dsi.errors_lock);
+       dsi.errors = 0;
+
+       init_completion(&dsi.bta_completion);
+       init_completion(&dsi.update_completion);
+
+       dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi");
+       if (IS_ERR(dsi.thread)) {
+               DSSERR("cannot create kthread\n");
+               r = PTR_ERR(dsi.thread);
+               goto err0;
+       }
+       sched_setscheduler(dsi.thread, SCHED_FIFO, &param);
+
+       init_waitqueue_head(&dsi.waitqueue);
+       spin_lock_init(&dsi.update_lock);
+
+       mutex_init(&dsi.lock);
+       mutex_init(&dsi.bus_lock);
+
+#ifdef DSI_CATCH_MISSING_TE
+       init_timer(&dsi.te_timer);
+       dsi.te_timer.function = dsi_te_timeout;
+       dsi.te_timer.data = 0;
+#endif
+
+       dsi.update_mode = OMAP_DSS_UPDATE_DISABLED;
+       dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED;
+
+       dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS);
+       if (!dsi.base) {
+               DSSERR("can't ioremap DSI\n");
+               r = -ENOMEM;
+               goto err1;
+       }
+
+       dsi.vdds_dsi_reg = regulator_get(&pdev->dev, "vdds_dsi");
+       if (IS_ERR(dsi.vdds_dsi_reg)) {
+               iounmap(dsi.base);
+               DSSERR("can't get VDDS_DSI regulator\n");
+               r = PTR_ERR(dsi.vdds_dsi_reg);
+               goto err2;
+       }
+
+       enable_clocks(1);
+
+       rev = dsi_read_reg(DSI_REVISION);
+       printk(KERN_INFO "OMAP DSI rev %d.%d\n",
+              FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+       enable_clocks(0);
+
+       wake_up_process(dsi.thread);
+
+       return 0;
+err2:
+       iounmap(dsi.base);
+err1:
+       kthread_stop(dsi.thread);
+err0:
+       return r;
+}
+
+void dsi_exit(void)
+{
+       kthread_stop(dsi.thread);
+
+       regulator_put(dsi.vdds_dsi_reg);
+
+       iounmap(dsi.base);
+
+       DSSDBG("omap_dsi_exit\n");
+}
+
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
new file mode 100644 (file)
index 0000000..9b05ee6
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * linux/drivers/video/omap2/dss/dss.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "DSS"
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+#include <linux/clk.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+#define DSS_BASE                       0x48050000
+
+#define DSS_SZ_REGS                    SZ_512
+
+struct dss_reg {
+       u16 idx;
+};
+
+#define DSS_REG(idx)                   ((const struct dss_reg) { idx })
+
+#define DSS_REVISION                   DSS_REG(0x0000)
+#define DSS_SYSCONFIG                  DSS_REG(0x0010)
+#define DSS_SYSSTATUS                  DSS_REG(0x0014)
+#define DSS_IRQSTATUS                  DSS_REG(0x0018)
+#define DSS_CONTROL                    DSS_REG(0x0040)
+#define DSS_SDI_CONTROL                        DSS_REG(0x0044)
+#define DSS_PLL_CONTROL                        DSS_REG(0x0048)
+#define DSS_SDI_STATUS                 DSS_REG(0x005C)
+
+#define REG_GET(idx, start, end) \
+       FLD_GET(dss_read_reg(idx), start, end)
+
+#define REG_FLD_MOD(idx, val, start, end) \
+       dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end))
+
+static struct {
+       void __iomem    *base;
+
+       struct clk      *dpll4_m4_ck;
+
+       unsigned long   cache_req_pck;
+       unsigned long   cache_prate;
+       struct dss_clock_info cache_dss_cinfo;
+       struct dispc_clock_info cache_dispc_cinfo;
+
+       u32             ctx[DSS_SZ_REGS / sizeof(u32)];
+} dss;
+
+static int _omap_dss_wait_reset(void);
+
+static inline void dss_write_reg(const struct dss_reg idx, u32 val)
+{
+       __raw_writel(val, dss.base + idx.idx);
+}
+
+static inline u32 dss_read_reg(const struct dss_reg idx)
+{
+       return __raw_readl(dss.base + idx.idx);
+}
+
+#define SR(reg) \
+       dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg)
+#define RR(reg) \
+       dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
+
+void dss_save_context(void)
+{
+       if (cpu_is_omap24xx())
+               return;
+
+       SR(SYSCONFIG);
+       SR(CONTROL);
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+       SR(SDI_CONTROL);
+       SR(PLL_CONTROL);
+#endif
+}
+
+void dss_restore_context(void)
+{
+       if (_omap_dss_wait_reset())
+               DSSERR("DSS not coming out of reset after sleep\n");
+
+       RR(SYSCONFIG);
+       RR(CONTROL);
+
+#ifdef CONFIG_OMAP2_DSS_SDI
+       RR(SDI_CONTROL);
+       RR(PLL_CONTROL);
+#endif
+}
+
+#undef SR
+#undef RR
+
+void dss_sdi_init(u8 datapairs)
+{
+       u32 l;
+
+       BUG_ON(datapairs > 3 || datapairs < 1);
+
+       l = dss_read_reg(DSS_SDI_CONTROL);
+       l = FLD_MOD(l, 0xf, 19, 15);            /* SDI_PDIV */
+       l = FLD_MOD(l, datapairs-1, 3, 2);      /* SDI_PRSEL */
+       l = FLD_MOD(l, 2, 1, 0);                /* SDI_BWSEL */
+       dss_write_reg(DSS_SDI_CONTROL, l);
+
+       l = dss_read_reg(DSS_PLL_CONTROL);
+       l = FLD_MOD(l, 0x7, 25, 22);    /* SDI_PLL_FREQSEL */
+       l = FLD_MOD(l, 0xb, 16, 11);    /* SDI_PLL_REGN */
+       l = FLD_MOD(l, 0xb4, 10, 1);    /* SDI_PLL_REGM */
+       dss_write_reg(DSS_PLL_CONTROL, l);
+}
+
+int dss_sdi_enable(void)
+{
+       unsigned long timeout;
+
+       dispc_pck_free_enable(1);
+
+       /* Reset SDI PLL */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */
+       udelay(1);      /* wait 2x PCLK */
+
+       /* Lock SDI PLL */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */
+
+       /* Waiting for PLL lock request to complete */
+       timeout = jiffies + msecs_to_jiffies(500);
+       while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) {
+               if (time_after_eq(jiffies, timeout)) {
+                       DSSERR("PLL lock request timed out\n");
+                       goto err1;
+               }
+       }
+
+       /* Clearing PLL_GO bit */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28);
+
+       /* Waiting for PLL to lock */
+       timeout = jiffies + msecs_to_jiffies(500);
+       while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) {
+               if (time_after_eq(jiffies, timeout)) {
+                       DSSERR("PLL lock timed out\n");
+                       goto err1;
+               }
+       }
+
+       dispc_lcd_enable_signal(1);
+
+       /* Waiting for SDI reset to complete */
+       timeout = jiffies + msecs_to_jiffies(500);
+       while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) {
+               if (time_after_eq(jiffies, timeout)) {
+                       DSSERR("SDI reset timed out\n");
+                       goto err2;
+               }
+       }
+
+       return 0;
+
+ err2:
+       dispc_lcd_enable_signal(0);
+ err1:
+       /* Reset SDI PLL */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
+
+       dispc_pck_free_enable(0);
+
+       return -ETIMEDOUT;
+}
+
+void dss_sdi_disable(void)
+{
+       dispc_lcd_enable_signal(0);
+
+       dispc_pck_free_enable(0);
+
+       /* Reset SDI PLL */
+       REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */
+}
+
+void dss_dump_clocks(struct seq_file *s)
+{
+       unsigned long dpll4_ck_rate;
+       unsigned long dpll4_m4_ck_rate;
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+       dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck);
+
+       seq_printf(s, "- DSS -\n");
+
+       seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate);
+
+       seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n",
+                       dpll4_ck_rate,
+                       dpll4_ck_rate / dpll4_m4_ck_rate,
+                       dss_clk_get_rate(DSS_CLK_FCK1));
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+void dss_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       DUMPREG(DSS_REVISION);
+       DUMPREG(DSS_SYSCONFIG);
+       DUMPREG(DSS_SYSSTATUS);
+       DUMPREG(DSS_IRQSTATUS);
+       DUMPREG(DSS_CONTROL);
+       DUMPREG(DSS_SDI_CONTROL);
+       DUMPREG(DSS_PLL_CONTROL);
+       DUMPREG(DSS_SDI_STATUS);
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+void dss_select_clk_source(bool dsi, bool dispc)
+{
+       u32 r;
+       r = dss_read_reg(DSS_CONTROL);
+       r = FLD_MOD(r, dsi, 1, 1);      /* DSI_CLK_SWITCH */
+       r = FLD_MOD(r, dispc, 0, 0);    /* DISPC_CLK_SWITCH */
+       dss_write_reg(DSS_CONTROL, r);
+}
+
+int dss_get_dsi_clk_source(void)
+{
+       return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1);
+}
+
+int dss_get_dispc_clk_source(void)
+{
+       return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0);
+}
+
+/* calculate clock rates using dividers in cinfo */
+int dss_calc_clock_rates(struct dss_clock_info *cinfo)
+{
+       unsigned long prate;
+
+       if (cinfo->fck_div > 16 || cinfo->fck_div == 0)
+               return -EINVAL;
+
+       prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+
+       cinfo->fck = prate / cinfo->fck_div;
+
+       return 0;
+}
+
+int dss_set_clock_div(struct dss_clock_info *cinfo)
+{
+       unsigned long prate;
+       int r;
+
+       if (cpu_is_omap34xx()) {
+               prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+               DSSDBG("dpll4_m4 = %ld\n", prate);
+
+               r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div);
+               if (r)
+                       return r;
+       }
+
+       DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div);
+
+       return 0;
+}
+
+int dss_get_clock_div(struct dss_clock_info *cinfo)
+{
+       cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1);
+
+       if (cpu_is_omap34xx()) {
+               unsigned long prate;
+               prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+               cinfo->fck_div = prate / (cinfo->fck / 2);
+       } else {
+               cinfo->fck_div = 0;
+       }
+
+       return 0;
+}
+
+unsigned long dss_get_dpll4_rate(void)
+{
+       if (cpu_is_omap34xx())
+               return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
+       else
+               return 0;
+}
+
+int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
+               struct dss_clock_info *dss_cinfo,
+               struct dispc_clock_info *dispc_cinfo)
+{
+       unsigned long prate;
+       struct dss_clock_info best_dss;
+       struct dispc_clock_info best_dispc;
+
+       unsigned long fck;
+
+       u16 fck_div;
+
+       int match = 0;
+       int min_fck_per_pck;
+
+       prate = dss_get_dpll4_rate();
+
+       fck = dss_clk_get_rate(DSS_CLK_FCK1);
+       if (req_pck == dss.cache_req_pck &&
+                       ((cpu_is_omap34xx() && prate == dss.cache_prate) ||
+                        dss.cache_dss_cinfo.fck == fck)) {
+               DSSDBG("dispc clock info found from cache.\n");
+               *dss_cinfo = dss.cache_dss_cinfo;
+               *dispc_cinfo = dss.cache_dispc_cinfo;
+               return 0;
+       }
+
+       min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK;
+
+       if (min_fck_per_pck &&
+               req_pck * min_fck_per_pck > DISPC_MAX_FCK) {
+               DSSERR("Requested pixel clock not possible with the current "
+                               "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning "
+                               "the constraint off.\n");
+               min_fck_per_pck = 0;
+       }
+
+retry:
+       memset(&best_dss, 0, sizeof(best_dss));
+       memset(&best_dispc, 0, sizeof(best_dispc));
+
+       if (cpu_is_omap24xx()) {
+               struct dispc_clock_info cur_dispc;
+               /* XXX can we change the clock on omap2? */
+               fck = dss_clk_get_rate(DSS_CLK_FCK1);
+               fck_div = 1;
+
+               dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+               match = 1;
+
+               best_dss.fck = fck;
+               best_dss.fck_div = fck_div;
+
+               best_dispc = cur_dispc;
+
+               goto found;
+       } else if (cpu_is_omap34xx()) {
+               for (fck_div = 16; fck_div > 0; --fck_div) {
+                       struct dispc_clock_info cur_dispc;
+
+                       fck = prate / fck_div * 2;
+
+                       if (fck > DISPC_MAX_FCK)
+                               continue;
+
+                       if (min_fck_per_pck &&
+                                       fck < req_pck * min_fck_per_pck)
+                               continue;
+
+                       match = 1;
+
+                       dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+
+                       if (abs(cur_dispc.pck - req_pck) <
+                                       abs(best_dispc.pck - req_pck)) {
+
+                               best_dss.fck = fck;
+                               best_dss.fck_div = fck_div;
+
+                               best_dispc = cur_dispc;
+
+                               if (cur_dispc.pck == req_pck)
+                                       goto found;
+                       }
+               }
+       } else {
+               BUG();
+       }
+
+found:
+       if (!match) {
+               if (min_fck_per_pck) {
+                       DSSERR("Could not find suitable clock settings.\n"
+                                       "Turning FCK/PCK constraint off and"
+                                       "trying again.\n");
+                       min_fck_per_pck = 0;
+                       goto retry;
+               }
+
+               DSSERR("Could not find suitable clock settings.\n");
+
+               return -EINVAL;
+       }
+
+       if (dss_cinfo)
+               *dss_cinfo = best_dss;
+       if (dispc_cinfo)
+               *dispc_cinfo = best_dispc;
+
+       dss.cache_req_pck = req_pck;
+       dss.cache_prate = prate;
+       dss.cache_dss_cinfo = best_dss;
+       dss.cache_dispc_cinfo = best_dispc;
+
+       return 0;
+}
+
+
+
+static irqreturn_t dss_irq_handler_omap2(int irq, void *arg)
+{
+       dispc_irq_handler();
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t dss_irq_handler_omap3(int irq, void *arg)
+{
+       u32 irqstatus;
+
+       irqstatus = dss_read_reg(DSS_IRQSTATUS);
+
+       if (irqstatus & (1<<0)) /* DISPC_IRQ */
+               dispc_irq_handler();
+#ifdef CONFIG_OMAP2_DSS_DSI
+       if (irqstatus & (1<<1)) /* DSI_IRQ */
+               dsi_irq_handler();
+#endif
+
+       return IRQ_HANDLED;
+}
+
+static int _omap_dss_wait_reset(void)
+{
+       unsigned timeout = 1000;
+
+       while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
+               udelay(1);
+               if (!--timeout) {
+                       DSSERR("soft reset failed\n");
+                       return -ENODEV;
+               }
+       }
+
+       return 0;
+}
+
+static int _omap_dss_reset(void)
+{
+       /* Soft reset */
+       REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
+       return _omap_dss_wait_reset();
+}
+
+void dss_set_venc_output(enum omap_dss_venc_type type)
+{
+       int l = 0;
+
+       if (type == OMAP_DSS_VENC_TYPE_COMPOSITE)
+               l = 0;
+       else if (type == OMAP_DSS_VENC_TYPE_SVIDEO)
+               l = 1;
+       else
+               BUG();
+
+       /* venc out selection. 0 = comp, 1 = svideo */
+       REG_FLD_MOD(DSS_CONTROL, l, 6, 6);
+}
+
+void dss_set_dac_pwrdn_bgz(bool enable)
+{
+       REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */
+}
+
+int dss_init(bool skip_init)
+{
+       int r;
+       u32 rev;
+
+       dss.base = ioremap(DSS_BASE, DSS_SZ_REGS);
+       if (!dss.base) {
+               DSSERR("can't ioremap DSS\n");
+               r = -ENOMEM;
+               goto fail0;
+       }
+
+       if (!skip_init) {
+               /* disable LCD and DIGIT output. This seems to fix the synclost
+                * problem that we get, if the bootloader starts the DSS and
+                * the kernel resets it */
+               omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
+
+               /* We need to wait here a bit, otherwise we sometimes start to
+                * get synclost errors, and after that only power cycle will
+                * restore DSS functionality. I have no idea why this happens.
+                * And we have to wait _before_ resetting the DSS, but after
+                * enabling clocks.
+                */
+               msleep(50);
+
+               _omap_dss_reset();
+       }
+
+       /* autoidle */
+       REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
+
+       /* Select DPLL */
+       REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+       REG_FLD_MOD(DSS_CONTROL, 1, 4, 4);      /* venc dac demen */
+       REG_FLD_MOD(DSS_CONTROL, 1, 3, 3);      /* venc clock 4x enable */
+       REG_FLD_MOD(DSS_CONTROL, 0, 2, 2);      /* venc clock mode = normal */
+#endif
+
+       r = request_irq(INT_24XX_DSS_IRQ,
+                       cpu_is_omap24xx()
+                       ? dss_irq_handler_omap2
+                       : dss_irq_handler_omap3,
+                       0, "OMAP DSS", NULL);
+
+       if (r < 0) {
+               DSSERR("omap2 dss: request_irq failed\n");
+               goto fail1;
+       }
+
+       if (cpu_is_omap34xx()) {
+               dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
+               if (IS_ERR(dss.dpll4_m4_ck)) {
+                       DSSERR("Failed to get dpll4_m4_ck\n");
+                       r = PTR_ERR(dss.dpll4_m4_ck);
+                       goto fail2;
+               }
+       }
+
+       dss_save_context();
+
+       rev = dss_read_reg(DSS_REVISION);
+       printk(KERN_INFO "OMAP DSS rev %d.%d\n",
+                       FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+       return 0;
+
+fail2:
+       free_irq(INT_24XX_DSS_IRQ, NULL);
+fail1:
+       iounmap(dss.base);
+fail0:
+       return r;
+}
+
+void dss_exit(void)
+{
+       if (cpu_is_omap34xx())
+               clk_put(dss.dpll4_m4_ck);
+
+       free_irq(INT_24XX_DSS_IRQ, NULL);
+
+       iounmap(dss.base);
+}
+
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
new file mode 100644 (file)
index 0000000..8da5ac4
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * linux/drivers/video/omap2/dss/dss.h
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP2_DSS_H
+#define __OMAP2_DSS_H
+
+#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT
+#define DEBUG
+#endif
+
+#ifdef DEBUG
+extern unsigned int dss_debug;
+#ifdef DSS_SUBSYS_NAME
+#define DSSDBG(format, ...) \
+       if (dss_debug) \
+               printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME ": " format, \
+               ## __VA_ARGS__)
+#else
+#define DSSDBG(format, ...) \
+       if (dss_debug) \
+               printk(KERN_DEBUG "omapdss: " format, ## __VA_ARGS__)
+#endif
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSDBGF(format, ...) \
+       if (dss_debug) \
+               printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \
+                               ": %s(" format ")\n", \
+                               __func__, \
+                               ## __VA_ARGS__)
+#else
+#define DSSDBGF(format, ...) \
+       if (dss_debug) \
+               printk(KERN_DEBUG "omapdss: " \
+                               ": %s(" format ")\n", \
+                               __func__, \
+                               ## __VA_ARGS__)
+#endif
+
+#else /* DEBUG */
+#define DSSDBG(format, ...)
+#define DSSDBGF(format, ...)
+#endif
+
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSERR(format, ...) \
+       printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \
+       ## __VA_ARGS__)
+#else
+#define DSSERR(format, ...) \
+       printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__)
+#endif
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSINFO(format, ...) \
+       printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \
+       ## __VA_ARGS__)
+#else
+#define DSSINFO(format, ...) \
+       printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__)
+#endif
+
+#ifdef DSS_SUBSYS_NAME
+#define DSSWARN(format, ...) \
+       printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \
+       ## __VA_ARGS__)
+#else
+#define DSSWARN(format, ...) \
+       printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__)
+#endif
+
+/* OMAP TRM gives bitfields as start:end, where start is the higher bit
+   number. For example 7:0 */
+#define FLD_MASK(start, end)   (((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
+#define FLD_MOD(orig, val, start, end) \
+       (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
+
+#define DISPC_MAX_FCK 173000000
+
+enum omap_burst_size {
+       OMAP_DSS_BURST_4x32 = 0,
+       OMAP_DSS_BURST_8x32 = 1,
+       OMAP_DSS_BURST_16x32 = 2,
+};
+
+enum omap_parallel_interface_mode {
+       OMAP_DSS_PARALLELMODE_BYPASS,           /* MIPI DPI */
+       OMAP_DSS_PARALLELMODE_RFBI,             /* MIPI DBI */
+       OMAP_DSS_PARALLELMODE_DSI,
+};
+
+enum dss_clock {
+       DSS_CLK_ICK     = 1 << 0,
+       DSS_CLK_FCK1    = 1 << 1,
+       DSS_CLK_FCK2    = 1 << 2,
+       DSS_CLK_54M     = 1 << 3,
+       DSS_CLK_96M     = 1 << 4,
+};
+
+struct dss_clock_info {
+       /* rates that we get with dividers below */
+       unsigned long fck;
+
+       /* dividers */
+       u16 fck_div;
+};
+
+struct dispc_clock_info {
+       /* rates that we get with dividers below */
+       unsigned long lck;
+       unsigned long pck;
+
+       /* dividers */
+       u16 lck_div;
+       u16 pck_div;
+};
+
+struct dsi_clock_info {
+       /* rates that we get with dividers below */
+       unsigned long fint;
+       unsigned long clkin4ddr;
+       unsigned long clkin;
+       unsigned long dsi1_pll_fclk;
+       unsigned long dsi2_pll_fclk;
+
+       unsigned long lp_clk;
+
+       /* dividers */
+       u16 regn;
+       u16 regm;
+       u16 regm3;
+       u16 regm4;
+
+       u16 lp_clk_div;
+
+       u8 highfreq;
+       bool use_dss2_fck;
+};
+
+struct seq_file;
+struct platform_device;
+
+/* core */
+void dss_clk_enable(enum dss_clock clks);
+void dss_clk_disable(enum dss_clock clks);
+unsigned long dss_clk_get_rate(enum dss_clock clk);
+int dss_need_ctx_restore(void);
+void dss_dump_clocks(struct seq_file *s);
+struct bus_type *dss_get_bus(void);
+
+/* display */
+int dss_suspend_all_devices(void);
+int dss_resume_all_devices(void);
+void dss_disable_all_devices(void);
+
+void dss_init_device(struct platform_device *pdev,
+               struct omap_dss_device *dssdev);
+void dss_uninit_device(struct platform_device *pdev,
+               struct omap_dss_device *dssdev);
+bool dss_use_replication(struct omap_dss_device *dssdev,
+               enum omap_color_mode mode);
+void default_get_overlay_fifo_thresholds(enum omap_plane plane,
+               u32 fifo_size, enum omap_burst_size *burst_size,
+               u32 *fifo_low, u32 *fifo_high);
+
+/* manager */
+int dss_init_overlay_managers(struct platform_device *pdev);
+void dss_uninit_overlay_managers(struct platform_device *pdev);
+int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl);
+void dss_setup_partial_planes(struct omap_dss_device *dssdev,
+                               u16 *x, u16 *y, u16 *w, u16 *h);
+void dss_start_update(struct omap_dss_device *dssdev);
+
+/* overlay */
+void dss_init_overlays(struct platform_device *pdev);
+void dss_uninit_overlays(struct platform_device *pdev);
+int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev);
+void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
+#ifdef L4_EXAMPLE
+void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr);
+#endif
+void dss_recheck_connections(struct omap_dss_device *dssdev, bool force);
+
+/* DSS */
+int dss_init(bool skip_init);
+void dss_exit(void);
+
+void dss_save_context(void);
+void dss_restore_context(void);
+
+void dss_dump_regs(struct seq_file *s);
+
+void dss_sdi_init(u8 datapairs);
+int dss_sdi_enable(void);
+void dss_sdi_disable(void);
+
+void dss_select_clk_source(bool dsi, bool dispc);
+int dss_get_dsi_clk_source(void);
+int dss_get_dispc_clk_source(void);
+void dss_set_venc_output(enum omap_dss_venc_type type);
+void dss_set_dac_pwrdn_bgz(bool enable);
+
+unsigned long dss_get_dpll4_rate(void);
+int dss_calc_clock_rates(struct dss_clock_info *cinfo);
+int dss_set_clock_div(struct dss_clock_info *cinfo);
+int dss_get_clock_div(struct dss_clock_info *cinfo);
+int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
+               struct dss_clock_info *dss_cinfo,
+               struct dispc_clock_info *dispc_cinfo);
+
+/* SDI */
+int sdi_init(bool skip_init);
+void sdi_exit(void);
+int sdi_init_display(struct omap_dss_device *display);
+
+/* DSI */
+int dsi_init(struct platform_device *pdev);
+void dsi_exit(void);
+
+void dsi_dump_clocks(struct seq_file *s);
+void dsi_dump_regs(struct seq_file *s);
+
+void dsi_save_context(void);
+void dsi_restore_context(void);
+
+int dsi_init_display(struct omap_dss_device *display);
+void dsi_irq_handler(void);
+unsigned long dsi_get_dsi1_pll_rate(void);
+int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo);
+int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck,
+               struct dsi_clock_info *cinfo,
+               struct dispc_clock_info *dispc_cinfo);
+int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk,
+               bool enable_hsdiv);
+void dsi_pll_uninit(void);
+void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
+               u32 fifo_size, enum omap_burst_size *burst_size,
+               u32 *fifo_low, u32 *fifo_high);
+
+/* DPI */
+int dpi_init(void);
+void dpi_exit(void);
+int dpi_init_display(struct omap_dss_device *dssdev);
+
+/* DISPC */
+int dispc_init(void);
+void dispc_exit(void);
+void dispc_dump_clocks(struct seq_file *s);
+void dispc_dump_regs(struct seq_file *s);
+void dispc_irq_handler(void);
+void dispc_fake_vsync_irq(void);
+
+void dispc_save_context(void);
+void dispc_restore_context(void);
+
+void dispc_enable_sidle(void);
+void dispc_disable_sidle(void);
+
+void dispc_lcd_enable_signal_polarity(bool act_high);
+void dispc_lcd_enable_signal(bool enable);
+void dispc_pck_free_enable(bool enable);
+void dispc_enable_fifohandcheck(bool enable);
+
+void dispc_set_lcd_size(u16 width, u16 height);
+void dispc_set_digit_size(u16 width, u16 height);
+u32 dispc_get_plane_fifo_size(enum omap_plane plane);
+void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high);
+void dispc_enable_fifomerge(bool enable);
+void dispc_set_burst_size(enum omap_plane plane,
+               enum omap_burst_size burst_size);
+
+void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
+void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
+void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y);
+void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height);
+void dispc_set_channel_out(enum omap_plane plane,
+               enum omap_channel channel_out);
+
+int dispc_setup_plane(enum omap_plane plane,
+                     u32 paddr, u16 screen_width,
+                     u16 pos_x, u16 pos_y,
+                     u16 width, u16 height,
+                     u16 out_width, u16 out_height,
+                     enum omap_color_mode color_mode,
+                     bool ilace,
+                     enum omap_dss_rotation_type rotation_type,
+                     u8 rotation, bool mirror,
+                     u8 global_alpha);
+
+bool dispc_go_busy(enum omap_channel channel);
+void dispc_go(enum omap_channel channel);
+void dispc_enable_lcd_out(bool enable);
+void dispc_enable_digit_out(bool enable);
+int dispc_enable_plane(enum omap_plane plane, bool enable);
+void dispc_enable_replication(enum omap_plane plane, bool enable);
+
+void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode);
+void dispc_set_tft_data_lines(u8 data_lines);
+void dispc_set_lcd_display_type(enum omap_lcd_display_type type);
+void dispc_set_loadmode(enum omap_dss_load_mode mode);
+
+void dispc_set_default_color(enum omap_channel channel, u32 color);
+u32 dispc_get_default_color(enum omap_channel channel);
+void dispc_set_trans_key(enum omap_channel ch,
+               enum omap_dss_trans_key_type type,
+               u32 trans_key);
+void dispc_get_trans_key(enum omap_channel ch,
+               enum omap_dss_trans_key_type *type,
+               u32 *trans_key);
+void dispc_enable_trans_key(enum omap_channel ch, bool enable);
+void dispc_enable_alpha_blending(enum omap_channel ch, bool enable);
+bool dispc_trans_key_enabled(enum omap_channel ch);
+bool dispc_alpha_blending_enabled(enum omap_channel ch);
+
+bool dispc_lcd_timings_ok(struct omap_video_timings *timings);
+void dispc_set_lcd_timings(struct omap_video_timings *timings);
+unsigned long dispc_fclk_rate(void);
+unsigned long dispc_lclk_rate(void);
+unsigned long dispc_pclk_rate(void);
+void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb);
+void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+               struct dispc_clock_info *cinfo);
+int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
+               struct dispc_clock_info *cinfo);
+int dispc_set_clock_div(struct dispc_clock_info *cinfo);
+int dispc_get_clock_div(struct dispc_clock_info *cinfo);
+
+
+/* VENC */
+int venc_init(struct platform_device *pdev);
+void venc_exit(void);
+void venc_dump_regs(struct seq_file *s);
+int venc_init_display(struct omap_dss_device *display);
+
+/* RFBI */
+int rfbi_init(void);
+void rfbi_exit(void);
+void rfbi_dump_regs(struct seq_file *s);
+
+int rfbi_configure(int rfbi_module, int bpp, int lines);
+void rfbi_enable_rfbi(bool enable);
+void rfbi_transfer_area(u16 width, u16 height,
+                            void (callback)(void *data), void *data);
+void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t);
+unsigned long rfbi_get_max_tx_rate(void);
+int rfbi_init_display(struct omap_dss_device *display);
+
+#endif
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
new file mode 100644 (file)
index 0000000..27d9c46
--- /dev/null
@@ -0,0 +1,1487 @@
+/*
+ * linux/drivers/video/omap2/dss/manager.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "MANAGER"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+static int num_managers;
+static struct list_head manager_list;
+
+static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
+}
+
+static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       mgr->device ? mgr->device->name : "<none>");
+}
+
+static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
+               const char *buf, size_t size)
+{
+       int r = 0;
+       size_t len = size;
+       struct omap_dss_device *dssdev = NULL;
+
+       int match(struct omap_dss_device *dssdev, void *data)
+       {
+               const char *str = data;
+               return sysfs_streq(dssdev->name, str);
+       }
+
+       if (buf[size-1] == '\n')
+               --len;
+
+       if (len > 0)
+               dssdev = omap_dss_find_device((void *)buf, match);
+
+       if (len > 0 && dssdev == NULL)
+               return -EINVAL;
+
+       if (dssdev)
+               DSSDBG("display %s found\n", dssdev->name);
+
+       if (mgr->device) {
+               r = mgr->unset_device(mgr);
+               if (r) {
+                       DSSERR("failed to unset display\n");
+                       goto put_device;
+               }
+       }
+
+       if (dssdev) {
+               r = mgr->set_device(mgr, dssdev);
+               if (r) {
+                       DSSERR("failed to set manager\n");
+                       goto put_device;
+               }
+
+               r = mgr->apply(mgr);
+               if (r) {
+                       DSSERR("failed to apply dispc config\n");
+                       goto put_device;
+               }
+       }
+
+put_device:
+       if (dssdev)
+               omap_dss_put_device(dssdev);
+
+       return r ? r : size;
+}
+
+static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
+                                         char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color);
+}
+
+static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
+                                          const char *buf, size_t size)
+{
+       struct omap_overlay_manager_info info;
+       u32 color;
+       int r;
+
+       if (sscanf(buf, "%d", &color) != 1)
+               return -EINVAL;
+
+       mgr->get_manager_info(mgr, &info);
+
+       info.default_color = color;
+
+       r = mgr->set_manager_info(mgr, &info);
+       if (r)
+               return r;
+
+       r = mgr->apply(mgr);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static const char *trans_key_type_str[] = {
+       "gfx-destination",
+       "video-source",
+};
+
+static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
+                                          char *buf)
+{
+       enum omap_dss_trans_key_type key_type;
+
+       key_type = mgr->info.trans_key_type;
+       BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
+}
+
+static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
+                                           const char *buf, size_t size)
+{
+       enum omap_dss_trans_key_type key_type;
+       struct omap_overlay_manager_info info;
+       int r;
+
+       for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+                       key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
+               if (sysfs_streq(buf, trans_key_type_str[key_type]))
+                       break;
+       }
+
+       if (key_type == ARRAY_SIZE(trans_key_type_str))
+               return -EINVAL;
+
+       mgr->get_manager_info(mgr, &info);
+
+       info.trans_key_type = key_type;
+
+       r = mgr->set_manager_info(mgr, &info);
+       if (r)
+               return r;
+
+       r = mgr->apply(mgr);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
+                                           char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key);
+}
+
+static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
+                                            const char *buf, size_t size)
+{
+       struct omap_overlay_manager_info info;
+       u32 key_value;
+       int r;
+
+       if (sscanf(buf, "%d", &key_value) != 1)
+               return -EINVAL;
+
+       mgr->get_manager_info(mgr, &info);
+
+       info.trans_key = key_value;
+
+       r = mgr->set_manager_info(mgr, &info);
+       if (r)
+               return r;
+
+       r = mgr->apply(mgr);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
+                                             char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled);
+}
+
+static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
+                                              const char *buf, size_t size)
+{
+       struct omap_overlay_manager_info info;
+       int enable;
+       int r;
+
+       if (sscanf(buf, "%d", &enable) != 1)
+               return -EINVAL;
+
+       mgr->get_manager_info(mgr, &info);
+
+       info.trans_enabled = enable ? true : false;
+
+       r = mgr->set_manager_info(mgr, &info);
+       if (r)
+               return r;
+
+       r = mgr->apply(mgr);
+       if (r)
+               return r;
+
+       return size;
+}
+
+static ssize_t manager_alpha_blending_enabled_show(
+               struct omap_overlay_manager *mgr, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled);
+}
+
+static ssize_t manager_alpha_blending_enabled_store(
+               struct omap_overlay_manager *mgr,
+               const char *buf, size_t size)
+{
+       struct omap_overlay_manager_info info;
+       int enable;
+       int r;
+
+       if (sscanf(buf, "%d", &enable) != 1)
+               return -EINVAL;
+
+       mgr->get_manager_info(mgr, &info);
+
+       info.alpha_enabled = enable ? true : false;
+
+       r = mgr->set_manager_info(mgr, &info);
+       if (r)
+               return r;
+
+       r = mgr->apply(mgr);
+       if (r)
+               return r;
+
+       return size;
+}
+
+struct manager_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct omap_overlay_manager *, char *);
+       ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
+};
+
+#define MANAGER_ATTR(_name, _mode, _show, _store) \
+       struct manager_attribute manager_attr_##_name = \
+       __ATTR(_name, _mode, _show, _store)
+
+static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
+static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
+               manager_display_show, manager_display_store);
+static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
+               manager_default_color_show, manager_default_color_store);
+static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
+               manager_trans_key_type_show, manager_trans_key_type_store);
+static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
+               manager_trans_key_value_show, manager_trans_key_value_store);
+static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
+               manager_trans_key_enabled_show,
+               manager_trans_key_enabled_store);
+static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
+               manager_alpha_blending_enabled_show,
+               manager_alpha_blending_enabled_store);
+
+
+static struct attribute *manager_sysfs_attrs[] = {
+       &manager_attr_name.attr,
+       &manager_attr_display.attr,
+       &manager_attr_default_color.attr,
+       &manager_attr_trans_key_type.attr,
+       &manager_attr_trans_key_value.attr,
+       &manager_attr_trans_key_enabled.attr,
+       &manager_attr_alpha_blending_enabled.attr,
+       NULL
+};
+
+static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
+               char *buf)
+{
+       struct omap_overlay_manager *manager;
+       struct manager_attribute *manager_attr;
+
+       manager = container_of(kobj, struct omap_overlay_manager, kobj);
+       manager_attr = container_of(attr, struct manager_attribute, attr);
+
+       if (!manager_attr->show)
+               return -ENOENT;
+
+       return manager_attr->show(manager, buf);
+}
+
+static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
+               const char *buf, size_t size)
+{
+       struct omap_overlay_manager *manager;
+       struct manager_attribute *manager_attr;
+
+       manager = container_of(kobj, struct omap_overlay_manager, kobj);
+       manager_attr = container_of(attr, struct manager_attribute, attr);
+
+       if (!manager_attr->store)
+               return -ENOENT;
+
+       return manager_attr->store(manager, buf, size);
+}
+
+static struct sysfs_ops manager_sysfs_ops = {
+       .show = manager_attr_show,
+       .store = manager_attr_store,
+};
+
+static struct kobj_type manager_ktype = {
+       .sysfs_ops = &manager_sysfs_ops,
+       .default_attrs = manager_sysfs_attrs,
+};
+
+/*
+ * We have 4 levels of cache for the dispc settings. First two are in SW and
+ * the latter two in HW.
+ *
+ * +--------------------+
+ * |overlay/manager_info|
+ * +--------------------+
+ *          v
+ *        apply()
+ *          v
+ * +--------------------+
+ * |     dss_cache      |
+ * +--------------------+
+ *          v
+ *      configure()
+ *          v
+ * +--------------------+
+ * |  shadow registers  |
+ * +--------------------+
+ *          v
+ * VFP or lcd/digit_enable
+ *          v
+ * +--------------------+
+ * |      registers     |
+ * +--------------------+
+ */
+
+struct overlay_cache_data {
+       /* If true, cache changed, but not written to shadow registers. Set
+        * in apply(), cleared when registers written. */
+       bool dirty;
+       /* If true, shadow registers contain changed values not yet in real
+        * registers. Set when writing to shadow registers, cleared at
+        * VSYNC/EVSYNC */
+       bool shadow_dirty;
+
+       bool enabled;
+
+       u32 paddr;
+       void __iomem *vaddr;
+       u16 screen_width;
+       u16 width;
+       u16 height;
+       enum omap_color_mode color_mode;
+       u8 rotation;
+       enum omap_dss_rotation_type rotation_type;
+       bool mirror;
+
+       u16 pos_x;
+       u16 pos_y;
+       u16 out_width;  /* if 0, out_width == width */
+       u16 out_height; /* if 0, out_height == height */
+       u8 global_alpha;
+
+       enum omap_channel channel;
+       bool replication;
+       bool ilace;
+
+       enum omap_burst_size burst_size;
+       u32 fifo_low;
+       u32 fifo_high;
+
+       bool manual_update;
+};
+
+struct manager_cache_data {
+       /* If true, cache changed, but not written to shadow registers. Set
+        * in apply(), cleared when registers written. */
+       bool dirty;
+       /* If true, shadow registers contain changed values not yet in real
+        * registers. Set when writing to shadow registers, cleared at
+        * VSYNC/EVSYNC */
+       bool shadow_dirty;
+
+       u32 default_color;
+
+       enum omap_dss_trans_key_type trans_key_type;
+       u32 trans_key;
+       bool trans_enabled;
+
+       bool alpha_enabled;
+
+       bool manual_upd_display;
+       bool manual_update;
+       bool do_manual_update;
+
+       /* manual update region */
+       u16 x, y, w, h;
+};
+
+static struct {
+       spinlock_t lock;
+       struct overlay_cache_data overlay_cache[3];
+       struct manager_cache_data manager_cache[2];
+
+       bool irq_enabled;
+} dss_cache;
+
+
+
+static int omap_dss_set_device(struct omap_overlay_manager *mgr,
+               struct omap_dss_device *dssdev)
+{
+       int i;
+       int r;
+
+       if (dssdev->manager) {
+               DSSERR("display '%s' already has a manager '%s'\n",
+                              dssdev->name, dssdev->manager->name);
+               return -EINVAL;
+       }
+
+       if ((mgr->supported_displays & dssdev->type) == 0) {
+               DSSERR("display '%s' does not support manager '%s'\n",
+                              dssdev->name, mgr->name);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < mgr->num_overlays; i++) {
+               struct omap_overlay *ovl = mgr->overlays[i];
+
+               if (ovl->manager != mgr || !ovl->info.enabled)
+                       continue;
+
+               r = dss_check_overlay(ovl, dssdev);
+               if (r)
+                       return r;
+       }
+
+       dssdev->manager = mgr;
+       mgr->device = dssdev;
+       mgr->device_changed = true;
+
+       return 0;
+}
+
+static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
+{
+       if (!mgr->device) {
+               DSSERR("failed to unset display, display not set.\n");
+               return -EINVAL;
+       }
+
+       mgr->device->manager = NULL;
+       mgr->device = NULL;
+       mgr->device_changed = true;
+
+       return 0;
+}
+
+static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
+{
+       unsigned long timeout = msecs_to_jiffies(500);
+       struct manager_cache_data *mc;
+       enum omap_channel channel;
+       u32 irq;
+       int r;
+       int i;
+
+       if (!mgr->device)
+               return 0;
+
+       if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
+               irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
+               channel = OMAP_DSS_CHANNEL_DIGIT;
+       } else {
+               if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+                       enum omap_dss_update_mode mode;
+                       mode = mgr->device->get_update_mode(mgr->device);
+                       if (mode != OMAP_DSS_UPDATE_AUTO)
+                               return 0;
+
+                       irq = DISPC_IRQ_FRAMEDONE;
+               } else {
+                       irq = DISPC_IRQ_VSYNC;
+               }
+               channel = OMAP_DSS_CHANNEL_LCD;
+       }
+
+       mc = &dss_cache.manager_cache[mgr->id];
+       i = 0;
+       while (1) {
+               unsigned long flags;
+               bool shadow_dirty, dirty;
+
+               spin_lock_irqsave(&dss_cache.lock, flags);
+               dirty = mc->dirty;
+               shadow_dirty = mc->shadow_dirty;
+               spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+               if (!dirty && !shadow_dirty) {
+                       r = 0;
+                       break;
+               }
+
+               /* 4 iterations is the worst case:
+                * 1 - initial iteration, dirty = true (between VFP and VSYNC)
+                * 2 - first VSYNC, dirty = true
+                * 3 - dirty = false, shadow_dirty = true
+                * 4 - shadow_dirty = false */
+               if (i++ == 3) {
+                       DSSERR("mgr(%d)->wait_for_go() not finishing\n",
+                                       mgr->id);
+                       r = 0;
+                       break;
+               }
+
+               r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+               if (r == -ERESTARTSYS)
+                       break;
+
+               if (r) {
+                       DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
+                       break;
+               }
+       }
+
+       return r;
+}
+
+int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
+{
+       unsigned long timeout = msecs_to_jiffies(500);
+       enum omap_channel channel;
+       struct overlay_cache_data *oc;
+       struct omap_dss_device *dssdev;
+       u32 irq;
+       int r;
+       int i;
+
+       if (!ovl->manager || !ovl->manager->device)
+               return 0;
+
+       dssdev = ovl->manager->device;
+
+       if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
+               irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
+               channel = OMAP_DSS_CHANNEL_DIGIT;
+       } else {
+               if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+                       enum omap_dss_update_mode mode;
+                       mode = dssdev->get_update_mode(dssdev);
+                       if (mode != OMAP_DSS_UPDATE_AUTO)
+                               return 0;
+
+                       irq = DISPC_IRQ_FRAMEDONE;
+               } else {
+                       irq = DISPC_IRQ_VSYNC;
+               }
+               channel = OMAP_DSS_CHANNEL_LCD;
+       }
+
+       oc = &dss_cache.overlay_cache[ovl->id];
+       i = 0;
+       while (1) {
+               unsigned long flags;
+               bool shadow_dirty, dirty;
+
+               spin_lock_irqsave(&dss_cache.lock, flags);
+               dirty = oc->dirty;
+               shadow_dirty = oc->shadow_dirty;
+               spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+               if (!dirty && !shadow_dirty) {
+                       r = 0;
+                       break;
+               }
+
+               /* 4 iterations is the worst case:
+                * 1 - initial iteration, dirty = true (between VFP and VSYNC)
+                * 2 - first VSYNC, dirty = true
+                * 3 - dirty = false, shadow_dirty = true
+                * 4 - shadow_dirty = false */
+               if (i++ == 3) {
+                       DSSERR("ovl(%d)->wait_for_go() not finishing\n",
+                                       ovl->id);
+                       r = 0;
+                       break;
+               }
+
+               r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+               if (r == -ERESTARTSYS)
+                       break;
+
+               if (r) {
+                       DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
+                       break;
+               }
+       }
+
+       return r;
+}
+
+static int overlay_enabled(struct omap_overlay *ovl)
+{
+       return ovl->info.enabled && ovl->manager && ovl->manager->device;
+}
+
+/* Is rect1 a subset of rect2? */
+static bool rectangle_subset(int x1, int y1, int w1, int h1,
+               int x2, int y2, int w2, int h2)
+{
+       if (x1 < x2 || y1 < y2)
+               return false;
+
+       if (x1 + w1 > x2 + w2)
+               return false;
+
+       if (y1 + h1 > y2 + h2)
+               return false;
+
+       return true;
+}
+
+/* Do rect1 and rect2 overlap? */
+static bool rectangle_intersects(int x1, int y1, int w1, int h1,
+               int x2, int y2, int w2, int h2)
+{
+       if (x1 >= x2 + w2)
+               return false;
+
+       if (x2 >= x1 + w1)
+               return false;
+
+       if (y1 >= y2 + h2)
+               return false;
+
+       if (y2 >= y1 + h1)
+               return false;
+
+       return true;
+}
+
+static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
+{
+       if (oc->out_width != 0 && oc->width != oc->out_width)
+               return true;
+
+       if (oc->out_height != 0 && oc->height != oc->out_height)
+               return true;
+
+       return false;
+}
+
+static int configure_overlay(enum omap_plane plane)
+{
+       struct overlay_cache_data *c;
+       struct manager_cache_data *mc;
+       u16 outw, outh;
+       u16 x, y, w, h;
+       u32 paddr;
+       int r;
+
+       DSSDBGF("%d", plane);
+
+       c = &dss_cache.overlay_cache[plane];
+
+       if (!c->enabled) {
+               dispc_enable_plane(plane, 0);
+               return 0;
+       }
+
+       mc = &dss_cache.manager_cache[c->channel];
+
+       x = c->pos_x;
+       y = c->pos_y;
+       w = c->width;
+       h = c->height;
+       outw = c->out_width == 0 ? c->width : c->out_width;
+       outh = c->out_height == 0 ? c->height : c->out_height;
+       paddr = c->paddr;
+
+       if (c->manual_update && mc->do_manual_update) {
+               unsigned bpp;
+               /* If the overlay is outside the update region, disable it */
+               if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
+                                       x, y, outw, outh)) {
+                       dispc_enable_plane(plane, 0);
+                       return 0;
+               }
+
+               switch (c->color_mode) {
+               case OMAP_DSS_COLOR_RGB16:
+               case OMAP_DSS_COLOR_ARGB16:
+               case OMAP_DSS_COLOR_YUV2:
+               case OMAP_DSS_COLOR_UYVY:
+                       bpp = 16;
+                       break;
+
+               case OMAP_DSS_COLOR_RGB24P:
+                       bpp = 24;
+                       break;
+
+               case OMAP_DSS_COLOR_RGB24U:
+               case OMAP_DSS_COLOR_ARGB32:
+               case OMAP_DSS_COLOR_RGBA32:
+               case OMAP_DSS_COLOR_RGBX32:
+                       bpp = 32;
+                       break;
+
+               default:
+                       BUG();
+               }
+
+               if (dispc_is_overlay_scaled(c)) {
+                       /* If the overlay is scaled, the update area has
+                        * already been enlarged to cover the whole overlay. We
+                        * only need to adjust x/y here */
+                       x = c->pos_x - mc->x;
+                       y = c->pos_y - mc->y;
+               } else {
+                       if (mc->x > c->pos_x) {
+                               x = 0;
+                               w -= (mc->x - c->pos_x);
+                               paddr += (mc->x - c->pos_x) * bpp / 8;
+                       } else {
+                               x = c->pos_x - mc->x;
+                       }
+
+                       if (mc->y > c->pos_y) {
+                               y = 0;
+                               h -= (mc->y - c->pos_y);
+                               paddr += (mc->y - c->pos_y) * c->screen_width *
+                                       bpp / 8;
+                       } else {
+                               y = c->pos_y - mc->y;
+                       }
+
+                       if (mc->w < (x+w))
+                               w -= (x+w) - (mc->w);
+
+                       if (mc->h < (y+h))
+                               h -= (y+h) - (mc->h);
+
+                       outw = w;
+                       outh = h;
+               }
+       }
+
+       r = dispc_setup_plane(plane,
+                       paddr,
+                       c->screen_width,
+                       x, y,
+                       w, h,
+                       outw, outh,
+                       c->color_mode,
+                       c->ilace,
+                       c->rotation_type,
+                       c->rotation,
+                       c->mirror,
+                       c->global_alpha);
+
+       if (r) {
+               /* this shouldn't happen */
+               DSSERR("dispc_setup_plane failed for ovl %d\n", plane);
+               dispc_enable_plane(plane, 0);
+               return r;
+       }
+
+       dispc_enable_replication(plane, c->replication);
+
+       dispc_set_burst_size(plane, c->burst_size);
+       dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
+
+       dispc_enable_plane(plane, 1);
+
+       return 0;
+}
+
+static void configure_manager(enum omap_channel channel)
+{
+       struct manager_cache_data *c;
+
+       DSSDBGF("%d", channel);
+
+       c = &dss_cache.manager_cache[channel];
+
+       dispc_set_trans_key(channel, c->trans_key_type, c->trans_key);
+       dispc_enable_trans_key(channel, c->trans_enabled);
+       dispc_enable_alpha_blending(channel, c->alpha_enabled);
+}
+
+/* configure_dispc() tries to write values from cache to shadow registers.
+ * It writes only to those managers/overlays that are not busy.
+ * returns 0 if everything could be written to shadow registers.
+ * returns 1 if not everything could be written to shadow registers. */
+static int configure_dispc(void)
+{
+       struct overlay_cache_data *oc;
+       struct manager_cache_data *mc;
+       const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+       const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+       int i;
+       int r;
+       bool mgr_busy[2];
+       bool mgr_go[2];
+       bool busy;
+
+       r = 0;
+       busy = false;
+
+       mgr_busy[0] = dispc_go_busy(0);
+       mgr_busy[1] = dispc_go_busy(1);
+       mgr_go[0] = false;
+       mgr_go[1] = false;
+
+       /* Commit overlay settings */
+       for (i = 0; i < num_ovls; ++i) {
+               oc = &dss_cache.overlay_cache[i];
+               mc = &dss_cache.manager_cache[oc->channel];
+
+               if (!oc->dirty)
+                       continue;
+
+               if (oc->manual_update && !mc->do_manual_update)
+                       continue;
+
+               if (mgr_busy[oc->channel]) {
+                       busy = true;
+                       continue;
+               }
+
+               r = configure_overlay(i);
+               if (r)
+                       DSSERR("configure_overlay %d failed\n", i);
+
+               oc->dirty = false;
+               oc->shadow_dirty = true;
+               mgr_go[oc->channel] = true;
+       }
+
+       /* Commit manager settings */
+       for (i = 0; i < num_mgrs; ++i) {
+               mc = &dss_cache.manager_cache[i];
+
+               if (!mc->dirty)
+                       continue;
+
+               if (mc->manual_update && !mc->do_manual_update)
+                       continue;
+
+               if (mgr_busy[i]) {
+                       busy = true;
+                       continue;
+               }
+
+               configure_manager(i);
+               mc->dirty = false;
+               mc->shadow_dirty = true;
+               mgr_go[i] = true;
+       }
+
+       /* set GO */
+       for (i = 0; i < num_mgrs; ++i) {
+               mc = &dss_cache.manager_cache[i];
+
+               if (!mgr_go[i])
+                       continue;
+
+               /* We don't need GO with manual update display. LCD iface will
+                * always be turned off after frame, and new settings will be
+                * taken in to use at next update */
+               if (!mc->manual_upd_display)
+                       dispc_go(i);
+       }
+
+       if (busy)
+               r = 1;
+       else
+               r = 0;
+
+       return r;
+}
+
+/* Configure dispc for partial update. Return possibly modified update
+ * area */
+void dss_setup_partial_planes(struct omap_dss_device *dssdev,
+               u16 *xi, u16 *yi, u16 *wi, u16 *hi)
+{
+       struct overlay_cache_data *oc;
+       struct manager_cache_data *mc;
+       const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+       struct omap_overlay_manager *mgr;
+       int i;
+       u16 x, y, w, h;
+       unsigned long flags;
+
+       x = *xi;
+       y = *yi;
+       w = *wi;
+       h = *hi;
+
+       DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
+               *xi, *yi, *wi, *hi);
+
+       mgr = dssdev->manager;
+
+       if (!mgr) {
+               DSSDBG("no manager\n");
+               return;
+       }
+
+       spin_lock_irqsave(&dss_cache.lock, flags);
+
+       /* We need to show the whole overlay if it is scaled. So look for
+        * those, and make the update area larger if found.
+        * Also mark the overlay cache dirty */
+       for (i = 0; i < num_ovls; ++i) {
+               unsigned x1, y1, x2, y2;
+               unsigned outw, outh;
+
+               oc = &dss_cache.overlay_cache[i];
+
+               if (oc->channel != mgr->id)
+                       continue;
+
+               oc->dirty = true;
+
+               if (!oc->enabled)
+                       continue;
+
+               if (!dispc_is_overlay_scaled(oc))
+                       continue;
+
+               outw = oc->out_width == 0 ? oc->width : oc->out_width;
+               outh = oc->out_height == 0 ? oc->height : oc->out_height;
+
+               /* is the overlay outside the update region? */
+               if (!rectangle_intersects(x, y, w, h,
+                                       oc->pos_x, oc->pos_y,
+                                       outw, outh))
+                       continue;
+
+               /* if the overlay totally inside the update region? */
+               if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh,
+                                       x, y, w, h))
+                       continue;
+
+               if (x > oc->pos_x)
+                       x1 = oc->pos_x;
+               else
+                       x1 = x;
+
+               if (y > oc->pos_y)
+                       y1 = oc->pos_y;
+               else
+                       y1 = y;
+
+               if ((x + w) < (oc->pos_x + outw))
+                       x2 = oc->pos_x + outw;
+               else
+                       x2 = x + w;
+
+               if ((y + h) < (oc->pos_y + outh))
+                       y2 = oc->pos_y + outh;
+               else
+                       y2 = y + h;
+
+               x = x1;
+               y = y1;
+               w = x2 - x1;
+               h = y2 - y1;
+
+               DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n",
+                               i, x, y, w, h);
+       }
+
+       mc = &dss_cache.manager_cache[mgr->id];
+       mc->do_manual_update = true;
+       mc->x = x;
+       mc->y = y;
+       mc->w = w;
+       mc->h = h;
+
+       configure_dispc();
+
+       mc->do_manual_update = false;
+
+       spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+       *xi = x;
+       *yi = y;
+       *wi = w;
+       *hi = h;
+}
+
+void dss_start_update(struct omap_dss_device *dssdev)
+{
+       struct manager_cache_data *mc;
+       struct overlay_cache_data *oc;
+       const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+       const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+       struct omap_overlay_manager *mgr;
+       int i;
+
+       mgr = dssdev->manager;
+
+       for (i = 0; i < num_ovls; ++i) {
+               oc = &dss_cache.overlay_cache[i];
+               if (oc->channel != mgr->id)
+                       continue;
+
+               oc->shadow_dirty = false;
+       }
+
+       for (i = 0; i < num_mgrs; ++i) {
+               mc = &dss_cache.manager_cache[i];
+               if (mgr->id != i)
+                       continue;
+
+               mc->shadow_dirty = false;
+       }
+
+       dispc_enable_lcd_out(1);
+}
+
+static void dss_apply_irq_handler(void *data, u32 mask)
+{
+       struct manager_cache_data *mc;
+       struct overlay_cache_data *oc;
+       const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+       const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache);
+       int i, r;
+       bool mgr_busy[2];
+
+       mgr_busy[0] = dispc_go_busy(0);
+       mgr_busy[1] = dispc_go_busy(1);
+
+       spin_lock(&dss_cache.lock);
+
+       for (i = 0; i < num_ovls; ++i) {
+               oc = &dss_cache.overlay_cache[i];
+               if (!mgr_busy[oc->channel])
+                       oc->shadow_dirty = false;
+       }
+
+       for (i = 0; i < num_mgrs; ++i) {
+               mc = &dss_cache.manager_cache[i];
+               if (!mgr_busy[i])
+                       mc->shadow_dirty = false;
+       }
+
+       r = configure_dispc();
+       if (r == 1)
+               goto end;
+
+       /* re-read busy flags */
+       mgr_busy[0] = dispc_go_busy(0);
+       mgr_busy[1] = dispc_go_busy(1);
+
+       /* keep running as long as there are busy managers, so that
+        * we can collect overlay-applied information */
+       for (i = 0; i < num_mgrs; ++i) {
+               if (mgr_busy[i])
+                       goto end;
+       }
+
+       omap_dispc_unregister_isr(dss_apply_irq_handler, NULL,
+                       DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
+                       DISPC_IRQ_EVSYNC_EVEN);
+       dss_cache.irq_enabled = false;
+
+end:
+       spin_unlock(&dss_cache.lock);
+}
+
+static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
+{
+       struct overlay_cache_data *oc;
+       struct manager_cache_data *mc;
+       int i;
+       struct omap_overlay *ovl;
+       int num_planes_enabled = 0;
+       bool use_fifomerge;
+       unsigned long flags;
+       int r;
+
+       DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
+
+       spin_lock_irqsave(&dss_cache.lock, flags);
+
+       /* Configure overlays */
+       for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+               struct omap_dss_device *dssdev;
+
+               ovl = omap_dss_get_overlay(i);
+
+               if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                       continue;
+
+               oc = &dss_cache.overlay_cache[ovl->id];
+
+               if (!overlay_enabled(ovl)) {
+                       if (oc->enabled) {
+                               oc->enabled = false;
+                               oc->dirty = true;
+                       }
+                       continue;
+               }
+
+               if (!ovl->info_dirty) {
+                       if (oc->enabled)
+                               ++num_planes_enabled;
+                       continue;
+               }
+
+               dssdev = ovl->manager->device;
+
+               if (dss_check_overlay(ovl, dssdev)) {
+                       if (oc->enabled) {
+                               oc->enabled = false;
+                               oc->dirty = true;
+                       }
+                       continue;
+               }
+
+               ovl->info_dirty = false;
+               oc->dirty = true;
+
+               oc->paddr = ovl->info.paddr;
+               oc->vaddr = ovl->info.vaddr;
+               oc->screen_width = ovl->info.screen_width;
+               oc->width = ovl->info.width;
+               oc->height = ovl->info.height;
+               oc->color_mode = ovl->info.color_mode;
+               oc->rotation = ovl->info.rotation;
+               oc->rotation_type = ovl->info.rotation_type;
+               oc->mirror = ovl->info.mirror;
+               oc->pos_x = ovl->info.pos_x;
+               oc->pos_y = ovl->info.pos_y;
+               oc->out_width = ovl->info.out_width;
+               oc->out_height = ovl->info.out_height;
+               oc->global_alpha = ovl->info.global_alpha;
+
+               oc->replication =
+                       dss_use_replication(dssdev, ovl->info.color_mode);
+
+               oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
+
+               oc->channel = ovl->manager->id;
+
+               oc->enabled = true;
+
+               oc->manual_update =
+                       dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+                       dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
+
+               ++num_planes_enabled;
+       }
+
+       /* Configure managers */
+       list_for_each_entry(mgr, &manager_list, list) {
+               struct omap_dss_device *dssdev;
+
+               if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
+                       continue;
+
+               mc = &dss_cache.manager_cache[mgr->id];
+
+               if (mgr->device_changed) {
+                       mgr->device_changed = false;
+                       mgr->info_dirty  = true;
+               }
+
+               if (!mgr->info_dirty)
+                       continue;
+
+               if (!mgr->device)
+                       continue;
+
+               dssdev = mgr->device;
+
+               mgr->info_dirty = false;
+               mc->dirty = true;
+
+               mc->default_color = mgr->info.default_color;
+               mc->trans_key_type = mgr->info.trans_key_type;
+               mc->trans_key = mgr->info.trans_key;
+               mc->trans_enabled = mgr->info.trans_enabled;
+               mc->alpha_enabled = mgr->info.alpha_enabled;
+
+               mc->manual_upd_display =
+                       dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+
+               mc->manual_update =
+                       dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+                       dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO;
+       }
+
+       /* XXX TODO: Try to get fifomerge working. The problem is that it
+        * affects both managers, not individually but at the same time. This
+        * means the change has to be well synchronized. I guess the proper way
+        * is to have a two step process for fifo merge:
+        *        fifomerge enable:
+        *             1. disable other planes, leaving one plane enabled
+        *             2. wait until the planes are disabled on HW
+        *             3. config merged fifo thresholds, enable fifomerge
+        *        fifomerge disable:
+        *             1. config unmerged fifo thresholds, disable fifomerge
+        *             2. wait until fifo changes are in HW
+        *             3. enable planes
+        */
+       use_fifomerge = false;
+
+       /* Configure overlay fifos */
+       for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+               struct omap_dss_device *dssdev;
+               u32 size;
+
+               ovl = omap_dss_get_overlay(i);
+
+               if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+                       continue;
+
+               oc = &dss_cache.overlay_cache[ovl->id];
+
+               if (!oc->enabled)
+                       continue;
+
+               dssdev = ovl->manager->device;
+
+               size = dispc_get_plane_fifo_size(ovl->id);
+               if (use_fifomerge)
+                       size *= 3;
+
+               switch (dssdev->type) {
+               case OMAP_DISPLAY_TYPE_DPI:
+               case OMAP_DISPLAY_TYPE_DBI:
+               case OMAP_DISPLAY_TYPE_SDI:
+               case OMAP_DISPLAY_TYPE_VENC:
+                       default_get_overlay_fifo_thresholds(ovl->id, size,
+                                       &oc->burst_size, &oc->fifo_low,
+                                       &oc->fifo_high);
+                       break;
+#ifdef CONFIG_OMAP2_DSS_DSI
+               case OMAP_DISPLAY_TYPE_DSI:
+                       dsi_get_overlay_fifo_thresholds(ovl->id, size,
+                                       &oc->burst_size, &oc->fifo_low,
+                                       &oc->fifo_high);
+                       break;
+#endif
+               default:
+                       BUG();
+               }
+       }
+
+       r = 0;
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       if (!dss_cache.irq_enabled) {
+               r = omap_dispc_register_isr(dss_apply_irq_handler, NULL,
+                               DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
+                               DISPC_IRQ_EVSYNC_EVEN);
+               dss_cache.irq_enabled = true;
+       }
+       configure_dispc();
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+       return r;
+}
+
+static int dss_check_manager(struct omap_overlay_manager *mgr)
+{
+       /* OMAP supports only graphics source transparency color key and alpha
+        * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
+
+       if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
+                       mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
+               struct omap_overlay_manager_info *info)
+{
+       int r;
+       struct omap_overlay_manager_info old_info;
+
+       old_info = mgr->info;
+       mgr->info = *info;
+
+       r = dss_check_manager(mgr);
+       if (r) {
+               mgr->info = old_info;
+               return r;
+       }
+
+       mgr->info_dirty = true;
+
+       return 0;
+}
+
+static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
+               struct omap_overlay_manager_info *info)
+{
+       *info = mgr->info;
+}
+
+static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
+{
+       ++num_managers;
+       list_add_tail(&manager->list, &manager_list);
+}
+
+int dss_init_overlay_managers(struct platform_device *pdev)
+{
+       int i, r;
+
+       spin_lock_init(&dss_cache.lock);
+
+       INIT_LIST_HEAD(&manager_list);
+
+       num_managers = 0;
+
+       for (i = 0; i < 2; ++i) {
+               struct omap_overlay_manager *mgr;
+               mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+               BUG_ON(mgr == NULL);
+
+               switch (i) {
+               case 0:
+                       mgr->name = "lcd";
+                       mgr->id = OMAP_DSS_CHANNEL_LCD;
+                       mgr->supported_displays =
+                               OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI |
+                               OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI;
+                       break;
+               case 1:
+                       mgr->name = "tv";
+                       mgr->id = OMAP_DSS_CHANNEL_DIGIT;
+                       mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC;
+                       break;
+               }
+
+               mgr->set_device = &omap_dss_set_device;
+               mgr->unset_device = &omap_dss_unset_device;
+               mgr->apply = &omap_dss_mgr_apply;
+               mgr->set_manager_info = &omap_dss_mgr_set_info;
+               mgr->get_manager_info = &omap_dss_mgr_get_info;
+               mgr->wait_for_go = &dss_mgr_wait_for_go;
+
+               mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC;
+
+               dss_overlay_setup_dispc_manager(mgr);
+
+               omap_dss_add_overlay_manager(mgr);
+
+               r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
+                               &pdev->dev.kobj, "manager%d", i);
+
+               if (r) {
+                       DSSERR("failed to create sysfs file\n");
+                       continue;
+               }
+       }
+
+#ifdef L4_EXAMPLE
+       {
+               int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr)
+               {
+                       DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name);
+
+                       return 0;
+               }
+
+               struct omap_overlay_manager *mgr;
+               mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+
+               BUG_ON(mgr == NULL);
+
+               mgr->name = "l4";
+               mgr->supported_displays =
+                       OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI;
+
+               mgr->set_device = &omap_dss_set_device;
+               mgr->unset_device = &omap_dss_unset_device;
+               mgr->apply = &omap_dss_mgr_apply_l4;
+               mgr->set_manager_info = &omap_dss_mgr_set_info;
+               mgr->get_manager_info = &omap_dss_mgr_get_info;
+
+               dss_overlay_setup_l4_manager(mgr);
+
+               omap_dss_add_overlay_manager(mgr);
+
+               r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
+                               &pdev->dev.kobj, "managerl4");
+
+               if (r)
+                       DSSERR("failed to create sysfs file\n");
+       }
+#endif
+
+       return 0;
+}
+
+void dss_uninit_overlay_managers(struct platform_device *pdev)
+{
+       struct omap_overlay_manager *mgr;
+
+       while (!list_empty(&manager_list)) {
+               mgr = list_first_entry(&manager_list,
+                               struct omap_overlay_manager, list);
+               list_del(&mgr->list);
+               kobject_del(&mgr->kobj);
+               kobject_put(&mgr->kobj);
+               kfree(mgr);
+       }
+
+       num_managers = 0;
+}
+
+int omap_dss_get_num_overlay_managers(void)
+{
+       return num_managers;
+}
+EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
+
+struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
+{
+       int i = 0;
+       struct omap_overlay_manager *mgr;
+
+       list_for_each_entry(mgr, &manager_list, list) {
+               if (i++ == num)
+                       return mgr;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(omap_dss_get_overlay_manager);
+
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
new file mode 100644 (file)
index 0000000..b7f9a73
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ * linux/drivers/video/omap2/dss/overlay.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "OVERLAY"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+static int num_overlays;
+static struct list_head overlay_list;
+
+static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
+}
+
+static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       ovl->manager ? ovl->manager->name : "<none>");
+}
+
+static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
+               size_t size)
+{
+       int i, r;
+       struct omap_overlay_manager *mgr = NULL;
+       struct omap_overlay_manager *old_mgr;
+       int len = size;
+
+       if (buf[size-1] == '\n')
+               --len;
+
+       if (len > 0) {
+               for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
+                       mgr = omap_dss_get_overlay_manager(i);
+
+                       if (strncmp(buf, mgr->name, len) == 0)
+                               break;
+
+                       mgr = NULL;
+               }
+       }
+
+       if (len > 0 && mgr == NULL)
+               return -EINVAL;
+
+       if (mgr)
+               DSSDBG("manager %s found\n", mgr->name);
+
+       if (mgr == ovl->manager)
+               return size;
+
+       old_mgr = ovl->manager;
+
+       /* detach old manager */
+       if (old_mgr) {
+               r = ovl->unset_manager(ovl);
+               if (r) {
+                       DSSERR("detach failed\n");
+                       return r;
+               }
+
+               r = old_mgr->apply(old_mgr);
+               if (r)
+                       return r;
+       }
+
+       if (mgr) {
+               r = ovl->set_manager(ovl, mgr);
+               if (r) {
+                       DSSERR("Failed to attach overlay\n");
+                       return r;
+               }
+
+               r = mgr->apply(mgr);
+               if (r)
+                       return r;
+       }
+
+       return size;
+}
+
+static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+                       ovl->info.width, ovl->info.height);
+}
+
+static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width);
+}
+
+static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+                       ovl->info.pos_x, ovl->info.pos_y);
+}
+
+static ssize_t overlay_position_store(struct omap_overlay *ovl,
+               const char *buf, size_t size)
+{
+       int r;
+       char *last;
+       struct omap_overlay_info info;
+
+       ovl->get_overlay_info(ovl, &info);
+
+       info.pos_x = simple_strtoul(buf, &last, 10);
+       ++last;
+       if (last - buf >= size)
+               return -EINVAL;
+
+       info.pos_y = simple_strtoul(last, &last, 10);
+
+       r = ovl->set_overlay_info(ovl, &info);
+       if (r)
+               return r;
+
+       if (ovl->manager) {
+               r = ovl->manager->apply(ovl->manager);
+               if (r)
+                       return r;
+       }
+
+       return size;
+}
+
+static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d,%d\n",
+                       ovl->info.out_width, ovl->info.out_height);
+}
+
+static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
+               const char *buf, size_t size)
+{
+       int r;
+       char *last;
+       struct omap_overlay_info info;
+
+       ovl->get_overlay_info(ovl, &info);
+
+       info.out_width = simple_strtoul(buf, &last, 10);
+       ++last;
+       if (last - buf >= size)
+               return -EINVAL;
+
+       info.out_height = simple_strtoul(last, &last, 10);
+
+       r = ovl->set_overlay_info(ovl, &info);
+       if (r)
+               return r;
+
+       if (ovl->manager) {
+               r = ovl->manager->apply(ovl->manager);
+               if (r)
+                       return r;
+       }
+
+       return size;
+}
+
+static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled);
+}
+
+static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
+               size_t size)
+{
+       int r;
+       struct omap_overlay_info info;
+
+       ovl->get_overlay_info(ovl, &info);
+
+       info.enabled = simple_strtoul(buf, NULL, 10);
+
+       r = ovl->set_overlay_info(ovl, &info);
+       if (r)
+               return r;
+
+       if (ovl->manager) {
+               r = ovl->manager->apply(ovl->manager);
+               if (r)
+                       return r;
+       }
+
+       return size;
+}
+
+static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       ovl->info.global_alpha);
+}
+
+static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
+               const char *buf, size_t size)
+{
+       int r;
+       struct omap_overlay_info info;
+
+       ovl->get_overlay_info(ovl, &info);
+
+       /* Video1 plane does not support global alpha
+        * to always make it 255 completely opaque
+        */
+       if (ovl->id == OMAP_DSS_VIDEO1)
+               info.global_alpha = 255;
+       else
+               info.global_alpha = simple_strtoul(buf, NULL, 10);
+
+       r = ovl->set_overlay_info(ovl, &info);
+       if (r)
+               return r;
+
+       if (ovl->manager) {
+               r = ovl->manager->apply(ovl->manager);
+               if (r)
+                       return r;
+       }
+
+       return size;
+}
+
+struct overlay_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct omap_overlay *, char *);
+       ssize_t (*store)(struct omap_overlay *, const char *, size_t);
+};
+
+#define OVERLAY_ATTR(_name, _mode, _show, _store) \
+       struct overlay_attribute overlay_attr_##_name = \
+       __ATTR(_name, _mode, _show, _store)
+
+static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
+static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
+               overlay_manager_show, overlay_manager_store);
+static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
+static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
+static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
+               overlay_position_show, overlay_position_store);
+static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
+               overlay_output_size_show, overlay_output_size_store);
+static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
+               overlay_enabled_show, overlay_enabled_store);
+static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
+               overlay_global_alpha_show, overlay_global_alpha_store);
+
+static struct attribute *overlay_sysfs_attrs[] = {
+       &overlay_attr_name.attr,
+       &overlay_attr_manager.attr,
+       &overlay_attr_input_size.attr,
+       &overlay_attr_screen_width.attr,
+       &overlay_attr_position.attr,
+       &overlay_attr_output_size.attr,
+       &overlay_attr_enabled.attr,
+       &overlay_attr_global_alpha.attr,
+       NULL
+};
+
+static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
+               char *buf)
+{
+       struct omap_overlay *overlay;
+       struct overlay_attribute *overlay_attr;
+
+       overlay = container_of(kobj, struct omap_overlay, kobj);
+       overlay_attr = container_of(attr, struct overlay_attribute, attr);
+
+       if (!overlay_attr->show)
+               return -ENOENT;
+
+       return overlay_attr->show(overlay, buf);
+}
+
+static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
+               const char *buf, size_t size)
+{
+       struct omap_overlay *overlay;
+       struct overlay_attribute *overlay_attr;
+
+       overlay = container_of(kobj, struct omap_overlay, kobj);
+       overlay_attr = container_of(attr, struct overlay_attribute, attr);
+
+       if (!overlay_attr->store)
+               return -ENOENT;
+
+       return overlay_attr->store(overlay, buf, size);
+}
+
+static struct sysfs_ops overlay_sysfs_ops = {
+       .show = overlay_attr_show,
+       .store = overlay_attr_store,
+};
+
+static struct kobj_type overlay_ktype = {
+       .sysfs_ops = &overlay_sysfs_ops,
+       .default_attrs = overlay_sysfs_attrs,
+};
+
+/* Check if overlay parameters are compatible with display */
+int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
+{
+       struct omap_overlay_info *info;
+       u16 outw, outh;
+       u16 dw, dh;
+
+       if (!dssdev)
+               return 0;
+
+       if (!ovl->info.enabled)
+               return 0;
+
+       info = &ovl->info;
+
+       if (info->paddr == 0) {
+               DSSDBG("check_overlay failed: paddr 0\n");
+               return -EINVAL;
+       }
+
+       dssdev->get_resolution(dssdev, &dw, &dh);
+
+       DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n",
+                       ovl->id,
+                       info->pos_x, info->pos_y,
+                       info->width, info->height,
+                       info->out_width, info->out_height,
+                       dw, dh);
+
+       if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+               outw = info->width;
+               outh = info->height;
+       } else {
+               if (info->out_width == 0)
+                       outw = info->width;
+               else
+                       outw = info->out_width;
+
+               if (info->out_height == 0)
+                       outh = info->height;
+               else
+                       outh = info->out_height;
+       }
+
+       if (dw < info->pos_x + outw) {
+               DSSDBG("check_overlay failed 1: %d < %d + %d\n",
+                               dw, info->pos_x, outw);
+               return -EINVAL;
+       }
+
+       if (dh < info->pos_y + outh) {
+               DSSDBG("check_overlay failed 2: %d < %d + %d\n",
+                               dh, info->pos_y, outh);
+               return -EINVAL;
+       }
+
+       if ((ovl->supported_modes & info->color_mode) == 0) {
+               DSSERR("overlay doesn't support mode %d\n", info->color_mode);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
+               struct omap_overlay_info *info)
+{
+       int r;
+       struct omap_overlay_info old_info;
+
+       old_info = ovl->info;
+       ovl->info = *info;
+
+       if (ovl->manager) {
+               r = dss_check_overlay(ovl, ovl->manager->device);
+               if (r) {
+                       ovl->info = old_info;
+                       return r;
+               }
+       }
+
+       ovl->info_dirty = true;
+
+       return 0;
+}
+
+static void dss_ovl_get_overlay_info(struct omap_overlay *ovl,
+               struct omap_overlay_info *info)
+{
+       *info = ovl->info;
+}
+
+static int dss_ovl_wait_for_go(struct omap_overlay *ovl)
+{
+       return dss_mgr_wait_for_go_ovl(ovl);
+}
+
+static int omap_dss_set_manager(struct omap_overlay *ovl,
+               struct omap_overlay_manager *mgr)
+{
+       if (!mgr)
+               return -EINVAL;
+
+       if (ovl->manager) {
+               DSSERR("overlay '%s' already has a manager '%s'\n",
+                               ovl->name, ovl->manager->name);
+               return -EINVAL;
+       }
+
+       if (ovl->info.enabled) {
+               DSSERR("overlay has to be disabled to change the manager\n");
+               return -EINVAL;
+       }
+
+       ovl->manager = mgr;
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       /* XXX: on manual update display, in auto update mode, a bug happens
+        * here. When an overlay is first enabled on LCD, then it's disabled,
+        * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT
+        * errors. Waiting before changing the channel_out fixes it. I'm
+        * guessing that the overlay is still somehow being used for the LCD,
+        * but I don't understand how or why. */
+       msleep(40);
+       dispc_set_channel_out(ovl->id, mgr->id);
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       return 0;
+}
+
+static int omap_dss_unset_manager(struct omap_overlay *ovl)
+{
+       int r;
+
+       if (!ovl->manager) {
+               DSSERR("failed to detach overlay: manager not set\n");
+               return -EINVAL;
+       }
+
+       if (ovl->info.enabled) {
+               DSSERR("overlay has to be disabled to unset the manager\n");
+               return -EINVAL;
+       }
+
+       r = ovl->wait_for_go(ovl);
+       if (r)
+               return r;
+
+       ovl->manager = NULL;
+
+       return 0;
+}
+
+int omap_dss_get_num_overlays(void)
+{
+       return num_overlays;
+}
+EXPORT_SYMBOL(omap_dss_get_num_overlays);
+
+struct omap_overlay *omap_dss_get_overlay(int num)
+{
+       int i = 0;
+       struct omap_overlay *ovl;
+
+       list_for_each_entry(ovl, &overlay_list, list) {
+               if (i++ == num)
+                       return ovl;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(omap_dss_get_overlay);
+
+static void omap_dss_add_overlay(struct omap_overlay *overlay)
+{
+       ++num_overlays;
+       list_add_tail(&overlay->list, &overlay_list);
+}
+
+static struct omap_overlay *dispc_overlays[3];
+
+void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
+{
+       mgr->num_overlays = 3;
+       mgr->overlays = dispc_overlays;
+}
+
+#ifdef L4_EXAMPLE
+static struct omap_overlay *l4_overlays[1];
+void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
+{
+       mgr->num_overlays = 1;
+       mgr->overlays = l4_overlays;
+}
+#endif
+
+void dss_init_overlays(struct platform_device *pdev)
+{
+       int i, r;
+
+       INIT_LIST_HEAD(&overlay_list);
+
+       num_overlays = 0;
+
+       for (i = 0; i < 3; ++i) {
+               struct omap_overlay *ovl;
+               ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
+
+               BUG_ON(ovl == NULL);
+
+               switch (i) {
+               case 0:
+                       ovl->name = "gfx";
+                       ovl->id = OMAP_DSS_GFX;
+                       ovl->supported_modes = cpu_is_omap34xx() ?
+                               OMAP_DSS_COLOR_GFX_OMAP3 :
+                               OMAP_DSS_COLOR_GFX_OMAP2;
+                       ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
+                       ovl->info.global_alpha = 255;
+                       break;
+               case 1:
+                       ovl->name = "vid1";
+                       ovl->id = OMAP_DSS_VIDEO1;
+                       ovl->supported_modes = cpu_is_omap34xx() ?
+                               OMAP_DSS_COLOR_VID1_OMAP3 :
+                               OMAP_DSS_COLOR_VID_OMAP2;
+                       ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
+                               OMAP_DSS_OVL_CAP_DISPC;
+                       ovl->info.global_alpha = 255;
+                       break;
+               case 2:
+                       ovl->name = "vid2";
+                       ovl->id = OMAP_DSS_VIDEO2;
+                       ovl->supported_modes = cpu_is_omap34xx() ?
+                               OMAP_DSS_COLOR_VID2_OMAP3 :
+                               OMAP_DSS_COLOR_VID_OMAP2;
+                       ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
+                               OMAP_DSS_OVL_CAP_DISPC;
+                       ovl->info.global_alpha = 255;
+                       break;
+               }
+
+               ovl->set_manager = &omap_dss_set_manager;
+               ovl->unset_manager = &omap_dss_unset_manager;
+               ovl->set_overlay_info = &dss_ovl_set_overlay_info;
+               ovl->get_overlay_info = &dss_ovl_get_overlay_info;
+               ovl->wait_for_go = &dss_ovl_wait_for_go;
+
+               omap_dss_add_overlay(ovl);
+
+               r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
+                               &pdev->dev.kobj, "overlay%d", i);
+
+               if (r) {
+                       DSSERR("failed to create sysfs file\n");
+                       continue;
+               }
+
+               dispc_overlays[i] = ovl;
+       }
+
+#ifdef L4_EXAMPLE
+       {
+               struct omap_overlay *ovl;
+               ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
+
+               BUG_ON(ovl == NULL);
+
+               ovl->name = "l4";
+               ovl->supported_modes = OMAP_DSS_COLOR_RGB24U;
+
+               ovl->set_manager = &omap_dss_set_manager;
+               ovl->unset_manager = &omap_dss_unset_manager;
+               ovl->set_overlay_info = &dss_ovl_set_overlay_info;
+               ovl->get_overlay_info = &dss_ovl_get_overlay_info;
+
+               omap_dss_add_overlay(ovl);
+
+               r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
+                               &pdev->dev.kobj, "overlayl4");
+
+               if (r)
+                       DSSERR("failed to create sysfs file\n");
+
+               l4_overlays[0] = ovl;
+       }
+#endif
+}
+
+/* connect overlays to the new device, if not already connected. if force
+ * selected, connect always. */
+void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
+{
+       int i;
+       struct omap_overlay_manager *lcd_mgr;
+       struct omap_overlay_manager *tv_mgr;
+       struct omap_overlay_manager *mgr = NULL;
+
+       lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
+       tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
+
+       if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) {
+               if (!lcd_mgr->device || force) {
+                       if (lcd_mgr->device)
+                               lcd_mgr->unset_device(lcd_mgr);
+                       lcd_mgr->set_device(lcd_mgr, dssdev);
+                       mgr = lcd_mgr;
+               }
+       }
+
+       if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
+               if (!tv_mgr->device || force) {
+                       if (tv_mgr->device)
+                               tv_mgr->unset_device(tv_mgr);
+                       tv_mgr->set_device(tv_mgr, dssdev);
+                       mgr = tv_mgr;
+               }
+       }
+
+       if (mgr) {
+               for (i = 0; i < 3; i++) {
+                       struct omap_overlay *ovl;
+                       ovl = omap_dss_get_overlay(i);
+                       if (!ovl->manager || force) {
+                               if (ovl->manager)
+                                       omap_dss_unset_manager(ovl);
+                               omap_dss_set_manager(ovl, mgr);
+                       }
+               }
+       }
+}
+
+void dss_uninit_overlays(struct platform_device *pdev)
+{
+       struct omap_overlay *ovl;
+
+       while (!list_empty(&overlay_list)) {
+               ovl = list_first_entry(&overlay_list,
+                               struct omap_overlay, list);
+               list_del(&ovl->list);
+               kobject_del(&ovl->kobj);
+               kobject_put(&ovl->kobj);
+               kfree(ovl);
+       }
+
+       num_overlays = 0;
+}
+
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
new file mode 100644 (file)
index 0000000..d0b3006
--- /dev/null
@@ -0,0 +1,1309 @@
+/*
+ * linux/drivers/video/omap2/dss/rfbi.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "RFBI"
+
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/kfifo.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/seq_file.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+/*#define MEASURE_PERF*/
+
+#define RFBI_BASE               0x48050800
+
+struct rfbi_reg { u16 idx; };
+
+#define RFBI_REG(idx)          ((const struct rfbi_reg) { idx })
+
+#define RFBI_REVISION          RFBI_REG(0x0000)
+#define RFBI_SYSCONFIG         RFBI_REG(0x0010)
+#define RFBI_SYSSTATUS         RFBI_REG(0x0014)
+#define RFBI_CONTROL           RFBI_REG(0x0040)
+#define RFBI_PIXEL_CNT         RFBI_REG(0x0044)
+#define RFBI_LINE_NUMBER       RFBI_REG(0x0048)
+#define RFBI_CMD               RFBI_REG(0x004c)
+#define RFBI_PARAM             RFBI_REG(0x0050)
+#define RFBI_DATA              RFBI_REG(0x0054)
+#define RFBI_READ              RFBI_REG(0x0058)
+#define RFBI_STATUS            RFBI_REG(0x005c)
+
+#define RFBI_CONFIG(n)         RFBI_REG(0x0060 + (n)*0x18)
+#define RFBI_ONOFF_TIME(n)     RFBI_REG(0x0064 + (n)*0x18)
+#define RFBI_CYCLE_TIME(n)     RFBI_REG(0x0068 + (n)*0x18)
+#define RFBI_DATA_CYCLE1(n)    RFBI_REG(0x006c + (n)*0x18)
+#define RFBI_DATA_CYCLE2(n)    RFBI_REG(0x0070 + (n)*0x18)
+#define RFBI_DATA_CYCLE3(n)    RFBI_REG(0x0074 + (n)*0x18)
+
+#define RFBI_VSYNC_WIDTH       RFBI_REG(0x0090)
+#define RFBI_HSYNC_WIDTH       RFBI_REG(0x0094)
+
+#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param))
+
+#define REG_FLD_MOD(idx, val, start, end) \
+       rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end))
+
+/* To work around an RFBI transfer rate limitation */
+#define OMAP_RFBI_RATE_LIMIT    1
+
+enum omap_rfbi_cycleformat {
+       OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0,
+       OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1,
+       OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2,
+       OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3,
+};
+
+enum omap_rfbi_datatype {
+       OMAP_DSS_RFBI_DATATYPE_12 = 0,
+       OMAP_DSS_RFBI_DATATYPE_16 = 1,
+       OMAP_DSS_RFBI_DATATYPE_18 = 2,
+       OMAP_DSS_RFBI_DATATYPE_24 = 3,
+};
+
+enum omap_rfbi_parallelmode {
+       OMAP_DSS_RFBI_PARALLELMODE_8 = 0,
+       OMAP_DSS_RFBI_PARALLELMODE_9 = 1,
+       OMAP_DSS_RFBI_PARALLELMODE_12 = 2,
+       OMAP_DSS_RFBI_PARALLELMODE_16 = 3,
+};
+
+enum update_cmd {
+       RFBI_CMD_UPDATE = 0,
+       RFBI_CMD_SYNC   = 1,
+};
+
+static int rfbi_convert_timings(struct rfbi_timings *t);
+static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div);
+static void process_cmd_fifo(void);
+
+static struct {
+       void __iomem    *base;
+
+       unsigned long   l4_khz;
+
+       enum omap_rfbi_datatype datatype;
+       enum omap_rfbi_parallelmode parallelmode;
+
+       enum omap_rfbi_te_mode te_mode;
+       int te_enabled;
+
+       void (*framedone_callback)(void *data);
+       void *framedone_callback_data;
+
+       struct omap_dss_device *dssdev[2];
+
+       struct kfifo      *cmd_fifo;
+       spinlock_t        cmd_lock;
+       struct completion cmd_done;
+       atomic_t          cmd_fifo_full;
+       atomic_t          cmd_pending;
+#ifdef MEASURE_PERF
+       unsigned perf_bytes;
+       ktime_t perf_setup_time;
+       ktime_t perf_start_time;
+#endif
+} rfbi;
+
+struct update_region {
+       u16     x;
+       u16     y;
+       u16     w;
+       u16     h;
+};
+
+struct update_param {
+       u8 rfbi_module;
+       u8 cmd;
+
+       union {
+               struct update_region r;
+               struct completion *sync;
+       } par;
+};
+
+static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val)
+{
+       __raw_writel(val, rfbi.base + idx.idx);
+}
+
+static inline u32 rfbi_read_reg(const struct rfbi_reg idx)
+{
+       return __raw_readl(rfbi.base + idx.idx);
+}
+
+static void rfbi_enable_clocks(bool enable)
+{
+       if (enable)
+               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       else
+               dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+void omap_rfbi_write_command(const void *buf, u32 len)
+{
+       rfbi_enable_clocks(1);
+       switch (rfbi.parallelmode) {
+       case OMAP_DSS_RFBI_PARALLELMODE_8:
+       {
+               const u8 *b = buf;
+               for (; len; len--)
+                       rfbi_write_reg(RFBI_CMD, *b++);
+               break;
+       }
+
+       case OMAP_DSS_RFBI_PARALLELMODE_16:
+       {
+               const u16 *w = buf;
+               BUG_ON(len & 1);
+               for (; len; len -= 2)
+                       rfbi_write_reg(RFBI_CMD, *w++);
+               break;
+       }
+
+       case OMAP_DSS_RFBI_PARALLELMODE_9:
+       case OMAP_DSS_RFBI_PARALLELMODE_12:
+       default:
+               BUG();
+       }
+       rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_write_command);
+
+void omap_rfbi_read_data(void *buf, u32 len)
+{
+       rfbi_enable_clocks(1);
+       switch (rfbi.parallelmode) {
+       case OMAP_DSS_RFBI_PARALLELMODE_8:
+       {
+               u8 *b = buf;
+               for (; len; len--) {
+                       rfbi_write_reg(RFBI_READ, 0);
+                       *b++ = rfbi_read_reg(RFBI_READ);
+               }
+               break;
+       }
+
+       case OMAP_DSS_RFBI_PARALLELMODE_16:
+       {
+               u16 *w = buf;
+               BUG_ON(len & ~1);
+               for (; len; len -= 2) {
+                       rfbi_write_reg(RFBI_READ, 0);
+                       *w++ = rfbi_read_reg(RFBI_READ);
+               }
+               break;
+       }
+
+       case OMAP_DSS_RFBI_PARALLELMODE_9:
+       case OMAP_DSS_RFBI_PARALLELMODE_12:
+       default:
+               BUG();
+       }
+       rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_read_data);
+
+void omap_rfbi_write_data(const void *buf, u32 len)
+{
+       rfbi_enable_clocks(1);
+       switch (rfbi.parallelmode) {
+       case OMAP_DSS_RFBI_PARALLELMODE_8:
+       {
+               const u8 *b = buf;
+               for (; len; len--)
+                       rfbi_write_reg(RFBI_PARAM, *b++);
+               break;
+       }
+
+       case OMAP_DSS_RFBI_PARALLELMODE_16:
+       {
+               const u16 *w = buf;
+               BUG_ON(len & 1);
+               for (; len; len -= 2)
+                       rfbi_write_reg(RFBI_PARAM, *w++);
+               break;
+       }
+
+       case OMAP_DSS_RFBI_PARALLELMODE_9:
+       case OMAP_DSS_RFBI_PARALLELMODE_12:
+       default:
+               BUG();
+
+       }
+       rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_write_data);
+
+void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
+               u16 x, u16 y,
+               u16 w, u16 h)
+{
+       int start_offset = scr_width * y + x;
+       int horiz_offset = scr_width - w;
+       int i;
+
+       rfbi_enable_clocks(1);
+
+       if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 &&
+          rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) {
+               const u16 __iomem *pd = buf;
+               pd += start_offset;
+
+               for (; h; --h) {
+                       for (i = 0; i < w; ++i) {
+                               const u8 __iomem *b = (const u8 __iomem *)pd;
+                               rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1));
+                               rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0));
+                               ++pd;
+                       }
+                       pd += horiz_offset;
+               }
+       } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 &&
+          rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) {
+               const u32 __iomem *pd = buf;
+               pd += start_offset;
+
+               for (; h; --h) {
+                       for (i = 0; i < w; ++i) {
+                               const u8 __iomem *b = (const u8 __iomem *)pd;
+                               rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2));
+                               rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1));
+                               rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0));
+                               ++pd;
+                       }
+                       pd += horiz_offset;
+               }
+       } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 &&
+          rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) {
+               const u16 __iomem *pd = buf;
+               pd += start_offset;
+
+               for (; h; --h) {
+                       for (i = 0; i < w; ++i) {
+                               rfbi_write_reg(RFBI_PARAM, __raw_readw(pd));
+                               ++pd;
+                       }
+                       pd += horiz_offset;
+               }
+       } else {
+               BUG();
+       }
+
+       rfbi_enable_clocks(0);
+}
+EXPORT_SYMBOL(omap_rfbi_write_pixels);
+
+#ifdef MEASURE_PERF
+static void perf_mark_setup(void)
+{
+       rfbi.perf_setup_time = ktime_get();
+}
+
+static void perf_mark_start(void)
+{
+       rfbi.perf_start_time = ktime_get();
+}
+
+static void perf_show(const char *name)
+{
+       ktime_t t, setup_time, trans_time;
+       u32 total_bytes;
+       u32 setup_us, trans_us, total_us;
+
+       t = ktime_get();
+
+       setup_time = ktime_sub(rfbi.perf_start_time, rfbi.perf_setup_time);
+       setup_us = (u32)ktime_to_us(setup_time);
+       if (setup_us == 0)
+               setup_us = 1;
+
+       trans_time = ktime_sub(t, rfbi.perf_start_time);
+       trans_us = (u32)ktime_to_us(trans_time);
+       if (trans_us == 0)
+               trans_us = 1;
+
+       total_us = setup_us + trans_us;
+
+       total_bytes = rfbi.perf_bytes;
+
+       DSSINFO("%s update %u us + %u us = %u us (%uHz), %u bytes, "
+                       "%u kbytes/sec\n",
+                       name,
+                       setup_us,
+                       trans_us,
+                       total_us,
+                       1000*1000 / total_us,
+                       total_bytes,
+                       total_bytes * 1000 / total_us);
+}
+#else
+#define perf_mark_setup()
+#define perf_mark_start()
+#define perf_show(x)
+#endif
+
+void rfbi_transfer_area(u16 width, u16 height,
+                            void (callback)(void *data), void *data)
+{
+       u32 l;
+
+       /*BUG_ON(callback == 0);*/
+       BUG_ON(rfbi.framedone_callback != NULL);
+
+       DSSDBG("rfbi_transfer_area %dx%d\n", width, height);
+
+       dispc_set_lcd_size(width, height);
+
+       dispc_enable_lcd_out(1);
+
+       rfbi.framedone_callback = callback;
+       rfbi.framedone_callback_data = data;
+
+       rfbi_enable_clocks(1);
+
+       rfbi_write_reg(RFBI_PIXEL_CNT, width * height);
+
+       l = rfbi_read_reg(RFBI_CONTROL);
+       l = FLD_MOD(l, 1, 0, 0); /* enable */
+       if (!rfbi.te_enabled)
+               l = FLD_MOD(l, 1, 4, 4); /* ITE */
+
+       perf_mark_start();
+
+       rfbi_write_reg(RFBI_CONTROL, l);
+}
+
+static void framedone_callback(void *data, u32 mask)
+{
+       void (*callback)(void *data);
+
+       DSSDBG("FRAMEDONE\n");
+
+       perf_show("DISPC");
+
+       REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0);
+
+       rfbi_enable_clocks(0);
+
+       callback = rfbi.framedone_callback;
+       rfbi.framedone_callback = NULL;
+
+       /*callback(rfbi.framedone_callback_data);*/
+
+       atomic_set(&rfbi.cmd_pending, 0);
+
+       process_cmd_fifo();
+}
+
+#if 1 /* VERBOSE */
+static void rfbi_print_timings(void)
+{
+       u32 l;
+       u32 time;
+
+       l = rfbi_read_reg(RFBI_CONFIG(0));
+       time = 1000000000 / rfbi.l4_khz;
+       if (l & (1 << 4))
+               time *= 2;
+
+       DSSDBG("Tick time %u ps\n", time);
+       l = rfbi_read_reg(RFBI_ONOFF_TIME(0));
+       DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
+               "REONTIME %d, REOFFTIME %d\n",
+               l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
+               (l >> 20) & 0x0f, (l >> 24) & 0x3f);
+
+       l = rfbi_read_reg(RFBI_CYCLE_TIME(0));
+       DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
+               "ACCESSTIME %d\n",
+               (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f,
+               (l >> 22) & 0x3f);
+}
+#else
+static void rfbi_print_timings(void) {}
+#endif
+
+
+
+
+static u32 extif_clk_period;
+
+static inline unsigned long round_to_extif_ticks(unsigned long ps, int div)
+{
+       int bus_tick = extif_clk_period * div;
+       return (ps + bus_tick - 1) / bus_tick * bus_tick;
+}
+
+static int calc_reg_timing(struct rfbi_timings *t, int div)
+{
+       t->clk_div = div;
+
+       t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div);
+
+       t->we_on_time = round_to_extif_ticks(t->we_on_time, div);
+       t->we_off_time = round_to_extif_ticks(t->we_off_time, div);
+       t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div);
+
+       t->re_on_time = round_to_extif_ticks(t->re_on_time, div);
+       t->re_off_time = round_to_extif_ticks(t->re_off_time, div);
+       t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div);
+
+       t->access_time = round_to_extif_ticks(t->access_time, div);
+       t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div);
+       t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div);
+
+       DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n",
+              t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
+       DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n",
+              t->we_on_time, t->we_off_time, t->re_cycle_time,
+              t->we_cycle_time);
+       DSSDBG("[reg]rdaccess %d cspulse %d\n",
+              t->access_time, t->cs_pulse_width);
+
+       return rfbi_convert_timings(t);
+}
+
+static int calc_extif_timings(struct rfbi_timings *t)
+{
+       u32 max_clk_div;
+       int div;
+
+       rfbi_get_clk_info(&extif_clk_period, &max_clk_div);
+       for (div = 1; div <= max_clk_div; div++) {
+               if (calc_reg_timing(t, div) == 0)
+                       break;
+       }
+
+       if (div <= max_clk_div)
+               return 0;
+
+       DSSERR("can't setup timings\n");
+       return -1;
+}
+
+
+void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t)
+{
+       int r;
+
+       if (!t->converted) {
+               r = calc_extif_timings(t);
+               if (r < 0)
+                       DSSERR("Failed to calc timings\n");
+       }
+
+       BUG_ON(!t->converted);
+
+       rfbi_enable_clocks(1);
+       rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]);
+       rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]);
+
+       /* TIMEGRANULARITY */
+       REG_FLD_MOD(RFBI_CONFIG(rfbi_module),
+                   (t->tim[2] ? 1 : 0), 4, 4);
+
+       rfbi_print_timings();
+       rfbi_enable_clocks(0);
+}
+
+static int ps_to_rfbi_ticks(int time, int div)
+{
+       unsigned long tick_ps;
+       int ret;
+
+       /* Calculate in picosecs to yield more exact results */
+       tick_ps = 1000000000 / (rfbi.l4_khz) * div;
+
+       ret = (time + tick_ps - 1) / tick_ps;
+
+       return ret;
+}
+
+#ifdef OMAP_RFBI_RATE_LIMIT
+unsigned long rfbi_get_max_tx_rate(void)
+{
+       unsigned long   l4_rate, dss1_rate;
+       int             min_l4_ticks = 0;
+       int             i;
+
+       /* According to TI this can't be calculated so make the
+        * adjustments for a couple of known frequencies and warn for
+        * others.
+        */
+       static const struct {
+               unsigned long l4_clk;           /* HZ */
+               unsigned long dss1_clk;         /* HZ */
+               unsigned long min_l4_ticks;
+       } ftab[] = {
+               { 55,   132,    7, },           /* 7.86 MPix/s */
+               { 110,  110,    12, },          /* 9.16 MPix/s */
+               { 110,  132,    10, },          /* 11   Mpix/s */
+               { 120,  120,    10, },          /* 12   Mpix/s */
+               { 133,  133,    10, },          /* 13.3 Mpix/s */
+       };
+
+       l4_rate = rfbi.l4_khz / 1000;
+       dss1_rate = dss_clk_get_rate(DSS_CLK_FCK1) / 1000000;
+
+       for (i = 0; i < ARRAY_SIZE(ftab); i++) {
+               /* Use a window instead of an exact match, to account
+                * for different DPLL multiplier / divider pairs.
+                */
+               if (abs(ftab[i].l4_clk - l4_rate) < 3 &&
+                   abs(ftab[i].dss1_clk - dss1_rate) < 3) {
+                       min_l4_ticks = ftab[i].min_l4_ticks;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(ftab)) {
+               /* Can't be sure, return anyway the maximum not
+                * rate-limited. This might cause a problem only for the
+                * tearing synchronisation.
+                */
+               DSSERR("can't determine maximum RFBI transfer rate\n");
+               return rfbi.l4_khz * 1000;
+       }
+       return rfbi.l4_khz * 1000 / min_l4_ticks;
+}
+#else
+int rfbi_get_max_tx_rate(void)
+{
+       return rfbi.l4_khz * 1000;
+}
+#endif
+
+static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
+{
+       *clk_period = 1000000000 / rfbi.l4_khz;
+       *max_clk_div = 2;
+}
+
+static int rfbi_convert_timings(struct rfbi_timings *t)
+{
+       u32 l;
+       int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
+       int actim, recyc, wecyc;
+       int div = t->clk_div;
+
+       if (div <= 0 || div > 2)
+               return -1;
+
+       /* Make sure that after conversion it still holds that:
+        * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
+        * csoff > cson, csoff >= max(weoff, reoff), actim > reon
+        */
+       weon = ps_to_rfbi_ticks(t->we_on_time, div);
+       weoff = ps_to_rfbi_ticks(t->we_off_time, div);
+       if (weoff <= weon)
+               weoff = weon + 1;
+       if (weon > 0x0f)
+               return -1;
+       if (weoff > 0x3f)
+               return -1;
+
+       reon = ps_to_rfbi_ticks(t->re_on_time, div);
+       reoff = ps_to_rfbi_ticks(t->re_off_time, div);
+       if (reoff <= reon)
+               reoff = reon + 1;
+       if (reon > 0x0f)
+               return -1;
+       if (reoff > 0x3f)
+               return -1;
+
+       cson = ps_to_rfbi_ticks(t->cs_on_time, div);
+       csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
+       if (csoff <= cson)
+               csoff = cson + 1;
+       if (csoff < max(weoff, reoff))
+               csoff = max(weoff, reoff);
+       if (cson > 0x0f)
+               return -1;
+       if (csoff > 0x3f)
+               return -1;
+
+       l =  cson;
+       l |= csoff << 4;
+       l |= weon  << 10;
+       l |= weoff << 14;
+       l |= reon  << 20;
+       l |= reoff << 24;
+
+       t->tim[0] = l;
+
+       actim = ps_to_rfbi_ticks(t->access_time, div);
+       if (actim <= reon)
+               actim = reon + 1;
+       if (actim > 0x3f)
+               return -1;
+
+       wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
+       if (wecyc < weoff)
+               wecyc = weoff;
+       if (wecyc > 0x3f)
+               return -1;
+
+       recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
+       if (recyc < reoff)
+               recyc = reoff;
+       if (recyc > 0x3f)
+               return -1;
+
+       cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
+       if (cs_pulse > 0x3f)
+               return -1;
+
+       l =  wecyc;
+       l |= recyc    << 6;
+       l |= cs_pulse << 12;
+       l |= actim    << 22;
+
+       t->tim[1] = l;
+
+       t->tim[2] = div - 1;
+
+       t->converted = 1;
+
+       return 0;
+}
+
+/* xxx FIX module selection missing */
+int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode,
+                            unsigned hs_pulse_time, unsigned vs_pulse_time,
+                            int hs_pol_inv, int vs_pol_inv, int extif_div)
+{
+       int hs, vs;
+       int min;
+       u32 l;
+
+       hs = ps_to_rfbi_ticks(hs_pulse_time, 1);
+       vs = ps_to_rfbi_ticks(vs_pulse_time, 1);
+       if (hs < 2)
+               return -EDOM;
+       if (mode == OMAP_DSS_RFBI_TE_MODE_2)
+               min = 2;
+       else /* OMAP_DSS_RFBI_TE_MODE_1 */
+               min = 4;
+       if (vs < min)
+               return -EDOM;
+       if (vs == hs)
+               return -EINVAL;
+       rfbi.te_mode = mode;
+       DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n",
+               mode, hs, vs, hs_pol_inv, vs_pol_inv);
+
+       rfbi_enable_clocks(1);
+       rfbi_write_reg(RFBI_HSYNC_WIDTH, hs);
+       rfbi_write_reg(RFBI_VSYNC_WIDTH, vs);
+
+       l = rfbi_read_reg(RFBI_CONFIG(0));
+       if (hs_pol_inv)
+               l &= ~(1 << 21);
+       else
+               l |= 1 << 21;
+       if (vs_pol_inv)
+               l &= ~(1 << 20);
+       else
+               l |= 1 << 20;
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_rfbi_setup_te);
+
+/* xxx FIX module selection missing */
+int omap_rfbi_enable_te(bool enable, unsigned line)
+{
+       u32 l;
+
+       DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode);
+       if (line > (1 << 11) - 1)
+               return -EINVAL;
+
+       rfbi_enable_clocks(1);
+       l = rfbi_read_reg(RFBI_CONFIG(0));
+       l &= ~(0x3 << 2);
+       if (enable) {
+               rfbi.te_enabled = 1;
+               l |= rfbi.te_mode << 2;
+       } else
+               rfbi.te_enabled = 0;
+       rfbi_write_reg(RFBI_CONFIG(0), l);
+       rfbi_write_reg(RFBI_LINE_NUMBER, line);
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_rfbi_enable_te);
+
+#if 0
+static void rfbi_enable_config(int enable1, int enable2)
+{
+       u32 l;
+       int cs = 0;
+
+       if (enable1)
+               cs |= 1<<0;
+       if (enable2)
+               cs |= 1<<1;
+
+       rfbi_enable_clocks(1);
+
+       l = rfbi_read_reg(RFBI_CONTROL);
+
+       l = FLD_MOD(l, cs, 3, 2);
+       l = FLD_MOD(l, 0, 1, 1);
+
+       rfbi_write_reg(RFBI_CONTROL, l);
+
+
+       l = rfbi_read_reg(RFBI_CONFIG(0));
+       l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */
+       /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */
+       /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */
+
+       l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */
+       l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */
+       l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */
+
+       l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0);
+       rfbi_write_reg(RFBI_CONFIG(0), l);
+
+       rfbi_enable_clocks(0);
+}
+#endif
+
+int rfbi_configure(int rfbi_module, int bpp, int lines)
+{
+       u32 l;
+       int cycle1 = 0, cycle2 = 0, cycle3 = 0;
+       enum omap_rfbi_cycleformat cycleformat;
+       enum omap_rfbi_datatype datatype;
+       enum omap_rfbi_parallelmode parallelmode;
+
+       switch (bpp) {
+       case 12:
+               datatype = OMAP_DSS_RFBI_DATATYPE_12;
+               break;
+       case 16:
+               datatype = OMAP_DSS_RFBI_DATATYPE_16;
+               break;
+       case 18:
+               datatype = OMAP_DSS_RFBI_DATATYPE_18;
+               break;
+       case 24:
+               datatype = OMAP_DSS_RFBI_DATATYPE_24;
+               break;
+       default:
+               BUG();
+               return 1;
+       }
+       rfbi.datatype = datatype;
+
+       switch (lines) {
+       case 8:
+               parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8;
+               break;
+       case 9:
+               parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9;
+               break;
+       case 12:
+               parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12;
+               break;
+       case 16:
+               parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16;
+               break;
+       default:
+               BUG();
+               return 1;
+       }
+       rfbi.parallelmode = parallelmode;
+
+       if ((bpp % lines) == 0) {
+               switch (bpp / lines) {
+               case 1:
+                       cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1;
+                       break;
+               case 2:
+                       cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1;
+                       break;
+               case 3:
+                       cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1;
+                       break;
+               default:
+                       BUG();
+                       return 1;
+               }
+       } else if ((2 * bpp % lines) == 0) {
+               if ((2 * bpp / lines) == 3)
+                       cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2;
+               else {
+                       BUG();
+                       return 1;
+               }
+       } else {
+               BUG();
+               return 1;
+       }
+
+       switch (cycleformat) {
+       case OMAP_DSS_RFBI_CYCLEFORMAT_1_1:
+               cycle1 = lines;
+               break;
+
+       case OMAP_DSS_RFBI_CYCLEFORMAT_2_1:
+               cycle1 = lines;
+               cycle2 = lines;
+               break;
+
+       case OMAP_DSS_RFBI_CYCLEFORMAT_3_1:
+               cycle1 = lines;
+               cycle2 = lines;
+               cycle3 = lines;
+               break;
+
+       case OMAP_DSS_RFBI_CYCLEFORMAT_3_2:
+               cycle1 = lines;
+               cycle2 = (lines / 2) | ((lines / 2) << 16);
+               cycle3 = (lines << 16);
+               break;
+       }
+
+       rfbi_enable_clocks(1);
+
+       REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */
+
+       l = 0;
+       l |= FLD_VAL(parallelmode, 1, 0);
+       l |= FLD_VAL(0, 3, 2);          /* TRIGGERMODE: ITE */
+       l |= FLD_VAL(0, 4, 4);          /* TIMEGRANULARITY */
+       l |= FLD_VAL(datatype, 6, 5);
+       /* l |= FLD_VAL(2, 8, 7); */    /* L4FORMAT, 2pix/L4 */
+       l |= FLD_VAL(0, 8, 7);  /* L4FORMAT, 1pix/L4 */
+       l |= FLD_VAL(cycleformat, 10, 9);
+       l |= FLD_VAL(0, 12, 11);        /* UNUSEDBITS */
+       l |= FLD_VAL(0, 16, 16);        /* A0POLARITY */
+       l |= FLD_VAL(0, 17, 17);        /* REPOLARITY */
+       l |= FLD_VAL(0, 18, 18);        /* WEPOLARITY */
+       l |= FLD_VAL(0, 19, 19);        /* CSPOLARITY */
+       l |= FLD_VAL(1, 20, 20);        /* TE_VSYNC_POLARITY */
+       l |= FLD_VAL(1, 21, 21);        /* HSYNCPOLARITY */
+       rfbi_write_reg(RFBI_CONFIG(rfbi_module), l);
+
+       rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1);
+       rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2);
+       rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3);
+
+
+       l = rfbi_read_reg(RFBI_CONTROL);
+       l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */
+       l = FLD_MOD(l, 0, 1, 1); /* clear bypass */
+       rfbi_write_reg(RFBI_CONTROL, l);
+
+
+       DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n",
+              bpp, lines, cycle1, cycle2, cycle3);
+
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+EXPORT_SYMBOL(rfbi_configure);
+
+static int rfbi_find_display(struct omap_dss_device *dssdev)
+{
+       if (dssdev == rfbi.dssdev[0])
+               return 0;
+
+       if (dssdev == rfbi.dssdev[1])
+               return 1;
+
+       BUG();
+       return -1;
+}
+
+
+static void signal_fifo_waiters(void)
+{
+       if (atomic_read(&rfbi.cmd_fifo_full) > 0) {
+               /* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */
+               complete(&rfbi.cmd_done);
+               atomic_dec(&rfbi.cmd_fifo_full);
+       }
+}
+
+/* returns 1 for async op, and 0 for sync op */
+static int do_update(struct omap_dss_device *dssdev, struct update_region *upd)
+{
+       u16 x = upd->x;
+       u16 y = upd->y;
+       u16 w = upd->w;
+       u16 h = upd->h;
+
+       perf_mark_setup();
+
+       if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+               /*dssdev->driver->enable_te(dssdev, 1); */
+               dss_setup_partial_planes(dssdev, &x, &y, &w, &h);
+       }
+
+#ifdef MEASURE_PERF
+       rfbi.perf_bytes = w * h * 2; /* XXX always 16bit */
+#endif
+
+       dssdev->driver->setup_update(dssdev, x, y, w, h);
+
+       if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) {
+               rfbi_transfer_area(w, h, NULL, NULL);
+               return 1;
+       } else {
+               struct omap_overlay *ovl;
+               void __iomem *addr;
+               int scr_width;
+
+               ovl = dssdev->manager->overlays[0];
+               scr_width = ovl->info.screen_width;
+               addr = ovl->info.vaddr;
+
+               omap_rfbi_write_pixels(addr, scr_width, x, y, w, h);
+
+               perf_show("L4");
+
+               return 0;
+       }
+}
+
+static void process_cmd_fifo(void)
+{
+       int len;
+       struct update_param p;
+       struct omap_dss_device *dssdev;
+       unsigned long flags;
+
+       if (atomic_inc_return(&rfbi.cmd_pending) != 1)
+               return;
+
+       while (true) {
+               spin_lock_irqsave(rfbi.cmd_fifo->lock, flags);
+
+               len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p,
+                                 sizeof(struct update_param));
+               if (len == 0) {
+                       DSSDBG("nothing more in fifo\n");
+                       atomic_set(&rfbi.cmd_pending, 0);
+                       spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+                       break;
+               }
+
+               /* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/
+
+               spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+
+               BUG_ON(len != sizeof(struct update_param));
+               BUG_ON(p.rfbi_module > 1);
+
+               dssdev = rfbi.dssdev[p.rfbi_module];
+
+               if (p.cmd == RFBI_CMD_UPDATE) {
+                       if (do_update(dssdev, &p.par.r))
+                               break; /* async op */
+               } else if (p.cmd == RFBI_CMD_SYNC) {
+                       DSSDBG("Signaling SYNC done!\n");
+                       complete(p.par.sync);
+               } else
+                       BUG();
+       }
+
+       signal_fifo_waiters();
+}
+
+static void rfbi_push_cmd(struct update_param *p)
+{
+       int ret;
+
+       while (1) {
+               unsigned long flags;
+               int available;
+
+               spin_lock_irqsave(rfbi.cmd_fifo->lock, flags);
+               available = RFBI_CMD_FIFO_LEN_BYTES -
+                       __kfifo_len(rfbi.cmd_fifo);
+
+/*             DSSDBG("%d bytes left in fifo\n", available); */
+               if (available < sizeof(struct update_param)) {
+                       DSSDBG("Going to wait because FIFO FULL..\n");
+                       spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+                       atomic_inc(&rfbi.cmd_fifo_full);
+                       wait_for_completion(&rfbi.cmd_done);
+                       /*DSSDBG("Woke up because fifo not full anymore\n");*/
+                       continue;
+               }
+
+               ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p,
+                                 sizeof(struct update_param));
+/*             DSSDBG("pushed %d bytes\n", ret);*/
+
+               spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags);
+
+               BUG_ON(ret != sizeof(struct update_param));
+
+               break;
+       }
+}
+
+static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h)
+{
+       struct update_param p;
+
+       p.rfbi_module = rfbi_module;
+       p.cmd = RFBI_CMD_UPDATE;
+
+       p.par.r.x = x;
+       p.par.r.y = y;
+       p.par.r.w = w;
+       p.par.r.h = h;
+
+       DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h);
+
+       rfbi_push_cmd(&p);
+
+       process_cmd_fifo();
+}
+
+static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp)
+{
+       struct update_param p;
+
+       p.rfbi_module = rfbi_module;
+       p.cmd = RFBI_CMD_SYNC;
+       p.par.sync = sync_comp;
+
+       rfbi_push_cmd(&p);
+
+       DSSDBG("RFBI sync pushed to cmd fifo\n");
+
+       process_cmd_fifo();
+}
+
+void rfbi_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r))
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       DUMPREG(RFBI_REVISION);
+       DUMPREG(RFBI_SYSCONFIG);
+       DUMPREG(RFBI_SYSSTATUS);
+       DUMPREG(RFBI_CONTROL);
+       DUMPREG(RFBI_PIXEL_CNT);
+       DUMPREG(RFBI_LINE_NUMBER);
+       DUMPREG(RFBI_CMD);
+       DUMPREG(RFBI_PARAM);
+       DUMPREG(RFBI_DATA);
+       DUMPREG(RFBI_READ);
+       DUMPREG(RFBI_STATUS);
+
+       DUMPREG(RFBI_CONFIG(0));
+       DUMPREG(RFBI_ONOFF_TIME(0));
+       DUMPREG(RFBI_CYCLE_TIME(0));
+       DUMPREG(RFBI_DATA_CYCLE1(0));
+       DUMPREG(RFBI_DATA_CYCLE2(0));
+       DUMPREG(RFBI_DATA_CYCLE3(0));
+
+       DUMPREG(RFBI_CONFIG(1));
+       DUMPREG(RFBI_ONOFF_TIME(1));
+       DUMPREG(RFBI_CYCLE_TIME(1));
+       DUMPREG(RFBI_DATA_CYCLE1(1));
+       DUMPREG(RFBI_DATA_CYCLE2(1));
+       DUMPREG(RFBI_DATA_CYCLE3(1));
+
+       DUMPREG(RFBI_VSYNC_WIDTH);
+       DUMPREG(RFBI_HSYNC_WIDTH);
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+#undef DUMPREG
+}
+
+int rfbi_init(void)
+{
+       u32 rev;
+       u32 l;
+
+       spin_lock_init(&rfbi.cmd_lock);
+       rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL,
+                                   &rfbi.cmd_lock);
+       if (IS_ERR(rfbi.cmd_fifo))
+               return -ENOMEM;
+
+       init_completion(&rfbi.cmd_done);
+       atomic_set(&rfbi.cmd_fifo_full, 0);
+       atomic_set(&rfbi.cmd_pending, 0);
+
+       rfbi.base = ioremap(RFBI_BASE, SZ_256);
+       if (!rfbi.base) {
+               DSSERR("can't ioremap RFBI\n");
+               return -ENOMEM;
+       }
+
+       rfbi_enable_clocks(1);
+
+       msleep(10);
+
+       rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000;
+
+       /* Enable autoidle and smart-idle */
+       l = rfbi_read_reg(RFBI_SYSCONFIG);
+       l |= (1 << 0) | (2 << 3);
+       rfbi_write_reg(RFBI_SYSCONFIG, l);
+
+       rev = rfbi_read_reg(RFBI_REVISION);
+       printk(KERN_INFO "OMAP RFBI rev %d.%d\n",
+              FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+
+void rfbi_exit(void)
+{
+       DSSDBG("rfbi_exit\n");
+
+       kfifo_free(rfbi.cmd_fifo);
+
+       iounmap(rfbi.base);
+}
+
+/* struct omap_display support */
+static int rfbi_display_update(struct omap_dss_device *dssdev,
+                       u16 x, u16 y, u16 w, u16 h)
+{
+       int rfbi_module;
+
+       if (w == 0 || h == 0)
+               return 0;
+
+       rfbi_module = rfbi_find_display(dssdev);
+
+       rfbi_push_update(rfbi_module, x, y, w, h);
+
+       return 0;
+}
+
+static int rfbi_display_sync(struct omap_dss_device *dssdev)
+{
+       struct completion sync_comp;
+       int rfbi_module;
+
+       rfbi_module = rfbi_find_display(dssdev);
+
+       init_completion(&sync_comp);
+       rfbi_push_sync(rfbi_module, &sync_comp);
+       DSSDBG("Waiting for SYNC to happen...\n");
+       wait_for_completion(&sync_comp);
+       DSSDBG("Released from SYNC\n");
+       return 0;
+}
+
+static int rfbi_display_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+       dssdev->driver->enable_te(dssdev, enable);
+       return 0;
+}
+
+static int rfbi_display_enable(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       r = omap_dss_start_device(dssdev);
+       if (r) {
+               DSSERR("failed to start device\n");
+               goto err0;
+       }
+
+       r = omap_dispc_register_isr(framedone_callback, NULL,
+                       DISPC_IRQ_FRAMEDONE);
+       if (r) {
+               DSSERR("can't get FRAMEDONE irq\n");
+               goto err1;
+       }
+
+       dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+
+       dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI);
+
+       dispc_set_tft_data_lines(dssdev->ctrl.pixel_size);
+
+       rfbi_configure(dssdev->phy.rfbi.channel,
+                              dssdev->ctrl.pixel_size,
+                              dssdev->phy.rfbi.data_lines);
+
+       rfbi_set_timings(dssdev->phy.rfbi.channel,
+                        &dssdev->ctrl.rfbi_timings);
+
+
+       if (dssdev->driver->enable) {
+               r = dssdev->driver->enable(dssdev);
+               if (r)
+                       goto err2;
+       }
+
+       return 0;
+err2:
+       omap_dispc_unregister_isr(framedone_callback, NULL,
+                       DISPC_IRQ_FRAMEDONE);
+err1:
+       omap_dss_stop_device(dssdev);
+err0:
+       return r;
+}
+
+static void rfbi_display_disable(struct omap_dss_device *dssdev)
+{
+       dssdev->driver->disable(dssdev);
+       omap_dispc_unregister_isr(framedone_callback, NULL,
+                       DISPC_IRQ_FRAMEDONE);
+       omap_dss_stop_device(dssdev);
+}
+
+int rfbi_init_display(struct omap_dss_device *dssdev)
+{
+       dssdev->enable = rfbi_display_enable;
+       dssdev->disable = rfbi_display_disable;
+       dssdev->update = rfbi_display_update;
+       dssdev->sync = rfbi_display_sync;
+       dssdev->enable_te = rfbi_display_enable_te;
+
+       rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev;
+
+       dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+
+       return 0;
+}
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
new file mode 100644 (file)
index 0000000..c24f307
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * linux/drivers/video/omap2/dss/sdi.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "SDI"
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <plat/display.h>
+#include "dss.h"
+
+static struct {
+       bool skip_init;
+       bool update_enabled;
+} sdi;
+
+static void sdi_basic_init(void)
+{
+       dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS);
+
+       dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT);
+       dispc_set_tft_data_lines(24);
+       dispc_lcd_enable_signal_polarity(1);
+}
+
+static int sdi_display_enable(struct omap_dss_device *dssdev)
+{
+       struct omap_video_timings *t = &dssdev->panel.timings;
+       struct dss_clock_info dss_cinfo;
+       struct dispc_clock_info dispc_cinfo;
+       u16 lck_div, pck_div;
+       unsigned long fck;
+       unsigned long pck;
+       int r;
+
+       r = omap_dss_start_device(dssdev);
+       if (r) {
+               DSSERR("failed to start device\n");
+               goto err0;
+       }
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+               DSSERR("dssdev already enabled\n");
+               r = -EINVAL;
+               goto err1;
+       }
+
+       /* In case of skip_init sdi_init has already enabled the clocks */
+       if (!sdi.skip_init)
+               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       sdi_basic_init();
+
+       /* 15.5.9.1.2 */
+       dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
+
+       dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi,
+                       dssdev->panel.acb);
+
+       if (!sdi.skip_init) {
+               r = dss_calc_clock_div(1, t->pixel_clock * 1000,
+                               &dss_cinfo, &dispc_cinfo);
+       } else {
+               r = dss_get_clock_div(&dss_cinfo);
+               r = dispc_get_clock_div(&dispc_cinfo);
+       }
+
+       if (r)
+               goto err2;
+
+       fck = dss_cinfo.fck;
+       lck_div = dispc_cinfo.lck_div;
+       pck_div = dispc_cinfo.pck_div;
+
+       pck = fck / lck_div / pck_div / 1000;
+
+       if (pck != t->pixel_clock) {
+               DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
+                               "got %lu kHz\n",
+                               t->pixel_clock, pck);
+
+               t->pixel_clock = pck;
+       }
+
+
+       dispc_set_lcd_timings(t);
+
+       r = dss_set_clock_div(&dss_cinfo);
+       if (r)
+               goto err2;
+
+       r = dispc_set_clock_div(&dispc_cinfo);
+       if (r)
+               goto err2;
+
+       if (!sdi.skip_init) {
+               dss_sdi_init(dssdev->phy.sdi.datapairs);
+               r = dss_sdi_enable();
+               if (r)
+                       goto err1;
+               mdelay(2);
+       }
+
+       dispc_enable_lcd_out(1);
+
+       if (dssdev->driver->enable) {
+               r = dssdev->driver->enable(dssdev);
+               if (r)
+                       goto err3;
+       }
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       sdi.skip_init = 0;
+
+       return 0;
+err3:
+       dispc_enable_lcd_out(0);
+err2:
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+err1:
+       omap_dss_stop_device(dssdev);
+err0:
+       return r;
+}
+
+static int sdi_display_resume(struct omap_dss_device *dssdev);
+
+static void sdi_display_disable(struct omap_dss_device *dssdev)
+{
+       if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
+               return;
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
+               if (sdi_display_resume(dssdev))
+                       return;
+
+       if (dssdev->driver->disable)
+               dssdev->driver->disable(dssdev);
+
+       dispc_enable_lcd_out(0);
+
+       dss_sdi_disable();
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+       omap_dss_stop_device(dssdev);
+}
+
+static int sdi_display_suspend(struct omap_dss_device *dssdev)
+{
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+               return -EINVAL;
+
+       if (dssdev->driver->suspend)
+               dssdev->driver->suspend(dssdev);
+
+       dispc_enable_lcd_out(0);
+
+       dss_sdi_disable();
+
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+       return 0;
+}
+
+static int sdi_display_resume(struct omap_dss_device *dssdev)
+{
+       int r;
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
+               return -EINVAL;
+
+       dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+       r = dss_sdi_enable();
+       if (r)
+               goto err;
+       mdelay(2);
+
+       dispc_enable_lcd_out(1);
+
+       if (dssdev->driver->resume)
+               dssdev->driver->resume(dssdev);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+       return 0;
+err:
+       dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       return r;
+}
+
+static int sdi_display_set_update_mode(struct omap_dss_device *dssdev,
+               enum omap_dss_update_mode mode)
+{
+       if (mode == OMAP_DSS_UPDATE_MANUAL)
+               return -EINVAL;
+
+       if (mode == OMAP_DSS_UPDATE_DISABLED) {
+               dispc_enable_lcd_out(0);
+               sdi.update_enabled = 0;
+       } else {
+               dispc_enable_lcd_out(1);
+               sdi.update_enabled = 1;
+       }
+
+       return 0;
+}
+
+static enum omap_dss_update_mode sdi_display_get_update_mode(
+               struct omap_dss_device *dssdev)
+{
+       return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO :
+               OMAP_DSS_UPDATE_DISABLED;
+}
+
+static void sdi_get_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       *timings = dssdev->panel.timings;
+}
+
+int sdi_init_display(struct omap_dss_device *dssdev)
+{
+       DSSDBG("SDI init\n");
+
+       dssdev->enable = sdi_display_enable;
+       dssdev->disable = sdi_display_disable;
+       dssdev->suspend = sdi_display_suspend;
+       dssdev->resume = sdi_display_resume;
+       dssdev->set_update_mode = sdi_display_set_update_mode;
+       dssdev->get_update_mode = sdi_display_get_update_mode;
+       dssdev->get_timings = sdi_get_timings;
+
+       return 0;
+}
+
+int sdi_init(bool skip_init)
+{
+       /* we store this for first display enable, then clear it */
+       sdi.skip_init = skip_init;
+
+       /*
+        * Enable clocks already here, otherwise there would be a toggle
+        * of them until sdi_display_enable is called.
+        */
+       if (skip_init)
+               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1);
+       return 0;
+}
+
+void sdi_exit(void)
+{
+}
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
new file mode 100644 (file)
index 0000000..749a5a0
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * linux/drivers/video/omap2/dss/venc.c
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * VENC settings from TI's DSS 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.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define DSS_SUBSYS_NAME "VENC"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <plat/display.h>
+#include <plat/cpu.h>
+
+#include "dss.h"
+
+#define VENC_BASE      0x48050C00
+
+/* Venc registers */
+#define VENC_REV_ID                            0x00
+#define VENC_STATUS                            0x04
+#define VENC_F_CONTROL                         0x08
+#define VENC_VIDOUT_CTRL                       0x10
+#define VENC_SYNC_CTRL                         0x14
+#define VENC_LLEN                              0x1C
+#define VENC_FLENS                             0x20
+#define VENC_HFLTR_CTRL                                0x24
+#define VENC_CC_CARR_WSS_CARR                  0x28
+#define VENC_C_PHASE                           0x2C
+#define VENC_GAIN_U                            0x30
+#define VENC_GAIN_V                            0x34
+#define VENC_GAIN_Y                            0x38
+#define VENC_BLACK_LEVEL                       0x3C
+#define VENC_BLANK_LEVEL                       0x40
+#define VENC_X_COLOR                           0x44
+#define VENC_M_CONTROL                         0x48
+#define VENC_BSTAMP_WSS_DATA                   0x4C
+#define VENC_S_CARR                            0x50
+#define VENC_LINE21                            0x54
+#define VENC_LN_SEL                            0x58
+#define VENC_L21__WC_CTL                       0x5C
+#define VENC_HTRIGGER_VTRIGGER                 0x60
+#define VENC_SAVID__EAVID                      0x64
+#define VENC_FLEN__FAL                         0x68
+#define VENC_LAL__PHASE_RESET                  0x6C
+#define VENC_HS_INT_START_STOP_X               0x70
+#define VENC_HS_EXT_START_STOP_X               0x74
+#define VENC_VS_INT_START_X                    0x78
+#define VENC_VS_INT_STOP_X__VS_INT_START_Y     0x7C
+#define VENC_VS_INT_STOP_Y__VS_EXT_START_X     0x80
+#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y     0x84
+#define VENC_VS_EXT_STOP_Y                     0x88
+#define VENC_AVID_START_STOP_X                 0x90
+#define VENC_AVID_START_STOP_Y                 0x94
+#define VENC_FID_INT_START_X__FID_INT_START_Y  0xA0
+#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4
+#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8
+#define VENC_TVDETGP_INT_START_STOP_X          0xB0
+#define VENC_TVDETGP_INT_START_STOP_Y          0xB4
+#define VENC_GEN_CTRL                          0xB8
+#define VENC_OUTPUT_CONTROL                    0xC4
+#define VENC_OUTPUT_TEST                       0xC8
+#define VENC_DAC_B__DAC_C                      0xC8
+
+struct venc_config {
+       u32 f_control;
+       u32 vidout_ctrl;
+       u32 sync_ctrl;
+       u32 llen;
+       u32 flens;
+       u32 hfltr_ctrl;
+       u32 cc_carr_wss_carr;
+       u32 c_phase;
+       u32 gain_u;
+       u32 gain_v;
+       u32 gain_y;
+       u32 black_level;
+       u32 blank_level;
+       u32 x_color;
+       u32 m_control;
+       u32 bstamp_wss_data;
+       u32 s_carr;
+       u32 line21;
+       u32 ln_sel;
+       u32 l21__wc_ctl;
+       u32 htrigger_vtrigger;
+       u32 savid__eavid;
+       u32 flen__fal;
+       u32 lal__phase_reset;
+       u32 hs_int_start_stop_x;
+       u32 hs_ext_start_stop_x;
+       u32 vs_int_start_x;
+       u32 vs_int_stop_x__vs_int_start_y;
+       u32 vs_int_stop_y__vs_ext_start_x;
+       u32 vs_ext_stop_x__vs_ext_start_y;
+       u32 vs_ext_stop_y;
+       u32 avid_start_stop_x;
+       u32 avid_start_stop_y;
+       u32 fid_int_start_x__fid_int_start_y;
+       u32 fid_int_offset_y__fid_ext_start_x;
+       u32 fid_ext_start_y__fid_ext_offset_y;
+       u32 tvdetgp_int_start_stop_x;
+       u32 tvdetgp_int_start_stop_y;
+       u32 gen_ctrl;
+};
+
+/* from TRM */
+static const struct venc_config venc_config_pal_trm = {
+       .f_control                              = 0,
+       .vidout_ctrl                            = 1,
+       .sync_ctrl                              = 0x40,
+       .llen                                   = 0x35F, /* 863 */
+       .flens                                  = 0x270, /* 624 */
+       .hfltr_ctrl                             = 0,
+       .cc_carr_wss_carr                       = 0x2F7225ED,
+       .c_phase                                = 0,
+       .gain_u                                 = 0x111,
+       .gain_v                                 = 0x181,
+       .gain_y                                 = 0x140,
+       .black_level                            = 0x3B,
+       .blank_level                            = 0x3B,
+       .x_color                                = 0x7,
+       .m_control                              = 0x2,
+       .bstamp_wss_data                        = 0x3F,
+       .s_carr                                 = 0x2A098ACB,
+       .line21                                 = 0,
+       .ln_sel                                 = 0x01290015,
+       .l21__wc_ctl                            = 0x0000F603,
+       .htrigger_vtrigger                      = 0,
+
+       .savid__eavid                           = 0x06A70108,
+       .flen__fal                              = 0x00180270,
+       .lal__phase_reset                       = 0x00040135,
+       .hs_int_start_stop_x                    = 0x00880358,
+       .hs_ext_start_stop_x                    = 0x000F035F,
+       .vs_int_start_x                         = 0x01A70000,
+       .vs_int_stop_x__vs_int_start_y          = 0x000001A7,
+       .vs_int_stop_y__vs_ext_start_x          = 0x01AF0000,
+       .vs_ext_stop_x__vs_ext_start_y          = 0x000101AF,
+       .vs_ext_stop_y                          = 0x00000025,
+       .avid_start_stop_x                      = 0x03530083,
+       .avid_start_stop_y                      = 0x026C002E,
+       .fid_int_start_x__fid_int_start_y       = 0x0001008A,
+       .fid_int_offset_y__fid_ext_start_x      = 0x002E0138,
+       .fid_ext_start_y__fid_ext_offset_y      = 0x01380001,
+
+       .tvdetgp_int_start_stop_x               = 0x00140001,
+       .tvdetgp_int_start_stop_y               = 0x00010001,
+       .gen_ctrl                               = 0x00FF0000,
+};
+
+/* from TRM */
+static const struct venc_config venc_config_ntsc_trm = {
+       .f_control                              = 0,
+       .vidout_ctrl                            = 1,
+       .sync_ctrl                              = 0x8040,
+       .llen                                   = 0x359,
+       .flens                                  = 0x20C,
+       .hfltr_ctrl                             = 0,
+       .cc_carr_wss_carr                       = 0x043F2631,
+       .c_phase                                = 0,
+       .gain_u                                 = 0x102,
+       .gain_v                                 = 0x16C,
+       .gain_y                                 = 0x12F,
+       .black_level                            = 0x43,
+       .blank_level                            = 0x38,
+       .x_color                                = 0x7,
+       .m_control                              = 0x1,
+       .bstamp_wss_data                        = 0x38,
+       .s_carr                                 = 0x21F07C1F,
+       .line21                                 = 0,
+       .ln_sel                                 = 0x01310011,
+       .l21__wc_ctl                            = 0x0000F003,
+       .htrigger_vtrigger                      = 0,
+
+       .savid__eavid                           = 0x069300F4,
+       .flen__fal                              = 0x0016020C,
+       .lal__phase_reset                       = 0x00060107,
+       .hs_int_start_stop_x                    = 0x008E0350,
+       .hs_ext_start_stop_x                    = 0x000F0359,
+       .vs_int_start_x                         = 0x01A00000,
+       .vs_int_stop_x__vs_int_start_y          = 0x020701A0,
+       .vs_int_stop_y__vs_ext_start_x          = 0x01AC0024,
+       .vs_ext_stop_x__vs_ext_start_y          = 0x020D01AC,
+       .vs_ext_stop_y                          = 0x00000006,
+       .avid_start_stop_x                      = 0x03480078,
+       .avid_start_stop_y                      = 0x02060024,
+       .fid_int_start_x__fid_int_start_y       = 0x0001008A,
+       .fid_int_offset_y__fid_ext_start_x      = 0x01AC0106,
+       .fid_ext_start_y__fid_ext_offset_y      = 0x01060006,
+
+       .tvdetgp_int_start_stop_x               = 0x00140001,
+       .tvdetgp_int_start_stop_y               = 0x00010001,
+       .gen_ctrl                               = 0x00F90000,
+};
+
+static const struct venc_config venc_config_pal_bdghi = {
+       .f_control                              = 0,
+       .vidout_ctrl                            = 0,
+       .sync_ctrl                              = 0,
+       .hfltr_ctrl                             = 0,
+       .x_color                                = 0,
+       .line21                                 = 0,
+       .ln_sel                                 = 21,
+       .htrigger_vtrigger                      = 0,
+       .tvdetgp_int_start_stop_x               = 0x00140001,
+       .tvdetgp_int_start_stop_y               = 0x00010001,
+       .gen_ctrl                               = 0x00FB0000,
+
+       .llen                                   = 864-1,
+       .flens                                  = 625-1,
+       .cc_carr_wss_carr                       = 0x2F7625ED,
+       .c_phase                                = 0xDF,
+       .gain_u                                 = 0x111,
+       .gain_v                                 = 0x181,
+       .gain_y                                 = 0x140,
+       .black_level                            = 0x3e,
+       .blank_level                            = 0x3e,
+       .m_control                              = 0<<2 | 1<<1,
+       .bstamp_wss_data                        = 0x42,
+       .s_carr                                 = 0x2a098acb,
+       .l21__wc_ctl                            = 0<<13 | 0x16<<8 | 0<<0,
+       .savid__eavid                           = 0x06A70108,
+       .flen__fal                              = 23<<16 | 624<<0,
+       .lal__phase_reset                       = 2<<17 | 310<<0,
+       .hs_int_start_stop_x                    = 0x00920358,
+       .hs_ext_start_stop_x                    = 0x000F035F,
+       .vs_int_start_x                         = 0x1a7<<16,
+       .vs_int_stop_x__vs_int_start_y          = 0x000601A7,
+       .vs_int_stop_y__vs_ext_start_x          = 0x01AF0036,
+       .vs_ext_stop_x__vs_ext_start_y          = 0x27101af,
+       .vs_ext_stop_y                          = 0x05,
+       .avid_start_stop_x                      = 0x03530082,
+       .avid_start_stop_y                      = 0x0270002E,
+       .fid_int_start_x__fid_int_start_y       = 0x0005008A,
+       .fid_int_offset_y__fid_ext_start_x      = 0x002E0138,
+       .fid_ext_start_y__fid_ext_offset_y      = 0x01380005,
+};
+
+const struct omap_video_timings omap_dss_pal_timings = {
+       .x_res          = 720,
+       .y_res          = 574,
+       .pixel_clock    = 13500,
+       .hsw            = 64,
+       .hfp            = 12,
+       .hbp            = 68,
+       .vsw            = 5,
+       .vfp            = 5,
+       .vbp            = 41,
+};
+EXPORT_SYMBOL(omap_dss_pal_timings);
+
+const struct omap_video_timings omap_dss_ntsc_timings = {
+       .x_res          = 720,
+       .y_res          = 482,
+       .pixel_clock    = 13500,
+       .hsw            = 64,
+       .hfp            = 16,
+       .hbp            = 58,
+       .vsw            = 6,
+       .vfp            = 6,
+       .vbp            = 31,
+};
+EXPORT_SYMBOL(omap_dss_ntsc_timings);
+
+static struct {
+       void __iomem *base;
+       struct mutex venc_lock;
+       u32 wss_data;
+       struct regulator *vdda_dac_reg;
+} venc;
+
+static inline void venc_write_reg(int idx, u32 val)
+{
+       __raw_writel(val, venc.base + idx);
+}
+
+static inline u32 venc_read_reg(int idx)
+{
+       u32 l = __raw_readl(venc.base + idx);
+       return l;
+}
+
+static void venc_write_config(const struct venc_config *config)
+{
+       DSSDBG("write venc conf\n");
+
+       venc_write_reg(VENC_LLEN, config->llen);
+       venc_write_reg(VENC_FLENS, config->flens);
+       venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr);
+       venc_write_reg(VENC_C_PHASE, config->c_phase);
+       venc_write_reg(VENC_GAIN_U, config->gain_u);
+       venc_write_reg(VENC_GAIN_V, config->gain_v);
+       venc_write_reg(VENC_GAIN_Y, config->gain_y);
+       venc_write_reg(VENC_BLACK_LEVEL, config->black_level);
+       venc_write_reg(VENC_BLANK_LEVEL, config->blank_level);
+       venc_write_reg(VENC_M_CONTROL, config->m_control);
+       venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
+                       venc.wss_data);
+       venc_write_reg(VENC_S_CARR, config->s_carr);
+       venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl);
+       venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid);
+       venc_write_reg(VENC_FLEN__FAL, config->flen__fal);
+       venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset);
+       venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x);
+       venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x);
+       venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x);
+       venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y,
+                      config->vs_int_stop_x__vs_int_start_y);
+       venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X,
+                      config->vs_int_stop_y__vs_ext_start_x);
+       venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y,
+                      config->vs_ext_stop_x__vs_ext_start_y);
+       venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y);
+       venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x);
+       venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y);
+       venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y,
+                      config->fid_int_start_x__fid_int_start_y);
+       venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X,
+                      config->fid_int_offset_y__fid_ext_start_x);
+       venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y,
+                      config->fid_ext_start_y__fid_ext_offset_y);
+
+       venc_write_reg(VENC_DAC_B__DAC_C,  venc_read_reg(VENC_DAC_B__DAC_C));
+       venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl);
+       venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl);
+       venc_write_reg(VENC_X_COLOR, config->x_color);
+       venc_write_reg(VENC_LINE21, config->line21);
+       venc_write_reg(VENC_LN_SEL, config->ln_sel);
+       venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger);
+       venc_write_reg(VENC_TVDETGP_INT_START_STOP_X,
+                      config->tvdetgp_int_start_stop_x);
+       venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y,
+                      config->tvdetgp_int_start_stop_y);
+       venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl);
+       venc_write_reg(VENC_F_CONTROL, config->f_control);
+       venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl);
+}
+
+static void venc_reset(void)
+{
+       int t = 1000;
+
+       venc_write_reg(VENC_F_CONTROL, 1<<8);
+       while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) {
+               if (--t == 0) {
+                       DSSERR("Failed to reset venc\n");
+                       return;
+               }
+       }
+
+       /* the magical sleep that makes things work */
+       msleep(20);
+}
+
+static void venc_enable_clocks(int enable)
+{
+       if (enable)
+               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M |
+                               DSS_CLK_96M);
+       else
+               dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M |
+                               DSS_CLK_96M);
+}
+
+static const struct venc_config *venc_timings_to_config(
+               struct omap_video_timings *timings)
+{
+       if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0)
+               return &venc_config_pal_trm;
+
+       if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0)
+               return &venc_config_ntsc_trm;
+
+       BUG();
+}
+
+
+
+
+
+/* driver */
+static int venc_panel_probe(struct omap_dss_device *dssdev)
+{
+       dssdev->panel.timings = omap_dss_pal_timings;
+
+       return 0;
+}
+
+static void venc_panel_remove(struct omap_dss_device *dssdev)
+{
+}
+
+static int venc_panel_enable(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       /* wait couple of vsyncs until enabling the LCD */
+       msleep(50);
+
+       if (dssdev->platform_enable)
+               r = dssdev->platform_enable(dssdev);
+
+       return r;
+}
+
+static void venc_panel_disable(struct omap_dss_device *dssdev)
+{
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+
+       /* wait at least 5 vsyncs after disabling the LCD */
+
+       msleep(100);
+}
+
+static int venc_panel_suspend(struct omap_dss_device *dssdev)
+{
+       venc_panel_disable(dssdev);
+       return 0;
+}
+
+static int venc_panel_resume(struct omap_dss_device *dssdev)
+{
+       return venc_panel_enable(dssdev);
+}
+
+static struct omap_dss_driver venc_driver = {
+       .probe          = venc_panel_probe,
+       .remove         = venc_panel_remove,
+
+       .enable         = venc_panel_enable,
+       .disable        = venc_panel_disable,
+       .suspend        = venc_panel_suspend,
+       .resume         = venc_panel_resume,
+
+       .driver         = {
+               .name   = "venc",
+               .owner  = THIS_MODULE,
+       },
+};
+/* driver end */
+
+
+
+int venc_init(struct platform_device *pdev)
+{
+       u8 rev_id;
+
+       mutex_init(&venc.venc_lock);
+
+       venc.wss_data = 0;
+
+       venc.base = ioremap(VENC_BASE, SZ_1K);
+       if (!venc.base) {
+               DSSERR("can't ioremap VENC\n");
+               return -ENOMEM;
+       }
+
+       venc.vdda_dac_reg = regulator_get(&pdev->dev, "vdda_dac");
+       if (IS_ERR(venc.vdda_dac_reg)) {
+               iounmap(venc.base);
+               DSSERR("can't get VDDA_DAC regulator\n");
+               return PTR_ERR(venc.vdda_dac_reg);
+       }
+
+       venc_enable_clocks(1);
+
+       rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
+       printk(KERN_INFO "OMAP VENC rev %d\n", rev_id);
+
+       venc_enable_clocks(0);
+
+       return omap_dss_register_driver(&venc_driver);
+}
+
+void venc_exit(void)
+{
+       omap_dss_unregister_driver(&venc_driver);
+
+       regulator_put(venc.vdda_dac_reg);
+
+       iounmap(venc.base);
+}
+
+static void venc_power_on(struct omap_dss_device *dssdev)
+{
+       u32 l;
+
+       venc_enable_clocks(1);
+
+       venc_reset();
+       venc_write_config(venc_timings_to_config(&dssdev->panel.timings));
+
+       dss_set_venc_output(dssdev->phy.venc.type);
+       dss_set_dac_pwrdn_bgz(1);
+
+       l = 0;
+
+       if (dssdev->phy.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE)
+               l |= 1 << 1;
+       else /* S-Video */
+               l |= (1 << 0) | (1 << 2);
+
+       if (dssdev->phy.venc.invert_polarity == false)
+               l |= 1 << 3;
+
+       venc_write_reg(VENC_OUTPUT_CONTROL, l);
+
+       dispc_set_digit_size(dssdev->panel.timings.x_res,
+                       dssdev->panel.timings.y_res/2);
+
+       regulator_enable(venc.vdda_dac_reg);
+
+       if (dssdev->platform_enable)
+               dssdev->platform_enable(dssdev);
+
+       dispc_enable_digit_out(1);
+}
+
+static void venc_power_off(struct omap_dss_device *dssdev)
+{
+       venc_write_reg(VENC_OUTPUT_CONTROL, 0);
+       dss_set_dac_pwrdn_bgz(0);
+
+       dispc_enable_digit_out(0);
+
+       if (dssdev->platform_disable)
+               dssdev->platform_disable(dssdev);
+
+       regulator_disable(venc.vdda_dac_reg);
+
+       venc_enable_clocks(0);
+}
+
+static int venc_enable_display(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       DSSDBG("venc_enable_display\n");
+
+       mutex_lock(&venc.venc_lock);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       venc_power_on(dssdev);
+
+       venc.wss_data = 0;
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+err:
+       mutex_unlock(&venc.venc_lock);
+
+       return r;
+}
+
+static void venc_disable_display(struct omap_dss_device *dssdev)
+{
+       DSSDBG("venc_disable_display\n");
+
+       mutex_lock(&venc.venc_lock);
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED)
+               goto end;
+
+       if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) {
+               /* suspended is the same as disabled with venc */
+               dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+               goto end;
+       }
+
+       venc_power_off(dssdev);
+
+       dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+end:
+       mutex_unlock(&venc.venc_lock);
+}
+
+static int venc_display_suspend(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       DSSDBG("venc_display_suspend\n");
+
+       mutex_lock(&venc.venc_lock);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       venc_power_off(dssdev);
+
+       dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+err:
+       mutex_unlock(&venc.venc_lock);
+
+       return r;
+}
+
+static int venc_display_resume(struct omap_dss_device *dssdev)
+{
+       int r = 0;
+
+       DSSDBG("venc_display_resume\n");
+
+       mutex_lock(&venc.venc_lock);
+
+       if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       venc_power_on(dssdev);
+
+       dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+err:
+       mutex_unlock(&venc.venc_lock);
+
+       return r;
+}
+
+static void venc_get_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       *timings = dssdev->panel.timings;
+}
+
+static void venc_set_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       DSSDBG("venc_set_timings\n");
+
+       /* Reset WSS data when the TV standard changes. */
+       if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings)))
+               venc.wss_data = 0;
+
+       dssdev->panel.timings = *timings;
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+               /* turn the venc off and on to get new timings to use */
+               venc_disable_display(dssdev);
+               venc_enable_display(dssdev);
+       }
+}
+
+static int venc_check_timings(struct omap_dss_device *dssdev,
+                       struct omap_video_timings *timings)
+{
+       DSSDBG("venc_check_timings\n");
+
+       if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0)
+               return 0;
+
+       if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0)
+               return 0;
+
+       return -EINVAL;
+}
+
+static u32 venc_get_wss(struct omap_dss_device *dssdev)
+{
+       /* Invert due to VENC_L21_WC_CTL:INV=1 */
+       return (venc.wss_data >> 8) ^ 0xfffff;
+}
+
+static int venc_set_wss(struct omap_dss_device *dssdev,        u32 wss)
+{
+       const struct venc_config *config;
+
+       DSSDBG("venc_set_wss\n");
+
+       mutex_lock(&venc.venc_lock);
+
+       config = venc_timings_to_config(&dssdev->panel.timings);
+
+       /* Invert due to VENC_L21_WC_CTL:INV=1 */
+       venc.wss_data = (wss ^ 0xfffff) << 8;
+
+       venc_enable_clocks(1);
+
+       venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
+                       venc.wss_data);
+
+       venc_enable_clocks(0);
+
+       mutex_unlock(&venc.venc_lock);
+
+       return 0;
+}
+
+static enum omap_dss_update_mode venc_display_get_update_mode(
+               struct omap_dss_device *dssdev)
+{
+       if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+               return OMAP_DSS_UPDATE_AUTO;
+       else
+               return OMAP_DSS_UPDATE_DISABLED;
+}
+
+int venc_init_display(struct omap_dss_device *dssdev)
+{
+       DSSDBG("init_display\n");
+
+       dssdev->enable = venc_enable_display;
+       dssdev->disable = venc_disable_display;
+       dssdev->suspend = venc_display_suspend;
+       dssdev->resume = venc_display_resume;
+       dssdev->get_timings = venc_get_timings;
+       dssdev->set_timings = venc_set_timings;
+       dssdev->check_timings = venc_check_timings;
+       dssdev->get_wss = venc_get_wss;
+       dssdev->set_wss = venc_set_wss;
+       dssdev->get_update_mode = venc_display_get_update_mode;
+
+       return 0;
+}
+
+void venc_dump_regs(struct seq_file *s)
+{
+#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
+
+       venc_enable_clocks(1);
+
+       DUMPREG(VENC_F_CONTROL);
+       DUMPREG(VENC_VIDOUT_CTRL);
+       DUMPREG(VENC_SYNC_CTRL);
+       DUMPREG(VENC_LLEN);
+       DUMPREG(VENC_FLENS);
+       DUMPREG(VENC_HFLTR_CTRL);
+       DUMPREG(VENC_CC_CARR_WSS_CARR);
+       DUMPREG(VENC_C_PHASE);
+       DUMPREG(VENC_GAIN_U);
+       DUMPREG(VENC_GAIN_V);
+       DUMPREG(VENC_GAIN_Y);
+       DUMPREG(VENC_BLACK_LEVEL);
+       DUMPREG(VENC_BLANK_LEVEL);
+       DUMPREG(VENC_X_COLOR);
+       DUMPREG(VENC_M_CONTROL);
+       DUMPREG(VENC_BSTAMP_WSS_DATA);
+       DUMPREG(VENC_S_CARR);
+       DUMPREG(VENC_LINE21);
+       DUMPREG(VENC_LN_SEL);
+       DUMPREG(VENC_L21__WC_CTL);
+       DUMPREG(VENC_HTRIGGER_VTRIGGER);
+       DUMPREG(VENC_SAVID__EAVID);
+       DUMPREG(VENC_FLEN__FAL);
+       DUMPREG(VENC_LAL__PHASE_RESET);
+       DUMPREG(VENC_HS_INT_START_STOP_X);
+       DUMPREG(VENC_HS_EXT_START_STOP_X);
+       DUMPREG(VENC_VS_INT_START_X);
+       DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y);
+       DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X);
+       DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y);
+       DUMPREG(VENC_VS_EXT_STOP_Y);
+       DUMPREG(VENC_AVID_START_STOP_X);
+       DUMPREG(VENC_AVID_START_STOP_Y);
+       DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y);
+       DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X);
+       DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y);
+       DUMPREG(VENC_TVDETGP_INT_START_STOP_X);
+       DUMPREG(VENC_TVDETGP_INT_START_STOP_Y);
+       DUMPREG(VENC_GEN_CTRL);
+       DUMPREG(VENC_OUTPUT_CONTROL);
+       DUMPREG(VENC_OUTPUT_TEST);
+
+       venc_enable_clocks(0);
+
+#undef DUMPREG
+}
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
new file mode 100644 (file)
index 0000000..bb694cc
--- /dev/null
@@ -0,0 +1,37 @@
+menuconfig FB_OMAP2
+        tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)"
+        depends on FB && OMAP2_DSS
+
+       select OMAP2_VRAM
+       select OMAP2_VRFB
+        select FB_CFB_FILLRECT
+        select FB_CFB_COPYAREA
+        select FB_CFB_IMAGEBLIT
+        help
+          Frame buffer driver for OMAP2/3 based boards.
+
+config FB_OMAP2_DEBUG_SUPPORT
+       bool "Debug support for OMAP2/3 FB"
+       default y
+       depends on FB_OMAP2
+       help
+         Support for debug output. You have to enable the actual printing
+         with debug module parameter.
+
+config FB_OMAP2_FORCE_AUTO_UPDATE
+       bool "Force main display to automatic update mode"
+       depends on FB_OMAP2
+       help
+         Forces main display to automatic update mode (if possible),
+         and also enables tearsync (if possible). By default
+         displays that support manual update are started in manual
+         update mode.
+
+config FB_OMAP2_NUM_FBS
+       int "Number of framebuffers"
+       range 1 10
+       default 3
+       depends on FB_OMAP2
+       help
+         Select the number of framebuffers created. OMAP2/3 has 3 overlays
+         so normally this would be 3.
diff --git a/drivers/video/omap2/omapfb/Makefile b/drivers/video/omap2/omapfb/Makefile
new file mode 100644 (file)
index 0000000..51c2e00
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_FB_OMAP2) += omapfb.o
+omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
new file mode 100644 (file)
index 0000000..4c4bafd
--- /dev/null
@@ -0,0 +1,755 @@
+/*
+ * linux/drivers/video/omap2/omapfb-ioctl.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/omapfb.h>
+#include <linux/vmalloc.h>
+
+#include <plat/display.h>
+#include <plat/vrfb.h>
+#include <plat/vram.h>
+
+#include "omapfb.h"
+
+static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_overlay *ovl;
+       struct omap_overlay_info info;
+       int r = 0;
+
+       DBG("omapfb_setup_plane\n");
+
+       if (ofbi->num_overlays != 1) {
+               r = -EINVAL;
+               goto out;
+       }
+
+       /* XXX uses only the first overlay */
+       ovl = ofbi->overlays[0];
+
+       if (pi->enabled && !ofbi->region.size) {
+               /*
+                * This plane's memory was freed, can't enable it
+                * until it's reallocated.
+                */
+               r = -EINVAL;
+               goto out;
+       }
+
+       ovl->get_overlay_info(ovl, &info);
+
+       info.pos_x = pi->pos_x;
+       info.pos_y = pi->pos_y;
+       info.out_width = pi->out_width;
+       info.out_height = pi->out_height;
+       info.enabled = pi->enabled;
+
+       r = ovl->set_overlay_info(ovl, &info);
+       if (r)
+               goto out;
+
+       if (ovl->manager) {
+               r = ovl->manager->apply(ovl->manager);
+               if (r)
+                       goto out;
+       }
+
+out:
+       if (r)
+               dev_err(fbdev->dev, "setup_plane failed\n");
+       return r;
+}
+
+static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+
+       if (ofbi->num_overlays != 1) {
+               memset(pi, 0, sizeof(*pi));
+       } else {
+               struct omap_overlay_info *ovli;
+               struct omap_overlay *ovl;
+
+               ovl = ofbi->overlays[0];
+               ovli = &ovl->info;
+
+               pi->pos_x = ovli->pos_x;
+               pi->pos_y = ovli->pos_y;
+               pi->enabled = ovli->enabled;
+               pi->channel_out = 0; /* xxx */
+               pi->mirror = 0;
+               pi->out_width = ovli->out_width;
+               pi->out_height = ovli->out_height;
+       }
+
+       return 0;
+}
+
+static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omapfb2_mem_region *rg;
+       int r, i;
+       size_t size;
+
+       if (mi->type > OMAPFB_MEMTYPE_MAX)
+               return -EINVAL;
+
+       size = PAGE_ALIGN(mi->size);
+
+       rg = &ofbi->region;
+
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               if (ofbi->overlays[i]->info.enabled)
+                       return -EBUSY;
+       }
+
+       if (rg->size != size || rg->type != mi->type) {
+               r = omapfb_realloc_fbmem(fbi, size, mi->type);
+               if (r) {
+                       dev_err(fbdev->dev, "realloc fbmem failed\n");
+                       return r;
+               }
+       }
+
+       return 0;
+}
+
+static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_mem_region *rg;
+
+       rg = &ofbi->region;
+       memset(mi, 0, sizeof(*mi));
+
+       mi->size = rg->size;
+       mi->type = rg->type;
+
+       return 0;
+}
+
+static int omapfb_update_window_nolock(struct fb_info *fbi,
+               u32 x, u32 y, u32 w, u32 h)
+{
+       struct omap_dss_device *display = fb2display(fbi);
+       u16 dw, dh;
+
+       if (!display)
+               return 0;
+
+       if (w == 0 || h == 0)
+               return 0;
+
+       display->get_resolution(display, &dw, &dh);
+
+       if (x + w > dw || y + h > dh)
+               return -EINVAL;
+
+       return display->update(display, x, y, w, h);
+}
+
+/* This function is exported for SGX driver use */
+int omapfb_update_window(struct fb_info *fbi,
+               u32 x, u32 y, u32 w, u32 h)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       int r;
+
+       omapfb_lock(fbdev);
+       lock_fb_info(fbi);
+
+       r = omapfb_update_window_nolock(fbi, x, y, w, h);
+
+       unlock_fb_info(fbi);
+       omapfb_unlock(fbdev);
+
+       return r;
+}
+EXPORT_SYMBOL(omapfb_update_window);
+
+static int omapfb_set_update_mode(struct fb_info *fbi,
+                                  enum omapfb_update_mode mode)
+{
+       struct omap_dss_device *display = fb2display(fbi);
+       enum omap_dss_update_mode um;
+       int r;
+
+       if (!display || !display->set_update_mode)
+               return -EINVAL;
+
+       switch (mode) {
+       case OMAPFB_UPDATE_DISABLED:
+               um = OMAP_DSS_UPDATE_DISABLED;
+               break;
+
+       case OMAPFB_AUTO_UPDATE:
+               um = OMAP_DSS_UPDATE_AUTO;
+               break;
+
+       case OMAPFB_MANUAL_UPDATE:
+               um = OMAP_DSS_UPDATE_MANUAL;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       r = display->set_update_mode(display, um);
+
+       return r;
+}
+
+static int omapfb_get_update_mode(struct fb_info *fbi,
+               enum omapfb_update_mode *mode)
+{
+       struct omap_dss_device *display = fb2display(fbi);
+       enum omap_dss_update_mode m;
+
+       if (!display || !display->get_update_mode)
+               return -EINVAL;
+
+       m = display->get_update_mode(display);
+
+       switch (m) {
+       case OMAP_DSS_UPDATE_DISABLED:
+               *mode = OMAPFB_UPDATE_DISABLED;
+               break;
+       case OMAP_DSS_UPDATE_AUTO:
+               *mode = OMAPFB_AUTO_UPDATE;
+               break;
+       case OMAP_DSS_UPDATE_MANUAL:
+               *mode = OMAPFB_MANUAL_UPDATE;
+               break;
+       default:
+               BUG();
+       }
+
+       return 0;
+}
+
+/* XXX this color key handling is a hack... */
+static struct omapfb_color_key omapfb_color_keys[2];
+
+static int _omapfb_set_color_key(struct omap_overlay_manager *mgr,
+               struct omapfb_color_key *ck)
+{
+       struct omap_overlay_manager_info info;
+       enum omap_dss_trans_key_type kt;
+       int r;
+
+       mgr->get_manager_info(mgr, &info);
+
+       if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) {
+               info.trans_enabled = false;
+               omapfb_color_keys[mgr->id] = *ck;
+
+               r = mgr->set_manager_info(mgr, &info);
+               if (r)
+                       return r;
+
+               r = mgr->apply(mgr);
+
+               return r;
+       }
+
+       switch (ck->key_type) {
+       case OMAPFB_COLOR_KEY_GFX_DST:
+               kt = OMAP_DSS_COLOR_KEY_GFX_DST;
+               break;
+       case OMAPFB_COLOR_KEY_VID_SRC:
+               kt = OMAP_DSS_COLOR_KEY_VID_SRC;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       info.default_color = ck->background;
+       info.trans_key = ck->trans_key;
+       info.trans_key_type = kt;
+       info.trans_enabled = true;
+
+       omapfb_color_keys[mgr->id] = *ck;
+
+       r = mgr->set_manager_info(mgr, &info);
+       if (r)
+               return r;
+
+       r = mgr->apply(mgr);
+
+       return r;
+}
+
+static int omapfb_set_color_key(struct fb_info *fbi,
+               struct omapfb_color_key *ck)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       int r;
+       int i;
+       struct omap_overlay_manager *mgr = NULL;
+
+       omapfb_lock(fbdev);
+
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               if (ofbi->overlays[i]->manager) {
+                       mgr = ofbi->overlays[i]->manager;
+                       break;
+               }
+       }
+
+       if (!mgr) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       r = _omapfb_set_color_key(mgr, ck);
+err:
+       omapfb_unlock(fbdev);
+
+       return r;
+}
+
+static int omapfb_get_color_key(struct fb_info *fbi,
+               struct omapfb_color_key *ck)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_overlay_manager *mgr = NULL;
+       int r = 0;
+       int i;
+
+       omapfb_lock(fbdev);
+
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               if (ofbi->overlays[i]->manager) {
+                       mgr = ofbi->overlays[i]->manager;
+                       break;
+               }
+       }
+
+       if (!mgr) {
+               r = -EINVAL;
+               goto err;
+       }
+
+       *ck = omapfb_color_keys[mgr->id];
+err:
+       omapfb_unlock(fbdev);
+
+       return r;
+}
+
+static int omapfb_memory_read(struct fb_info *fbi,
+               struct omapfb_memory_read *mr)
+{
+       struct omap_dss_device *display = fb2display(fbi);
+       void *buf;
+       int r;
+
+       if (!display || !display->memory_read)
+               return -ENOENT;
+
+       if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size))
+               return -EFAULT;
+
+       if (mr->w * mr->h * 3 > mr->buffer_size)
+               return -EINVAL;
+
+       buf = vmalloc(mr->buffer_size);
+       if (!buf) {
+               DBG("vmalloc failed\n");
+               return -ENOMEM;
+       }
+
+       r = display->memory_read(display, buf, mr->buffer_size,
+                       mr->x, mr->y, mr->w, mr->h);
+
+       if (r > 0) {
+               if (copy_to_user(mr->buffer, buf, mr->buffer_size))
+                       r = -EFAULT;
+       }
+
+       vfree(buf);
+
+       return r;
+}
+
+static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev,
+                            struct omapfb_ovl_colormode *mode)
+{
+       int ovl_idx = mode->overlay_idx;
+       int mode_idx = mode->mode_idx;
+       struct omap_overlay *ovl;
+       enum omap_color_mode supported_modes;
+       struct fb_var_screeninfo var;
+       int i;
+
+       if (ovl_idx >= fbdev->num_overlays)
+               return -ENODEV;
+       ovl = fbdev->overlays[ovl_idx];
+       supported_modes = ovl->supported_modes;
+
+       mode_idx = mode->mode_idx;
+
+       for (i = 0; i < sizeof(supported_modes) * 8; i++) {
+               if (!(supported_modes & (1 << i)))
+                       continue;
+               /*
+                * It's possible that the FB doesn't support a mode
+                * that is supported by the overlay, so call the
+                * following here.
+                */
+               if (dss_mode_to_fb_mode(1 << i, &var) < 0)
+                       continue;
+
+               mode_idx--;
+               if (mode_idx < 0)
+                       break;
+       }
+
+       if (i == sizeof(supported_modes) * 8)
+               return -ENOENT;
+
+       mode->bits_per_pixel = var.bits_per_pixel;
+       mode->nonstd = var.nonstd;
+       mode->red = var.red;
+       mode->green = var.green;
+       mode->blue = var.blue;
+       mode->transp = var.transp;
+
+       return 0;
+}
+
+static int omapfb_wait_for_go(struct fb_info *fbi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       int r = 0;
+       int i;
+
+       for (i = 0; i < ofbi->num_overlays; ++i) {
+               struct omap_overlay *ovl = ofbi->overlays[i];
+               r = ovl->wait_for_go(ovl);
+               if (r)
+                       break;
+       }
+
+       return r;
+}
+
+int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_dss_device *display = fb2display(fbi);
+
+       union {
+               struct omapfb_update_window_old uwnd_o;
+               struct omapfb_update_window     uwnd;
+               struct omapfb_plane_info        plane_info;
+               struct omapfb_caps              caps;
+               struct omapfb_mem_info          mem_info;
+               struct omapfb_color_key         color_key;
+               struct omapfb_ovl_colormode     ovl_colormode;
+               enum omapfb_update_mode         update_mode;
+               int test_num;
+               struct omapfb_memory_read       memory_read;
+               struct omapfb_vram_info         vram_info;
+               struct omapfb_tearsync_info     tearsync_info;
+       } p;
+
+       int r = 0;
+
+       switch (cmd) {
+       case OMAPFB_SYNC_GFX:
+               DBG("ioctl SYNC_GFX\n");
+               if (!display || !display->sync) {
+                       /* DSS1 never returns an error here, so we neither */
+                       /*r = -EINVAL;*/
+                       break;
+               }
+
+               r = display->sync(display);
+               break;
+
+       case OMAPFB_UPDATE_WINDOW_OLD:
+               DBG("ioctl UPDATE_WINDOW_OLD\n");
+               if (!display || !display->update) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               if (copy_from_user(&p.uwnd_o,
+                                       (void __user *)arg,
+                                       sizeof(p.uwnd_o))) {
+                       r = -EFAULT;
+                       break;
+               }
+
+               r = omapfb_update_window_nolock(fbi, p.uwnd_o.x, p.uwnd_o.y,
+                               p.uwnd_o.width, p.uwnd_o.height);
+               break;
+
+       case OMAPFB_UPDATE_WINDOW:
+               DBG("ioctl UPDATE_WINDOW\n");
+               if (!display || !display->update) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               if (copy_from_user(&p.uwnd, (void __user *)arg,
+                                       sizeof(p.uwnd))) {
+                       r = -EFAULT;
+                       break;
+               }
+
+               r = omapfb_update_window_nolock(fbi, p.uwnd.x, p.uwnd.y,
+                               p.uwnd.width, p.uwnd.height);
+               break;
+
+       case OMAPFB_SETUP_PLANE:
+               DBG("ioctl SETUP_PLANE\n");
+               if (copy_from_user(&p.plane_info, (void __user *)arg,
+                                       sizeof(p.plane_info)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_setup_plane(fbi, &p.plane_info);
+               break;
+
+       case OMAPFB_QUERY_PLANE:
+               DBG("ioctl QUERY_PLANE\n");
+               r = omapfb_query_plane(fbi, &p.plane_info);
+               if (r < 0)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.plane_info,
+                                       sizeof(p.plane_info)))
+                       r = -EFAULT;
+               break;
+
+       case OMAPFB_SETUP_MEM:
+               DBG("ioctl SETUP_MEM\n");
+               if (copy_from_user(&p.mem_info, (void __user *)arg,
+                                       sizeof(p.mem_info)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_setup_mem(fbi, &p.mem_info);
+               break;
+
+       case OMAPFB_QUERY_MEM:
+               DBG("ioctl QUERY_MEM\n");
+               r = omapfb_query_mem(fbi, &p.mem_info);
+               if (r < 0)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.mem_info,
+                                       sizeof(p.mem_info)))
+                       r = -EFAULT;
+               break;
+
+       case OMAPFB_GET_CAPS:
+               DBG("ioctl GET_CAPS\n");
+               if (!display) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               memset(&p.caps, 0, sizeof(p.caps));
+               if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
+                       p.caps.ctrl |= OMAPFB_CAPS_MANUAL_UPDATE;
+               if (display->caps & OMAP_DSS_DISPLAY_CAP_TEAR_ELIM)
+                       p.caps.ctrl |= OMAPFB_CAPS_TEARSYNC;
+
+               if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
+                       r = -EFAULT;
+               break;
+
+       case OMAPFB_GET_OVERLAY_COLORMODE:
+               DBG("ioctl GET_OVERLAY_COLORMODE\n");
+               if (copy_from_user(&p.ovl_colormode, (void __user *)arg,
+                                  sizeof(p.ovl_colormode))) {
+                       r = -EFAULT;
+                       break;
+               }
+               r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode);
+               if (r < 0)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.ovl_colormode,
+                                sizeof(p.ovl_colormode)))
+                       r = -EFAULT;
+               break;
+
+       case OMAPFB_SET_UPDATE_MODE:
+               DBG("ioctl SET_UPDATE_MODE\n");
+               if (get_user(p.update_mode, (int __user *)arg))
+                       r = -EFAULT;
+               else
+                       r = omapfb_set_update_mode(fbi, p.update_mode);
+               break;
+
+       case OMAPFB_GET_UPDATE_MODE:
+               DBG("ioctl GET_UPDATE_MODE\n");
+               r = omapfb_get_update_mode(fbi, &p.update_mode);
+               if (r)
+                       break;
+               if (put_user(p.update_mode,
+                                       (enum omapfb_update_mode __user *)arg))
+                       r = -EFAULT;
+               break;
+
+       case OMAPFB_SET_COLOR_KEY:
+               DBG("ioctl SET_COLOR_KEY\n");
+               if (copy_from_user(&p.color_key, (void __user *)arg,
+                                  sizeof(p.color_key)))
+                       r = -EFAULT;
+               else
+                       r = omapfb_set_color_key(fbi, &p.color_key);
+               break;
+
+       case OMAPFB_GET_COLOR_KEY:
+               DBG("ioctl GET_COLOR_KEY\n");
+               r = omapfb_get_color_key(fbi, &p.color_key);
+               if (r)
+                       break;
+               if (copy_to_user((void __user *)arg, &p.color_key,
+                                sizeof(p.color_key)))
+                       r = -EFAULT;
+               break;
+
+       case OMAPFB_WAITFORVSYNC:
+               DBG("ioctl WAITFORVSYNC\n");
+               if (!display) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = display->wait_vsync(display);
+               break;
+
+       case OMAPFB_WAITFORGO:
+               DBG("ioctl WAITFORGO\n");
+               if (!display) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = omapfb_wait_for_go(fbi);
+               break;
+
+       /* LCD and CTRL tests do the same thing for backward
+        * compatibility */
+       case OMAPFB_LCD_TEST:
+               DBG("ioctl LCD_TEST\n");
+               if (get_user(p.test_num, (int __user *)arg)) {
+                       r = -EFAULT;
+                       break;
+               }
+               if (!display || !display->run_test) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = display->run_test(display, p.test_num);
+
+               break;
+
+       case OMAPFB_CTRL_TEST:
+               DBG("ioctl CTRL_TEST\n");
+               if (get_user(p.test_num, (int __user *)arg)) {
+                       r = -EFAULT;
+                       break;
+               }
+               if (!display || !display->run_test) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = display->run_test(display, p.test_num);
+
+               break;
+
+       case OMAPFB_MEMORY_READ:
+               DBG("ioctl MEMORY_READ\n");
+
+               if (copy_from_user(&p.memory_read, (void __user *)arg,
+                                       sizeof(p.memory_read))) {
+                       r = -EFAULT;
+                       break;
+               }
+
+               r = omapfb_memory_read(fbi, &p.memory_read);
+
+               break;
+
+       case OMAPFB_GET_VRAM_INFO: {
+               unsigned long vram, free, largest;
+
+               DBG("ioctl GET_VRAM_INFO\n");
+
+               omap_vram_get_info(&vram, &free, &largest);
+               p.vram_info.total = vram;
+               p.vram_info.free = free;
+               p.vram_info.largest_free_block = largest;
+
+               if (copy_to_user((void __user *)arg, &p.vram_info,
+                                       sizeof(p.vram_info)))
+                       r = -EFAULT;
+               break;
+       }
+
+       case OMAPFB_SET_TEARSYNC: {
+               DBG("ioctl SET_TEARSYNC\n");
+
+               if (copy_from_user(&p.tearsync_info, (void __user *)arg,
+                                       sizeof(p.tearsync_info))) {
+                       r = -EFAULT;
+                       break;
+               }
+
+               if (!display->enable_te) {
+                       r = -ENODEV;
+                       break;
+               }
+
+               r = display->enable_te(display, !!p.tearsync_info.enabled);
+
+               break;
+       }
+
+       default:
+               dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd);
+               r = -EINVAL;
+       }
+
+       if (r < 0)
+               DBG("ioctl failed: %d\n", r);
+
+       return r;
+}
+
+
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
new file mode 100644 (file)
index 0000000..ef29983
--- /dev/null
@@ -0,0 +1,2261 @@
+/*
+ * linux/drivers/video/omap2/omapfb-main.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/omapfb.h>
+
+#include <plat/display.h>
+#include <plat/vram.h>
+#include <plat/vrfb.h>
+
+#include "omapfb.h"
+
+#define MODULE_NAME     "omapfb"
+
+#define OMAPFB_PLANE_XRES_MIN          8
+#define OMAPFB_PLANE_YRES_MIN          8
+
+static char *def_mode;
+static char *def_vram;
+static int def_vrfb;
+static int def_rotate;
+static int def_mirror;
+
+#ifdef DEBUG
+unsigned int omapfb_debug;
+module_param_named(debug, omapfb_debug, bool, 0644);
+static unsigned int omapfb_test_pattern;
+module_param_named(test, omapfb_test_pattern, bool, 0644);
+#endif
+
+static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi);
+
+#ifdef DEBUG
+static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color)
+{
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct fb_fix_screeninfo *fix = &fbi->fix;
+       void __iomem *addr = fbi->screen_base;
+       const unsigned bytespp = var->bits_per_pixel >> 3;
+       const unsigned line_len = fix->line_length / bytespp;
+
+       int r = (color >> 16) & 0xff;
+       int g = (color >> 8) & 0xff;
+       int b = (color >> 0) & 0xff;
+
+       if (var->bits_per_pixel == 16) {
+               u16 __iomem *p = (u16 __iomem *)addr;
+               p += y * line_len + x;
+
+               r = r * 32 / 256;
+               g = g * 64 / 256;
+               b = b * 32 / 256;
+
+               __raw_writew((r << 11) | (g << 5) | (b << 0), p);
+       } else if (var->bits_per_pixel == 24) {
+               u8 __iomem *p = (u8 __iomem *)addr;
+               p += (y * line_len + x) * 3;
+
+               __raw_writeb(b, p + 0);
+               __raw_writeb(g, p + 1);
+               __raw_writeb(r, p + 2);
+       } else if (var->bits_per_pixel == 32) {
+               u32 __iomem *p = (u32 __iomem *)addr;
+               p += y * line_len + x;
+               __raw_writel(color, p);
+       }
+}
+
+static void fill_fb(struct fb_info *fbi)
+{
+       struct fb_var_screeninfo *var = &fbi->var;
+       const short w = var->xres_virtual;
+       const short h = var->yres_virtual;
+       void __iomem *addr = fbi->screen_base;
+       int y, x;
+
+       if (!addr)
+               return;
+
+       DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length);
+
+       for (y = 0; y < h; y++) {
+               for (x = 0; x < w; x++) {
+                       if (x < 20 && y < 20)
+                               draw_pixel(fbi, x, y, 0xffffff);
+                       else if (x < 20 && (y > 20 && y < h - 20))
+                               draw_pixel(fbi, x, y, 0xff);
+                       else if (y < 20 && (x > 20 && x < w - 20))
+                               draw_pixel(fbi, x, y, 0xff00);
+                       else if (x > w - 20 && (y > 20 && y < h - 20))
+                               draw_pixel(fbi, x, y, 0xff0000);
+                       else if (y > h - 20 && (x > 20 && x < w - 20))
+                               draw_pixel(fbi, x, y, 0xffff00);
+                       else if (x == 20 || x == w - 20 ||
+                                       y == 20 || y == h - 20)
+                               draw_pixel(fbi, x, y, 0xffffff);
+                       else if (x == y || w - x == h - y)
+                               draw_pixel(fbi, x, y, 0xff00ff);
+                       else if (w - x == y || x == h - y)
+                               draw_pixel(fbi, x, y, 0x00ffff);
+                       else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) {
+                               int t = x * 3 / w;
+                               unsigned r = 0, g = 0, b = 0;
+                               unsigned c;
+                               if (var->bits_per_pixel == 16) {
+                                       if (t == 0)
+                                               b = (y % 32) * 256 / 32;
+                                       else if (t == 1)
+                                               g = (y % 64) * 256 / 64;
+                                       else if (t == 2)
+                                               r = (y % 32) * 256 / 32;
+                               } else {
+                                       if (t == 0)
+                                               b = (y % 256);
+                                       else if (t == 1)
+                                               g = (y % 256);
+                                       else if (t == 2)
+                                               r = (y % 256);
+                               }
+                               c = (r << 16) | (g << 8) | (b << 0);
+                               draw_pixel(fbi, x, y, c);
+                       } else {
+                               draw_pixel(fbi, x, y, 0);
+                       }
+               }
+       }
+}
+#endif
+
+static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot)
+{
+       struct vrfb *vrfb = &ofbi->region.vrfb;
+       unsigned offset;
+
+       switch (rot) {
+       case FB_ROTATE_UR:
+               offset = 0;
+               break;
+       case FB_ROTATE_CW:
+               offset = vrfb->yoffset;
+               break;
+       case FB_ROTATE_UD:
+               offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset;
+               break;
+       case FB_ROTATE_CCW:
+               offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN;
+               break;
+       default:
+               BUG();
+       }
+
+       offset *= vrfb->bytespp;
+
+       return offset;
+}
+
+static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot)
+{
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+               return ofbi->region.vrfb.paddr[rot]
+                       + omapfb_get_vrfb_offset(ofbi, rot);
+       } else {
+               return ofbi->region.paddr;
+       }
+}
+
+static u32 omapfb_get_region_paddr(struct omapfb_info *ofbi)
+{
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+               return ofbi->region.vrfb.paddr[0];
+       else
+               return ofbi->region.paddr;
+}
+
+static void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi)
+{
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+               return ofbi->region.vrfb.vaddr[0];
+       else
+               return ofbi->region.vaddr;
+}
+
+static struct omapfb_colormode omapfb_colormodes[] = {
+       {
+               .dssmode = OMAP_DSS_COLOR_UYVY,
+               .bits_per_pixel = 16,
+               .nonstd = OMAPFB_COLOR_YUV422,
+       }, {
+               .dssmode = OMAP_DSS_COLOR_YUV2,
+               .bits_per_pixel = 16,
+               .nonstd = OMAPFB_COLOR_YUY422,
+       }, {
+               .dssmode = OMAP_DSS_COLOR_ARGB16,
+               .bits_per_pixel = 16,
+               .red    = { .length = 4, .offset = 8, .msb_right = 0 },
+               .green  = { .length = 4, .offset = 4, .msb_right = 0 },
+               .blue   = { .length = 4, .offset = 0, .msb_right = 0 },
+               .transp = { .length = 4, .offset = 12, .msb_right = 0 },
+       }, {
+               .dssmode = OMAP_DSS_COLOR_RGB16,
+               .bits_per_pixel = 16,
+               .red    = { .length = 5, .offset = 11, .msb_right = 0 },
+               .green  = { .length = 6, .offset = 5, .msb_right = 0 },
+               .blue   = { .length = 5, .offset = 0, .msb_right = 0 },
+               .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+       }, {
+               .dssmode = OMAP_DSS_COLOR_RGB24P,
+               .bits_per_pixel = 24,
+               .red    = { .length = 8, .offset = 16, .msb_right = 0 },
+               .green  = { .length = 8, .offset = 8, .msb_right = 0 },
+               .blue   = { .length = 8, .offset = 0, .msb_right = 0 },
+               .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+       }, {
+               .dssmode = OMAP_DSS_COLOR_RGB24U,
+               .bits_per_pixel = 32,
+               .red    = { .length = 8, .offset = 16, .msb_right = 0 },
+               .green  = { .length = 8, .offset = 8, .msb_right = 0 },
+               .blue   = { .length = 8, .offset = 0, .msb_right = 0 },
+               .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+       }, {
+               .dssmode = OMAP_DSS_COLOR_ARGB32,
+               .bits_per_pixel = 32,
+               .red    = { .length = 8, .offset = 16, .msb_right = 0 },
+               .green  = { .length = 8, .offset = 8, .msb_right = 0 },
+               .blue   = { .length = 8, .offset = 0, .msb_right = 0 },
+               .transp = { .length = 8, .offset = 24, .msb_right = 0 },
+       }, {
+               .dssmode = OMAP_DSS_COLOR_RGBA32,
+               .bits_per_pixel = 32,
+               .red    = { .length = 8, .offset = 24, .msb_right = 0 },
+               .green  = { .length = 8, .offset = 16, .msb_right = 0 },
+               .blue   = { .length = 8, .offset = 8, .msb_right = 0 },
+               .transp = { .length = 8, .offset = 0, .msb_right = 0 },
+       }, {
+               .dssmode = OMAP_DSS_COLOR_RGBX32,
+               .bits_per_pixel = 32,
+               .red    = { .length = 8, .offset = 24, .msb_right = 0 },
+               .green  = { .length = 8, .offset = 16, .msb_right = 0 },
+               .blue   = { .length = 8, .offset = 8, .msb_right = 0 },
+               .transp = { .length = 0, .offset = 0, .msb_right = 0 },
+       },
+};
+
+static bool cmp_var_to_colormode(struct fb_var_screeninfo *var,
+               struct omapfb_colormode *color)
+{
+       bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2)
+       {
+               return f1->length == f2->length &&
+                       f1->offset == f2->offset &&
+                       f1->msb_right == f2->msb_right;
+       }
+
+       if (var->bits_per_pixel == 0 ||
+                       var->red.length == 0 ||
+                       var->blue.length == 0 ||
+                       var->green.length == 0)
+               return 0;
+
+       return var->bits_per_pixel == color->bits_per_pixel &&
+               cmp_component(&var->red, &color->red) &&
+               cmp_component(&var->green, &color->green) &&
+               cmp_component(&var->blue, &color->blue) &&
+               cmp_component(&var->transp, &color->transp);
+}
+
+static void assign_colormode_to_var(struct fb_var_screeninfo *var,
+               struct omapfb_colormode *color)
+{
+       var->bits_per_pixel = color->bits_per_pixel;
+       var->nonstd = color->nonstd;
+       var->red = color->red;
+       var->green = color->green;
+       var->blue = color->blue;
+       var->transp = color->transp;
+}
+
+static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var,
+               enum omap_color_mode *mode)
+{
+       enum omap_color_mode dssmode;
+       int i;
+
+       /* first match with nonstd field */
+       if (var->nonstd) {
+               for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+                       struct omapfb_colormode *m = &omapfb_colormodes[i];
+                       if (var->nonstd == m->nonstd) {
+                               assign_colormode_to_var(var, m);
+                               *mode = m->dssmode;
+                               return 0;
+                       }
+               }
+
+               return -EINVAL;
+       }
+
+       /* then try exact match of bpp and colors */
+       for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+               struct omapfb_colormode *m = &omapfb_colormodes[i];
+               if (cmp_var_to_colormode(var, m)) {
+                       assign_colormode_to_var(var, m);
+                       *mode = m->dssmode;
+                       return 0;
+               }
+       }
+
+       /* match with bpp if user has not filled color fields
+        * properly */
+       switch (var->bits_per_pixel) {
+       case 1:
+               dssmode = OMAP_DSS_COLOR_CLUT1;
+               break;
+       case 2:
+               dssmode = OMAP_DSS_COLOR_CLUT2;
+               break;
+       case 4:
+               dssmode = OMAP_DSS_COLOR_CLUT4;
+               break;
+       case 8:
+               dssmode = OMAP_DSS_COLOR_CLUT8;
+               break;
+       case 12:
+               dssmode = OMAP_DSS_COLOR_RGB12U;
+               break;
+       case 16:
+               dssmode = OMAP_DSS_COLOR_RGB16;
+               break;
+       case 24:
+               dssmode = OMAP_DSS_COLOR_RGB24P;
+               break;
+       case 32:
+               dssmode = OMAP_DSS_COLOR_RGB24U;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+               struct omapfb_colormode *m = &omapfb_colormodes[i];
+               if (dssmode == m->dssmode) {
+                       assign_colormode_to_var(var, m);
+                       *mode = m->dssmode;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int check_fb_res_bounds(struct fb_var_screeninfo *var)
+{
+       int xres_min = OMAPFB_PLANE_XRES_MIN;
+       int xres_max = 2048;
+       int yres_min = OMAPFB_PLANE_YRES_MIN;
+       int yres_max = 2048;
+
+       /* XXX: some applications seem to set virtual res to 0. */
+       if (var->xres_virtual == 0)
+               var->xres_virtual = var->xres;
+
+       if (var->yres_virtual == 0)
+               var->yres_virtual = var->yres;
+
+       if (var->xres_virtual < xres_min || var->yres_virtual < yres_min)
+               return -EINVAL;
+
+       if (var->xres < xres_min)
+               var->xres = xres_min;
+       if (var->yres < yres_min)
+               var->yres = yres_min;
+       if (var->xres > xres_max)
+               var->xres = xres_max;
+       if (var->yres > yres_max)
+               var->yres = yres_max;
+
+       if (var->xres > var->xres_virtual)
+               var->xres = var->xres_virtual;
+       if (var->yres > var->yres_virtual)
+               var->yres = var->yres_virtual;
+
+       return 0;
+}
+
+static void shrink_height(unsigned long max_frame_size,
+               struct fb_var_screeninfo *var)
+{
+       DBG("can't fit FB into memory, reducing y\n");
+       var->yres_virtual = max_frame_size /
+               (var->xres_virtual * var->bits_per_pixel >> 3);
+
+       if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN)
+               var->yres_virtual = OMAPFB_PLANE_YRES_MIN;
+
+       if (var->yres > var->yres_virtual)
+               var->yres = var->yres_virtual;
+}
+
+static void shrink_width(unsigned long max_frame_size,
+               struct fb_var_screeninfo *var)
+{
+       DBG("can't fit FB into memory, reducing x\n");
+       var->xres_virtual = max_frame_size / var->yres_virtual /
+               (var->bits_per_pixel >> 3);
+
+       if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN)
+               var->xres_virtual = OMAPFB_PLANE_XRES_MIN;
+
+       if (var->xres > var->xres_virtual)
+               var->xres = var->xres_virtual;
+}
+
+static int check_vrfb_fb_size(unsigned long region_size,
+               const struct fb_var_screeninfo *var)
+{
+       unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual,
+               var->yres_virtual, var->bits_per_pixel >> 3);
+
+       return min_phys_size > region_size ? -EINVAL : 0;
+}
+
+static int check_fb_size(const struct omapfb_info *ofbi,
+               struct fb_var_screeninfo *var)
+{
+       unsigned long max_frame_size = ofbi->region.size;
+       int bytespp = var->bits_per_pixel >> 3;
+       unsigned long line_size = var->xres_virtual * bytespp;
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+               /* One needs to check for both VRFB and OMAPFB limitations. */
+               if (check_vrfb_fb_size(max_frame_size, var))
+                       shrink_height(omap_vrfb_max_height(
+                               max_frame_size, var->xres_virtual, bytespp) *
+                               line_size, var);
+
+               if (check_vrfb_fb_size(max_frame_size, var)) {
+                       DBG("cannot fit FB to memory\n");
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size);
+
+       if (line_size * var->yres_virtual > max_frame_size)
+               shrink_height(max_frame_size, var);
+
+       if (line_size * var->yres_virtual > max_frame_size) {
+               shrink_width(max_frame_size, var);
+               line_size = var->xres_virtual * bytespp;
+       }
+
+       if (line_size * var->yres_virtual > max_frame_size) {
+               DBG("cannot fit FB to memory\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Consider if VRFB assisted rotation is in use and if the virtual space for
+ * the zero degree view needs to be mapped. The need for mapping also acts as
+ * the trigger for setting up the hardware on the context in question. This
+ * ensures that one does not attempt to access the virtual view before the
+ * hardware is serving the address translations.
+ */
+static int setup_vrfb_rotation(struct fb_info *fbi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_mem_region *rg = &ofbi->region;
+       struct vrfb *vrfb = &rg->vrfb;
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct fb_fix_screeninfo *fix = &fbi->fix;
+       unsigned bytespp;
+       bool yuv_mode;
+       enum omap_color_mode mode;
+       int r;
+       bool reconf;
+
+       if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB)
+               return 0;
+
+       DBG("setup_vrfb_rotation\n");
+
+       r = fb_mode_to_dss_mode(var, &mode);
+       if (r)
+               return r;
+
+       bytespp = var->bits_per_pixel >> 3;
+
+       yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY;
+
+       /* We need to reconfigure VRFB if the resolution changes, if yuv mode
+        * is enabled/disabled, or if bytes per pixel changes */
+
+       /* XXX we shouldn't allow this when framebuffer is mmapped */
+
+       reconf = false;
+
+       if (yuv_mode != vrfb->yuv_mode)
+               reconf = true;
+       else if (bytespp != vrfb->bytespp)
+               reconf = true;
+       else if (vrfb->xres != var->xres_virtual ||
+                       vrfb->yres != var->yres_virtual)
+               reconf = true;
+
+       if (vrfb->vaddr[0] && reconf) {
+               fbi->screen_base = NULL;
+               fix->smem_start = 0;
+               fix->smem_len = 0;
+               iounmap(vrfb->vaddr[0]);
+               vrfb->vaddr[0] = NULL;
+               DBG("setup_vrfb_rotation: reset fb\n");
+       }
+
+       if (vrfb->vaddr[0])
+               return 0;
+
+       omap_vrfb_setup(&rg->vrfb, rg->paddr,
+                       var->xres_virtual,
+                       var->yres_virtual,
+                       bytespp, yuv_mode);
+
+       /* Now one can ioremap the 0 angle view */
+       r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0);
+       if (r)
+               return r;
+
+       /* used by open/write in fbmem.c */
+       fbi->screen_base = ofbi->region.vrfb.vaddr[0];
+
+       fix->smem_start = ofbi->region.vrfb.paddr[0];
+
+       switch (var->nonstd) {
+       case OMAPFB_COLOR_YUV422:
+       case OMAPFB_COLOR_YUY422:
+               fix->line_length =
+                       (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2;
+               break;
+       default:
+               fix->line_length =
+                       (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;
+               break;
+       }
+
+       fix->smem_len = var->yres_virtual * fix->line_length;
+
+       return 0;
+}
+
+int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
+                       struct fb_var_screeninfo *var)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {
+               struct omapfb_colormode *mode = &omapfb_colormodes[i];
+               if (dssmode == mode->dssmode) {
+                       assign_colormode_to_var(var, mode);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+void set_fb_fix(struct fb_info *fbi)
+{
+       struct fb_fix_screeninfo *fix = &fbi->fix;
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_mem_region *rg = &ofbi->region;
+
+       DBG("set_fb_fix\n");
+
+       /* used by open/write in fbmem.c */
+       fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi);
+
+       /* used by mmap in fbmem.c */
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+               switch (var->nonstd) {
+               case OMAPFB_COLOR_YUV422:
+               case OMAPFB_COLOR_YUY422:
+                       fix->line_length =
+                               (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2;
+                       break;
+               default:
+                       fix->line_length =
+                               (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;
+                       break;
+               }
+
+               fix->smem_len = var->yres_virtual * fix->line_length;
+       } else {
+               fix->line_length =
+                       (var->xres_virtual * var->bits_per_pixel) >> 3;
+               fix->smem_len = rg->size;
+       }
+
+       fix->smem_start = omapfb_get_region_paddr(ofbi);
+
+       fix->type = FB_TYPE_PACKED_PIXELS;
+
+       if (var->nonstd)
+               fix->visual = FB_VISUAL_PSEUDOCOLOR;
+       else {
+               switch (var->bits_per_pixel) {
+               case 32:
+               case 24:
+               case 16:
+               case 12:
+                       fix->visual = FB_VISUAL_TRUECOLOR;
+                       /* 12bpp is stored in 16 bits */
+                       break;
+               case 1:
+               case 2:
+               case 4:
+               case 8:
+                       fix->visual = FB_VISUAL_PSEUDOCOLOR;
+                       break;
+               }
+       }
+
+       fix->accel = FB_ACCEL_NONE;
+
+       fix->xpanstep = 1;
+       fix->ypanstep = 1;
+}
+
+/* check new var and possibly modify it to be ok */
+int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omap_dss_device *display = fb2display(fbi);
+       enum omap_color_mode mode = 0;
+       int i;
+       int r;
+
+       DBG("check_fb_var %d\n", ofbi->id);
+
+       if (ofbi->region.size == 0)
+               return 0;
+
+       r = fb_mode_to_dss_mode(var, &mode);
+       if (r) {
+               DBG("cannot convert var to omap dss mode\n");
+               return r;
+       }
+
+       for (i = 0; i < ofbi->num_overlays; ++i) {
+               if ((ofbi->overlays[i]->supported_modes & mode) == 0) {
+                       DBG("invalid mode\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (var->rotate < 0 || var->rotate > 3)
+               return -EINVAL;
+
+       if (check_fb_res_bounds(var))
+               return -EINVAL;
+
+       if (check_fb_size(ofbi, var))
+               return -EINVAL;
+
+       if (var->xres + var->xoffset > var->xres_virtual)
+               var->xoffset = var->xres_virtual - var->xres;
+       if (var->yres + var->yoffset > var->yres_virtual)
+               var->yoffset = var->yres_virtual - var->yres;
+
+       DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n",
+                       var->xres, var->yres,
+                       var->xres_virtual, var->yres_virtual);
+
+       var->height             = -1;
+       var->width              = -1;
+       var->grayscale          = 0;
+
+       if (display && display->get_timings) {
+               struct omap_video_timings timings;
+               display->get_timings(display, &timings);
+
+               /* pixclock in ps, the rest in pixclock */
+               var->pixclock = timings.pixel_clock != 0 ?
+                       KHZ2PICOS(timings.pixel_clock) :
+                       0;
+               var->left_margin = timings.hfp;
+               var->right_margin = timings.hbp;
+               var->upper_margin = timings.vfp;
+               var->lower_margin = timings.vbp;
+               var->hsync_len = timings.hsw;
+               var->vsync_len = timings.vsw;
+       } else {
+               var->pixclock = 0;
+               var->left_margin = 0;
+               var->right_margin = 0;
+               var->upper_margin = 0;
+               var->lower_margin = 0;
+               var->hsync_len = 0;
+               var->vsync_len = 0;
+       }
+
+       /* TODO: get these from panel->config */
+       var->vmode              = FB_VMODE_NONINTERLACED;
+       var->sync               = 0;
+
+       return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------------
+ * fbdev framework callbacks
+ * ---------------------------------------------------------------------------
+ */
+static int omapfb_open(struct fb_info *fbi, int user)
+{
+       return 0;
+}
+
+static int omapfb_release(struct fb_info *fbi, int user)
+{
+#if 0
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_dss_device *display = fb2display(fbi);
+
+       DBG("Closing fb with plane index %d\n", ofbi->id);
+
+       omapfb_lock(fbdev);
+
+       if (display && display->get_update_mode && display->update) {
+               /* XXX this update should be removed, I think. But it's
+                * good for debugging */
+               if (display->get_update_mode(display) ==
+                               OMAP_DSS_UPDATE_MANUAL) {
+                       u16 w, h;
+
+                       if (display->sync)
+                               display->sync(display);
+
+                       display->get_resolution(display, &w, &h);
+                       display->update(display, 0, 0, w, h);
+               }
+       }
+
+       if (display && display->sync)
+               display->sync(display);
+
+       omapfb_unlock(fbdev);
+#endif
+       return 0;
+}
+
+static unsigned calc_rotation_offset_dma(struct fb_var_screeninfo *var,
+               struct fb_fix_screeninfo *fix, int rotation)
+{
+       unsigned offset;
+
+       offset = var->yoffset * fix->line_length +
+               var->xoffset * (var->bits_per_pixel >> 3);
+
+       return offset;
+}
+
+static unsigned calc_rotation_offset_vrfb(struct fb_var_screeninfo *var,
+               struct fb_fix_screeninfo *fix, int rotation)
+{
+       unsigned offset;
+
+       if (rotation == FB_ROTATE_UD)
+               offset = (var->yres_virtual - var->yres) *
+                       fix->line_length;
+       else if (rotation == FB_ROTATE_CW)
+               offset = (var->yres_virtual - var->yres) *
+                       (var->bits_per_pixel >> 3);
+       else
+               offset = 0;
+
+       if (rotation == FB_ROTATE_UR)
+               offset += var->yoffset * fix->line_length +
+                       var->xoffset * (var->bits_per_pixel >> 3);
+       else if (rotation == FB_ROTATE_UD)
+               offset -= var->yoffset * fix->line_length +
+                       var->xoffset * (var->bits_per_pixel >> 3);
+       else if (rotation == FB_ROTATE_CW)
+               offset -= var->xoffset * fix->line_length +
+                       var->yoffset * (var->bits_per_pixel >> 3);
+       else if (rotation == FB_ROTATE_CCW)
+               offset += var->xoffset * fix->line_length +
+                       var->yoffset * (var->bits_per_pixel >> 3);
+
+       return offset;
+}
+
+
+/* setup overlay according to the fb */
+static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
+               u16 posx, u16 posy, u16 outw, u16 outh)
+{
+       int r = 0;
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct fb_fix_screeninfo *fix = &fbi->fix;
+       enum omap_color_mode mode = 0;
+       int offset;
+       u32 data_start_p;
+       void __iomem *data_start_v;
+       struct omap_overlay_info info;
+       int xres, yres;
+       int screen_width;
+       int mirror;
+       int rotation = var->rotate;
+       int i;
+
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               if (ovl != ofbi->overlays[i])
+                       continue;
+
+               rotation = (rotation + ofbi->rotation[i]) % 4;
+               break;
+       }
+
+       DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id,
+                       posx, posy, outw, outh);
+
+       if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) {
+               xres = var->yres;
+               yres = var->xres;
+       } else {
+               xres = var->xres;
+               yres = var->yres;
+       }
+
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+               data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);
+               data_start_v = NULL;
+       } else {
+               data_start_p = omapfb_get_region_paddr(ofbi);
+               data_start_v = omapfb_get_region_vaddr(ofbi);
+       }
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+               offset = calc_rotation_offset_vrfb(var, fix, rotation);
+       else
+               offset = calc_rotation_offset_dma(var, fix, rotation);
+
+       data_start_p += offset;
+       data_start_v += offset;
+
+       if (offset)
+               DBG("offset %d, %d = %d\n",
+                               var->xoffset, var->yoffset, offset);
+
+       DBG("paddr %x, vaddr %p\n", data_start_p, data_start_v);
+
+       r = fb_mode_to_dss_mode(var, &mode);
+       if (r) {
+               DBG("fb_mode_to_dss_mode failed");
+               goto err;
+       }
+
+       switch (var->nonstd) {
+       case OMAPFB_COLOR_YUV422:
+       case OMAPFB_COLOR_YUY422:
+               if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+                       screen_width = fix->line_length
+                               / (var->bits_per_pixel >> 2);
+                       break;
+               }
+       default:
+               screen_width = fix->line_length / (var->bits_per_pixel >> 3);
+               break;
+       }
+
+       ovl->get_overlay_info(ovl, &info);
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
+               mirror = 0;
+       else
+               mirror = ofbi->mirror;
+
+       info.paddr = data_start_p;
+       info.vaddr = data_start_v;
+       info.screen_width = screen_width;
+       info.width = xres;
+       info.height = yres;
+       info.color_mode = mode;
+       info.rotation_type = ofbi->rotation_type;
+       info.rotation = rotation;
+       info.mirror = mirror;
+
+       info.pos_x = posx;
+       info.pos_y = posy;
+       info.out_width = outw;
+       info.out_height = outh;
+
+       r = ovl->set_overlay_info(ovl, &info);
+       if (r) {
+               DBG("ovl->setup_overlay_info failed\n");
+               goto err;
+       }
+
+       return 0;
+
+err:
+       DBG("setup_overlay failed\n");
+       return r;
+}
+
+/* apply var to the overlay */
+int omapfb_apply_changes(struct fb_info *fbi, int init)
+{
+       int r = 0;
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct omap_overlay *ovl;
+       u16 posx, posy;
+       u16 outw, outh;
+       int i;
+
+#ifdef DEBUG
+       if (omapfb_test_pattern)
+               fill_fb(fbi);
+#endif
+
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               ovl = ofbi->overlays[i];
+
+               DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id);
+
+               if (ofbi->region.size == 0) {
+                       /* the fb is not available. disable the overlay */
+                       omapfb_overlay_enable(ovl, 0);
+                       if (!init && ovl->manager)
+                               ovl->manager->apply(ovl->manager);
+                       continue;
+               }
+
+               if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
+                       int rotation = (var->rotate + ofbi->rotation[i]) % 4;
+                       if (rotation == FB_ROTATE_CW ||
+                                       rotation == FB_ROTATE_CCW) {
+                               outw = var->yres;
+                               outh = var->xres;
+                       } else {
+                               outw = var->xres;
+                               outh = var->yres;
+                       }
+               } else {
+                       outw = ovl->info.out_width;
+                       outh = ovl->info.out_height;
+               }
+
+               if (init) {
+                       posx = 0;
+                       posy = 0;
+               } else {
+                       posx = ovl->info.pos_x;
+                       posy = ovl->info.pos_y;
+               }
+
+               r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh);
+               if (r)
+                       goto err;
+
+               if (!init && ovl->manager)
+                       ovl->manager->apply(ovl->manager);
+       }
+       return 0;
+err:
+       DBG("apply_changes failed\n");
+       return r;
+}
+
+/* checks var and eventually tweaks it to something supported,
+ * DO NOT MODIFY PAR */
+static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
+{
+       int r;
+
+       DBG("check_var(%d)\n", FB2OFB(fbi)->id);
+
+       r = check_fb_var(fbi, var);
+
+       return r;
+}
+
+/* set the video mode according to info->var */
+static int omapfb_set_par(struct fb_info *fbi)
+{
+       int r;
+
+       DBG("set_par(%d)\n", FB2OFB(fbi)->id);
+
+       set_fb_fix(fbi);
+
+       r = setup_vrfb_rotation(fbi);
+       if (r)
+               return r;
+
+       r = omapfb_apply_changes(fbi, 0);
+
+       return r;
+}
+
+static int omapfb_pan_display(struct fb_var_screeninfo *var,
+               struct fb_info *fbi)
+{
+       struct fb_var_screeninfo new_var;
+       int r;
+
+       DBG("pan_display(%d)\n", FB2OFB(fbi)->id);
+
+       if (var->xoffset == fbi->var.xoffset &&
+           var->yoffset == fbi->var.yoffset)
+               return 0;
+
+       new_var = fbi->var;
+       new_var.xoffset = var->xoffset;
+       new_var.yoffset = var->yoffset;
+
+       fbi->var = new_var;
+
+       r = omapfb_apply_changes(fbi, 0);
+
+       return r;
+}
+
+static void mmap_user_open(struct vm_area_struct *vma)
+{
+       struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+
+       atomic_inc(&ofbi->map_count);
+}
+
+static void mmap_user_close(struct vm_area_struct *vma)
+{
+       struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data;
+
+       atomic_dec(&ofbi->map_count);
+}
+
+static struct vm_operations_struct mmap_user_ops = {
+       .open = mmap_user_open,
+       .close = mmap_user_close,
+};
+
+static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct fb_fix_screeninfo *fix = &fbi->fix;
+       unsigned long off;
+       unsigned long start;
+       u32 len;
+
+       if (vma->vm_end - vma->vm_start == 0)
+               return 0;
+       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+               return -EINVAL;
+       off = vma->vm_pgoff << PAGE_SHIFT;
+
+       start = omapfb_get_region_paddr(ofbi);
+       len = fix->smem_len;
+       if (off >= len)
+               return -EINVAL;
+       if ((vma->vm_end - vma->vm_start + off) > len)
+               return -EINVAL;
+
+       off += start;
+
+       DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off);
+
+       vma->vm_pgoff = off >> PAGE_SHIFT;
+       vma->vm_flags |= VM_IO | VM_RESERVED;
+       vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+       vma->vm_ops = &mmap_user_ops;
+       vma->vm_private_data = ofbi;
+       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+                            vma->vm_end - vma->vm_start, vma->vm_page_prot))
+               return -EAGAIN;
+       /* vm_ops.open won't be called for mmap itself. */
+       atomic_inc(&ofbi->map_count);
+       return 0;
+}
+
+/* Store a single color palette entry into a pseudo palette or the hardware
+ * palette if one is available. For now we support only 16bpp and thus store
+ * the entry only to the pseudo palette.
+ */
+static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,
+               u_int blue, u_int transp, int update_hw_pal)
+{
+       /*struct omapfb_info *ofbi = FB2OFB(fbi);*/
+       /*struct omapfb2_device *fbdev = ofbi->fbdev;*/
+       struct fb_var_screeninfo *var = &fbi->var;
+       int r = 0;
+
+       enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */
+
+       /*switch (plane->color_mode) {*/
+       switch (mode) {
+       case OMAPFB_COLOR_YUV422:
+       case OMAPFB_COLOR_YUV420:
+       case OMAPFB_COLOR_YUY422:
+               r = -EINVAL;
+               break;
+       case OMAPFB_COLOR_CLUT_8BPP:
+       case OMAPFB_COLOR_CLUT_4BPP:
+       case OMAPFB_COLOR_CLUT_2BPP:
+       case OMAPFB_COLOR_CLUT_1BPP:
+               /*
+                  if (fbdev->ctrl->setcolreg)
+                  r = fbdev->ctrl->setcolreg(regno, red, green, blue,
+                  transp, update_hw_pal);
+                  */
+               /* Fallthrough */
+               r = -EINVAL;
+               break;
+       case OMAPFB_COLOR_RGB565:
+       case OMAPFB_COLOR_RGB444:
+       case OMAPFB_COLOR_RGB24P:
+       case OMAPFB_COLOR_RGB24U:
+               if (r != 0)
+                       break;
+
+               if (regno < 0) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               if (regno < 16) {
+                       u16 pal;
+                       pal = ((red >> (16 - var->red.length)) <<
+                                       var->red.offset) |
+                               ((green >> (16 - var->green.length)) <<
+                                var->green.offset) |
+                               (blue >> (16 - var->blue.length));
+                       ((u32 *)(fbi->pseudo_palette))[regno] = pal;
+               }
+               break;
+       default:
+               BUG();
+       }
+       return r;
+}
+
+static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+               u_int transp, struct fb_info *info)
+{
+       DBG("setcolreg\n");
+
+       return _setcolreg(info, regno, red, green, blue, transp, 1);
+}
+
+static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+       int count, index, r;
+       u16 *red, *green, *blue, *transp;
+       u16 trans = 0xffff;
+
+       DBG("setcmap\n");
+
+       red     = cmap->red;
+       green   = cmap->green;
+       blue    = cmap->blue;
+       transp  = cmap->transp;
+       index   = cmap->start;
+
+       for (count = 0; count < cmap->len; count++) {
+               if (transp)
+                       trans = *transp++;
+               r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,
+                               count == cmap->len - 1);
+               if (r != 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+static int omapfb_blank(int blank, struct fb_info *fbi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_dss_device *display = fb2display(fbi);
+       int do_update = 0;
+       int r = 0;
+
+       omapfb_lock(fbdev);
+
+       switch (blank) {
+       case FB_BLANK_UNBLANK:
+               if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
+                       goto exit;
+
+               if (display->resume)
+                       r = display->resume(display);
+
+               if (r == 0 && display->get_update_mode &&
+                               display->get_update_mode(display) ==
+                               OMAP_DSS_UPDATE_MANUAL)
+                       do_update = 1;
+
+               break;
+
+       case FB_BLANK_NORMAL:
+               /* FB_BLANK_NORMAL could be implemented.
+                * Needs DSS additions. */
+       case FB_BLANK_VSYNC_SUSPEND:
+       case FB_BLANK_HSYNC_SUSPEND:
+       case FB_BLANK_POWERDOWN:
+               if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
+                       goto exit;
+
+               if (display->suspend)
+                       r = display->suspend(display);
+
+               break;
+
+       default:
+               r = -EINVAL;
+       }
+
+exit:
+       omapfb_unlock(fbdev);
+
+       if (r == 0 && do_update && display->update) {
+               u16 w, h;
+               display->get_resolution(display, &w, &h);
+
+               r = display->update(display, 0, 0, w, h);
+       }
+
+       return r;
+}
+
+#if 0
+/* XXX fb_read and fb_write are needed for VRFB */
+ssize_t omapfb_write(struct fb_info *info, const char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos);
+       /* XXX needed for VRFB */
+       return count;
+}
+#endif
+
+static struct fb_ops omapfb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_open        = omapfb_open,
+       .fb_release     = omapfb_release,
+       .fb_fillrect    = cfb_fillrect,
+       .fb_copyarea    = cfb_copyarea,
+       .fb_imageblit   = cfb_imageblit,
+       .fb_blank       = omapfb_blank,
+       .fb_ioctl       = omapfb_ioctl,
+       .fb_check_var   = omapfb_check_var,
+       .fb_set_par     = omapfb_set_par,
+       .fb_pan_display = omapfb_pan_display,
+       .fb_mmap        = omapfb_mmap,
+       .fb_setcolreg   = omapfb_setcolreg,
+       .fb_setcmap     = omapfb_setcmap,
+       /*.fb_write     = omapfb_write,*/
+};
+
+static void omapfb_free_fbmem(struct fb_info *fbi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omapfb2_mem_region *rg;
+
+       rg = &ofbi->region;
+
+       if (rg->paddr)
+               if (omap_vram_free(rg->paddr, rg->size))
+                       dev_err(fbdev->dev, "VRAM FREE failed\n");
+
+       if (rg->vaddr)
+               iounmap(rg->vaddr);
+
+       if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+               /* unmap the 0 angle rotation */
+               if (rg->vrfb.vaddr[0]) {
+                       iounmap(rg->vrfb.vaddr[0]);
+                       omap_vrfb_release_ctx(&rg->vrfb);
+               }
+       }
+
+       rg->vaddr = NULL;
+       rg->paddr = 0;
+       rg->alloc = 0;
+       rg->size = 0;
+}
+
+static void clear_fb_info(struct fb_info *fbi)
+{
+       memset(&fbi->var, 0, sizeof(fbi->var));
+       memset(&fbi->fix, 0, sizeof(fbi->fix));
+       strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id));
+}
+
+static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev)
+{
+       int i;
+
+       DBG("free all fbmem\n");
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               struct fb_info *fbi = fbdev->fbs[i];
+               omapfb_free_fbmem(fbi);
+               clear_fb_info(fbi);
+       }
+
+       return 0;
+}
+
+static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
+               unsigned long paddr)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omapfb2_mem_region *rg;
+       void __iomem *vaddr;
+       int r;
+
+       rg = &ofbi->region;
+       memset(rg, 0, sizeof(*rg));
+
+       size = PAGE_ALIGN(size);
+
+       if (!paddr) {
+               DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);
+               r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr);
+       } else {
+               DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr,
+                               ofbi->id);
+               r = omap_vram_reserve(paddr, size);
+       }
+
+       if (r) {
+               dev_err(fbdev->dev, "failed to allocate framebuffer\n");
+               return -ENOMEM;
+       }
+
+       if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) {
+               vaddr = ioremap_wc(paddr, size);
+
+               if (!vaddr) {
+                       dev_err(fbdev->dev, "failed to ioremap framebuffer\n");
+                       omap_vram_free(paddr, size);
+                       return -ENOMEM;
+               }
+
+               DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr);
+       } else {
+               r = omap_vrfb_request_ctx(&rg->vrfb);
+               if (r) {
+                       dev_err(fbdev->dev, "vrfb create ctx failed\n");
+                       return r;
+               }
+
+               vaddr = NULL;
+       }
+
+       rg->paddr = paddr;
+       rg->vaddr = vaddr;
+       rg->size = size;
+       rg->alloc = 1;
+
+       return 0;
+}
+
+/* allocate fbmem using display resolution as reference */
+static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
+               unsigned long paddr)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omap_dss_device *display;
+       int bytespp;
+
+       display =  fb2display(fbi);
+
+       if (!display)
+               return 0;
+
+       switch (display->get_recommended_bpp(display)) {
+       case 16:
+               bytespp = 2;
+               break;
+       case 24:
+               bytespp = 4;
+               break;
+       default:
+               bytespp = 4;
+               break;
+       }
+
+       if (!size) {
+               u16 w, h;
+
+               display->get_resolution(display, &w, &h);
+
+               if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {
+                       size = max(omap_vrfb_min_phys_size(w, h, bytespp),
+                                       omap_vrfb_min_phys_size(h, w, bytespp));
+
+                       DBG("adjusting fb mem size for VRFB, %u -> %lu\n",
+                                       w * h * bytespp, size);
+               } else {
+                       size = w * h * bytespp;
+               }
+       }
+
+       if (!size)
+               return 0;
+
+       return omapfb_alloc_fbmem(fbi, size, paddr);
+}
+
+static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt)
+{
+       enum omap_color_mode mode;
+
+       switch (fmt) {
+       case OMAPFB_COLOR_RGB565:
+               mode = OMAP_DSS_COLOR_RGB16;
+               break;
+       case OMAPFB_COLOR_YUV422:
+               mode = OMAP_DSS_COLOR_YUV2;
+               break;
+       case OMAPFB_COLOR_CLUT_8BPP:
+               mode = OMAP_DSS_COLOR_CLUT8;
+               break;
+       case OMAPFB_COLOR_CLUT_4BPP:
+               mode = OMAP_DSS_COLOR_CLUT4;
+               break;
+       case OMAPFB_COLOR_CLUT_2BPP:
+               mode = OMAP_DSS_COLOR_CLUT2;
+               break;
+       case OMAPFB_COLOR_CLUT_1BPP:
+               mode = OMAP_DSS_COLOR_CLUT1;
+               break;
+       case OMAPFB_COLOR_RGB444:
+               mode = OMAP_DSS_COLOR_RGB12U;
+               break;
+       case OMAPFB_COLOR_YUY422:
+               mode = OMAP_DSS_COLOR_UYVY;
+               break;
+       case OMAPFB_COLOR_ARGB16:
+               mode = OMAP_DSS_COLOR_ARGB16;
+               break;
+       case OMAPFB_COLOR_RGB24U:
+               mode = OMAP_DSS_COLOR_RGB24U;
+               break;
+       case OMAPFB_COLOR_RGB24P:
+               mode = OMAP_DSS_COLOR_RGB24P;
+               break;
+       case OMAPFB_COLOR_ARGB32:
+               mode = OMAP_DSS_COLOR_ARGB32;
+               break;
+       case OMAPFB_COLOR_RGBA32:
+               mode = OMAP_DSS_COLOR_RGBA32;
+               break;
+       case OMAPFB_COLOR_RGBX32:
+               mode = OMAP_DSS_COLOR_RGBX32;
+               break;
+       default:
+               mode = -EINVAL;
+       }
+
+       return mode;
+}
+
+static int omapfb_parse_vram_param(const char *param, int max_entries,
+               unsigned long *sizes, unsigned long *paddrs)
+{
+       int fbnum;
+       unsigned long size;
+       unsigned long paddr = 0;
+       char *p, *start;
+
+       start = (char *)param;
+
+       while (1) {
+               p = start;
+
+               fbnum = simple_strtoul(p, &p, 10);
+
+               if (p == param)
+                       return -EINVAL;
+
+               if (*p != ':')
+                       return -EINVAL;
+
+               if (fbnum >= max_entries)
+                       return -EINVAL;
+
+               size = memparse(p + 1, &p);
+
+               if (!size)
+                       return -EINVAL;
+
+               paddr = 0;
+
+               if (*p == '@') {
+                       paddr = simple_strtoul(p + 1, &p, 16);
+
+                       if (!paddr)
+                               return -EINVAL;
+
+               }
+
+               paddrs[fbnum] = paddr;
+               sizes[fbnum] = size;
+
+               if (*p == 0)
+                       break;
+
+               if (*p != ',')
+                       return -EINVAL;
+
+               ++p;
+
+               start = p;
+       }
+
+       return 0;
+}
+
+static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)
+{
+       int i, r;
+       unsigned long vram_sizes[10];
+       unsigned long vram_paddrs[10];
+
+       memset(&vram_sizes, 0, sizeof(vram_sizes));
+       memset(&vram_paddrs, 0, sizeof(vram_paddrs));
+
+       if (def_vram && omapfb_parse_vram_param(def_vram, 10,
+                               vram_sizes, vram_paddrs)) {
+               dev_err(fbdev->dev, "failed to parse vram parameter\n");
+
+               memset(&vram_sizes, 0, sizeof(vram_sizes));
+               memset(&vram_paddrs, 0, sizeof(vram_paddrs));
+       }
+
+       if (fbdev->dev->platform_data) {
+               struct omapfb_platform_data *opd;
+               opd = fbdev->dev->platform_data;
+               for (i = 0; i < opd->mem_desc.region_cnt; ++i) {
+                       if (!vram_sizes[i]) {
+                               unsigned long size;
+                               unsigned long paddr;
+
+                               size = opd->mem_desc.region[i].size;
+                               paddr = opd->mem_desc.region[i].paddr;
+
+                               vram_sizes[i] = size;
+                               vram_paddrs[i] = paddr;
+                       }
+               }
+       }
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               /* allocate memory automatically only for fb0, or if
+                * excplicitly defined with vram or plat data option */
+               if (i == 0 || vram_sizes[i] != 0) {
+                       r = omapfb_alloc_fbmem_display(fbdev->fbs[i],
+                                       vram_sizes[i], vram_paddrs[i]);
+
+                       if (r)
+                               return r;
+               }
+       }
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+               struct omapfb2_mem_region *rg;
+               rg = &ofbi->region;
+
+               DBG("region%d phys %08x virt %p size=%lu\n",
+                               i,
+                               rg->paddr,
+                               rg->vaddr,
+                               rg->size);
+       }
+
+       return 0;
+}
+
+int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_dss_device *display = fb2display(fbi);
+       struct omapfb2_mem_region *rg = &ofbi->region;
+       unsigned long old_size = rg->size;
+       unsigned long old_paddr = rg->paddr;
+       int old_type = rg->type;
+       int r;
+
+       if (type > OMAPFB_MEMTYPE_MAX)
+               return -EINVAL;
+
+       size = PAGE_ALIGN(size);
+
+       if (old_size == size && old_type == type)
+               return 0;
+
+       if (display && display->sync)
+                       display->sync(display);
+
+       omapfb_free_fbmem(fbi);
+
+       if (size == 0) {
+               clear_fb_info(fbi);
+               return 0;
+       }
+
+       r = omapfb_alloc_fbmem(fbi, size, 0);
+
+       if (r) {
+               if (old_size)
+                       omapfb_alloc_fbmem(fbi, old_size, old_paddr);
+
+               if (rg->size == 0)
+                       clear_fb_info(fbi);
+
+               return r;
+       }
+
+       if (old_size == size)
+               return 0;
+
+       if (old_size == 0) {
+               DBG("initializing fb %d\n", ofbi->id);
+               r = omapfb_fb_init(fbdev, fbi);
+               if (r) {
+                       DBG("omapfb_fb_init failed\n");
+                       goto err;
+               }
+               r = omapfb_apply_changes(fbi, 1);
+               if (r) {
+                       DBG("omapfb_apply_changes failed\n");
+                       goto err;
+               }
+       } else {
+               struct fb_var_screeninfo new_var;
+               memcpy(&new_var, &fbi->var, sizeof(new_var));
+               r = check_fb_var(fbi, &new_var);
+               if (r)
+                       goto err;
+               memcpy(&fbi->var, &new_var, sizeof(fbi->var));
+               set_fb_fix(fbi);
+               r = setup_vrfb_rotation(fbi);
+               if (r)
+                       goto err;
+       }
+
+       return 0;
+err:
+       omapfb_free_fbmem(fbi);
+       clear_fb_info(fbi);
+       return r;
+}
+
+/* initialize fb_info, var, fix to something sane based on the display */
+static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
+{
+       struct fb_var_screeninfo *var = &fbi->var;
+       struct omap_dss_device *display = fb2display(fbi);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       int r = 0;
+
+       fbi->fbops = &omapfb_ops;
+       fbi->flags = FBINFO_FLAG_DEFAULT;
+       fbi->pseudo_palette = fbdev->pseudo_palette;
+
+       if (ofbi->region.size == 0) {
+               clear_fb_info(fbi);
+               return 0;
+       }
+
+       var->nonstd = 0;
+       var->bits_per_pixel = 0;
+
+       var->rotate = def_rotate;
+
+       /*
+        * Check if there is a default color format set in the board file,
+        * and use this format instead the default deducted from the
+        * display bpp.
+        */
+       if (fbdev->dev->platform_data) {
+               struct omapfb_platform_data *opd;
+               int id = ofbi->id;
+
+               opd = fbdev->dev->platform_data;
+               if (opd->mem_desc.region[id].format_used) {
+                       enum omap_color_mode mode;
+                       enum omapfb_color_format format;
+
+                       format = opd->mem_desc.region[id].format;
+                       mode = fb_format_to_dss_mode(format);
+                       if (mode < 0) {
+                               r = mode;
+                               goto err;
+                       }
+                       r = dss_mode_to_fb_mode(mode, var);
+                       if (r < 0)
+                               goto err;
+               }
+       }
+
+       if (display) {
+               u16 w, h;
+               int rotation = (var->rotate + ofbi->rotation[0]) % 4;
+
+               display->get_resolution(display, &w, &h);
+
+               if (rotation == FB_ROTATE_CW ||
+                               rotation == FB_ROTATE_CCW) {
+                       var->xres = h;
+                       var->yres = w;
+               } else {
+                       var->xres = w;
+                       var->yres = h;
+               }
+
+               var->xres_virtual = var->xres;
+               var->yres_virtual = var->yres;
+
+               if (!var->bits_per_pixel) {
+                       switch (display->get_recommended_bpp(display)) {
+                       case 16:
+                               var->bits_per_pixel = 16;
+                               break;
+                       case 24:
+                               var->bits_per_pixel = 32;
+                               break;
+                       default:
+                               dev_err(fbdev->dev, "illegal display "
+                                               "bpp\n");
+                               return -EINVAL;
+                       }
+               }
+       } else {
+               /* if there's no display, let's just guess some basic values */
+               var->xres = 320;
+               var->yres = 240;
+               var->xres_virtual = var->xres;
+               var->yres_virtual = var->yres;
+               if (!var->bits_per_pixel)
+                       var->bits_per_pixel = 16;
+       }
+
+       r = check_fb_var(fbi, var);
+       if (r)
+               goto err;
+
+       set_fb_fix(fbi);
+       r = setup_vrfb_rotation(fbi);
+       if (r)
+               goto err;
+
+       r = fb_alloc_cmap(&fbi->cmap, 256, 0);
+       if (r)
+               dev_err(fbdev->dev, "unable to allocate color map memory\n");
+
+err:
+       return r;
+}
+
+static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi)
+{
+       fb_dealloc_cmap(&fbi->cmap);
+}
+
+
+static void omapfb_free_resources(struct omapfb2_device *fbdev)
+{
+       int i;
+
+       DBG("free_resources\n");
+
+       if (fbdev == NULL)
+               return;
+
+       for (i = 0; i < fbdev->num_fbs; i++)
+               unregister_framebuffer(fbdev->fbs[i]);
+
+       /* free the reserved fbmem */
+       omapfb_free_all_fbmem(fbdev);
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               fbinfo_cleanup(fbdev, fbdev->fbs[i]);
+               framebuffer_release(fbdev->fbs[i]);
+       }
+
+       for (i = 0; i < fbdev->num_displays; i++) {
+               if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED)
+                       fbdev->displays[i]->disable(fbdev->displays[i]);
+
+               omap_dss_put_device(fbdev->displays[i]);
+       }
+
+       dev_set_drvdata(fbdev->dev, NULL);
+       kfree(fbdev);
+}
+
+static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
+{
+       int r, i;
+
+       fbdev->num_fbs = 0;
+
+       DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS);
+
+       /* allocate fb_infos */
+       for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) {
+               struct fb_info *fbi;
+               struct omapfb_info *ofbi;
+
+               fbi = framebuffer_alloc(sizeof(struct omapfb_info),
+                               fbdev->dev);
+
+               if (fbi == NULL) {
+                       dev_err(fbdev->dev,
+                               "unable to allocate memory for plane info\n");
+                       return -ENOMEM;
+               }
+
+               clear_fb_info(fbi);
+
+               fbdev->fbs[i] = fbi;
+
+               ofbi = FB2OFB(fbi);
+               ofbi->fbdev = fbdev;
+               ofbi->id = i;
+
+               /* assign these early, so that fb alloc can use them */
+               ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB :
+                       OMAP_DSS_ROT_DMA;
+               ofbi->mirror = def_mirror;
+
+               fbdev->num_fbs++;
+       }
+
+       DBG("fb_infos allocated\n");
+
+       /* assign overlays for the fbs */
+       for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) {
+               struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+               ofbi->overlays[0] = fbdev->overlays[i];
+               ofbi->num_overlays = 1;
+       }
+
+       /* allocate fb memories */
+       r = omapfb_allocate_all_fbs(fbdev);
+       if (r) {
+               dev_err(fbdev->dev, "failed to allocate fbmem\n");
+               return r;
+       }
+
+       DBG("fbmems allocated\n");
+
+       /* setup fb_infos */
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               r = omapfb_fb_init(fbdev, fbdev->fbs[i]);
+               if (r) {
+                       dev_err(fbdev->dev, "failed to setup fb_info\n");
+                       return r;
+               }
+       }
+
+       DBG("fb_infos initialized\n");
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               r = register_framebuffer(fbdev->fbs[i]);
+               if (r != 0) {
+                       dev_err(fbdev->dev,
+                               "registering framebuffer %d failed\n", i);
+                       return r;
+               }
+       }
+
+       DBG("framebuffers registered\n");
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               r = omapfb_apply_changes(fbdev->fbs[i], 1);
+               if (r) {
+                       dev_err(fbdev->dev, "failed to change mode\n");
+                       return r;
+               }
+       }
+
+       DBG("create sysfs for fbs\n");
+       r = omapfb_create_sysfs(fbdev);
+       if (r) {
+               dev_err(fbdev->dev, "failed to create sysfs entries\n");
+               return r;
+       }
+
+       /* Enable fb0 */
+       if (fbdev->num_fbs > 0) {
+               struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]);
+
+               if (ofbi->num_overlays > 0) {
+                       struct omap_overlay *ovl = ofbi->overlays[0];
+
+                       r = omapfb_overlay_enable(ovl, 1);
+
+                       if (r) {
+                               dev_err(fbdev->dev,
+                                               "failed to enable overlay\n");
+                               return r;
+                       }
+               }
+       }
+
+       DBG("create_framebuffers done\n");
+
+       return 0;
+}
+
+static int omapfb_mode_to_timings(const char *mode_str,
+               struct omap_video_timings *timings, u8 *bpp)
+{
+       struct fb_info fbi;
+       struct fb_var_screeninfo var;
+       struct fb_ops fbops;
+       int r;
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+       if (strcmp(mode_str, "pal") == 0) {
+               *timings = omap_dss_pal_timings;
+               *bpp = 0;
+               return 0;
+       } else if (strcmp(mode_str, "ntsc") == 0) {
+               *timings = omap_dss_ntsc_timings;
+               *bpp = 0;
+               return 0;
+       }
+#endif
+
+       /* this is quite a hack, but I wanted to use the modedb and for
+        * that we need fb_info and var, so we create dummy ones */
+
+       memset(&fbi, 0, sizeof(fbi));
+       memset(&var, 0, sizeof(var));
+       memset(&fbops, 0, sizeof(fbops));
+       fbi.fbops = &fbops;
+
+       r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24);
+
+       if (r != 0) {
+               timings->pixel_clock = PICOS2KHZ(var.pixclock);
+               timings->hfp = var.left_margin;
+               timings->hbp = var.right_margin;
+               timings->vfp = var.upper_margin;
+               timings->vbp = var.lower_margin;
+               timings->hsw = var.hsync_len;
+               timings->vsw = var.vsync_len;
+               timings->x_res = var.xres;
+               timings->y_res = var.yres;
+
+               switch (var.bits_per_pixel) {
+               case 16:
+                       *bpp = 16;
+                       break;
+               case 24:
+               case 32:
+               default:
+                       *bpp = 24;
+                       break;
+               }
+
+               return 0;
+       } else {
+               return -EINVAL;
+       }
+}
+
+static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str)
+{
+       int r;
+       u8 bpp;
+       struct omap_video_timings timings;
+
+       r = omapfb_mode_to_timings(mode_str, &timings, &bpp);
+       if (r)
+               return r;
+
+       display->panel.recommended_bpp = bpp;
+
+       if (!display->check_timings || !display->set_timings)
+               return -EINVAL;
+
+       r = display->check_timings(display, &timings);
+       if (r)
+               return r;
+
+       display->set_timings(display, &timings);
+
+       return 0;
+}
+
+static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
+{
+       char *str, *options, *this_opt;
+       int r = 0;
+
+       str = kmalloc(strlen(def_mode) + 1, GFP_KERNEL);
+       strcpy(str, def_mode);
+       options = str;
+
+       while (!r && (this_opt = strsep(&options, ",")) != NULL) {
+               char *p, *display_str, *mode_str;
+               struct omap_dss_device *display;
+               int i;
+
+               p = strchr(this_opt, ':');
+               if (!p) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               *p = 0;
+               display_str = this_opt;
+               mode_str = p + 1;
+
+               display = NULL;
+               for (i = 0; i < fbdev->num_displays; ++i) {
+                       if (strcmp(fbdev->displays[i]->name,
+                                               display_str) == 0) {
+                               display = fbdev->displays[i];
+                               break;
+                       }
+               }
+
+               if (!display) {
+                       r = -EINVAL;
+                       break;
+               }
+
+               r = omapfb_set_def_mode(display, mode_str);
+               if (r)
+                       break;
+       }
+
+       kfree(str);
+
+       return r;
+}
+
+static int omapfb_probe(struct platform_device *pdev)
+{
+       struct omapfb2_device *fbdev = NULL;
+       int r = 0;
+       int i;
+       struct omap_overlay *ovl;
+       struct omap_dss_device *def_display;
+       struct omap_dss_device *dssdev;
+
+       DBG("omapfb_probe\n");
+
+       if (pdev->num_resources != 0) {
+               dev_err(&pdev->dev, "probed for an unknown device\n");
+               r = -ENODEV;
+               goto err0;
+       }
+
+       fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL);
+       if (fbdev == NULL) {
+               r = -ENOMEM;
+               goto err0;
+       }
+
+       mutex_init(&fbdev->mtx);
+
+       fbdev->dev = &pdev->dev;
+       platform_set_drvdata(pdev, fbdev);
+
+       fbdev->num_displays = 0;
+       dssdev = NULL;
+       for_each_dss_dev(dssdev) {
+               omap_dss_get_device(dssdev);
+               fbdev->displays[fbdev->num_displays++] = dssdev;
+       }
+
+       if (fbdev->num_displays == 0) {
+               dev_err(&pdev->dev, "no displays\n");
+               r = -EINVAL;
+               goto cleanup;
+       }
+
+       fbdev->num_overlays = omap_dss_get_num_overlays();
+       for (i = 0; i < fbdev->num_overlays; i++)
+               fbdev->overlays[i] = omap_dss_get_overlay(i);
+
+       fbdev->num_managers = omap_dss_get_num_overlay_managers();
+       for (i = 0; i < fbdev->num_managers; i++)
+               fbdev->managers[i] = omap_dss_get_overlay_manager(i);
+
+       if (def_mode && strlen(def_mode) > 0) {
+               if (omapfb_parse_def_modes(fbdev))
+                       dev_warn(&pdev->dev, "cannot parse default modes\n");
+       }
+
+       r = omapfb_create_framebuffers(fbdev);
+       if (r)
+               goto cleanup;
+
+       for (i = 0; i < fbdev->num_managers; i++) {
+               struct omap_overlay_manager *mgr;
+               mgr = fbdev->managers[i];
+               r = mgr->apply(mgr);
+               if (r)
+                       dev_warn(fbdev->dev, "failed to apply dispc config\n");
+       }
+
+       DBG("mgr->apply'ed\n");
+
+       /* gfx overlay should be the default one. find a display
+        * connected to that, and use it as default display */
+       ovl = omap_dss_get_overlay(0);
+       if (ovl->manager && ovl->manager->device) {
+               def_display = ovl->manager->device;
+       } else {
+               dev_warn(&pdev->dev, "cannot find default display\n");
+               def_display = NULL;
+       }
+
+       if (def_display) {
+#ifndef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
+               u16 w, h;
+#endif
+               r = def_display->enable(def_display);
+               if (r)
+                       dev_warn(fbdev->dev, "Failed to enable display '%s'\n",
+                                       def_display->name);
+
+               /* set the update mode */
+               if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
+#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE
+                       if (def_display->enable_te)
+                               def_display->enable_te(def_display, 1);
+                       if (def_display->set_update_mode)
+                               def_display->set_update_mode(def_display,
+                                               OMAP_DSS_UPDATE_AUTO);
+#else /* MANUAL_UPDATE */
+                       if (def_display->enable_te)
+                               def_display->enable_te(def_display, 0);
+                       if (def_display->set_update_mode)
+                               def_display->set_update_mode(def_display,
+                                               OMAP_DSS_UPDATE_MANUAL);
+
+                       def_display->get_resolution(def_display, &w, &h);
+                       def_display->update(def_display, 0, 0, w, h);
+#endif
+               } else {
+                       if (def_display->set_update_mode)
+                               def_display->set_update_mode(def_display,
+                                               OMAP_DSS_UPDATE_AUTO);
+               }
+       }
+
+       return 0;
+
+cleanup:
+       omapfb_free_resources(fbdev);
+err0:
+       dev_err(&pdev->dev, "failed to setup omapfb\n");
+       return r;
+}
+
+static int omapfb_remove(struct platform_device *pdev)
+{
+       struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
+
+       /* FIXME: wait till completion of pending events */
+
+       omapfb_remove_sysfs(fbdev);
+
+       omapfb_free_resources(fbdev);
+
+       return 0;
+}
+
+static struct platform_driver omapfb_driver = {
+       .probe          = omapfb_probe,
+       .remove         = omapfb_remove,
+       .driver         = {
+               .name   = "omapfb",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omapfb_init(void)
+{
+       DBG("omapfb_init\n");
+
+       if (platform_driver_register(&omapfb_driver)) {
+               printk(KERN_ERR "failed to register omapfb driver\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit omapfb_exit(void)
+{
+       DBG("omapfb_exit\n");
+       platform_driver_unregister(&omapfb_driver);
+}
+
+module_param_named(mode, def_mode, charp, 0);
+module_param_named(vram, def_vram, charp, 0);
+module_param_named(rotate, def_rotate, int, 0);
+module_param_named(vrfb, def_vrfb, bool, 0);
+module_param_named(mirror, def_mirror, bool, 0);
+
+/* late_initcall to let panel/ctrl drivers loaded first.
+ * I guess better option would be a more dynamic approach,
+ * so that omapfb reacts to new panels when they are loaded */
+late_initcall(omapfb_init);
+/*module_init(omapfb_init);*/
+module_exit(omapfb_exit);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
+MODULE_DESCRIPTION("OMAP2/3 Framebuffer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/omap2/omapfb/omapfb-sysfs.c b/drivers/video/omap2/omapfb/omapfb-sysfs.c
new file mode 100644 (file)
index 0000000..62bb88f
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * linux/drivers/video/omap2/omapfb-sysfs.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fb.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/omapfb.h>
+
+#include <plat/display.h>
+#include <plat/vrfb.h>
+
+#include "omapfb.h"
+
+static ssize_t show_rotate_type(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type);
+}
+
+static ssize_t store_rotate_type(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       enum omap_dss_rotation_type rot_type;
+       int r;
+
+       rot_type = simple_strtoul(buf, NULL, 0);
+
+       if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB)
+               return -EINVAL;
+
+       lock_fb_info(fbi);
+
+       r = 0;
+       if (rot_type == ofbi->rotation_type)
+               goto out;
+
+       if (ofbi->region.size) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       ofbi->rotation_type = rot_type;
+
+       /*
+        * Since the VRAM for this FB is not allocated at the moment we don't
+        * need to do any further parameter checking at this point.
+        */
+out:
+       unlock_fb_info(fbi);
+
+       return r ? r : count;
+}
+
+
+static ssize_t show_mirror(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror);
+}
+
+static ssize_t store_mirror(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       bool mirror;
+       int r;
+       struct fb_var_screeninfo new_var;
+
+       mirror = simple_strtoul(buf, NULL, 0);
+
+       if (mirror != 0 && mirror != 1)
+               return -EINVAL;
+
+       lock_fb_info(fbi);
+
+       ofbi->mirror = mirror;
+
+       memcpy(&new_var, &fbi->var, sizeof(new_var));
+       r = check_fb_var(fbi, &new_var);
+       if (r)
+               goto out;
+       memcpy(&fbi->var, &new_var, sizeof(fbi->var));
+
+       set_fb_fix(fbi);
+
+       r = omapfb_apply_changes(fbi, 0);
+       if (r)
+               goto out;
+
+       r = count;
+out:
+       unlock_fb_info(fbi);
+
+       return r;
+}
+
+static ssize_t show_overlays(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       ssize_t l = 0;
+       int t;
+
+       omapfb_lock(fbdev);
+       lock_fb_info(fbi);
+
+       for (t = 0; t < ofbi->num_overlays; t++) {
+               struct omap_overlay *ovl = ofbi->overlays[t];
+               int ovlnum;
+
+               for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum)
+                       if (ovl == fbdev->overlays[ovlnum])
+                               break;
+
+               l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
+                               t == 0 ? "" : ",", ovlnum);
+       }
+
+       l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+       unlock_fb_info(fbi);
+       omapfb_unlock(fbdev);
+
+       return l;
+}
+
+static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev,
+               struct omap_overlay *ovl)
+{
+       int i, t;
+
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+               for (t = 0; t < ofbi->num_overlays; t++) {
+                       if (ofbi->overlays[t] == ovl)
+                               return ofbi;
+               }
+       }
+
+       return NULL;
+}
+
+static ssize_t store_overlays(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       struct omapfb2_device *fbdev = ofbi->fbdev;
+       struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB];
+       struct omap_overlay *ovl;
+       int num_ovls, r, i;
+       int len;
+       bool added = false;
+
+       num_ovls = 0;
+
+       len = strlen(buf);
+       if (buf[len - 1] == '\n')
+               len = len - 1;
+
+       omapfb_lock(fbdev);
+       lock_fb_info(fbi);
+
+       if (len > 0) {
+               char *p = (char *)buf;
+               int ovlnum;
+
+               while (p < buf + len) {
+                       int found;
+                       if (num_ovls == OMAPFB_MAX_OVL_PER_FB) {
+                               r = -EINVAL;
+                               goto out;
+                       }
+
+                       ovlnum = simple_strtoul(p, &p, 0);
+                       if (ovlnum > fbdev->num_overlays) {
+                               r = -EINVAL;
+                               goto out;
+                       }
+
+                       found = 0;
+                       for (i = 0; i < num_ovls; ++i) {
+                               if (ovls[i] == fbdev->overlays[ovlnum]) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+
+                       if (!found)
+                               ovls[num_ovls++] = fbdev->overlays[ovlnum];
+
+                       p++;
+               }
+       }
+
+       for (i = 0; i < num_ovls; ++i) {
+               struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]);
+               if (ofbi2 && ofbi2 != ofbi) {
+                       dev_err(fbdev->dev, "overlay already in use\n");
+                       r = -EINVAL;
+                       goto out;
+               }
+       }
+
+       /* detach unused overlays */
+       for (i = 0; i < ofbi->num_overlays; ++i) {
+               int t, found;
+
+               ovl = ofbi->overlays[i];
+
+               found = 0;
+
+               for (t = 0; t < num_ovls; ++t) {
+                       if (ovl == ovls[t]) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (found)
+                       continue;
+
+               DBG("detaching %d\n", ofbi->overlays[i]->id);
+
+               omapfb_overlay_enable(ovl, 0);
+
+               if (ovl->manager)
+                       ovl->manager->apply(ovl->manager);
+
+               for (t = i + 1; t < ofbi->num_overlays; t++) {
+                       ofbi->rotation[t-1] = ofbi->rotation[t];
+                       ofbi->overlays[t-1] = ofbi->overlays[t];
+               }
+
+               ofbi->num_overlays--;
+               i--;
+       }
+
+       for (i = 0; i < num_ovls; ++i) {
+               int t, found;
+
+               ovl = ovls[i];
+
+               found = 0;
+
+               for (t = 0; t < ofbi->num_overlays; ++t) {
+                       if (ovl == ofbi->overlays[t]) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (found)
+                       continue;
+               ofbi->rotation[ofbi->num_overlays] = 0;
+               ofbi->overlays[ofbi->num_overlays++] = ovl;
+
+               added = true;
+       }
+
+       if (added) {
+               r = omapfb_apply_changes(fbi, 0);
+               if (r)
+                       goto out;
+       }
+
+       r = count;
+out:
+       unlock_fb_info(fbi);
+       omapfb_unlock(fbdev);
+
+       return r;
+}
+
+static ssize_t show_overlays_rotate(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       ssize_t l = 0;
+       int t;
+
+       lock_fb_info(fbi);
+
+       for (t = 0; t < ofbi->num_overlays; t++) {
+               l += snprintf(buf + l, PAGE_SIZE - l, "%s%d",
+                               t == 0 ? "" : ",", ofbi->rotation[t]);
+       }
+
+       l += snprintf(buf + l, PAGE_SIZE - l, "\n");
+
+       unlock_fb_info(fbi);
+
+       return l;
+}
+
+static ssize_t store_overlays_rotate(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       int num_ovls = 0, r, i;
+       int len;
+       bool changed = false;
+       u8 rotation[OMAPFB_MAX_OVL_PER_FB];
+
+       len = strlen(buf);
+       if (buf[len - 1] == '\n')
+               len = len - 1;
+
+       lock_fb_info(fbi);
+
+       if (len > 0) {
+               char *p = (char *)buf;
+
+               while (p < buf + len) {
+                       int rot;
+
+                       if (num_ovls == ofbi->num_overlays) {
+                               r = -EINVAL;
+                               goto out;
+                       }
+
+                       rot = simple_strtoul(p, &p, 0);
+                       if (rot < 0 || rot > 3) {
+                               r = -EINVAL;
+                               goto out;
+                       }
+
+                       if (ofbi->rotation[num_ovls] != rot)
+                               changed = true;
+
+                       rotation[num_ovls++] = rot;
+
+                       p++;
+               }
+       }
+
+       if (num_ovls != ofbi->num_overlays) {
+               r = -EINVAL;
+               goto out;
+       }
+
+       if (changed) {
+               for (i = 0; i < num_ovls; ++i)
+                       ofbi->rotation[i] = rotation[i];
+
+               r = omapfb_apply_changes(fbi, 0);
+               if (r)
+                       goto out;
+
+               /* FIXME error handling? */
+       }
+
+       r = count;
+out:
+       unlock_fb_info(fbi);
+
+       return r;
+}
+
+static ssize_t show_size(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+
+       return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size);
+}
+
+static ssize_t store_size(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       unsigned long size;
+       int r;
+       int i;
+
+       size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0));
+
+       lock_fb_info(fbi);
+
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               if (ofbi->overlays[i]->info.enabled) {
+                       r = -EBUSY;
+                       goto out;
+               }
+       }
+
+       if (size != ofbi->region.size) {
+               r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type);
+               if (r) {
+                       dev_err(dev, "realloc fbmem failed\n");
+                       goto out;
+               }
+       }
+
+       r = count;
+out:
+       unlock_fb_info(fbi);
+
+       return r;
+}
+
+static ssize_t show_phys(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+
+       return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr);
+}
+
+static ssize_t show_virt(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct fb_info *fbi = dev_get_drvdata(dev);
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+
+       return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr);
+}
+
+static struct device_attribute omapfb_attrs[] = {
+       __ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,
+                       store_rotate_type),
+       __ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror),
+       __ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size),
+       __ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays),
+       __ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate,
+                       store_overlays_rotate),
+       __ATTR(phys_addr, S_IRUGO, show_phys, NULL),
+       __ATTR(virt_addr, S_IRUGO, show_virt, NULL),
+};
+
+int omapfb_create_sysfs(struct omapfb2_device *fbdev)
+{
+       int i;
+       int r;
+
+       DBG("create sysfs for fbs\n");
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               int t;
+               for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) {
+                       r = device_create_file(fbdev->fbs[i]->dev,
+                                       &omapfb_attrs[t]);
+
+                       if (r) {
+                               dev_err(fbdev->dev, "failed to create sysfs "
+                                               "file\n");
+                               return r;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+void omapfb_remove_sysfs(struct omapfb2_device *fbdev)
+{
+       int i, t;
+
+       DBG("remove sysfs for fbs\n");
+       for (i = 0; i < fbdev->num_fbs; i++) {
+               for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++)
+                       device_remove_file(fbdev->fbs[i]->dev,
+                                       &omapfb_attrs[t]);
+       }
+}
+
diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h
new file mode 100644 (file)
index 0000000..f7c9c73
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * linux/drivers/video/omap2/omapfb.h
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Some code and ideas taken from drivers/video/omap/ driver
+ * by Imre Deak.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__
+#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__
+
+#ifdef CONFIG_FB_OMAP2_DEBUG_SUPPORT
+#define DEBUG
+#endif
+
+#include <plat/display.h>
+
+#ifdef DEBUG
+extern unsigned int omapfb_debug;
+#define DBG(format, ...) \
+       if (omapfb_debug) \
+               printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par))
+
+/* max number of overlays to which a framebuffer data can be direct */
+#define OMAPFB_MAX_OVL_PER_FB 3
+
+struct omapfb2_mem_region {
+       u32             paddr;
+       void __iomem    *vaddr;
+       struct vrfb     vrfb;
+       unsigned long   size;
+       u8              type;           /* OMAPFB_PLANE_MEM_* */
+       bool            alloc;          /* allocated by the driver */
+       bool            map;            /* kernel mapped by the driver */
+};
+
+/* appended to fb_info */
+struct omapfb_info {
+       int id;
+       struct omapfb2_mem_region region;
+       atomic_t map_count;
+       int num_overlays;
+       struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB];
+       struct omapfb2_device *fbdev;
+       enum omap_dss_rotation_type rotation_type;
+       u8 rotation[OMAPFB_MAX_OVL_PER_FB];
+       bool mirror;
+};
+
+struct omapfb2_device {
+       struct device *dev;
+       struct mutex  mtx;
+
+       u32 pseudo_palette[17];
+
+       int state;
+
+       unsigned num_fbs;
+       struct fb_info *fbs[10];
+
+       unsigned num_displays;
+       struct omap_dss_device *displays[10];
+       unsigned num_overlays;
+       struct omap_overlay *overlays[10];
+       unsigned num_managers;
+       struct omap_overlay_manager *managers[10];
+};
+
+struct omapfb_colormode {
+       enum omap_color_mode dssmode;
+       u32 bits_per_pixel;
+       u32 nonstd;
+       struct fb_bitfield red;
+       struct fb_bitfield green;
+       struct fb_bitfield blue;
+       struct fb_bitfield transp;
+};
+
+void set_fb_fix(struct fb_info *fbi);
+int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var);
+int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type);
+int omapfb_apply_changes(struct fb_info *fbi, int init);
+
+int omapfb_create_sysfs(struct omapfb2_device *fbdev);
+void omapfb_remove_sysfs(struct omapfb2_device *fbdev);
+
+int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg);
+
+int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
+                       struct fb_var_screeninfo *var);
+
+/* find the display connected to this fb, if any */
+static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
+{
+       struct omapfb_info *ofbi = FB2OFB(fbi);
+       int i;
+
+       /* XXX: returns the display connected to first attached overlay */
+       for (i = 0; i < ofbi->num_overlays; i++) {
+               if (ofbi->overlays[i]->manager)
+                       return ofbi->overlays[i]->manager->device;
+       }
+
+       return NULL;
+}
+
+static inline void omapfb_lock(struct omapfb2_device *fbdev)
+{
+       mutex_lock(&fbdev->mtx);
+}
+
+static inline void omapfb_unlock(struct omapfb2_device *fbdev)
+{
+       mutex_unlock(&fbdev->mtx);
+}
+
+static inline int omapfb_overlay_enable(struct omap_overlay *ovl,
+               int enable)
+{
+       struct omap_overlay_info info;
+
+       ovl->get_overlay_info(ovl, &info);
+       info.enabled = enable;
+       return ovl->set_overlay_info(ovl, &info);
+}
+
+#endif
diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c
new file mode 100644 (file)
index 0000000..55a4de5
--- /dev/null
@@ -0,0 +1,655 @@
+/*
+ * VRAM manager for OMAP
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/bootmem.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#include <asm/setup.h>
+
+#include <plat/sram.h>
+#include <plat/vram.h>
+#include <plat/dma.h>
+
+#ifdef DEBUG
+#define DBG(format, ...) pr_debug("VRAM: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define OMAP2_SRAM_START               0x40200000
+/* Maximum size, in reality this is smaller if SRAM is partially locked. */
+#define OMAP2_SRAM_SIZE                        0xa0000         /* 640k */
+
+/* postponed regions are used to temporarily store region information at boot
+ * time when we cannot yet allocate the region list */
+#define MAX_POSTPONED_REGIONS 10
+
+static bool vram_initialized;
+static int postponed_cnt;
+static struct {
+       unsigned long paddr;
+       size_t size;
+} postponed_regions[MAX_POSTPONED_REGIONS];
+
+struct vram_alloc {
+       struct list_head list;
+       unsigned long paddr;
+       unsigned pages;
+};
+
+struct vram_region {
+       struct list_head list;
+       struct list_head alloc_list;
+       unsigned long paddr;
+       unsigned pages;
+};
+
+static DEFINE_MUTEX(region_mutex);
+static LIST_HEAD(region_list);
+
+static inline int region_mem_type(unsigned long paddr)
+{
+       if (paddr >= OMAP2_SRAM_START &&
+           paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
+               return OMAP_VRAM_MEMTYPE_SRAM;
+       else
+               return OMAP_VRAM_MEMTYPE_SDRAM;
+}
+
+static struct vram_region *omap_vram_create_region(unsigned long paddr,
+               unsigned pages)
+{
+       struct vram_region *rm;
+
+       rm = kzalloc(sizeof(*rm), GFP_KERNEL);
+
+       if (rm) {
+               INIT_LIST_HEAD(&rm->alloc_list);
+               rm->paddr = paddr;
+               rm->pages = pages;
+       }
+
+       return rm;
+}
+
+#if 0
+static void omap_vram_free_region(struct vram_region *vr)
+{
+       list_del(&vr->list);
+       kfree(vr);
+}
+#endif
+
+static struct vram_alloc *omap_vram_create_allocation(struct vram_region *vr,
+               unsigned long paddr, unsigned pages)
+{
+       struct vram_alloc *va;
+       struct vram_alloc *new;
+
+       new = kzalloc(sizeof(*va), GFP_KERNEL);
+
+       if (!new)
+               return NULL;
+
+       new->paddr = paddr;
+       new->pages = pages;
+
+       list_for_each_entry(va, &vr->alloc_list, list) {
+               if (va->paddr > new->paddr)
+                       break;
+       }
+
+       list_add_tail(&new->list, &va->list);
+
+       return new;
+}
+
+static void omap_vram_free_allocation(struct vram_alloc *va)
+{
+       list_del(&va->list);
+       kfree(va);
+}
+
+int omap_vram_add_region(unsigned long paddr, size_t size)
+{
+       struct vram_region *rm;
+       unsigned pages;
+
+       if (vram_initialized) {
+               DBG("adding region paddr %08lx size %d\n",
+                               paddr, size);
+
+               size &= PAGE_MASK;
+               pages = size >> PAGE_SHIFT;
+
+               rm = omap_vram_create_region(paddr, pages);
+               if (rm == NULL)
+                       return -ENOMEM;
+
+               list_add(&rm->list, &region_list);
+       } else {
+               if (postponed_cnt == MAX_POSTPONED_REGIONS)
+                       return -ENOMEM;
+
+               postponed_regions[postponed_cnt].paddr = paddr;
+               postponed_regions[postponed_cnt].size = size;
+
+               ++postponed_cnt;
+       }
+       return 0;
+}
+
+int omap_vram_free(unsigned long paddr, size_t size)
+{
+       struct vram_region *rm;
+       struct vram_alloc *alloc;
+       unsigned start, end;
+
+       DBG("free mem paddr %08lx size %d\n", paddr, size);
+
+       size = PAGE_ALIGN(size);
+
+       mutex_lock(&region_mutex);
+
+       list_for_each_entry(rm, &region_list, list) {
+               list_for_each_entry(alloc, &rm->alloc_list, list) {
+                       start = alloc->paddr;
+                       end = alloc->paddr + (alloc->pages >> PAGE_SHIFT);
+
+                       if (start >= paddr && end < paddr + size)
+                               goto found;
+               }
+       }
+
+       mutex_unlock(&region_mutex);
+       return -EINVAL;
+
+found:
+       omap_vram_free_allocation(alloc);
+
+       mutex_unlock(&region_mutex);
+       return 0;
+}
+EXPORT_SYMBOL(omap_vram_free);
+
+static int _omap_vram_reserve(unsigned long paddr, unsigned pages)
+{
+       struct vram_region *rm;
+       struct vram_alloc *alloc;
+       size_t size;
+
+       size = pages << PAGE_SHIFT;
+
+       list_for_each_entry(rm, &region_list, list) {
+               unsigned long start, end;
+
+               DBG("checking region %lx %d\n", rm->paddr, rm->pages);
+
+               if (region_mem_type(rm->paddr) != region_mem_type(paddr))
+                       continue;
+
+               start = rm->paddr;
+               end = start + (rm->pages << PAGE_SHIFT) - 1;
+               if (start > paddr || end < paddr + size - 1)
+                       continue;
+
+               DBG("block ok, checking allocs\n");
+
+               list_for_each_entry(alloc, &rm->alloc_list, list) {
+                       end = alloc->paddr - 1;
+
+                       if (start <= paddr && end >= paddr + size - 1)
+                               goto found;
+
+                       start = alloc->paddr + (alloc->pages << PAGE_SHIFT);
+               }
+
+               end = rm->paddr + (rm->pages << PAGE_SHIFT) - 1;
+
+               if (!(start <= paddr && end >= paddr + size - 1))
+                       continue;
+found:
+               DBG("found area start %lx, end %lx\n", start, end);
+
+               if (omap_vram_create_allocation(rm, paddr, pages) == NULL)
+                       return -ENOMEM;
+
+               return 0;
+       }
+
+       return -ENOMEM;
+}
+
+int omap_vram_reserve(unsigned long paddr, size_t size)
+{
+       unsigned pages;
+       int r;
+
+       DBG("reserve mem paddr %08lx size %d\n", paddr, size);
+
+       size = PAGE_ALIGN(size);
+       pages = size >> PAGE_SHIFT;
+
+       mutex_lock(&region_mutex);
+
+       r = _omap_vram_reserve(paddr, pages);
+
+       mutex_unlock(&region_mutex);
+
+       return r;
+}
+EXPORT_SYMBOL(omap_vram_reserve);
+
+static void _omap_vram_dma_cb(int lch, u16 ch_status, void *data)
+{
+       struct completion *compl = data;
+       complete(compl);
+}
+
+static int _omap_vram_clear(u32 paddr, unsigned pages)
+{
+       struct completion compl;
+       unsigned elem_count;
+       unsigned frame_count;
+       int r;
+       int lch;
+
+       init_completion(&compl);
+
+       r = omap_request_dma(OMAP_DMA_NO_DEVICE, "VRAM DMA",
+                       _omap_vram_dma_cb,
+                       &compl, &lch);
+       if (r) {
+               pr_err("VRAM: request_dma failed for memory clear\n");
+               return -EBUSY;
+       }
+
+       elem_count = pages * PAGE_SIZE / 4;
+       frame_count = 1;
+
+       omap_set_dma_transfer_params(lch, OMAP_DMA_DATA_TYPE_S32,
+                       elem_count, frame_count,
+                       OMAP_DMA_SYNC_ELEMENT,
+                       0, 0);
+
+       omap_set_dma_dest_params(lch, 0, OMAP_DMA_AMODE_POST_INC,
+                       paddr, 0, 0);
+
+       omap_set_dma_color_mode(lch, OMAP_DMA_CONSTANT_FILL, 0x000000);
+
+       omap_start_dma(lch);
+
+       if (wait_for_completion_timeout(&compl, msecs_to_jiffies(1000)) == 0) {
+               omap_stop_dma(lch);
+               pr_err("VRAM: dma timeout while clearing memory\n");
+               r = -EIO;
+               goto err;
+       }
+
+       r = 0;
+err:
+       omap_free_dma(lch);
+
+       return r;
+}
+
+static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr)
+{
+       struct vram_region *rm;
+       struct vram_alloc *alloc;
+
+       list_for_each_entry(rm, &region_list, list) {
+               unsigned long start, end;
+
+               DBG("checking region %lx %d\n", rm->paddr, rm->pages);
+
+               if (region_mem_type(rm->paddr) != mtype)
+                       continue;
+
+               start = rm->paddr;
+
+               list_for_each_entry(alloc, &rm->alloc_list, list) {
+                       end = alloc->paddr;
+
+                       if (end - start >= pages << PAGE_SHIFT)
+                               goto found;
+
+                       start = alloc->paddr + (alloc->pages << PAGE_SHIFT);
+               }
+
+               end = rm->paddr + (rm->pages << PAGE_SHIFT);
+found:
+               if (end - start < pages << PAGE_SHIFT)
+                       continue;
+
+               DBG("found %lx, end %lx\n", start, end);
+
+               alloc = omap_vram_create_allocation(rm, start, pages);
+               if (alloc == NULL)
+                       return -ENOMEM;
+
+               *paddr = start;
+
+               _omap_vram_clear(start, pages);
+
+               return 0;
+       }
+
+       return -ENOMEM;
+}
+
+int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr)
+{
+       unsigned pages;
+       int r;
+
+       BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size);
+
+       DBG("alloc mem type %d size %d\n", mtype, size);
+
+       size = PAGE_ALIGN(size);
+       pages = size >> PAGE_SHIFT;
+
+       mutex_lock(&region_mutex);
+
+       r = _omap_vram_alloc(mtype, pages, paddr);
+
+       mutex_unlock(&region_mutex);
+
+       return r;
+}
+EXPORT_SYMBOL(omap_vram_alloc);
+
+void omap_vram_get_info(unsigned long *vram,
+               unsigned long *free_vram,
+               unsigned long *largest_free_block)
+{
+       struct vram_region *vr;
+       struct vram_alloc *va;
+
+       *vram = 0;
+       *free_vram = 0;
+       *largest_free_block = 0;
+
+       mutex_lock(&region_mutex);
+
+       list_for_each_entry(vr, &region_list, list) {
+               unsigned free;
+               unsigned long pa;
+
+               pa = vr->paddr;
+               *vram += vr->pages << PAGE_SHIFT;
+
+               list_for_each_entry(va, &vr->alloc_list, list) {
+                       free = va->paddr - pa;
+                       *free_vram += free;
+                       if (free > *largest_free_block)
+                               *largest_free_block = free;
+                       pa = va->paddr + (va->pages << PAGE_SHIFT);
+               }
+
+               free = vr->paddr + (vr->pages << PAGE_SHIFT) - pa;
+               *free_vram += free;
+               if (free > *largest_free_block)
+                       *largest_free_block = free;
+       }
+
+       mutex_unlock(&region_mutex);
+}
+EXPORT_SYMBOL(omap_vram_get_info);
+
+#if defined(CONFIG_DEBUG_FS)
+static int vram_debug_show(struct seq_file *s, void *unused)
+{
+       struct vram_region *vr;
+       struct vram_alloc *va;
+       unsigned size;
+
+       mutex_lock(&region_mutex);
+
+       list_for_each_entry(vr, &region_list, list) {
+               size = vr->pages << PAGE_SHIFT;
+               seq_printf(s, "%08lx-%08lx (%d bytes)\n",
+                               vr->paddr, vr->paddr + size - 1,
+                               size);
+
+               list_for_each_entry(va, &vr->alloc_list, list) {
+                       size = va->pages << PAGE_SHIFT;
+                       seq_printf(s, "    %08lx-%08lx (%d bytes)\n",
+                                       va->paddr, va->paddr + size - 1,
+                                       size);
+               }
+       }
+
+       mutex_unlock(&region_mutex);
+
+       return 0;
+}
+
+static int vram_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, vram_debug_show, inode->i_private);
+}
+
+static const struct file_operations vram_debug_fops = {
+       .open           = vram_debug_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int __init omap_vram_create_debugfs(void)
+{
+       struct dentry *d;
+
+       d = debugfs_create_file("vram", S_IRUGO, NULL,
+                       NULL, &vram_debug_fops);
+       if (IS_ERR(d))
+               return PTR_ERR(d);
+
+       return 0;
+}
+#endif
+
+static __init int omap_vram_init(void)
+{
+       int i;
+
+       vram_initialized = 1;
+
+       for (i = 0; i < postponed_cnt; i++)
+               omap_vram_add_region(postponed_regions[i].paddr,
+                               postponed_regions[i].size);
+
+#ifdef CONFIG_DEBUG_FS
+       if (omap_vram_create_debugfs())
+               pr_err("VRAM: Failed to create debugfs file\n");
+#endif
+
+       return 0;
+}
+
+arch_initcall(omap_vram_init);
+
+/* boottime vram alloc stuff */
+
+/* set from board file */
+static u32 omap_vram_sram_start __initdata;
+static u32 omap_vram_sram_size __initdata;
+
+/* set from board file */
+static u32 omap_vram_sdram_start __initdata;
+static u32 omap_vram_sdram_size __initdata;
+
+/* set from kernel cmdline */
+static u32 omap_vram_def_sdram_size __initdata;
+static u32 omap_vram_def_sdram_start __initdata;
+
+static void __init omap_vram_early_vram(char **p)
+{
+       omap_vram_def_sdram_size = memparse(*p, p);
+       if (**p == ',')
+               omap_vram_def_sdram_start = simple_strtoul((*p) + 1, p, 16);
+}
+__early_param("vram=", omap_vram_early_vram);
+
+/*
+ * Called from map_io. We need to call to this early enough so that we
+ * can reserve the fixed SDRAM regions before VM could get hold of them.
+ */
+void __init omap_vram_reserve_sdram(void)
+{
+       struct bootmem_data     *bdata;
+       unsigned long           sdram_start, sdram_size;
+       u32 paddr;
+       u32 size = 0;
+
+       /* cmdline arg overrides the board file definition */
+       if (omap_vram_def_sdram_size) {
+               size = omap_vram_def_sdram_size;
+               paddr = omap_vram_def_sdram_start;
+       }
+
+       if (!size) {
+               size = omap_vram_sdram_size;
+               paddr = omap_vram_sdram_start;
+       }
+
+#ifdef CONFIG_OMAP2_VRAM_SIZE
+       if (!size) {
+               size = CONFIG_OMAP2_VRAM_SIZE * 1024 * 1024;
+               paddr = 0;
+       }
+#endif
+
+       if (!size)
+               return;
+
+       size = PAGE_ALIGN(size);
+
+       bdata = NODE_DATA(0)->bdata;
+       sdram_start = bdata->node_min_pfn << PAGE_SHIFT;
+       sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start;
+
+       if (paddr) {
+               if ((paddr & ~PAGE_MASK) || paddr < sdram_start ||
+                               paddr + size > sdram_start + sdram_size) {
+                       pr_err("Illegal SDRAM region for VRAM\n");
+                       return;
+               }
+
+               if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) {
+                       pr_err("FB: failed to reserve VRAM\n");
+                       return;
+               }
+       } else {
+               if (size > sdram_size) {
+                       pr_err("Illegal SDRAM size for VRAM\n");
+                       return;
+               }
+
+               paddr = virt_to_phys(alloc_bootmem_pages(size));
+               BUG_ON(paddr & ~PAGE_MASK);
+       }
+
+       omap_vram_add_region(paddr, size);
+
+       pr_info("Reserving %u bytes SDRAM for VRAM\n", size);
+}
+
+/*
+ * Called at sram init time, before anything is pushed to the SRAM stack.
+ * Because of the stack scheme, we will allocate everything from the
+ * start of the lowest address region to the end of SRAM. This will also
+ * include padding for page alignment and possible holes between regions.
+ *
+ * As opposed to the SDRAM case, we'll also do any dynamic allocations at
+ * this point, since the driver built as a module would have problem with
+ * freeing / reallocating the regions.
+ */
+unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart,
+                                 unsigned long sram_vstart,
+                                 unsigned long sram_size,
+                                 unsigned long pstart_avail,
+                                 unsigned long size_avail)
+{
+       unsigned long                   pend_avail;
+       unsigned long                   reserved;
+       u32 paddr;
+       u32 size;
+
+       paddr = omap_vram_sram_start;
+       size = omap_vram_sram_size;
+
+       if (!size)
+               return 0;
+
+       reserved = 0;
+       pend_avail = pstart_avail + size_avail;
+
+       if (!paddr) {
+               /* Dynamic allocation */
+               if ((size_avail & PAGE_MASK) < size) {
+                       pr_err("Not enough SRAM for VRAM\n");
+                       return 0;
+               }
+               size_avail = (size_avail - size) & PAGE_MASK;
+               paddr = pstart_avail + size_avail;
+       }
+
+       if (paddr < sram_pstart ||
+                       paddr + size > sram_pstart + sram_size) {
+               pr_err("Illegal SRAM region for VRAM\n");
+               return 0;
+       }
+
+       /* Reserve everything above the start of the region. */
+       if (pend_avail - paddr > reserved)
+               reserved = pend_avail - paddr;
+       size_avail = pend_avail - reserved - pstart_avail;
+
+       omap_vram_add_region(paddr, size);
+
+       if (reserved)
+               pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved);
+
+       return reserved;
+}
+
+void __init omap_vram_set_sdram_vram(u32 size, u32 start)
+{
+       omap_vram_sdram_start = start;
+       omap_vram_sdram_size = size;
+}
+
+void __init omap_vram_set_sram_vram(u32 size, u32 start)
+{
+       omap_vram_sram_start = start;
+       omap_vram_sram_size = size;
+}
diff --git a/drivers/video/omap2/vrfb.c b/drivers/video/omap2/vrfb.c
new file mode 100644 (file)
index 0000000..fd22716
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * VRFB Rotation Engine
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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.
+ *
+ * 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.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+
+#include <mach/io.h>
+#include <plat/vrfb.h>
+#include <plat/sdrc.h>
+
+#ifdef DEBUG
+#define DBG(format, ...) pr_debug("VRFB: " format, ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+#define SMS_ROT_VIRT_BASE(context, rot) \
+       (((context >= 4) ? 0xD0000000 : 0x70000000) \
+        + (0x4000000 * (context)) \
+        + (0x1000000 * (rot)))
+
+#define OMAP_VRFB_SIZE                 (2048 * 2048 * 4)
+
+#define VRFB_PAGE_WIDTH_EXP    5 /* Assuming SDRAM pagesize= 1024 */
+#define VRFB_PAGE_HEIGHT_EXP   5 /* 1024 = 2^5 * 2^5 */
+#define VRFB_PAGE_WIDTH                (1 << VRFB_PAGE_WIDTH_EXP)
+#define VRFB_PAGE_HEIGHT       (1 << VRFB_PAGE_HEIGHT_EXP)
+#define SMS_IMAGEHEIGHT_OFFSET 16
+#define SMS_IMAGEWIDTH_OFFSET  0
+#define SMS_PH_OFFSET          8
+#define SMS_PW_OFFSET          4
+#define SMS_PS_OFFSET          0
+
+#define VRFB_NUM_CTXS 12
+/* bitmap of reserved contexts */
+static unsigned long ctx_map;
+
+static DEFINE_MUTEX(ctx_lock);
+
+/*
+ * Access to this happens from client drivers or the PM core after wake-up.
+ * For the first case we require locking at the driver level, for the second
+ * we don't need locking, since no drivers will run until after the wake-up
+ * has finished.
+ */
+static struct {
+       u32 physical_ba;
+       u32 control;
+       u32 size;
+} vrfb_hw_context[VRFB_NUM_CTXS];
+
+static inline void restore_hw_context(int ctx)
+{
+       omap2_sms_write_rot_control(vrfb_hw_context[ctx].control, ctx);
+       omap2_sms_write_rot_size(vrfb_hw_context[ctx].size, ctx);
+       omap2_sms_write_rot_physical_ba(vrfb_hw_context[ctx].physical_ba, ctx);
+}
+
+static u32 get_image_width_roundup(u16 width, u8 bytespp)
+{
+       unsigned long stride = width * bytespp;
+       unsigned long ceil_pages_per_stride = (stride / VRFB_PAGE_WIDTH) +
+               (stride % VRFB_PAGE_WIDTH != 0);
+
+       return ceil_pages_per_stride * VRFB_PAGE_WIDTH / bytespp;
+}
+
+/*
+ * This the extra space needed in the VRFB physical area for VRFB to safely wrap
+ * any memory accesses to the invisible part of the virtual view to the physical
+ * area.
+ */
+static inline u32 get_extra_physical_size(u16 image_width_roundup, u8 bytespp)
+{
+       return (OMAP_VRFB_LINE_LEN - image_width_roundup) * VRFB_PAGE_HEIGHT *
+               bytespp;
+}
+
+void omap_vrfb_restore_context(void)
+{
+       int i;
+       unsigned long map = ctx_map;
+
+       for (i = ffs(map); i; i = ffs(map)) {
+               /* i=1..32 */
+               i--;
+               map &= ~(1 << i);
+               restore_hw_context(i);
+       }
+}
+
+void omap_vrfb_adjust_size(u16 *width, u16 *height,
+               u8 bytespp)
+{
+       *width = ALIGN(*width * bytespp, VRFB_PAGE_WIDTH) / bytespp;
+       *height = ALIGN(*height, VRFB_PAGE_HEIGHT);
+}
+EXPORT_SYMBOL(omap_vrfb_adjust_size);
+
+u32 omap_vrfb_min_phys_size(u16 width, u16 height, u8 bytespp)
+{
+       unsigned long image_width_roundup = get_image_width_roundup(width,
+               bytespp);
+
+       if (image_width_roundup > OMAP_VRFB_LINE_LEN)
+               return 0;
+
+       return (width * height * bytespp) + get_extra_physical_size(
+               image_width_roundup, bytespp);
+}
+EXPORT_SYMBOL(omap_vrfb_min_phys_size);
+
+u16 omap_vrfb_max_height(u32 phys_size, u16 width, u8 bytespp)
+{
+       unsigned long image_width_roundup = get_image_width_roundup(width,
+               bytespp);
+       unsigned long height;
+       unsigned long extra;
+
+       if (image_width_roundup > OMAP_VRFB_LINE_LEN)
+               return 0;
+
+       extra = get_extra_physical_size(image_width_roundup, bytespp);
+
+       if (phys_size < extra)
+               return 0;
+
+       height = (phys_size - extra) / (width * bytespp);
+
+       /* Virtual views provided by VRFB are limited to 2048x2048. */
+       return min_t(unsigned long, height, 2048);
+}
+EXPORT_SYMBOL(omap_vrfb_max_height);
+
+void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr,
+               u16 width, u16 height,
+               unsigned bytespp, bool yuv_mode)
+{
+       unsigned pixel_size_exp;
+       u16 vrfb_width;
+       u16 vrfb_height;
+       u8 ctx = vrfb->context;
+       u32 size;
+       u32 control;
+
+       DBG("omapfb_set_vrfb(%d, %lx, %dx%d, %d, %d)\n", ctx, paddr,
+                       width, height, bytespp, yuv_mode);
+
+       /* For YUV2 and UYVY modes VRFB needs to handle pixels a bit
+        * differently. See TRM. */
+       if (yuv_mode) {
+               bytespp *= 2;
+               width /= 2;
+       }
+
+       if (bytespp == 4)
+               pixel_size_exp = 2;
+       else if (bytespp == 2)
+               pixel_size_exp = 1;
+       else
+               BUG();
+
+       vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp;
+       vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT);
+
+       DBG("vrfb w %u, h %u bytespp %d\n", vrfb_width, vrfb_height, bytespp);
+
+       size  = vrfb_width << SMS_IMAGEWIDTH_OFFSET;
+       size |= vrfb_height << SMS_IMAGEHEIGHT_OFFSET;
+
+       control  = pixel_size_exp << SMS_PS_OFFSET;
+       control |= VRFB_PAGE_WIDTH_EXP  << SMS_PW_OFFSET;
+       control |= VRFB_PAGE_HEIGHT_EXP << SMS_PH_OFFSET;
+
+       vrfb_hw_context[ctx].physical_ba = paddr;
+       vrfb_hw_context[ctx].size = size;
+       vrfb_hw_context[ctx].control = control;
+
+       omap2_sms_write_rot_physical_ba(paddr, ctx);
+       omap2_sms_write_rot_size(size, ctx);
+       omap2_sms_write_rot_control(control, ctx);
+
+       DBG("vrfb offset pixels %d, %d\n",
+                       vrfb_width - width, vrfb_height - height);
+
+       vrfb->xres = width;
+       vrfb->yres = height;
+       vrfb->xoffset = vrfb_width - width;
+       vrfb->yoffset = vrfb_height - height;
+       vrfb->bytespp = bytespp;
+       vrfb->yuv_mode = yuv_mode;
+}
+EXPORT_SYMBOL(omap_vrfb_setup);
+
+int omap_vrfb_map_angle(struct vrfb *vrfb, u16 height, u8 rot)
+{
+       unsigned long size = height * OMAP_VRFB_LINE_LEN * vrfb->bytespp;
+
+       vrfb->vaddr[rot] = ioremap_wc(vrfb->paddr[rot], size);
+
+       if (!vrfb->vaddr[rot]) {
+               printk(KERN_ERR "vrfb: ioremap failed\n");
+               return -ENOMEM;
+       }
+
+       DBG("ioremapped vrfb area %d of size %lu into %p\n", rot, size,
+               vrfb->vaddr[rot]);
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_vrfb_map_angle);
+
+void omap_vrfb_release_ctx(struct vrfb *vrfb)
+{
+       int rot;
+       int ctx = vrfb->context;
+
+       if (ctx == 0xff)
+               return;
+
+       DBG("release ctx %d\n", ctx);
+
+       mutex_lock(&ctx_lock);
+
+       BUG_ON(!(ctx_map & (1 << ctx)));
+
+       clear_bit(ctx, &ctx_map);
+
+       for (rot = 0; rot < 4; ++rot) {
+               if (vrfb->paddr[rot]) {
+                       release_mem_region(vrfb->paddr[rot], OMAP_VRFB_SIZE);
+                       vrfb->paddr[rot] = 0;
+               }
+       }
+
+       vrfb->context = 0xff;
+
+       mutex_unlock(&ctx_lock);
+}
+EXPORT_SYMBOL(omap_vrfb_release_ctx);
+
+int omap_vrfb_request_ctx(struct vrfb *vrfb)
+{
+       int rot;
+       u32 paddr;
+       u8 ctx;
+       int r;
+
+       DBG("request ctx\n");
+
+       mutex_lock(&ctx_lock);
+
+       for (ctx = 0; ctx < VRFB_NUM_CTXS; ++ctx)
+               if ((ctx_map & (1 << ctx)) == 0)
+                       break;
+
+       if (ctx == VRFB_NUM_CTXS) {
+               pr_err("vrfb: no free contexts\n");
+               r = -EBUSY;
+               goto out;
+       }
+
+       DBG("found free ctx %d\n", ctx);
+
+       set_bit(ctx, &ctx_map);
+
+       memset(vrfb, 0, sizeof(*vrfb));
+
+       vrfb->context = ctx;
+
+       for (rot = 0; rot < 4; ++rot) {
+               paddr = SMS_ROT_VIRT_BASE(ctx, rot);
+               if (!request_mem_region(paddr, OMAP_VRFB_SIZE, "vrfb")) {
+                       pr_err("vrfb: failed to reserve VRFB "
+                                       "area for ctx %d, rotation %d\n",
+                                       ctx, rot * 90);
+                       omap_vrfb_release_ctx(vrfb);
+                       r = -ENOMEM;
+                       goto out;
+               }
+
+               vrfb->paddr[rot] = paddr;
+
+               DBG("VRFB %d/%d: %lx\n", ctx, rot*90, vrfb->paddr[rot]);
+       }
+
+       r = 0;
+out:
+       mutex_unlock(&ctx_lock);
+       return r;
+}
+EXPORT_SYMBOL(omap_vrfb_request_ctx);
index bba5371..f860122 100644 (file)
@@ -260,13 +260,13 @@ static int sgivwfb_check_var(struct fb_var_screeninfo *var,
        var->grayscale = 0;     /* No grayscale for now */
 
        /* determine valid resolution and timing */
-       for (min_mode = 0; min_mode < DBE_VT_SIZE; min_mode++) {
+       for (min_mode = 0; min_mode < ARRAY_SIZE(dbeVTimings); min_mode++) {
                if (dbeVTimings[min_mode].width >= var->xres &&
                    dbeVTimings[min_mode].height >= var->yres)
                        break;
        }
 
-       if (min_mode == DBE_VT_SIZE)
+       if (min_mode == ARRAY_SIZE(dbeVTimings))
                return -EINVAL; /* Resolution to high */
 
        /* XXX FIXME - should try to pick best refresh rate */
index 3ad5157..b4b5de9 100644 (file)
@@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
                                       struct list_head *pagelist)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
-       unsigned int nr_pages;
 
        /* enable clocks before accessing hardware */
        sh_mobile_lcdc_clk_on(ch->lcdc);
 
-       nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
-       dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
-
-       /* trigger panel update */
-       lcdc_write_chan(ch, LDSM2R, 1);
-
-       dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+       /*
+        * It's possible to get here without anything on the pagelist via
+        * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
+        * invocation. In the former case, the acceleration routines are
+        * stepped in to when using the framebuffer console causing the
+        * workqueue to be scheduled without any dirty pages on the list.
+        *
+        * Despite this, a panel update is still needed given that the
+        * acceleration routines have their own methods for writing in
+        * that still need to be updated.
+        *
+        * The fsync() and empty pagelist case could be optimized for,
+        * but we don't bother, as any application exhibiting such
+        * behaviour is fundamentally broken anyways.
+        */
+       if (!list_empty(pagelist)) {
+               unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
+
+               /* trigger panel update */
+               dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+               lcdc_write_chan(ch, LDSM2R, 1);
+               dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+       } else
+               lcdc_write_chan(ch, LDSM2R, 1);
 }
 
 static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
index 6120f0c..876648e 100644 (file)
@@ -756,9 +756,9 @@ hyperResetPlanes(struct stifb_info *fb, int enable)
                if (fb->info.var.bits_per_pixel == 32)
                        controlPlaneReg = 0x04000F00;
                else
-                       controlPlaneReg = 0x00000F00;   /* 0x00000800 should be enought, but lets clear all 4 bits */
+                       controlPlaneReg = 0x00000F00;   /* 0x00000800 should be enough, but lets clear all 4 bits */
        else
-               controlPlaneReg = 0x00000F00; /* 0x00000100 should be enought, but lets clear all 4 bits */
+               controlPlaneReg = 0x00000F00; /* 0x00000100 should be enough, but lets clear all 4 bits */
 
        switch (enable) {
        case ENABLE:
index ff43c88..9805483 100644 (file)
@@ -52,7 +52,7 @@
  *
  * 0.1.3 (released 1999-11-02) added Attila's panning support, code
  *                             reorg, hwcursor address page size alignment
- *                             (for mmaping both frame buffer and regs),
+ *                             (for mmapping both frame buffer and regs),
  *                             and my changes to get rid of hardcoded
  *                             VGA i/o register locations (uses PCI
  *                             configuration info now)
index c5c32b6..67b3693 100644 (file)
@@ -467,7 +467,7 @@ static int dvi_get_panel_size_from_DDCv1(void)
        default:
                viaparinfo->tmds_setting_info->dvi_panel_size =
                        VIA_RES_1024X768;
-               DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d !\
+               DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d !\
                                         set default panel size.\n", max_h);
                break;
        }
@@ -534,7 +534,7 @@ static int dvi_get_panel_size_from_DDCv2(void)
        default:
                viaparinfo->tmds_setting_info->dvi_panel_size =
                        VIA_RES_1024X768;
-               DEBUG_MSG(KERN_INFO "Unknow panel size max resolution = %d!\
+               DEBUG_MSG(KERN_INFO "Unknown panel size max resolution = %d!\
                                        set default panel size.\n", HSize);
                break;
        }
index 3df17dc..65ccd21 100644 (file)
@@ -446,7 +446,7 @@ static int vt8623fb_set_par(struct fb_info *info)
 
        svga_wseq_mask(0x1E, 0xF0, 0xF0); // DI/DVP bus
        svga_wseq_mask(0x2A, 0x0F, 0x0F); // DI/DVP bus
-       svga_wseq_mask(0x16, 0x08, 0xBF); // FIFO read treshold
+       svga_wseq_mask(0x16, 0x08, 0xBF); // FIFO read threshold
        vga_wseq(NULL, 0x17, 0x1F);       // FIFO depth
        vga_wseq(NULL, 0x18, 0x4E);
        svga_wseq_mask(0x1A, 0x08, 0x08); // enable MMIO ?
index 54cd916..91a68e9 100644 (file)
@@ -440,7 +440,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
        fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
        fb_info->fix.accel = FB_ACCEL_NONE;
 
-       fb_info->flags = FBINFO_FLAG_DEFAULT;
+       fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
 
        ret = fb_alloc_cmap(&fb_info->cmap, 256, 0);
        if (ret < 0) {
index 381026c..923cc68 100644 (file)
@@ -508,7 +508,7 @@ void coh901327_watchdog_reset(void)
         * deactivating the watchdog before it is shut down by it.
         *
         * NOTE: on future versions of the watchdog, this restriction is
-        * gone: the watchdog will be reloaded with a defaul value (1 min)
+        * gone: the watchdog will be reloaded with a default value (1 min)
         * instead of last value, and you can conveniently set the watchdog
         * timeout to 10ms (value = 1) without any problems.
         */
index b6b3f59..47d7197 100644 (file)
@@ -21,7 +21,7 @@
  *      wd#1 - 2 seconds;
  *      wd#2 - 7.2 ms;
  *  After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
- *  a system RESET and it starts wd#2 that unconditionaly will RESET
+ *  a system RESET and it starts wd#2 that unconditionally will RESET
  *  the system when the counter reaches zero.
  *
  *  14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
index 9748eed..c8eadd4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Watchdog driver for SiByte SB1 SoCs
  *
- * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@onstor.com>
+ * Copyright (C) 2007 OnStor, Inc. * Andrew Sharp <andy.sharp@lsi.com>
  *
  * This driver is intended to make the second of two hardware watchdogs
  * on the Sibyte 12XX and 11XX SoCs available to the user.  There are two
@@ -326,7 +326,7 @@ static void __exit sbwdog_exit(void)
 module_init(sbwdog_init);
 module_exit(sbwdog_exit);
 
-MODULE_AUTHOR("Andrew Sharp <andy.sharp@onstor.com>");
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>");
 MODULE_DESCRIPTION("SiByte Watchdog");
 
 module_param(timeout, ulong, 0);
index 3bde56b..5bfb1f2 100644 (file)
@@ -542,7 +542,7 @@ static struct notifier_block wdrtas_notifier = {
 /**
  * wdrtas_get_tokens - reads in RTAS tokens
  *
- * returns 0 on succes, <0 on failure
+ * returns 0 on success, <0 on failure
  *
  * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
  * this watchdog driver. It tolerates, if "get-sensor-state" and
@@ -598,7 +598,7 @@ static void wdrtas_unregister_devs(void)
 /**
  * wdrtas_register_devs - registers the misc dev handlers
  *
- * returns 0 on succes, <0 on failure
+ * returns 0 on success, <0 on failure
  *
  * wdrtas_register_devs registers the watchdog and temperature watchdog
  * misc devs
@@ -630,7 +630,7 @@ static int wdrtas_register_devs(void)
 /**
  * wdrtas_init - init function of the watchdog driver
  *
- * returns 0 on succes, <0 on failure
+ * returns 0 on success, <0 on failure
  *
  * registers the file handlers and the reboot notifier
  */
index d31505b..4204336 100644 (file)
@@ -66,8 +66,6 @@ struct balloon_stats {
        /* We aim for 'current allocation' == 'target allocation'. */
        unsigned long current_pages;
        unsigned long target_pages;
-       /* We may hit the hard limit in Xen. If we do then we remember it. */
-       unsigned long hard_limit;
        /*
         * Drivers may alter the memory reservation independently, but they
         * must inform the balloon driver so we avoid hitting the hard limit.
@@ -136,6 +134,8 @@ static void balloon_append(struct page *page)
                list_add(&page->lru, &ballooned_pages);
                balloon_stats.balloon_low++;
        }
+
+       totalram_pages--;
 }
 
 /* balloon_retrieve: rescue a page from the balloon, if it is not empty. */
@@ -156,6 +156,8 @@ static struct page *balloon_retrieve(void)
        else
                balloon_stats.balloon_low--;
 
+       totalram_pages++;
+
        return page;
 }
 
@@ -181,7 +183,7 @@ static void balloon_alarm(unsigned long unused)
 
 static unsigned long current_target(void)
 {
-       unsigned long target = min(balloon_stats.target_pages, balloon_stats.hard_limit);
+       unsigned long target = balloon_stats.target_pages;
 
        target = min(target,
                     balloon_stats.current_pages +
@@ -217,23 +219,10 @@ static int increase_reservation(unsigned long nr_pages)
        set_xen_guest_handle(reservation.extent_start, frame_list);
        reservation.nr_extents = nr_pages;
        rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
-       if (rc < nr_pages) {
-               if (rc > 0) {
-                       int ret;
-
-                       /* We hit the Xen hard limit: reprobe. */
-                       reservation.nr_extents = rc;
-                       ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
-                                                  &reservation);
-                       BUG_ON(ret != rc);
-               }
-               if (rc >= 0)
-                       balloon_stats.hard_limit = (balloon_stats.current_pages + rc -
-                                                   balloon_stats.driver_pages);
+       if (rc < 0)
                goto out;
-       }
 
-       for (i = 0; i < nr_pages; i++) {
+       for (i = 0; i < rc; i++) {
                page = balloon_retrieve();
                BUG_ON(page == NULL);
 
@@ -259,13 +248,12 @@ static int increase_reservation(unsigned long nr_pages)
                __free_page(page);
        }
 
-       balloon_stats.current_pages += nr_pages;
-       totalram_pages = balloon_stats.current_pages;
+       balloon_stats.current_pages += rc;
 
  out:
        spin_unlock_irqrestore(&balloon_lock, flags);
 
-       return 0;
+       return rc < 0 ? rc : rc != nr_pages;
 }
 
 static int decrease_reservation(unsigned long nr_pages)
@@ -323,7 +311,6 @@ static int decrease_reservation(unsigned long nr_pages)
        BUG_ON(ret != nr_pages);
 
        balloon_stats.current_pages -= nr_pages;
-       totalram_pages = balloon_stats.current_pages;
 
        spin_unlock_irqrestore(&balloon_lock, flags);
 
@@ -367,7 +354,6 @@ static void balloon_process(struct work_struct *work)
 static void balloon_set_new_target(unsigned long target)
 {
        /* No need for lock. Not read-modify-write updates. */
-       balloon_stats.hard_limit   = ~0UL;
        balloon_stats.target_pages = target;
        schedule_work(&balloon_worker);
 }
@@ -422,12 +408,10 @@ static int __init balloon_init(void)
        pr_info("xen_balloon: Initialising balloon driver.\n");
 
        balloon_stats.current_pages = min(xen_start_info->nr_pages, max_pfn);
-       totalram_pages   = balloon_stats.current_pages;
        balloon_stats.target_pages  = balloon_stats.current_pages;
        balloon_stats.balloon_low   = 0;
        balloon_stats.balloon_high  = 0;
        balloon_stats.driver_pages  = 0UL;
-       balloon_stats.hard_limit    = ~0UL;
 
        init_timer(&balloon_timer);
        balloon_timer.data = 0;
@@ -472,9 +456,6 @@ module_exit(balloon_exit);
 BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages));
 BALLOON_SHOW(low_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_low));
 BALLOON_SHOW(high_kb, "%lu\n", PAGES2KB(balloon_stats.balloon_high));
-BALLOON_SHOW(hard_limit_kb,
-            (balloon_stats.hard_limit!=~0UL) ? "%lu\n" : "???\n",
-            (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0);
 BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages));
 
 static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr,
@@ -544,7 +525,6 @@ static struct attribute *balloon_info_attrs[] = {
        &attr_current_kb.attr,
        &attr_low_kb.attr,
        &attr_high_kb.attr,
-       &attr_hard_limit_kb.attr,
        &attr_driver_kb.attr,
        NULL
 };
index 2f57276..ce602dd 100644 (file)
@@ -474,6 +474,9 @@ static void unbind_from_irq(unsigned int irq)
                bind_evtchn_to_cpu(evtchn, 0);
 
                evtchn_to_irq[evtchn] = -1;
+       }
+
+       if (irq_info[irq].type != IRQT_UNBOUND) {
                irq_info[irq] = mk_unbound_info();
 
                dynamic_irq_cleanup(irq);
index 10d03d7..c499793 100644 (file)
@@ -43,7 +43,6 @@ static int xen_suspend(void *data)
        if (err) {
                printk(KERN_ERR "xen_suspend: sysdev_suspend failed: %d\n",
                        err);
-               dpm_resume_noirq(PMSG_RESUME);
                return err;
        }
 
@@ -69,7 +68,6 @@ static int xen_suspend(void *data)
        }
 
        sysdev_resume();
-       dpm_resume_noirq(PMSG_RESUME);
 
        return 0;
 }
@@ -81,6 +79,12 @@ static void do_suspend(void)
 
        shutting_down = SHUTDOWN_SUSPEND;
 
+       err = stop_machine_create();
+       if (err) {
+               printk(KERN_ERR "xen suspend: failed to setup stop_machine %d\n", err);
+               goto out;
+       }
+
 #ifdef CONFIG_PREEMPT
        /* If the kernel is preemptible, we need to freeze all the processes
           to prevent them from being in the middle of a pagetable update
@@ -88,29 +92,32 @@ static void do_suspend(void)
        err = freeze_processes();
        if (err) {
                printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
-               return;
+               goto out_destroy_sm;
        }
 #endif
 
        err = dpm_suspend_start(PMSG_SUSPEND);
        if (err) {
                printk(KERN_ERR "xen suspend: dpm_suspend_start %d\n", err);
-               goto out;
+               goto out_thaw;
        }
 
-       printk(KERN_DEBUG "suspending xenstore...\n");
-       xs_suspend();
-
        err = dpm_suspend_noirq(PMSG_SUSPEND);
        if (err) {
                printk(KERN_ERR "dpm_suspend_noirq failed: %d\n", err);
-               goto resume_devices;
+               goto out_resume;
        }
 
+       printk(KERN_DEBUG "suspending xenstore...\n");
+       xs_suspend();
+
        err = stop_machine(xen_suspend, &cancelled, cpumask_of(0));
+
+       dpm_resume_noirq(PMSG_RESUME);
+
        if (err) {
                printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
-               goto out;
+               cancelled = 1;
        }
 
        if (!cancelled) {
@@ -119,17 +126,21 @@ static void do_suspend(void)
        } else
                xs_suspend_cancel();
 
-       dpm_resume_noirq(PMSG_RESUME);
-
-resume_devices:
+out_resume:
        dpm_resume_end(PMSG_RESUME);
 
        /* Make sure timer events get retriggered on all CPUs */
        clock_was_set();
-out:
+
+out_thaw:
 #ifdef CONFIG_PREEMPT
        thaw_processes();
+
+out_destroy_sm:
 #endif
+       stop_machine_destroy();
+
+out:
        shutting_down = SHUTDOWN_INVALID;
 }
 #endif /* CONFIG_PM_SLEEP */
index d42e25d..649fcdf 100644 (file)
@@ -454,21 +454,21 @@ static ssize_t xendev_show_nodename(struct device *dev,
 {
        return sprintf(buf, "%s\n", to_xenbus_device(dev)->nodename);
 }
-DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL);
+static DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL);
 
 static ssize_t xendev_show_devtype(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
        return sprintf(buf, "%s\n", to_xenbus_device(dev)->devicetype);
 }
-DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
+static DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL);
 
 static ssize_t xendev_show_modalias(struct device *dev,
                                    struct device_attribute *attr, char *buf)
 {
        return sprintf(buf, "xen:%s\n", to_xenbus_device(dev)->devicetype);
 }
-DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL);
+static DEVICE_ATTR(modalias, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_modalias, NULL);
 
 int xenbus_probe_node(struct xen_bus_type *bus,
                      const char *type,
@@ -843,7 +843,7 @@ postcore_initcall(xenbus_probe_init);
 
 MODULE_LICENSE("GPL");
 
-static int is_disconnected_device(struct device *dev, void *data)
+static int is_device_connecting(struct device *dev, void *data)
 {
        struct xenbus_device *xendev = to_xenbus_device(dev);
        struct device_driver *drv = data;
@@ -861,14 +861,15 @@ static int is_disconnected_device(struct device *dev, void *data)
                return 0;
 
        xendrv = to_xenbus_driver(dev->driver);
-       return (xendev->state != XenbusStateConnected ||
-               (xendrv->is_ready && !xendrv->is_ready(xendev)));
+       return (xendev->state < XenbusStateConnected ||
+               (xendev->state == XenbusStateConnected &&
+                xendrv->is_ready && !xendrv->is_ready(xendev)));
 }
 
-static int exists_disconnected_device(struct device_driver *drv)
+static int exists_connecting_device(struct device_driver *drv)
 {
        return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
-                               is_disconnected_device);
+                               is_device_connecting);
 }
 
 static int print_device_status(struct device *dev, void *data)
@@ -884,10 +885,13 @@ static int print_device_status(struct device *dev, void *data)
                /* Information only: is this too noisy? */
                printk(KERN_INFO "XENBUS: Device with no driver: %s\n",
                       xendev->nodename);
-       } else if (xendev->state != XenbusStateConnected) {
+       } else if (xendev->state < XenbusStateConnected) {
+               enum xenbus_state rstate = XenbusStateUnknown;
+               if (xendev->otherend)
+                       rstate = xenbus_read_driver_state(xendev->otherend);
                printk(KERN_WARNING "XENBUS: Timeout connecting "
-                      "to device: %s (state %d)\n",
-                      xendev->nodename, xendev->state);
+                      "to device: %s (local state %d, remote state %d)\n",
+                      xendev->nodename, xendev->state, rstate);
        }
 
        return 0;
@@ -897,7 +901,7 @@ static int print_device_status(struct device *dev, void *data)
 static int ready_to_wait_for_devices;
 
 /*
- * On a 10 second timeout, wait for all devices currently configured.  We need
+ * On a 5-minute timeout, wait for all devices currently configured.  We need
  * to do this to guarantee that the filesystems and / or network devices
  * needed for boot are available, before we can allow the boot to proceed.
  *
@@ -912,18 +916,30 @@ static int ready_to_wait_for_devices;
  */
 static void wait_for_devices(struct xenbus_driver *xendrv)
 {
-       unsigned long timeout = jiffies + 10*HZ;
+       unsigned long start = jiffies;
        struct device_driver *drv = xendrv ? &xendrv->driver : NULL;
+       unsigned int seconds_waited = 0;
 
        if (!ready_to_wait_for_devices || !xen_domain())
                return;
 
-       while (exists_disconnected_device(drv)) {
-               if (time_after(jiffies, timeout))
-                       break;
+       while (exists_connecting_device(drv)) {
+               if (time_after(jiffies, start + (seconds_waited+5)*HZ)) {
+                       if (!seconds_waited)
+                               printk(KERN_WARNING "XENBUS: Waiting for "
+                                      "devices to initialise: ");
+                       seconds_waited += 5;
+                       printk("%us...", 300 - seconds_waited);
+                       if (seconds_waited == 300)
+                               break;
+               }
+
                schedule_timeout_interruptible(HZ/10);
        }
 
+       if (seconds_waited)
+               printk("\n");
+
        bus_for_each_dev(&xenbus_frontend.bus, NULL, drv,
                         print_device_status);
 }
index b9b3bb5..d15ea17 100644 (file)
@@ -767,7 +767,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        
        current->mm->start_stack = bprm->p;
 
-       /* Now we do a little grungy work by mmaping the ELF image into
+       /* Now we do a little grungy work by mmapping the ELF image into
           the correct location in memory. */
        for(i = 0, elf_ppnt = elf_phdata;
            i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
index e23a63f..76e6713 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -272,7 +272,7 @@ EXPORT_SYMBOL(bio_init);
  *   for a &struct bio to become free. If a %NULL @bs is passed in, we will
  *   fall back to just using @kmalloc to allocate the required memory.
  *
- *   Note that the caller must set ->bi_destructor on succesful return
+ *   Note that the caller must set ->bi_destructor on successful return
  *   of a bio, to do the appropriate freeing of the bio once the reference
  *   count drops to zero.
  **/
index ccbdcb5..46bea0f 100644 (file)
@@ -256,7 +256,7 @@ out:
  * Insert @em into @tree or perform a simple forward/backward merge with
  * existing mappings.  The extent_map struct passed in will be inserted
  * into the tree directly, with an additional reference taken, or a
- * reference dropped if the merge attempt was sucessfull.
+ * reference dropped if the merge attempt was successfull.
  */
 int add_extent_mapping(struct extent_map_tree *tree,
                       struct extent_map *em)
index 79c1a93..a727b7c 100644 (file)
@@ -423,7 +423,7 @@ A partial list of the supported mount options follows:
                source name to use to represent the client netbios machine 
                name when doing the RFC1001 netbios session initialize.
   direct        Do not do inode data caching on files opened on this mount.
-               This precludes mmaping files on this mount. In some cases
+               This precludes mmapping files on this mount. In some cases
                with fast networks and little or no caching benefits on the
                client (e.g. when the application is doing large sequential
                reads bigger than page size without rereading the same data) 
index 5d0fde1..4b35f7e 100644 (file)
@@ -39,7 +39,7 @@
 
 /*
  * MAX_REQ is the maximum number of requests that WE will send
- * on one socket concurently. It also matches the most common
+ * on one socket concurrently. It also matches the most common
  * value of max multiplex returned by servers.  We may
  * eventually want to use the negotiated value (in case
  * future servers can handle more) when we are more confident that
index 2d07f89..3877737 100644 (file)
@@ -1227,7 +1227,7 @@ typedef struct smb_com_setattr_rsp {
 /* empty wct response to setattr */
 
 /*******************************************************/
-/* NT Transact structure defintions follow             */
+/* NT Transact structure definitions follow            */
 /* Currently only ioctl, acl (get security descriptor) */
 /* and notify are implemented                          */
 /*******************************************************/
index cababd8..cf18ee7 100644 (file)
@@ -914,8 +914,8 @@ undo_setattr:
 /*
  * If dentry->d_inode is null (usually meaning the cached dentry
  * is a negative dentry) then we would attempt a standard SMB delete, but
- * if that fails we can not attempt the fall back mechanisms on EACESS
- * but will return the EACESS to the caller.  Note that the VFS does not call
+ * if that fails we can not attempt the fall back mechanisms on EACCESS
+ * but will return the EACCESS to the caller. Note that the VFS does not call
  * unlink on negative dentries currently.
  */
 int cifs_unlink(struct inode *dir, struct dentry *dentry)
index 224a1f4..b6b6dcb 100644 (file)
@@ -371,7 +371,7 @@ E_P24(unsigned char *p21, const unsigned char *c8, unsigned char *p24)
        smbhash(p24 + 16, c8, p21 + 14, 1);
 }
 
-#if 0 /* currently unsued */
+#if 0 /* currently unused */
 static void
 D_P16(unsigned char *p14, unsigned char *in, unsigned char *out)
 {
index 229e722..2346895 100644 (file)
@@ -1920,7 +1920,7 @@ COMPATIBLE_IOCTL(TIOCSLTC)
 #endif
 #ifdef TIOCSTART
 /*
- * For these two we have defintions in ioctls.h and/or termios.h on
+ * For these two we have definitions in ioctls.h and/or termios.h on
  * some architectures but no actual implemention.  Some applications
  * like bash call them if they are defined in the headers, so we provide
  * entries here to avoid syslog message spew.
index d22438e..0d23b52 100644 (file)
@@ -184,7 +184,7 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
 /**
  * debugfs_create_file - create a file in the debugfs filesystem
  * @name: a pointer to a string containing the name of the file to create.
- * @mode: the permission that the file should have
+ * @mode: the permission that the file should have.
  * @parent: a pointer to the parent dentry for this file.  This should be a
  *          directory dentry if set.  If this paramater is NULL, then the
  *          file will be created in the root of the debugfs filesystem.
@@ -195,8 +195,8 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
  *        this file.
  *
  * This is the basic "create a file" function for debugfs.  It allows for a
- * wide range of flexibility in createing a file, or a directory (if you
- * want to create a directory, the debugfs_create_dir() function is
+ * wide range of flexibility in creating a file, or a directory (if you want
+ * to create a directory, the debugfs_create_dir() function is
  * recommended to be used instead.)
  *
  * This function will return a pointer to a dentry if it succeeds.  This
index fd9859f..0df2438 100644 (file)
@@ -410,10 +410,10 @@ static struct config_group *make_cluster(struct config_group *g,
        struct dlm_comms *cms = NULL;
        void *gps = NULL;
 
-       cl = kzalloc(sizeof(struct dlm_cluster), GFP_KERNEL);
-       gps = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL);
-       sps = kzalloc(sizeof(struct dlm_spaces), GFP_KERNEL);
-       cms = kzalloc(sizeof(struct dlm_comms), GFP_KERNEL);
+       cl = kzalloc(sizeof(struct dlm_cluster), GFP_NOFS);
+       gps = kcalloc(3, sizeof(struct config_group *), GFP_NOFS);
+       sps = kzalloc(sizeof(struct dlm_spaces), GFP_NOFS);
+       cms = kzalloc(sizeof(struct dlm_comms), GFP_NOFS);
 
        if (!cl || !gps || !sps || !cms)
                goto fail;
@@ -482,9 +482,9 @@ static struct config_group *make_space(struct config_group *g, const char *name)
        struct dlm_nodes *nds = NULL;
        void *gps = NULL;
 
-       sp = kzalloc(sizeof(struct dlm_space), GFP_KERNEL);
-       gps = kcalloc(2, sizeof(struct config_group *), GFP_KERNEL);
-       nds = kzalloc(sizeof(struct dlm_nodes), GFP_KERNEL);
+       sp = kzalloc(sizeof(struct dlm_space), GFP_NOFS);
+       gps = kcalloc(2, sizeof(struct config_group *), GFP_NOFS);
+       nds = kzalloc(sizeof(struct dlm_nodes), GFP_NOFS);
 
        if (!sp || !gps || !nds)
                goto fail;
@@ -536,7 +536,7 @@ static struct config_item *make_comm(struct config_group *g, const char *name)
 {
        struct dlm_comm *cm;
 
-       cm = kzalloc(sizeof(struct dlm_comm), GFP_KERNEL);
+       cm = kzalloc(sizeof(struct dlm_comm), GFP_NOFS);
        if (!cm)
                return ERR_PTR(-ENOMEM);
 
@@ -569,7 +569,7 @@ static struct config_item *make_node(struct config_group *g, const char *name)
        struct dlm_space *sp = config_item_to_space(g->cg_item.ci_parent);
        struct dlm_node *nd;
 
-       nd = kzalloc(sizeof(struct dlm_node), GFP_KERNEL);
+       nd = kzalloc(sizeof(struct dlm_node), GFP_NOFS);
        if (!nd)
                return ERR_PTR(-ENOMEM);
 
@@ -705,7 +705,7 @@ static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf, size_t len)
        if (cm->addr_count >= DLM_MAX_ADDR_COUNT)
                return -ENOSPC;
 
-       addr = kzalloc(sizeof(*addr), GFP_KERNEL);
+       addr = kzalloc(sizeof(*addr), GFP_NOFS);
        if (!addr)
                return -ENOMEM;
 
@@ -868,7 +868,7 @@ int dlm_nodeid_list(char *lsname, int **ids_out, int *ids_count_out,
 
        ids_count = sp->members_count;
 
-       ids = kcalloc(ids_count, sizeof(int), GFP_KERNEL);
+       ids = kcalloc(ids_count, sizeof(int), GFP_NOFS);
        if (!ids) {
                rv = -ENOMEM;
                goto out;
@@ -886,7 +886,7 @@ int dlm_nodeid_list(char *lsname, int **ids_out, int *ids_count_out,
        if (!new_count)
                goto out_ids;
 
-       new = kcalloc(new_count, sizeof(int), GFP_KERNEL);
+       new = kcalloc(new_count, sizeof(int), GFP_NOFS);
        if (!new) {
                kfree(ids);
                rv = -ENOMEM;
index 1c8bb8c..375a235 100644 (file)
@@ -404,7 +404,7 @@ static void *table_seq_start(struct seq_file *seq, loff_t *pos)
        if (bucket >= ls->ls_rsbtbl_size)
                return NULL;
 
-       ri = kzalloc(sizeof(struct rsbtbl_iter), GFP_KERNEL);
+       ri = kzalloc(sizeof(struct rsbtbl_iter), GFP_NOFS);
        if (!ri)
                return NULL;
        if (n == 0)
index c4dfa1d..7b84c1d 100644 (file)
@@ -49,8 +49,7 @@ 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,
-                            ls->ls_allocation);
+               de = kzalloc(sizeof(struct dlm_direntry) + len, GFP_NOFS);
        return de;
 }
 
@@ -212,7 +211,7 @@ int dlm_recover_directory(struct dlm_ls *ls)
 
        dlm_dir_clear(ls);
 
-       last_name = kmalloc(DLM_RESNAME_MAXLEN, ls->ls_allocation);
+       last_name = kmalloc(DLM_RESNAME_MAXLEN, GFP_NOFS);
        if (!last_name)
                goto out;
 
@@ -323,7 +322,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, ls->ls_allocation);
+       de = kzalloc(sizeof(struct dlm_direntry) + namelen, GFP_NOFS);
        if (!de)
                return -ENOMEM;
 
index d01ca0a..826d3dc 100644 (file)
@@ -473,7 +473,6 @@ struct dlm_ls {
        int                     ls_low_nodeid;
        int                     ls_total_weight;
        int                     *ls_node_array;
-       gfp_t                   ls_allocation;
 
        struct dlm_rsb          ls_stub_rsb;    /* for returning errors */
        struct dlm_lkb          ls_stub_lkb;    /* for returning errors */
index eb507c4..9c0c1db 100644 (file)
@@ -2689,7 +2689,7 @@ static int _create_message(struct dlm_ls *ls, int mb_len,
           pass into lowcomms_commit and a message buffer (mb) that we
           write our data into */
 
-       mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, ls->ls_allocation, &mb);
+       mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, GFP_NOFS, &mb);
        if (!mh)
                return -ENOBUFS;
 
@@ -4512,7 +4512,7 @@ int dlm_user_request(struct dlm_ls *ls, struct dlm_user_args *ua,
        }
 
        if (flags & DLM_LKF_VALBLK) {
-               ua->lksb.sb_lvbptr = kzalloc(DLM_USER_LVB_LEN, GFP_KERNEL);
+               ua->lksb.sb_lvbptr = kzalloc(DLM_USER_LVB_LEN, GFP_NOFS);
                if (!ua->lksb.sb_lvbptr) {
                        kfree(ua);
                        __put_lkb(ls, lkb);
@@ -4582,7 +4582,7 @@ int dlm_user_convert(struct dlm_ls *ls, struct dlm_user_args *ua_tmp,
        ua = lkb->lkb_ua;
 
        if (flags & DLM_LKF_VALBLK && !ua->lksb.sb_lvbptr) {
-               ua->lksb.sb_lvbptr = kzalloc(DLM_USER_LVB_LEN, GFP_KERNEL);
+               ua->lksb.sb_lvbptr = kzalloc(DLM_USER_LVB_LEN, GFP_NOFS);
                if (!ua->lksb.sb_lvbptr) {
                        error = -ENOMEM;
                        goto out_put;
index d489fcc..c010ecf 100644 (file)
@@ -430,7 +430,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
 
        error = -ENOMEM;
 
-       ls = kzalloc(sizeof(struct dlm_ls) + namelen, GFP_KERNEL);
+       ls = kzalloc(sizeof(struct dlm_ls) + namelen, GFP_NOFS);
        if (!ls)
                goto out;
        memcpy(ls->ls_name, name, namelen);
@@ -443,11 +443,6 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        if (flags & DLM_LSFL_TIMEWARN)
                set_bit(LSFL_TIMEWARN, &ls->ls_flags);
 
-       if (flags & DLM_LSFL_FS)
-               ls->ls_allocation = GFP_NOFS;
-       else
-               ls->ls_allocation = GFP_KERNEL;
-
        /* ls_exflags are forced to match among nodes, and we don't
           need to require all nodes to have some flags set */
        ls->ls_exflags = (flags & ~(DLM_LSFL_TIMEWARN | DLM_LSFL_FS |
@@ -456,7 +451,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        size = dlm_config.ci_rsbtbl_size;
        ls->ls_rsbtbl_size = size;
 
-       ls->ls_rsbtbl = kmalloc(sizeof(struct dlm_rsbtable) * size, GFP_KERNEL);
+       ls->ls_rsbtbl = kmalloc(sizeof(struct dlm_rsbtable) * size, GFP_NOFS);
        if (!ls->ls_rsbtbl)
                goto out_lsfree;
        for (i = 0; i < size; i++) {
@@ -468,7 +463,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        size = dlm_config.ci_lkbtbl_size;
        ls->ls_lkbtbl_size = size;
 
-       ls->ls_lkbtbl = kmalloc(sizeof(struct dlm_lkbtable) * size, GFP_KERNEL);
+       ls->ls_lkbtbl = kmalloc(sizeof(struct dlm_lkbtable) * size, GFP_NOFS);
        if (!ls->ls_lkbtbl)
                goto out_rsbfree;
        for (i = 0; i < size; i++) {
@@ -480,7 +475,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        size = dlm_config.ci_dirtbl_size;
        ls->ls_dirtbl_size = size;
 
-       ls->ls_dirtbl = kmalloc(sizeof(struct dlm_dirtable) * size, GFP_KERNEL);
+       ls->ls_dirtbl = kmalloc(sizeof(struct dlm_dirtable) * size, GFP_NOFS);
        if (!ls->ls_dirtbl)
                goto out_lkbfree;
        for (i = 0; i < size; i++) {
@@ -527,7 +522,7 @@ static int new_lockspace(const char *name, int namelen, void **lockspace,
        mutex_init(&ls->ls_requestqueue_mutex);
        mutex_init(&ls->ls_clear_proc_locks);
 
-       ls->ls_recover_buf = kmalloc(dlm_config.ci_buffer_size, GFP_KERNEL);
+       ls->ls_recover_buf = kmalloc(dlm_config.ci_buffer_size, GFP_NOFS);
        if (!ls->ls_recover_buf)
                goto out_dirfree;
 
index 70736eb..52cab16 100644 (file)
@@ -1060,7 +1060,7 @@ static void init_local(void)
                if (dlm_our_addr(&sas, i))
                        break;
 
-               addr = kmalloc(sizeof(*addr), GFP_KERNEL);
+               addr = kmalloc(sizeof(*addr), GFP_NOFS);
                if (!addr)
                        break;
                memcpy(addr, &sas, sizeof(*addr));
@@ -1099,7 +1099,7 @@ static int sctp_listen_for_all(void)
        struct sockaddr_storage localaddr;
        struct sctp_event_subscribe subscribe;
        int result = -EINVAL, num = 1, i, addr_len;
-       struct connection *con = nodeid2con(0, GFP_KERNEL);
+       struct connection *con = nodeid2con(0, GFP_NOFS);
        int bufsize = NEEDED_RMEM;
 
        if (!con)
@@ -1171,7 +1171,7 @@ out:
 static int tcp_listen_for_all(void)
 {
        struct socket *sock = NULL;
-       struct connection *con = nodeid2con(0, GFP_KERNEL);
+       struct connection *con = nodeid2con(0, GFP_NOFS);
        int result = -EINVAL;
 
        if (!con)
index b128775..84f70bf 100644 (file)
@@ -48,7 +48,7 @@ static int dlm_add_member(struct dlm_ls *ls, int nodeid)
        struct dlm_member *memb;
        int w, error;
 
-       memb = kzalloc(sizeof(struct dlm_member), ls->ls_allocation);
+       memb = kzalloc(sizeof(struct dlm_member), GFP_NOFS);
        if (!memb)
                return -ENOMEM;
 
@@ -143,7 +143,7 @@ static void make_member_array(struct dlm_ls *ls)
 
        ls->ls_total_weight = total;
 
-       array = kmalloc(sizeof(int) * total, ls->ls_allocation);
+       array = kmalloc(sizeof(int) * total, GFP_NOFS);
        if (!array)
                return;
 
@@ -226,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), ls->ls_allocation);
+               memb = kzalloc(sizeof(struct dlm_member), GFP_NOFS);
                if (!memb)
                        return -ENOMEM;
                memb->nodeid = rv->new[i];
@@ -341,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), ls->ls_allocation);
+       rv = kzalloc(sizeof(struct dlm_recover), GFP_NOFS);
        if (!rv)
                return -ENOMEM;
 
index c1775b8..8e0d00d 100644 (file)
@@ -39,7 +39,7 @@ char *dlm_allocate_lvb(struct dlm_ls *ls)
 {
        char *p;
 
-       p = kzalloc(ls->ls_lvblen, ls->ls_allocation);
+       p = kzalloc(ls->ls_lvblen, GFP_NOFS);
        return p;
 }
 
@@ -57,7 +57,7 @@ struct dlm_rsb *dlm_allocate_rsb(struct dlm_ls *ls, int namelen)
 
        DLM_ASSERT(namelen <= DLM_RESNAME_MAXLEN,);
 
-       r = kzalloc(sizeof(*r) + namelen, ls->ls_allocation);
+       r = kzalloc(sizeof(*r) + namelen, GFP_NOFS);
        return r;
 }
 
@@ -72,7 +72,7 @@ struct dlm_lkb *dlm_allocate_lkb(struct dlm_ls *ls)
 {
        struct dlm_lkb *lkb;
 
-       lkb = kmem_cache_zalloc(lkb_cache, ls->ls_allocation);
+       lkb = kmem_cache_zalloc(lkb_cache, GFP_NOFS);
        return lkb;
 }
 
index 55ea369..052095c 100644 (file)
@@ -26,7 +26,7 @@ static int prepare_data(u8 cmd, struct sk_buff **skbp, size_t size)
        struct sk_buff *skb;
        void *data;
 
-       skb = genlmsg_new(size, GFP_KERNEL);
+       skb = genlmsg_new(size, GFP_NOFS);
        if (!skb)
                return -ENOMEM;
 
index 16f682e..b5f89ae 100644 (file)
@@ -82,7 +82,7 @@ int dlm_posix_lock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
        if (!ls)
                return -EINVAL;
 
-       xop = kzalloc(sizeof(*xop), GFP_KERNEL);
+       xop = kzalloc(sizeof(*xop), GFP_NOFS);
        if (!xop) {
                rv = -ENOMEM;
                goto out;
@@ -143,7 +143,7 @@ out:
 }
 EXPORT_SYMBOL_GPL(dlm_posix_lock);
 
-/* Returns failure iff a succesful lock operation should be canceled */
+/* Returns failure iff a successful lock operation should be canceled */
 static int dlm_plock_callback(struct plock_op *op)
 {
        struct file *file;
@@ -211,7 +211,7 @@ int dlm_posix_unlock(dlm_lockspace_t *lockspace, u64 number, struct file *file,
        if (!ls)
                return -EINVAL;
 
-       op = kzalloc(sizeof(*op), GFP_KERNEL);
+       op = kzalloc(sizeof(*op), GFP_NOFS);
        if (!op) {
                rv = -ENOMEM;
                goto out;
@@ -266,7 +266,7 @@ int dlm_posix_get(dlm_lockspace_t *lockspace, u64 number, struct file *file,
        if (!ls)
                return -EINVAL;
 
-       op = kzalloc(sizeof(*op), GFP_KERNEL);
+       op = kzalloc(sizeof(*op), GFP_NOFS);
        if (!op) {
                rv = -ENOMEM;
                goto out;
index 67522c2..3c83a49 100644 (file)
@@ -38,7 +38,7 @@ static int create_rcom(struct dlm_ls *ls, int to_nodeid, int type, int len,
        char *mb;
        int mb_len = sizeof(struct dlm_rcom) + len;
 
-       mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, ls->ls_allocation, &mb);
+       mh = dlm_lowcomms_get_buffer(to_nodeid, mb_len, GFP_NOFS, &mb);
        if (!mh) {
                log_print("create_rcom to %d type %d len %d ENOBUFS",
                          to_nodeid, type, len);
index 7a2307c..a44fa22 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, ls->ls_allocation);
+       e = kmalloc(sizeof(struct rq_entry) + length, GFP_NOFS);
        if (!e) {
                log_print("dlm_add_requestqueue: out of memory len %d", length);
                return;
index ebce994..e73a4bb 100644 (file)
@@ -267,7 +267,7 @@ static int device_user_lock(struct dlm_user_proc *proc,
                goto out;
        }
 
-       ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
+       ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
        if (!ua)
                goto out;
        ua->proc = proc;
@@ -307,7 +307,7 @@ static int device_user_unlock(struct dlm_user_proc *proc,
        if (!ls)
                return -ENOENT;
 
-       ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
+       ua = kzalloc(sizeof(struct dlm_user_args), GFP_NOFS);
        if (!ua)
                goto out;
        ua->proc = proc;
@@ -352,7 +352,7 @@ static int dlm_device_register(struct dlm_ls *ls, char *name)
 
        error = -ENOMEM;
        len = strlen(name) + strlen(name_prefix) + 2;
-       ls->ls_device.name = kzalloc(len, GFP_KERNEL);
+       ls->ls_device.name = kzalloc(len, GFP_NOFS);
        if (!ls->ls_device.name)
                goto fail;
 
@@ -520,7 +520,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,
 #endif
                return -EINVAL;
 
-       kbuf = kzalloc(count + 1, GFP_KERNEL);
+       kbuf = kzalloc(count + 1, GFP_NOFS);
        if (!kbuf)
                return -ENOMEM;
 
@@ -546,7 +546,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,
 
                /* add 1 after namelen so that the name string is terminated */
                kbuf = kzalloc(sizeof(struct dlm_write_request) + namelen + 1,
-                              GFP_KERNEL);
+                              GFP_NOFS);
                if (!kbuf) {
                        kfree(k32buf);
                        return -ENOMEM;
@@ -648,7 +648,7 @@ static int device_open(struct inode *inode, struct file *file)
        if (!ls)
                return -ENOENT;
 
-       proc = kzalloc(sizeof(struct dlm_user_proc), GFP_KERNEL);
+       proc = kzalloc(sizeof(struct dlm_user_proc), GFP_NOFS);
        if (!proc) {
                dlm_put_lockspace(ls);
                return -ENOMEM;
index cc2d22d..2d0f757 100644 (file)
@@ -12,5 +12,5 @@
 # Kbuild - Gets included from the Kernels Makefile and build system
 #
 
-exofs-y := osd.o inode.o file.o symlink.o namei.o dir.o super.o
+exofs-y := ios.o inode.o file.o symlink.o namei.o dir.o super.o
 obj-$(CONFIG_EXOFS_FS) += exofs.o
index c6718e4..b1b178e 100644 (file)
@@ -49,6 +49,7 @@
 #define EXOFS_MIN_PID   0x10000        /* Smallest partition ID */
 #define EXOFS_OBJ_OFF  0x10000 /* offset for objects */
 #define EXOFS_SUPER_ID 0x10000 /* object ID for on-disk superblock */
+#define EXOFS_DEVTABLE_ID 0x10001 /* object ID for on-disk device table */
 #define EXOFS_ROOT_ID  0x10002 /* object ID for root directory */
 
 /* exofs Application specific page/attribute */
@@ -78,17 +79,67 @@ enum {
 #define EXOFS_SUPER_MAGIC      0x5DF5
 
 /*
- * The file system control block - stored in an object's data (mainly, the one
- * with ID EXOFS_SUPER_ID).  This is where the in-memory superblock is stored
- * on disk.  Right now it just has a magic value, which is basically a sanity
- * check on our ability to communicate with the object store.
+ * The file system control block - stored in object EXOFS_SUPER_ID's data.
+ * This is where the in-memory superblock is stored on disk.
  */
+enum {EXOFS_FSCB_VER = 1, EXOFS_DT_VER = 1};
 struct exofs_fscb {
        __le64  s_nextid;       /* Highest object ID used */
-       __le32  s_numfiles;     /* Number of files on fs */
+       __le64  s_numfiles;     /* Number of files on fs */
+       __le32  s_version;      /* == EXOFS_FSCB_VER */
        __le16  s_magic;        /* Magic signature */
        __le16  s_newfs;        /* Non-zero if this is a new fs */
-};
+
+       /* From here on it's a static part, only written by mkexofs */
+       __le64  s_dev_table_oid;   /* Resurved, not used */
+       __le64  s_dev_table_count; /* == 0 means no dev_table */
+} __packed;
+
+/*
+ * Describes the raid used in the FS. It is part of the device table.
+ * This here is taken from the pNFS-objects definition. In exofs we
+ * use one raid policy through-out the filesystem. (NOTE: the funny
+ * alignment at begining. We take care of it at exofs_device_table.
+ */
+struct exofs_dt_data_map {
+       __le32  cb_num_comps;
+       __le64  cb_stripe_unit;
+       __le32  cb_group_width;
+       __le32  cb_group_depth;
+       __le32  cb_mirror_cnt;
+       __le32  cb_raid_algorithm;
+} __packed;
+
+/*
+ * This is an osd device information descriptor. It is a single entry in
+ * the exofs device table. It describes an osd target lun which
+ * contains data belonging to this FS. (Same partition_id on all devices)
+ */
+struct exofs_dt_device_info {
+       __le32  systemid_len;
+       u8      systemid[OSD_SYSTEMID_LEN];
+       __le64  long_name_offset;       /* If !0 then offset-in-file */
+       __le32  osdname_len;            /* */
+       u8      osdname[44];            /* Embbeded, Ususally an asci uuid */
+} __packed;
+
+/*
+ * The EXOFS device table - stored in object EXOFS_DEVTABLE_ID's data.
+ * It contains the raid used for this multy-device FS and an array of
+ * participating devices.
+ */
+struct exofs_device_table {
+       __le32                          dt_version;     /* == EXOFS_DT_VER */
+       struct exofs_dt_data_map        dt_data_map;    /* Raid policy to use */
+
+       /* Resurved space For future use. Total includeing this:
+        * (8 * sizeof(le64))
+        */
+       __le64                          __Resurved[4];
+
+       __le64                          dt_num_devices; /* Array size */
+       struct exofs_dt_device_info     dt_dev_table[]; /* Array of devices */
+} __packed;
 
 /****************************************************************************
  * inode-related things
@@ -155,22 +206,4 @@ enum {
        (((name_len) + offsetof(struct exofs_dir_entry, name)  + \
          EXOFS_DIR_ROUND) & ~EXOFS_DIR_ROUND)
 
-/*************************
- * function declarations *
- *************************/
-/* osd.c                 */
-void exofs_make_credential(u8 cred_a[OSD_CAP_LEN],
-                          const struct osd_obj_id *obj);
-
-int exofs_check_ok_resid(struct osd_request *or, u64 *in_resid, u64 *out_resid);
-static inline int exofs_check_ok(struct osd_request *or)
-{
-       return exofs_check_ok_resid(or, NULL, NULL);
-}
-int exofs_sync_op(struct osd_request *or, int timeout, u8 *cred);
-int exofs_async_op(struct osd_request *or,
-       osd_req_done_fn *async_done, void *caller_context, u8 *cred);
-
-int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr);
-
 #endif /*ifndef __EXOFS_COM_H__*/
index 5ec72e0..c35fd46 100644 (file)
  * along with exofs; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
+#ifndef __EXOFS_H__
+#define __EXOFS_H__
 
 #include <linux/fs.h>
 #include <linux/time.h>
 #include "common.h"
 
-#ifndef __EXOFS_H__
-#define __EXOFS_H__
+/* FIXME: Remove once pnfs hits mainline
+ * #include <linux/exportfs/pnfs_osd_xdr.h>
+ */
+#include "pnfs.h"
 
 #define EXOFS_ERR(fmt, a...) printk(KERN_ERR "exofs: " fmt, ##a)
 
@@ -55,7 +59,7 @@
  * our extension to the in-memory superblock
  */
 struct exofs_sb_info {
-       struct osd_dev  *s_dev;                 /* returned by get_osd_dev    */
+       struct exofs_fscb s_fscb;               /* Written often, pre-allocate*/
        osd_id          s_pid;                  /* partition ID of file system*/
        int             s_timeout;              /* timeout for OSD operations */
        uint64_t        s_nextid;               /* highest object ID used     */
@@ -63,7 +67,11 @@ struct exofs_sb_info {
        spinlock_t      s_next_gen_lock;        /* spinlock for gen # update  */
        u32             s_next_generation;      /* next gen # to use          */
        atomic_t        s_curr_pending;         /* number of pending commands */
-       uint8_t         s_cred[OSD_CAP_LEN];    /* all-powerful credential    */
+       uint8_t         s_cred[OSD_CAP_LEN];    /* credential for the fscb    */
+
+       struct pnfs_osd_data_map data_map;      /* Default raid to use        */
+       unsigned        s_numdevs;              /* Num of devices in array    */
+       struct osd_dev  *s_ods[1];              /* Variable length, minimum 1 */
 };
 
 /*
@@ -79,6 +87,50 @@ struct exofs_i_info {
        struct inode   vfs_inode;          /* normal in-memory inode          */
 };
 
+static inline osd_id exofs_oi_objno(struct exofs_i_info *oi)
+{
+       return oi->vfs_inode.i_ino + EXOFS_OBJ_OFF;
+}
+
+struct exofs_io_state;
+typedef void (*exofs_io_done_fn)(struct exofs_io_state *or, void *private);
+
+struct exofs_io_state {
+       struct kref             kref;
+
+       void                    *private;
+       exofs_io_done_fn        done;
+
+       struct exofs_sb_info    *sbi;
+       struct osd_obj_id       obj;
+       u8                      *cred;
+
+       /* Global read/write IO*/
+       loff_t                  offset;
+       unsigned long           length;
+       void                    *kern_buff;
+       struct bio              *bio;
+
+       /* Attributes */
+       unsigned                in_attr_len;
+       struct osd_attr         *in_attr;
+       unsigned                out_attr_len;
+       struct osd_attr         *out_attr;
+
+       /* Variable array of size numdevs */
+       unsigned numdevs;
+       struct exofs_per_dev_state {
+               struct osd_request *or;
+               struct bio *bio;
+       } per_dev[];
+};
+
+static inline unsigned exofs_io_state_size(unsigned numdevs)
+{
+       return sizeof(struct exofs_io_state) +
+               sizeof(struct exofs_per_dev_state) * numdevs;
+}
+
 /*
  * our inode flags
  */
@@ -130,6 +182,42 @@ static inline struct exofs_i_info *exofs_i(struct inode *inode)
 /*************************
  * function declarations *
  *************************/
+
+/* ios.c */
+void exofs_make_credential(u8 cred_a[OSD_CAP_LEN],
+                          const struct osd_obj_id *obj);
+int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj,
+                   u64 offset, void *p, unsigned length);
+
+int  exofs_get_io_state(struct exofs_sb_info *sbi, struct exofs_io_state** ios);
+void exofs_put_io_state(struct exofs_io_state *ios);
+
+int exofs_check_io(struct exofs_io_state *ios, u64 *resid);
+
+int exofs_sbi_create(struct exofs_io_state *ios);
+int exofs_sbi_remove(struct exofs_io_state *ios);
+int exofs_sbi_write(struct exofs_io_state *ios);
+int exofs_sbi_read(struct exofs_io_state *ios);
+
+int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr);
+
+int exofs_oi_truncate(struct exofs_i_info *oi, u64 new_len);
+static inline int exofs_oi_write(struct exofs_i_info *oi,
+                                struct exofs_io_state *ios)
+{
+       ios->obj.id = exofs_oi_objno(oi);
+       ios->cred = oi->i_cred;
+       return exofs_sbi_write(ios);
+}
+
+static inline int exofs_oi_read(struct exofs_i_info *oi,
+                               struct exofs_io_state *ios)
+{
+       ios->obj.id = exofs_oi_objno(oi);
+       ios->cred = oi->i_cred;
+       return exofs_sbi_read(ios);
+}
+
 /* inode.c               */
 void exofs_truncate(struct inode *inode);
 int exofs_setattr(struct dentry *, struct iattr *);
@@ -169,6 +257,7 @@ extern const struct file_operations exofs_file_operations;
 
 /* inode.c           */
 extern const struct address_space_operations exofs_aops;
+extern const struct osd_attr g_attr_logical_length;
 
 /* namei.c           */
 extern const struct inode_operations exofs_dir_inode_operations;
index 6c10f74..698a863 100644 (file)
 
 #include "exofs.h"
 
-#ifdef CONFIG_EXOFS_DEBUG
-#  define EXOFS_DEBUG_OBJ_ISIZE 1
-#endif
+#define EXOFS_DBGMSG2(M...) do {} while (0)
+
+enum { BIO_MAX_PAGES_KMALLOC =
+               (PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),
+};
 
 struct page_collect {
        struct exofs_sb_info *sbi;
        struct request_queue *req_q;
        struct inode *inode;
        unsigned expected_pages;
+       struct exofs_io_state *ios;
 
        struct bio *bio;
        unsigned nr_pages;
@@ -54,22 +57,23 @@ struct page_collect {
 };
 
 static void _pcol_init(struct page_collect *pcol, unsigned expected_pages,
-               struct inode *inode)
+                      struct inode *inode)
 {
        struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
 
        pcol->sbi = sbi;
-       pcol->req_q = osd_request_queue(sbi->s_dev);
+       /* Create master bios on first Q, later on cloning, each clone will be
+        * allocated on it's destination Q
+        */
+       pcol->req_q = osd_request_queue(sbi->s_ods[0]);
        pcol->inode = inode;
        pcol->expected_pages = expected_pages;
 
+       pcol->ios = NULL;
        pcol->bio = NULL;
        pcol->nr_pages = 0;
        pcol->length = 0;
        pcol->pg_first = -1;
-
-       EXOFS_DBGMSG("_pcol_init ino=0x%lx expected_pages=%u\n", inode->i_ino,
-                    expected_pages);
 }
 
 static void _pcol_reset(struct page_collect *pcol)
@@ -80,35 +84,49 @@ static void _pcol_reset(struct page_collect *pcol)
        pcol->nr_pages = 0;
        pcol->length = 0;
        pcol->pg_first = -1;
-       EXOFS_DBGMSG("_pcol_reset ino=0x%lx expected_pages=%u\n",
-                    pcol->inode->i_ino, pcol->expected_pages);
+       pcol->ios = NULL;
 
        /* this is probably the end of the loop but in writes
         * it might not end here. don't be left with nothing
         */
        if (!pcol->expected_pages)
-               pcol->expected_pages = 128;
+               pcol->expected_pages = BIO_MAX_PAGES_KMALLOC;
 }
 
 static int pcol_try_alloc(struct page_collect *pcol)
 {
-       int pages = min_t(unsigned, pcol->expected_pages, BIO_MAX_PAGES);
+       int pages = min_t(unsigned, pcol->expected_pages,
+                         BIO_MAX_PAGES_KMALLOC);
+
+       if (!pcol->ios) { /* First time allocate io_state */
+               int ret = exofs_get_io_state(pcol->sbi, &pcol->ios);
+
+               if (ret)
+                       return ret;
+       }
 
        for (; pages; pages >>= 1) {
-               pcol->bio = bio_alloc(GFP_KERNEL, pages);
+               pcol->bio = bio_kmalloc(GFP_KERNEL, pages);
                if (likely(pcol->bio))
                        return 0;
        }
 
-       EXOFS_ERR("Failed to kcalloc expected_pages=%u\n",
+       EXOFS_ERR("Failed to bio_kmalloc expected_pages=%u\n",
                  pcol->expected_pages);
        return -ENOMEM;
 }
 
 static void pcol_free(struct page_collect *pcol)
 {
-       bio_put(pcol->bio);
-       pcol->bio = NULL;
+       if (pcol->bio) {
+               bio_put(pcol->bio);
+               pcol->bio = NULL;
+       }
+
+       if (pcol->ios) {
+               exofs_put_io_state(pcol->ios);
+               pcol->ios = NULL;
+       }
 }
 
 static int pcol_add_page(struct page_collect *pcol, struct page *page,
@@ -161,22 +179,17 @@ static void update_write_page(struct page *page, int ret)
 /* Called at the end of reads, to optionally unlock pages and update their
  * status.
  */
-static int __readpages_done(struct osd_request *or, struct page_collect *pcol,
-                           bool do_unlock)
+static int __readpages_done(struct page_collect *pcol, bool do_unlock)
 {
        struct bio_vec *bvec;
        int i;
        u64 resid;
        u64 good_bytes;
        u64 length = 0;
-       int ret = exofs_check_ok_resid(or, &resid, NULL);
-
-       osd_end_request(or);
+       int ret = exofs_check_io(pcol->ios, &resid);
 
        if (likely(!ret))
                good_bytes = pcol->length;
-       else if (!resid)
-               good_bytes = 0;
        else
                good_bytes = pcol->length - resid;
 
@@ -198,7 +211,7 @@ static int __readpages_done(struct osd_request *or, struct page_collect *pcol,
                else
                        page_stat = ret;
 
-               EXOFS_DBGMSG("    readpages_done(0x%lx, 0x%lx) %s\n",
+               EXOFS_DBGMSG2("    readpages_done(0x%lx, 0x%lx) %s\n",
                          inode->i_ino, page->index,
                          page_stat ? "bad_bytes" : "good_bytes");
 
@@ -214,13 +227,13 @@ static int __readpages_done(struct osd_request *or, struct page_collect *pcol,
 }
 
 /* callback of async reads */
-static void readpages_done(struct osd_request *or, void *p)
+static void readpages_done(struct exofs_io_state *ios, void *p)
 {
        struct page_collect *pcol = p;
 
-       __readpages_done(or, pcol, true);
+       __readpages_done(pcol, true);
        atomic_dec(&pcol->sbi->s_curr_pending);
-       kfree(p);
+       kfree(pcol);
 }
 
 static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw)
@@ -238,17 +251,13 @@ static void _unlock_pcol_pages(struct page_collect *pcol, int ret, int rw)
 
                unlock_page(page);
        }
-       pcol_free(pcol);
 }
 
 static int read_exec(struct page_collect *pcol, bool is_sync)
 {
        struct exofs_i_info *oi = exofs_i(pcol->inode);
-       struct osd_obj_id obj = {pcol->sbi->s_pid,
-                                       pcol->inode->i_ino + EXOFS_OBJ_OFF};
-       struct osd_request *or = NULL;
+       struct exofs_io_state *ios = pcol->ios;
        struct page_collect *pcol_copy = NULL;
-       loff_t i_start = pcol->pg_first << PAGE_CACHE_SHIFT;
        int ret;
 
        if (!pcol->bio)
@@ -257,17 +266,13 @@ static int read_exec(struct page_collect *pcol, bool is_sync)
        /* see comment in _readpage() about sync reads */
        WARN_ON(is_sync && (pcol->nr_pages != 1));
 
-       or = osd_start_request(pcol->sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       osd_req_read(or, &obj, i_start, pcol->bio, pcol->length);
+       ios->bio = pcol->bio;
+       ios->length = pcol->length;
+       ios->offset = pcol->pg_first << PAGE_CACHE_SHIFT;
 
        if (is_sync) {
-               exofs_sync_op(or, pcol->sbi->s_timeout, oi->i_cred);
-               return __readpages_done(or, pcol, false);
+               exofs_oi_read(oi, pcol->ios);
+               return __readpages_done(pcol, false);
        }
 
        pcol_copy = kmalloc(sizeof(*pcol_copy), GFP_KERNEL);
@@ -277,14 +282,16 @@ static int read_exec(struct page_collect *pcol, bool is_sync)
        }
 
        *pcol_copy = *pcol;
-       ret = exofs_async_op(or, readpages_done, pcol_copy, oi->i_cred);
+       ios->done = readpages_done;
+       ios->private = pcol_copy;
+       ret = exofs_oi_read(oi, ios);
        if (unlikely(ret))
                goto err;
 
        atomic_inc(&pcol->sbi->s_curr_pending);
 
        EXOFS_DBGMSG("read_exec obj=0x%llx start=0x%llx length=0x%lx\n",
-                 obj.id, _LLU(i_start), pcol->length);
+                 ios->obj.id, _LLU(ios->offset), pcol->length);
 
        /* pages ownership was passed to pcol_copy */
        _pcol_reset(pcol);
@@ -293,12 +300,10 @@ static int read_exec(struct page_collect *pcol, bool is_sync)
 err:
        if (!is_sync)
                _unlock_pcol_pages(pcol, ret, READ);
-       else /* Pages unlocked by caller in sync mode only free bio */
-               pcol_free(pcol);
+
+       pcol_free(pcol);
 
        kfree(pcol_copy);
-       if (or)
-               osd_end_request(or);
        return ret;
 }
 
@@ -370,12 +375,12 @@ try_again:
        if (len != PAGE_CACHE_SIZE)
                zero_user(page, len, PAGE_CACHE_SIZE - len);
 
-       EXOFS_DBGMSG("    readpage_strip(0x%lx, 0x%lx) len=0x%zx\n",
+       EXOFS_DBGMSG2("    readpage_strip(0x%lx, 0x%lx) len=0x%zx\n",
                     inode->i_ino, page->index, len);
 
        ret = pcol_add_page(pcol, page, len);
        if (ret) {
-               EXOFS_DBGMSG("Failed pcol_add_page pages[i]=%p "
+               EXOFS_DBGMSG2("Failed pcol_add_page pages[i]=%p "
                          "this_len=0x%zx nr_pages=%u length=0x%lx\n",
                          page, len, pcol->nr_pages, pcol->length);
 
@@ -419,9 +424,8 @@ static int _readpage(struct page *page, bool is_sync)
 
        _pcol_init(&pcol, 1, page->mapping->host);
 
-       /* readpage_strip might call read_exec(,async) inside at several places
-        * but this is safe for is_async=0 since read_exec will not do anything
-        * when we have a single page.
+       /* readpage_strip might call read_exec(,is_sync==false) at several
+        * places but not if we have a single page.
         */
        ret = readpage_strip(&pcol, page);
        if (ret) {
@@ -440,8 +444,8 @@ static int exofs_readpage(struct file *file, struct page *page)
        return _readpage(page, false);
 }
 
-/* Callback for osd_write. All writes are asynchronouse */
-static void writepages_done(struct osd_request *or, void *p)
+/* Callback for osd_write. All writes are asynchronous */
+static void writepages_done(struct exofs_io_state *ios, void *p)
 {
        struct page_collect *pcol = p;
        struct bio_vec *bvec;
@@ -449,16 +453,12 @@ static void writepages_done(struct osd_request *or, void *p)
        u64 resid;
        u64  good_bytes;
        u64  length = 0;
+       int ret = exofs_check_io(ios, &resid);
 
-       int ret = exofs_check_ok_resid(or, NULL, &resid);
-
-       osd_end_request(or);
        atomic_dec(&pcol->sbi->s_curr_pending);
 
        if (likely(!ret))
                good_bytes = pcol->length;
-       else if (!resid)
-               good_bytes = 0;
        else
                good_bytes = pcol->length - resid;
 
@@ -482,7 +482,7 @@ static void writepages_done(struct osd_request *or, void *p)
 
                update_write_page(page, page_stat);
                unlock_page(page);
-               EXOFS_DBGMSG("    writepages_done(0x%lx, 0x%lx) status=%d\n",
+               EXOFS_DBGMSG2("    writepages_done(0x%lx, 0x%lx) status=%d\n",
                             inode->i_ino, page->index, page_stat);
 
                length += bvec->bv_len;
@@ -496,23 +496,13 @@ static void writepages_done(struct osd_request *or, void *p)
 static int write_exec(struct page_collect *pcol)
 {
        struct exofs_i_info *oi = exofs_i(pcol->inode);
-       struct osd_obj_id obj = {pcol->sbi->s_pid,
-                                       pcol->inode->i_ino + EXOFS_OBJ_OFF};
-       struct osd_request *or = NULL;
+       struct exofs_io_state *ios = pcol->ios;
        struct page_collect *pcol_copy = NULL;
-       loff_t i_start = pcol->pg_first << PAGE_CACHE_SHIFT;
        int ret;
 
        if (!pcol->bio)
                return 0;
 
-       or = osd_start_request(pcol->sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("write_exec: Faild to osd_start_request()\n");
-               ret = -ENOMEM;
-               goto err;
-       }
-
        pcol_copy = kmalloc(sizeof(*pcol_copy), GFP_KERNEL);
        if (!pcol_copy) {
                EXOFS_ERR("write_exec: Faild to kmalloc(pcol)\n");
@@ -523,16 +513,22 @@ static int write_exec(struct page_collect *pcol)
        *pcol_copy = *pcol;
 
        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);
+
+       ios->bio = pcol_copy->bio;
+       ios->offset = pcol_copy->pg_first << PAGE_CACHE_SHIFT;
+       ios->length = pcol_copy->length;
+       ios->done = writepages_done;
+       ios->private = pcol_copy;
+
+       ret = exofs_oi_write(oi, ios);
        if (unlikely(ret)) {
-               EXOFS_ERR("write_exec: exofs_async_op() Faild\n");
+               EXOFS_ERR("write_exec: exofs_oi_write() Faild\n");
                goto err;
        }
 
        atomic_inc(&pcol->sbi->s_curr_pending);
        EXOFS_DBGMSG("write_exec(0x%lx, 0x%llx) start=0x%llx length=0x%lx\n",
-                 pcol->inode->i_ino, pcol->pg_first, _LLU(i_start),
+                 pcol->inode->i_ino, pcol->pg_first, _LLU(ios->offset),
                  pcol->length);
        /* pages ownership was passed to pcol_copy */
        _pcol_reset(pcol);
@@ -540,9 +536,9 @@ static int write_exec(struct page_collect *pcol)
 
 err:
        _unlock_pcol_pages(pcol, ret, WRITE);
+       pcol_free(pcol);
        kfree(pcol_copy);
-       if (or)
-               osd_end_request(or);
+
        return ret;
 }
 
@@ -586,6 +582,9 @@ static int writepage_strip(struct page *page,
                        if (PageError(page))
                                ClearPageError(page);
                        unlock_page(page);
+                       EXOFS_DBGMSG("writepage_strip(0x%lx, 0x%lx) "
+                                    "outside the limits\n",
+                                    inode->i_ino, page->index);
                        return 0;
                }
        }
@@ -600,6 +599,9 @@ try_again:
                ret = write_exec(pcol);
                if (unlikely(ret))
                        goto fail;
+
+               EXOFS_DBGMSG("writepage_strip(0x%lx, 0x%lx) Discontinuity\n",
+                            inode->i_ino, page->index);
                goto try_again;
        }
 
@@ -609,7 +611,7 @@ try_again:
                        goto fail;
        }
 
-       EXOFS_DBGMSG("    writepage_strip(0x%lx, 0x%lx) len=0x%zx\n",
+       EXOFS_DBGMSG2("    writepage_strip(0x%lx, 0x%lx) len=0x%zx\n",
                     inode->i_ino, page->index, len);
 
        ret = pcol_add_page(pcol, page, len);
@@ -634,6 +636,8 @@ try_again:
        return 0;
 
 fail:
+       EXOFS_DBGMSG("Error: writepage_strip(0x%lx, 0x%lx)=>%d\n",
+                    inode->i_ino, page->index, ret);
        set_bit(AS_EIO, &page->mapping->flags);
        unlock_page(page);
        return ret;
@@ -652,14 +656,17 @@ static int exofs_writepages(struct address_space *mapping,
                        wbc->range_end >> PAGE_CACHE_SHIFT;
 
        if (start || end)
-               expected_pages = min(end - start + 1, 32L);
+               expected_pages = end - start + 1;
        else
                expected_pages = mapping->nrpages;
 
-       EXOFS_DBGMSG("inode(0x%lx) wbc->start=0x%llx wbc->end=0x%llx"
-                    " m->nrpages=%lu start=0x%lx end=0x%lx\n",
+       if (expected_pages < 32L)
+               expected_pages = 32L;
+
+       EXOFS_DBGMSG("inode(0x%lx) wbc->start=0x%llx wbc->end=0x%llx "
+                    "nrpages=%lu start=0x%lx end=0x%lx expected_pages=%ld\n",
                     mapping->host->i_ino, wbc->range_start, wbc->range_end,
-                    mapping->nrpages, start, end);
+                    mapping->nrpages, start, end, expected_pages);
 
        _pcol_init(&pcol, expected_pages, mapping->host);
 
@@ -771,19 +778,28 @@ static int exofs_get_block(struct inode *inode, sector_t iblock,
 const struct osd_attr g_attr_logical_length = ATTR_DEF(
        OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8);
 
+static int _do_truncate(struct inode *inode)
+{
+       struct exofs_i_info *oi = exofs_i(inode);
+       loff_t isize = i_size_read(inode);
+       int ret;
+
+       inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+
+       nobh_truncate_page(inode->i_mapping, isize, exofs_get_block);
+
+       ret = exofs_oi_truncate(oi, (u64)isize);
+       EXOFS_DBGMSG("(0x%lx) size=0x%llx\n", inode->i_ino, isize);
+       return ret;
+}
+
 /*
  * Truncate a file to the specified size - all we have to do is set the size
  * attribute.  We make sure the object exists first.
  */
 void exofs_truncate(struct inode *inode)
 {
-       struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
        struct exofs_i_info *oi = exofs_i(inode);
-       struct osd_obj_id obj = {sbi->s_pid, inode->i_ino + EXOFS_OBJ_OFF};
-       struct osd_request *or;
-       struct osd_attr attr;
-       loff_t isize = i_size_read(inode);
-       __be64 newsize;
        int ret;
 
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
@@ -793,22 +809,6 @@ void exofs_truncate(struct inode *inode)
                return;
        if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
                return;
-       inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-
-       nobh_truncate_page(inode->i_mapping, isize, exofs_get_block);
-
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("ERROR: exofs_truncate: osd_start_request failed\n");
-               goto fail;
-       }
-
-       osd_req_set_attributes(or, &obj);
-
-       newsize = cpu_to_be64((u64)isize);
-       attr = g_attr_logical_length;
-       attr.val_ptr = &newsize;
-       osd_req_add_set_attr_list(or, &attr, 1);
 
        /* if we are about to truncate an object, and it hasn't been
         * created yet, wait
@@ -816,8 +816,7 @@ void exofs_truncate(struct inode *inode)
        if (unlikely(wait_obj_created(oi)))
                goto fail;
 
-       ret = exofs_sync_op(or, sbi->s_timeout, oi->i_cred);
-       osd_end_request(or);
+       ret = _do_truncate(inode);
        if (ret)
                goto fail;
 
@@ -847,65 +846,62 @@ int exofs_setattr(struct dentry *dentry, struct iattr *iattr)
 
 /*
  * Read an inode from the OSD, and return it as is.  We also return the size
- * attribute in the 'sanity' argument if we got compiled with debugging turned
- * on.
+ * attribute in the 'obj_size' argument.
  */
 static int exofs_get_inode(struct super_block *sb, struct exofs_i_info *oi,
-                   struct exofs_fcb *inode, uint64_t *sanity)
+                   struct exofs_fcb *inode, uint64_t *obj_size)
 {
        struct exofs_sb_info *sbi = sb->s_fs_info;
-       struct osd_request *or;
-       struct osd_attr attr;
-       struct osd_obj_id obj = {sbi->s_pid,
-                                oi->vfs_inode.i_ino + EXOFS_OBJ_OFF};
+       struct osd_attr attrs[2];
+       struct exofs_io_state *ios;
        int ret;
 
-       exofs_make_credential(oi->i_cred, &obj);
-
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("exofs_get_inode: osd_start_request failed.\n");
-               return -ENOMEM;
+       *obj_size = ~0;
+       ret = exofs_get_io_state(sbi, &ios);
+       if (unlikely(ret)) {
+               EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
+               return ret;
        }
-       osd_req_get_attributes(or, &obj);
 
-       /* we need the inode attribute */
-       osd_req_add_get_attr_list(or, &g_attr_inode_data, 1);
+       ios->obj.id = exofs_oi_objno(oi);
+       exofs_make_credential(oi->i_cred, &ios->obj);
+       ios->cred = oi->i_cred;
 
-#ifdef EXOFS_DEBUG_OBJ_ISIZE
-       /* we get the size attributes to do a sanity check */
-       osd_req_add_get_attr_list(or, &g_attr_logical_length, 1);
-#endif
+       attrs[0] = g_attr_inode_data;
+       attrs[1] = g_attr_logical_length;
+       ios->in_attr = attrs;
+       ios->in_attr_len = ARRAY_SIZE(attrs);
 
-       ret = exofs_sync_op(or, sbi->s_timeout, oi->i_cred);
+       ret = exofs_sbi_read(ios);
        if (ret)
                goto out;
 
-       attr = g_attr_inode_data;
-       ret = extract_attr_from_req(or, &attr);
+       ret = extract_attr_from_ios(ios, &attrs[0]);
        if (ret) {
-               EXOFS_ERR("exofs_get_inode: extract_attr_from_req failed\n");
+               EXOFS_ERR("%s: extract_attr of inode_data failed\n", __func__);
                goto out;
        }
+       WARN_ON(attrs[0].len != EXOFS_INO_ATTR_SIZE);
+       memcpy(inode, attrs[0].val_ptr, EXOFS_INO_ATTR_SIZE);
 
-       WARN_ON(attr.len != EXOFS_INO_ATTR_SIZE);
-       memcpy(inode, attr.val_ptr, EXOFS_INO_ATTR_SIZE);
-
-#ifdef EXOFS_DEBUG_OBJ_ISIZE
-       attr = g_attr_logical_length;
-       ret = extract_attr_from_req(or, &attr);
+       ret = extract_attr_from_ios(ios, &attrs[1]);
        if (ret) {
-               EXOFS_ERR("ERROR: extract attr from or failed\n");
+               EXOFS_ERR("%s: extract_attr of logical_length failed\n",
+                         __func__);
                goto out;
        }
-       *sanity = get_unaligned_be64(attr.val_ptr);
-#endif
+       *obj_size = get_unaligned_be64(attrs[1].val_ptr);
 
 out:
-       osd_end_request(or);
+       exofs_put_io_state(ios);
        return ret;
 }
 
+static void __oi_init(struct exofs_i_info *oi)
+{
+       init_waitqueue_head(&oi->i_wq);
+       oi->i_flags = 0;
+}
 /*
  * Fill in an inode read from the OSD and set it up for use
  */
@@ -914,7 +910,7 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
        struct exofs_i_info *oi;
        struct exofs_fcb fcb;
        struct inode *inode;
-       uint64_t uninitialized_var(sanity);
+       uint64_t obj_size;
        int ret;
 
        inode = iget_locked(sb, ino);
@@ -923,13 +919,13 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
        if (!(inode->i_state & I_NEW))
                return inode;
        oi = exofs_i(inode);
+       __oi_init(oi);
 
        /* read the inode from the osd */
-       ret = exofs_get_inode(sb, oi, &fcb, &sanity);
+       ret = exofs_get_inode(sb, oi, &fcb, &obj_size);
        if (ret)
                goto bad_inode;
 
-       init_waitqueue_head(&oi->i_wq);
        set_obj_created(oi);
 
        /* copy stuff from on-disk struct to in-memory struct */
@@ -947,14 +943,12 @@ struct inode *exofs_iget(struct super_block *sb, unsigned long ino)
        inode->i_blkbits = EXOFS_BLKSHIFT;
        inode->i_generation = le32_to_cpu(fcb.i_generation);
 
-#ifdef EXOFS_DEBUG_OBJ_ISIZE
-       if ((inode->i_size != sanity) &&
+       if ((inode->i_size != obj_size) &&
                (!exofs_inode_is_fast_symlink(inode))) {
-               EXOFS_ERR("WARNING: Size of object from inode and "
-                         "attributes differ (%lld != %llu)\n",
-                         inode->i_size, _LLU(sanity));
+               EXOFS_ERR("WARNING: Size of inode=%llu != object=%llu\n",
+                         inode->i_size, _LLU(obj_size));
+               /* FIXME: call exofs_inode_recovery() */
        }
-#endif
 
        oi->i_dir_start_lookup = 0;
 
@@ -1020,23 +1014,30 @@ int __exofs_wait_obj_created(struct exofs_i_info *oi)
  * set the obj_created flag so that other methods know that the object exists on
  * the OSD.
  */
-static void create_done(struct osd_request *or, void *p)
+static void create_done(struct exofs_io_state *ios, void *p)
 {
        struct inode *inode = p;
        struct exofs_i_info *oi = exofs_i(inode);
        struct exofs_sb_info *sbi = inode->i_sb->s_fs_info;
        int ret;
 
-       ret = exofs_check_ok(or);
-       osd_end_request(or);
+       ret = exofs_check_io(ios, NULL);
+       exofs_put_io_state(ios);
+
        atomic_dec(&sbi->s_curr_pending);
 
        if (unlikely(ret)) {
                EXOFS_ERR("object=0x%llx creation faild in pid=0x%llx",
-                         _LLU(sbi->s_pid), _LLU(inode->i_ino + EXOFS_OBJ_OFF));
-               make_bad_inode(inode);
-       } else
-               set_obj_created(oi);
+                         _LLU(exofs_oi_objno(oi)), _LLU(sbi->s_pid));
+               /*TODO: When FS is corrupted creation can fail, object already
+                * exist. Get rid of this asynchronous creation, if exist
+                * increment the obj counter and try the next object. Until we
+                * succeed. All these dangling objects will be made into lost
+                * files by chkfs.exofs
+                */
+       }
+
+       set_obj_created(oi);
 
        atomic_dec(&inode->i_count);
        wake_up(&oi->i_wq);
@@ -1051,8 +1052,7 @@ struct inode *exofs_new_inode(struct inode *dir, int mode)
        struct inode *inode;
        struct exofs_i_info *oi;
        struct exofs_sb_info *sbi;
-       struct osd_request *or;
-       struct osd_obj_id obj;
+       struct exofs_io_state *ios;
        int ret;
 
        sb = dir->i_sb;
@@ -1061,8 +1061,8 @@ struct inode *exofs_new_inode(struct inode *dir, int mode)
                return ERR_PTR(-ENOMEM);
 
        oi = exofs_i(inode);
+       __oi_init(oi);
 
-       init_waitqueue_head(&oi->i_wq);
        set_obj_2bcreated(oi);
 
        sbi = sb->s_fs_info;
@@ -1089,28 +1089,28 @@ struct inode *exofs_new_inode(struct inode *dir, int mode)
 
        mark_inode_dirty(inode);
 
-       obj.partition = sbi->s_pid;
-       obj.id = inode->i_ino + EXOFS_OBJ_OFF;
-       exofs_make_credential(oi->i_cred, &obj);
-
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("exofs_new_inode: osd_start_request failed\n");
-               return ERR_PTR(-ENOMEM);
+       ret = exofs_get_io_state(sbi, &ios);
+       if (unlikely(ret)) {
+               EXOFS_ERR("exofs_new_inode: exofs_get_io_state failed\n");
+               return ERR_PTR(ret);
        }
 
-       osd_req_create_object(or, &obj);
+       ios->obj.id = exofs_oi_objno(oi);
+       exofs_make_credential(oi->i_cred, &ios->obj);
 
        /* increment the refcount so that the inode will still be around when we
         * reach the callback
         */
        atomic_inc(&inode->i_count);
 
-       ret = exofs_async_op(or, create_done, inode, oi->i_cred);
+       ios->done = create_done;
+       ios->private = inode;
+       ios->cred = oi->i_cred;
+       ret = exofs_sbi_create(ios);
        if (ret) {
                atomic_dec(&inode->i_count);
-               osd_end_request(or);
-               return ERR_PTR(-EIO);
+               exofs_put_io_state(ios);
+               return ERR_PTR(ret);
        }
        atomic_inc(&sbi->s_curr_pending);
 
@@ -1128,11 +1128,11 @@ struct updatei_args {
 /*
  * Callback function from exofs_update_inode().
  */
-static void updatei_done(struct osd_request *or, void *p)
+static void updatei_done(struct exofs_io_state *ios, void *p)
 {
        struct updatei_args *args = p;
 
-       osd_end_request(or);
+       exofs_put_io_state(ios);
 
        atomic_dec(&args->sbi->s_curr_pending);
 
@@ -1148,8 +1148,7 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
        struct exofs_i_info *oi = exofs_i(inode);
        struct super_block *sb = inode->i_sb;
        struct exofs_sb_info *sbi = sb->s_fs_info;
-       struct osd_obj_id obj = {sbi->s_pid, inode->i_ino + EXOFS_OBJ_OFF};
-       struct osd_request *or;
+       struct exofs_io_state *ios;
        struct osd_attr attr;
        struct exofs_fcb *fcb;
        struct updatei_args *args;
@@ -1186,18 +1185,16 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
        } else
                memcpy(fcb->i_data, oi->i_data, sizeof(fcb->i_data));
 
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("exofs_update_inode: osd_start_request failed.\n");
-               ret = -ENOMEM;
+       ret = exofs_get_io_state(sbi, &ios);
+       if (unlikely(ret)) {
+               EXOFS_ERR("%s: exofs_get_io_state failed.\n", __func__);
                goto free_args;
        }
 
-       osd_req_set_attributes(or, &obj);
-
        attr = g_attr_inode_data;
        attr.val_ptr = fcb;
-       osd_req_add_set_attr_list(or, &attr, 1);
+       ios->out_attr_len = 1;
+       ios->out_attr = &attr;
 
        if (!obj_created(oi)) {
                EXOFS_DBGMSG("!obj_created\n");
@@ -1206,22 +1203,19 @@ static int exofs_update_inode(struct inode *inode, int do_sync)
                EXOFS_DBGMSG("wait_event done\n");
        }
 
-       if (do_sync) {
-               ret = exofs_sync_op(or, sbi->s_timeout, oi->i_cred);
-               osd_end_request(or);
-               goto free_args;
-       } else {
+       if (!do_sync) {
                args->sbi = sbi;
+               ios->done = updatei_done;
+               ios->private = args;
+       }
 
-               ret = exofs_async_op(or, updatei_done, args, oi->i_cred);
-               if (ret) {
-                       osd_end_request(or);
-                       goto free_args;
-               }
+       ret = exofs_oi_write(oi, ios);
+       if (!do_sync && !ret) {
                atomic_inc(&sbi->s_curr_pending);
                goto out; /* deallocation in updatei_done */
        }
 
+       exofs_put_io_state(ios);
 free_args:
        kfree(args);
 out:
@@ -1238,11 +1232,12 @@ int exofs_write_inode(struct inode *inode, int wait)
  * Callback function from exofs_delete_inode() - don't have much cleaning up to
  * do.
  */
-static void delete_done(struct osd_request *or, void *p)
+static void delete_done(struct exofs_io_state *ios, void *p)
 {
-       struct exofs_sb_info *sbi;
-       osd_end_request(or);
-       sbi = p;
+       struct exofs_sb_info *sbi = p;
+
+       exofs_put_io_state(ios);
+
        atomic_dec(&sbi->s_curr_pending);
 }
 
@@ -1256,8 +1251,7 @@ void exofs_delete_inode(struct inode *inode)
        struct exofs_i_info *oi = exofs_i(inode);
        struct super_block *sb = inode->i_sb;
        struct exofs_sb_info *sbi = sb->s_fs_info;
-       struct osd_obj_id obj = {sbi->s_pid, inode->i_ino + EXOFS_OBJ_OFF};
-       struct osd_request *or;
+       struct exofs_io_state *ios;
        int ret;
 
        truncate_inode_pages(&inode->i_data, 0);
@@ -1274,25 +1268,26 @@ void exofs_delete_inode(struct inode *inode)
 
        clear_inode(inode);
 
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("exofs_delete_inode: osd_start_request failed\n");
+       ret = exofs_get_io_state(sbi, &ios);
+       if (unlikely(ret)) {
+               EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__);
                return;
        }
 
-       osd_req_remove_object(or, &obj);
-
        /* if we are deleting an obj that hasn't been created yet, wait */
        if (!obj_created(oi)) {
                BUG_ON(!obj_2bcreated(oi));
                wait_event(oi->i_wq, obj_created(oi));
        }
 
-       ret = exofs_async_op(or, delete_done, sbi, oi->i_cred);
+       ios->obj.id = exofs_oi_objno(oi);
+       ios->done = delete_done;
+       ios->private = sbi;
+       ios->cred = oi->i_cred;
+       ret = exofs_sbi_remove(ios);
        if (ret) {
-               EXOFS_ERR(
-                      "ERROR: @exofs_delete_inode exofs_async_op failed\n");
-               osd_end_request(or);
+               EXOFS_ERR("%s: exofs_sbi_remove failed\n", __func__);
+               exofs_put_io_state(ios);
                return;
        }
        atomic_inc(&sbi->s_curr_pending);
diff --git a/fs/exofs/ios.c b/fs/exofs/ios.c
new file mode 100644 (file)
index 0000000..5bad01f
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2005, 2006
+ * Avishay Traeger (avishay@gmail.com)
+ * Copyright (C) 2008, 2009
+ * Boaz Harrosh <bharrosh@panasas.com>
+ *
+ * This file is part of exofs.
+ *
+ * exofs is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.  Since it is based on ext2, and the only
+ * valid version of GPL for the Linux kernel is version 2, the only valid
+ * version of GPL for exofs is version 2.
+ *
+ * exofs 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 exofs; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <scsi/scsi_device.h>
+
+#include "exofs.h"
+
+void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], const struct osd_obj_id *obj)
+{
+       osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
+}
+
+int exofs_read_kern(struct osd_dev *od, u8 *cred, struct osd_obj_id *obj,
+                   u64 offset, void *p, unsigned length)
+{
+       struct osd_request *or = osd_start_request(od, GFP_KERNEL);
+/*     struct osd_sense_info osi = {.key = 0};*/
+       int ret;
+
+       if (unlikely(!or)) {
+               EXOFS_DBGMSG("%s: osd_start_request failed.\n", __func__);
+               return -ENOMEM;
+       }
+       ret = osd_req_read_kern(or, obj, offset, p, length);
+       if (unlikely(ret)) {
+               EXOFS_DBGMSG("%s: osd_req_read_kern failed.\n", __func__);
+               goto out;
+       }
+
+       ret = osd_finalize_request(or, 0, cred, NULL);
+       if (unlikely(ret)) {
+               EXOFS_DBGMSG("Faild to osd_finalize_request() => %d\n", ret);
+               goto out;
+       }
+
+       ret = osd_execute_request(or);
+       if (unlikely(ret))
+               EXOFS_DBGMSG("osd_execute_request() => %d\n", ret);
+       /* osd_req_decode_sense(or, ret); */
+
+out:
+       osd_end_request(or);
+       return ret;
+}
+
+int exofs_get_io_state(struct exofs_sb_info *sbi, struct exofs_io_state** pios)
+{
+       struct exofs_io_state *ios;
+
+       /*TODO: Maybe use kmem_cach per sbi of size
+        * exofs_io_state_size(sbi->s_numdevs)
+        */
+       ios = kzalloc(exofs_io_state_size(sbi->s_numdevs), GFP_KERNEL);
+       if (unlikely(!ios)) {
+               *pios = NULL;
+               return -ENOMEM;
+       }
+
+       ios->sbi = sbi;
+       ios->obj.partition = sbi->s_pid;
+       *pios = ios;
+       return 0;
+}
+
+void exofs_put_io_state(struct exofs_io_state *ios)
+{
+       if (ios) {
+               unsigned i;
+
+               for (i = 0; i < ios->numdevs; i++) {
+                       struct exofs_per_dev_state *per_dev = &ios->per_dev[i];
+
+                       if (per_dev->or)
+                               osd_end_request(per_dev->or);
+                       if (per_dev->bio)
+                               bio_put(per_dev->bio);
+               }
+
+               kfree(ios);
+       }
+}
+
+static void _sync_done(struct exofs_io_state *ios, void *p)
+{
+       struct completion *waiting = p;
+
+       complete(waiting);
+}
+
+static void _last_io(struct kref *kref)
+{
+       struct exofs_io_state *ios = container_of(
+                                       kref, struct exofs_io_state, kref);
+
+       ios->done(ios, ios->private);
+}
+
+static void _done_io(struct osd_request *or, void *p)
+{
+       struct exofs_io_state *ios = p;
+
+       kref_put(&ios->kref, _last_io);
+}
+
+static int exofs_io_execute(struct exofs_io_state *ios)
+{
+       DECLARE_COMPLETION_ONSTACK(wait);
+       bool sync = (ios->done == NULL);
+       int i, ret;
+
+       if (sync) {
+               ios->done = _sync_done;
+               ios->private = &wait;
+       }
+
+       for (i = 0; i < ios->numdevs; i++) {
+               struct osd_request *or = ios->per_dev[i].or;
+               if (unlikely(!or))
+                       continue;
+
+               ret = osd_finalize_request(or, 0, ios->cred, NULL);
+               if (unlikely(ret)) {
+                       EXOFS_DBGMSG("Faild to osd_finalize_request() => %d\n",
+                                    ret);
+                       return ret;
+               }
+       }
+
+       kref_init(&ios->kref);
+
+       for (i = 0; i < ios->numdevs; i++) {
+               struct osd_request *or = ios->per_dev[i].or;
+               if (unlikely(!or))
+                       continue;
+
+               kref_get(&ios->kref);
+               osd_execute_request_async(or, _done_io, ios);
+       }
+
+       kref_put(&ios->kref, _last_io);
+       ret = 0;
+
+       if (sync) {
+               wait_for_completion(&wait);
+               ret = exofs_check_io(ios, NULL);
+       }
+       return ret;
+}
+
+int exofs_check_io(struct exofs_io_state *ios, u64 *resid)
+{
+       enum osd_err_priority acumulated_osd_err = 0;
+       int acumulated_lin_err = 0;
+       int i;
+
+       for (i = 0; i < ios->numdevs; i++) {
+               struct osd_sense_info osi;
+               int ret = osd_req_decode_sense(ios->per_dev[i].or, &osi);
+
+               if (likely(!ret))
+                       continue;
+
+               if (unlikely(ret == -EFAULT)) {
+                       EXOFS_DBGMSG("%s: EFAULT Need page clear\n", __func__);
+                       /*FIXME: All the pages in this device range should:
+                        *      clear_highpage(page);
+                        */
+               }
+
+               if (osi.osd_err_pri >= acumulated_osd_err) {
+                       acumulated_osd_err = osi.osd_err_pri;
+                       acumulated_lin_err = ret;
+               }
+       }
+
+       /* TODO: raid specific residual calculations */
+       if (resid) {
+               if (likely(!acumulated_lin_err))
+                       *resid = 0;
+               else
+                       *resid = ios->length;
+       }
+
+       return acumulated_lin_err;
+}
+
+int exofs_sbi_create(struct exofs_io_state *ios)
+{
+       int i, ret;
+
+       for (i = 0; i < ios->sbi->s_numdevs; i++) {
+               struct osd_request *or;
+
+               or = osd_start_request(ios->sbi->s_ods[i], GFP_KERNEL);
+               if (unlikely(!or)) {
+                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ios->per_dev[i].or = or;
+               ios->numdevs++;
+
+               osd_req_create_object(or, &ios->obj);
+       }
+       ret = exofs_io_execute(ios);
+
+out:
+       return ret;
+}
+
+int exofs_sbi_remove(struct exofs_io_state *ios)
+{
+       int i, ret;
+
+       for (i = 0; i < ios->sbi->s_numdevs; i++) {
+               struct osd_request *or;
+
+               or = osd_start_request(ios->sbi->s_ods[i], GFP_KERNEL);
+               if (unlikely(!or)) {
+                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ios->per_dev[i].or = or;
+               ios->numdevs++;
+
+               osd_req_remove_object(or, &ios->obj);
+       }
+       ret = exofs_io_execute(ios);
+
+out:
+       return ret;
+}
+
+int exofs_sbi_write(struct exofs_io_state *ios)
+{
+       int i, ret;
+
+       for (i = 0; i < ios->sbi->s_numdevs; i++) {
+               struct osd_request *or;
+
+               or = osd_start_request(ios->sbi->s_ods[i], GFP_KERNEL);
+               if (unlikely(!or)) {
+                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ios->per_dev[i].or = or;
+               ios->numdevs++;
+
+               if (ios->bio) {
+                       struct bio *bio;
+
+                       if (i != 0) {
+                               bio = bio_kmalloc(GFP_KERNEL,
+                                                 ios->bio->bi_max_vecs);
+                               if (unlikely(!bio)) {
+                                       ret = -ENOMEM;
+                                       goto out;
+                               }
+
+                               __bio_clone(bio, ios->bio);
+                               bio->bi_bdev = NULL;
+                               bio->bi_next = NULL;
+                               ios->per_dev[i].bio =  bio;
+                       } else {
+                               bio = ios->bio;
+                       }
+
+                       osd_req_write(or, &ios->obj, ios->offset, bio,
+                                     ios->length);
+/*                     EXOFS_DBGMSG("write sync=%d\n", sync);*/
+               } else if (ios->kern_buff) {
+                       osd_req_write_kern(or, &ios->obj, ios->offset,
+                                          ios->kern_buff, ios->length);
+/*                     EXOFS_DBGMSG("write_kern sync=%d\n", sync);*/
+               } else {
+                       osd_req_set_attributes(or, &ios->obj);
+/*                     EXOFS_DBGMSG("set_attributes sync=%d\n", sync);*/
+               }
+
+               if (ios->out_attr)
+                       osd_req_add_set_attr_list(or, ios->out_attr,
+                                                 ios->out_attr_len);
+
+               if (ios->in_attr)
+                       osd_req_add_get_attr_list(or, ios->in_attr,
+                                                 ios->in_attr_len);
+       }
+       ret = exofs_io_execute(ios);
+
+out:
+       return ret;
+}
+
+int exofs_sbi_read(struct exofs_io_state *ios)
+{
+       int i, ret;
+
+       for (i = 0; i < 1; i++) {
+               struct osd_request *or;
+               unsigned first_dev = (unsigned)ios->obj.id;
+
+               first_dev %= ios->sbi->s_numdevs;
+               or = osd_start_request(ios->sbi->s_ods[first_dev], GFP_KERNEL);
+               if (unlikely(!or)) {
+                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ios->per_dev[i].or = or;
+               ios->numdevs++;
+
+               if (ios->bio) {
+                       osd_req_read(or, &ios->obj, ios->offset, ios->bio,
+                                    ios->length);
+/*                     EXOFS_DBGMSG("read sync=%d\n", sync);*/
+               } else if (ios->kern_buff) {
+                       osd_req_read_kern(or, &ios->obj, ios->offset,
+                                          ios->kern_buff, ios->length);
+/*                     EXOFS_DBGMSG("read_kern sync=%d\n", sync);*/
+               } else {
+                       osd_req_get_attributes(or, &ios->obj);
+/*                     EXOFS_DBGMSG("get_attributes sync=%d\n", sync);*/
+               }
+
+               if (ios->out_attr)
+                       osd_req_add_set_attr_list(or, ios->out_attr,
+                                                 ios->out_attr_len);
+
+               if (ios->in_attr)
+                       osd_req_add_get_attr_list(or, ios->in_attr,
+                                                 ios->in_attr_len);
+       }
+       ret = exofs_io_execute(ios);
+
+out:
+       return ret;
+}
+
+int extract_attr_from_ios(struct exofs_io_state *ios, struct osd_attr *attr)
+{
+       struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */
+       void *iter = NULL;
+       int nelem;
+
+       do {
+               nelem = 1;
+               osd_req_decode_get_attr_list(ios->per_dev[0].or,
+                                            &cur_attr, &nelem, &iter);
+               if ((cur_attr.attr_page == attr->attr_page) &&
+                   (cur_attr.attr_id == attr->attr_id)) {
+                       attr->len = cur_attr.len;
+                       attr->val_ptr = cur_attr.val_ptr;
+                       return 0;
+               }
+       } while (iter);
+
+       return -EIO;
+}
+
+int exofs_oi_truncate(struct exofs_i_info *oi, u64 size)
+{
+       struct exofs_sb_info *sbi = oi->vfs_inode.i_sb->s_fs_info;
+       struct exofs_io_state *ios;
+       struct osd_attr attr;
+       __be64 newsize;
+       int i, ret;
+
+       if (exofs_get_io_state(sbi, &ios))
+               return -ENOMEM;
+
+       ios->obj.id = exofs_oi_objno(oi);
+       ios->cred = oi->i_cred;
+
+       newsize = cpu_to_be64(size);
+       attr = g_attr_logical_length;
+       attr.val_ptr = &newsize;
+
+       for (i = 0; i < sbi->s_numdevs; i++) {
+               struct osd_request *or;
+
+               or = osd_start_request(sbi->s_ods[i], GFP_KERNEL);
+               if (unlikely(!or)) {
+                       EXOFS_ERR("%s: osd_start_request failed\n", __func__);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               ios->per_dev[i].or = or;
+               ios->numdevs++;
+
+               osd_req_set_attributes(or, &ios->obj);
+               osd_req_add_set_attr_list(or, &attr, 1);
+       }
+       ret = exofs_io_execute(ios);
+
+out:
+       exofs_put_io_state(ios);
+       return ret;
+}
diff --git a/fs/exofs/osd.c b/fs/exofs/osd.c
deleted file mode 100644 (file)
index 4372542..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2005, 2006
- * Avishay Traeger (avishay@gmail.com)
- * Copyright (C) 2008, 2009
- * Boaz Harrosh <bharrosh@panasas.com>
- *
- * This file is part of exofs.
- *
- * exofs is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation.  Since it is based on ext2, and the only
- * valid version of GPL for the Linux kernel is version 2, the only valid
- * version of GPL for exofs is version 2.
- *
- * exofs 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 exofs; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <scsi/scsi_device.h>
-#include <scsi/osd_sense.h>
-
-#include "exofs.h"
-
-int exofs_check_ok_resid(struct osd_request *or, u64 *in_resid, u64 *out_resid)
-{
-       struct osd_sense_info osi;
-       int ret = osd_req_decode_sense(or, &osi);
-
-       if (ret) { /* translate to Linux codes */
-               if (osi.additional_code == scsi_invalid_field_in_cdb) {
-                       if (osi.cdb_field_offset == OSD_CFO_STARTING_BYTE)
-                               ret = -EFAULT;
-                       if (osi.cdb_field_offset == OSD_CFO_OBJECT_ID)
-                               ret = -ENOENT;
-                       else
-                               ret = -EINVAL;
-               } else if (osi.additional_code == osd_quota_error)
-                       ret = -ENOSPC;
-               else
-                       ret = -EIO;
-       }
-
-       /* FIXME: should be include in osd_sense_info */
-       if (in_resid)
-               *in_resid = or->in.req ? or->in.req->resid_len : 0;
-
-       if (out_resid)
-               *out_resid = or->out.req ? or->out.req->resid_len : 0;
-
-       return ret;
-}
-
-void exofs_make_credential(u8 cred_a[OSD_CAP_LEN], const struct osd_obj_id *obj)
-{
-       osd_sec_init_nosec_doall_caps(cred_a, obj, false, true);
-}
-
-/*
- * Perform a synchronous OSD operation.
- */
-int exofs_sync_op(struct osd_request *or, int timeout, uint8_t *credential)
-{
-       int ret;
-
-       or->timeout = timeout;
-       ret = osd_finalize_request(or, 0, credential, NULL);
-       if (ret) {
-               EXOFS_DBGMSG("Faild to osd_finalize_request() => %d\n", ret);
-               return ret;
-       }
-
-       ret = osd_execute_request(or);
-
-       if (ret)
-               EXOFS_DBGMSG("osd_execute_request() => %d\n", ret);
-       /* osd_req_decode_sense(or, ret); */
-       return ret;
-}
-
-/*
- * Perform an asynchronous OSD operation.
- */
-int exofs_async_op(struct osd_request *or, osd_req_done_fn *async_done,
-                  void *caller_context, u8 *cred)
-{
-       int ret;
-
-       ret = osd_finalize_request(or, 0, cred, NULL);
-       if (ret) {
-               EXOFS_DBGMSG("Faild to osd_finalize_request() => %d\n", ret);
-               return ret;
-       }
-
-       ret = osd_execute_request_async(or, async_done, caller_context);
-
-       if (ret)
-               EXOFS_DBGMSG("osd_execute_request_async() => %d\n", ret);
-       return ret;
-}
-
-int extract_attr_from_req(struct osd_request *or, struct osd_attr *attr)
-{
-       struct osd_attr cur_attr = {.attr_page = 0}; /* start with zeros */
-       void *iter = NULL;
-       int nelem;
-
-       do {
-               nelem = 1;
-               osd_req_decode_get_attr_list(or, &cur_attr, &nelem, &iter);
-               if ((cur_attr.attr_page == attr->attr_page) &&
-                   (cur_attr.attr_id == attr->attr_id)) {
-                       attr->len = cur_attr.len;
-                       attr->val_ptr = cur_attr.val_ptr;
-                       return 0;
-               }
-       } while (iter);
-
-       return -EIO;
-}
diff --git a/fs/exofs/pnfs.h b/fs/exofs/pnfs.h
new file mode 100644 (file)
index 0000000..423033a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008, 2009
+ * Boaz Harrosh <bharrosh@panasas.com>
+ *
+ * This file is part of exofs.
+ *
+ * exofs is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License  version 2 as published by the Free
+ * Software Foundation.
+ *
+ */
+
+/* FIXME: Remove this file once pnfs hits mainline */
+
+#ifndef __EXOFS_PNFS_H__
+#define __EXOFS_PNFS_H__
+
+#if defined(CONFIG_PNFS)
+
+
+/* FIXME: move this file to: linux/exportfs/pnfs_osd_xdr.h */
+#include "../nfs/objlayout/pnfs_osd_xdr.h"
+
+#else /* defined(CONFIG_PNFS) */
+
+enum pnfs_iomode {
+       IOMODE_READ = 1,
+       IOMODE_RW = 2,
+       IOMODE_ANY = 3,
+};
+
+/* Layout Structure */
+enum pnfs_osd_raid_algorithm4 {
+       PNFS_OSD_RAID_0         = 1,
+       PNFS_OSD_RAID_4         = 2,
+       PNFS_OSD_RAID_5         = 3,
+       PNFS_OSD_RAID_PQ        = 4     /* Reed-Solomon P+Q */
+};
+
+struct pnfs_osd_data_map {
+       u32     odm_num_comps;
+       u64     odm_stripe_unit;
+       u32     odm_group_width;
+       u32     odm_group_depth;
+       u32     odm_mirror_cnt;
+       u32     odm_raid_algorithm;
+};
+
+#endif /* else defined(CONFIG_PNFS) */
+
+#endif /* __EXOFS_PNFS_H__ */
index 9f500de..a1d1e77 100644 (file)
@@ -203,49 +203,45 @@ int exofs_sync_fs(struct super_block *sb, int wait)
 {
        struct exofs_sb_info *sbi;
        struct exofs_fscb *fscb;
-       struct osd_request *or;
-       struct osd_obj_id obj;
+       struct exofs_io_state *ios;
        int ret = -ENOMEM;
 
-       fscb = kzalloc(sizeof(struct exofs_fscb), GFP_KERNEL);
-       if (!fscb) {
-               EXOFS_ERR("exofs_write_super: memory allocation failed.\n");
-               return -ENOMEM;
-       }
-
        lock_super(sb);
        sbi = sb->s_fs_info;
+       fscb = &sbi->s_fscb;
+
+       ret = exofs_get_io_state(sbi, &ios);
+       if (ret)
+               goto out;
+
+       /* Note: We only write the changing part of the fscb. .i.e upto the
+        *       the fscb->s_dev_table_oid member. There is no read-modify-write
+        *       here.
+        */
+       ios->length = offsetof(struct exofs_fscb, s_dev_table_oid);
+       memset(fscb, 0, ios->length);
        fscb->s_nextid = cpu_to_le64(sbi->s_nextid);
        fscb->s_numfiles = cpu_to_le32(sbi->s_numfiles);
        fscb->s_magic = cpu_to_le16(sb->s_magic);
        fscb->s_newfs = 0;
+       fscb->s_version = EXOFS_FSCB_VER;
 
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_ERR("exofs_write_super: osd_start_request failed.\n");
-               goto out;
-       }
+       ios->obj.id = EXOFS_SUPER_ID;
+       ios->offset = 0;
+       ios->kern_buff = fscb;
+       ios->cred = sbi->s_cred;
 
-       obj.partition = sbi->s_pid;
-       obj.id = EXOFS_SUPER_ID;
-       ret = osd_req_write_kern(or, &obj, 0, fscb, sizeof(*fscb));
+       ret = exofs_sbi_write(ios);
        if (unlikely(ret)) {
-               EXOFS_ERR("exofs_write_super: osd_req_write_kern failed.\n");
-               goto out;
-       }
-
-       ret = exofs_sync_op(or, sbi->s_timeout, sbi->s_cred);
-       if (unlikely(ret)) {
-               EXOFS_ERR("exofs_write_super: exofs_sync_op failed.\n");
+               EXOFS_ERR("%s: exofs_sbi_write failed.\n", __func__);
                goto out;
        }
        sb->s_dirt = 0;
 
 out:
-       if (or)
-               osd_end_request(or);
+       EXOFS_DBGMSG("s_nextid=0x%llx ret=%d\n", _LLU(sbi->s_nextid), ret);
+       exofs_put_io_state(ios);
        unlock_super(sb);
-       kfree(fscb);
        return ret;
 }
 
@@ -257,6 +253,29 @@ static void exofs_write_super(struct super_block *sb)
                sb->s_dirt = 0;
 }
 
+static void _exofs_print_device(const char *msg, const char *dev_path,
+                               struct osd_dev *od, u64 pid)
+{
+       const struct osd_dev_info *odi = osduld_device_info(od);
+
+       printk(KERN_NOTICE "exofs: %s %s osd_name-%s pid-0x%llx\n",
+               msg, dev_path ?: "", odi->osdname, _LLU(pid));
+}
+
+void exofs_free_sbi(struct exofs_sb_info *sbi)
+{
+       while (sbi->s_numdevs) {
+               int i = --sbi->s_numdevs;
+               struct osd_dev *od = sbi->s_ods[i];
+
+               if (od) {
+                       sbi->s_ods[i] = NULL;
+                       osduld_put_device(od);
+               }
+       }
+       kfree(sbi);
+}
+
 /*
  * This function is called when the vfs is freeing the superblock.  We just
  * need to free our own part.
@@ -279,11 +298,182 @@ static void exofs_put_super(struct super_block *sb)
                                  msecs_to_jiffies(100));
        }
 
-       osduld_put_device(sbi->s_dev);
-       kfree(sb->s_fs_info);
+       _exofs_print_device("Unmounting", NULL, sbi->s_ods[0], sbi->s_pid);
+
+       exofs_free_sbi(sbi);
        sb->s_fs_info = NULL;
 }
 
+static int _read_and_match_data_map(struct exofs_sb_info *sbi, unsigned numdevs,
+                                   struct exofs_device_table *dt)
+{
+       sbi->data_map.odm_num_comps   =
+                               le32_to_cpu(dt->dt_data_map.cb_num_comps);
+       sbi->data_map.odm_stripe_unit =
+                               le64_to_cpu(dt->dt_data_map.cb_stripe_unit);
+       sbi->data_map.odm_group_width =
+                               le32_to_cpu(dt->dt_data_map.cb_group_width);
+       sbi->data_map.odm_group_depth =
+                               le32_to_cpu(dt->dt_data_map.cb_group_depth);
+       sbi->data_map.odm_mirror_cnt  =
+                               le32_to_cpu(dt->dt_data_map.cb_mirror_cnt);
+       sbi->data_map.odm_raid_algorithm  =
+                               le32_to_cpu(dt->dt_data_map.cb_raid_algorithm);
+
+/* FIXME: Hard coded mirror only for now. if not so do not mount */
+       if ((sbi->data_map.odm_num_comps != numdevs) ||
+           (sbi->data_map.odm_stripe_unit != EXOFS_BLKSIZE) ||
+           (sbi->data_map.odm_raid_algorithm != PNFS_OSD_RAID_0) ||
+           (sbi->data_map.odm_mirror_cnt != (numdevs - 1)))
+               return -EINVAL;
+       else
+               return 0;
+}
+
+/* @odi is valid only as long as @fscb_dev is valid */
+static int exofs_devs_2_odi(struct exofs_dt_device_info *dt_dev,
+                            struct osd_dev_info *odi)
+{
+       odi->systemid_len = le32_to_cpu(dt_dev->systemid_len);
+       memcpy(odi->systemid, dt_dev->systemid, odi->systemid_len);
+
+       odi->osdname_len = le32_to_cpu(dt_dev->osdname_len);
+       odi->osdname = dt_dev->osdname;
+
+       /* FIXME support long names. Will need a _put function */
+       if (dt_dev->long_name_offset)
+               return -EINVAL;
+
+       /* Make sure osdname is printable!
+        * mkexofs should give us space for a null-terminator else the
+        * device-table is invalid.
+        */
+       if (unlikely(odi->osdname_len >= sizeof(dt_dev->osdname)))
+               odi->osdname_len = sizeof(dt_dev->osdname) - 1;
+       dt_dev->osdname[odi->osdname_len] = 0;
+
+       /* If it's all zeros something is bad we read past end-of-obj */
+       return !(odi->systemid_len || odi->osdname_len);
+}
+
+static int exofs_read_lookup_dev_table(struct exofs_sb_info **psbi,
+                                      unsigned table_count)
+{
+       struct exofs_sb_info *sbi = *psbi;
+       struct osd_dev *fscb_od;
+       struct osd_obj_id obj = {.partition = sbi->s_pid,
+                                .id = EXOFS_DEVTABLE_ID};
+       struct exofs_device_table *dt;
+       unsigned table_bytes = table_count * sizeof(dt->dt_dev_table[0]) +
+                                            sizeof(*dt);
+       unsigned numdevs, i;
+       int ret;
+
+       dt = kmalloc(table_bytes, GFP_KERNEL);
+       if (unlikely(!dt)) {
+               EXOFS_ERR("ERROR: allocating %x bytes for device table\n",
+                         table_bytes);
+               return -ENOMEM;
+       }
+
+       fscb_od = sbi->s_ods[0];
+       sbi->s_ods[0] = NULL;
+       sbi->s_numdevs = 0;
+       ret = exofs_read_kern(fscb_od, sbi->s_cred, &obj, 0, dt, table_bytes);
+       if (unlikely(ret)) {
+               EXOFS_ERR("ERROR: reading device table\n");
+               goto out;
+       }
+
+       numdevs = le64_to_cpu(dt->dt_num_devices);
+       if (unlikely(!numdevs)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       WARN_ON(table_count != numdevs);
+
+       ret = _read_and_match_data_map(sbi, numdevs, dt);
+       if (unlikely(ret))
+               goto out;
+
+       if (likely(numdevs > 1)) {
+               unsigned size = numdevs * sizeof(sbi->s_ods[0]);
+
+               sbi = krealloc(sbi, sizeof(*sbi) + size, GFP_KERNEL);
+               if (unlikely(!sbi)) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               memset(&sbi->s_ods[1], 0, size - sizeof(sbi->s_ods[0]));
+               *psbi = sbi;
+       }
+
+       for (i = 0; i < numdevs; i++) {
+               struct exofs_fscb fscb;
+               struct osd_dev_info odi;
+               struct osd_dev *od;
+
+               if (exofs_devs_2_odi(&dt->dt_dev_table[i], &odi)) {
+                       EXOFS_ERR("ERROR: Read all-zeros device entry\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               printk(KERN_NOTICE "Add device[%d]: osd_name-%s\n",
+                      i, odi.osdname);
+
+               /* On all devices the device table is identical. The user can
+                * specify any one of the participating devices on the command
+                * line. We always keep them in device-table order.
+                */
+               if (fscb_od && osduld_device_same(fscb_od, &odi)) {
+                       sbi->s_ods[i] = fscb_od;
+                       ++sbi->s_numdevs;
+                       fscb_od = NULL;
+                       continue;
+               }
+
+               od = osduld_info_lookup(&odi);
+               if (unlikely(IS_ERR(od))) {
+                       ret = PTR_ERR(od);
+                       EXOFS_ERR("ERROR: device requested is not found "
+                                 "osd_name-%s =>%d\n", odi.osdname, ret);
+                       goto out;
+               }
+
+               sbi->s_ods[i] = od;
+               ++sbi->s_numdevs;
+
+               /* Read the fscb of the other devices to make sure the FS
+                * partition is there.
+                */
+               ret = exofs_read_kern(od, sbi->s_cred, &obj, 0, &fscb,
+                                     sizeof(fscb));
+               if (unlikely(ret)) {
+                       EXOFS_ERR("ERROR: Malformed participating device "
+                                 "error reading fscb osd_name-%s\n",
+                                 odi.osdname);
+                       goto out;
+               }
+
+               /* TODO: verify other information is correct and FS-uuid
+                *       matches. Benny what did you say about device table
+                *       generation and old devices?
+                */
+       }
+
+out:
+       kfree(dt);
+       if (unlikely(!ret && fscb_od)) {
+               EXOFS_ERR(
+                     "ERROR: Bad device-table container device not present\n");
+               osduld_put_device(fscb_od);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 /*
  * Read the superblock from the OSD and fill in the fields
  */
@@ -292,24 +482,25 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
        struct inode *root;
        struct exofs_mountopt *opts = data;
        struct exofs_sb_info *sbi;      /*extended info                  */
+       struct osd_dev *od;             /* Master device                 */
        struct exofs_fscb fscb;         /*on-disk superblock info        */
-       struct osd_request *or = NULL;
        struct osd_obj_id obj;
+       unsigned table_count;
        int ret;
 
        sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
        if (!sbi)
                return -ENOMEM;
-       sb->s_fs_info = sbi;
 
        /* use mount options to fill superblock */
-       sbi->s_dev = osduld_path_lookup(opts->dev_name);
-       if (IS_ERR(sbi->s_dev)) {
-               ret = PTR_ERR(sbi->s_dev);
-               sbi->s_dev = NULL;
+       od = osduld_path_lookup(opts->dev_name);
+       if (IS_ERR(od)) {
+               ret = PTR_ERR(od);
                goto free_sbi;
        }
 
+       sbi->s_ods[0] = od;
+       sbi->s_numdevs = 1;
        sbi->s_pid = opts->pid;
        sbi->s_timeout = opts->timeout;
 
@@ -323,35 +514,13 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_bdev = NULL;
        sb->s_dev = 0;
 
-       /* read data from on-disk superblock object */
        obj.partition = sbi->s_pid;
        obj.id = EXOFS_SUPER_ID;
        exofs_make_credential(sbi->s_cred, &obj);
 
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               if (!silent)
-                       EXOFS_ERR(
-                              "exofs_fill_super: osd_start_request failed.\n");
-               ret = -ENOMEM;
-               goto free_sbi;
-       }
-       ret = osd_req_read_kern(or, &obj, 0, &fscb, sizeof(fscb));
-       if (unlikely(ret)) {
-               if (!silent)
-                       EXOFS_ERR(
-                              "exofs_fill_super: osd_req_read_kern failed.\n");
-               ret = -ENOMEM;
-               goto free_sbi;
-       }
-
-       ret = exofs_sync_op(or, sbi->s_timeout, sbi->s_cred);
-       if (unlikely(ret)) {
-               if (!silent)
-                       EXOFS_ERR("exofs_fill_super: exofs_sync_op failed.\n");
-               ret = -EIO;
+       ret = exofs_read_kern(od, sbi->s_cred, &obj, 0, &fscb, sizeof(fscb));
+       if (unlikely(ret))
                goto free_sbi;
-       }
 
        sb->s_magic = le16_to_cpu(fscb.s_magic);
        sbi->s_nextid = le64_to_cpu(fscb.s_nextid);
@@ -364,12 +533,26 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
                ret = -EINVAL;
                goto free_sbi;
        }
+       if (le32_to_cpu(fscb.s_version) != EXOFS_FSCB_VER) {
+               EXOFS_ERR("ERROR: Bad FSCB version expected-%d got-%d\n",
+                         EXOFS_FSCB_VER, le32_to_cpu(fscb.s_version));
+               ret = -EINVAL;
+               goto free_sbi;
+       }
 
        /* start generation numbers from a random point */
        get_random_bytes(&sbi->s_next_generation, sizeof(u32));
        spin_lock_init(&sbi->s_next_gen_lock);
 
+       table_count = le64_to_cpu(fscb.s_dev_table_count);
+       if (table_count) {
+               ret = exofs_read_lookup_dev_table(&sbi, table_count);
+               if (unlikely(ret))
+                       goto free_sbi;
+       }
+
        /* set up operation vectors */
+       sb->s_fs_info = sbi;
        sb->s_op = &exofs_sops;
        sb->s_export_op = &exofs_export_ops;
        root = exofs_iget(sb, EXOFS_ROOT_ID - EXOFS_OBJ_OFF);
@@ -395,16 +578,15 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
                goto free_sbi;
        }
 
-       ret = 0;
-out:
-       if (or)
-               osd_end_request(or);
-       return ret;
+       _exofs_print_device("Mounting", opts->dev_name, sbi->s_ods[0],
+                           sbi->s_pid);
+       return 0;
 
 free_sbi:
-       osduld_put_device(sbi->s_dev); /* NULL safe */
-       kfree(sbi);
-       goto out;
+       EXOFS_ERR("Unable to mount exofs on %s pid=0x%llx err=%d\n",
+                 opts->dev_name, sbi->s_pid, ret);
+       exofs_free_sbi(sbi);
+       return ret;
 }
 
 /*
@@ -433,7 +615,7 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
        struct super_block *sb = dentry->d_sb;
        struct exofs_sb_info *sbi = sb->s_fs_info;
-       struct osd_obj_id obj = {sbi->s_pid, 0};
+       struct exofs_io_state *ios;
        struct osd_attr attrs[] = {
                ATTR_DEF(OSD_APAGE_PARTITION_QUOTAS,
                        OSD_ATTR_PQ_CAPACITY_QUOTA, sizeof(__be64)),
@@ -442,32 +624,33 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf)
        };
        uint64_t capacity = ULLONG_MAX;
        uint64_t used = ULLONG_MAX;
-       struct osd_request *or;
        uint8_t cred_a[OSD_CAP_LEN];
        int ret;
 
-       /* get used/capacity attributes */
-       exofs_make_credential(cred_a, &obj);
-
-       or = osd_start_request(sbi->s_dev, GFP_KERNEL);
-       if (unlikely(!or)) {
-               EXOFS_DBGMSG("exofs_statfs: osd_start_request failed.\n");
-               return -ENOMEM;
+       ret = exofs_get_io_state(sbi, &ios);
+       if (ret) {
+               EXOFS_DBGMSG("exofs_get_io_state failed.\n");
+               return ret;
        }
 
-       osd_req_get_attributes(or, &obj);
-       osd_req_add_get_attr_list(or, attrs, ARRAY_SIZE(attrs));
-       ret = exofs_sync_op(or, sbi->s_timeout, cred_a);
+       exofs_make_credential(cred_a, &ios->obj);
+       ios->cred = sbi->s_cred;
+       ios->in_attr = attrs;
+       ios->in_attr_len = ARRAY_SIZE(attrs);
+
+       ret = exofs_sbi_read(ios);
        if (unlikely(ret))
                goto out;
 
-       ret = extract_attr_from_req(or, &attrs[0]);
-       if (likely(!ret))
+       ret = extract_attr_from_ios(ios, &attrs[0]);
+       if (likely(!ret)) {
                capacity = get_unaligned_be64(attrs[0].val_ptr);
-       else
+               if (unlikely(!capacity))
+                       capacity = ULLONG_MAX;
+       } else
                EXOFS_DBGMSG("exofs_statfs: get capacity failed.\n");
 
-       ret = extract_attr_from_req(or, &attrs[1]);
+       ret = extract_attr_from_ios(ios, &attrs[1]);
        if (likely(!ret))
                used = get_unaligned_be64(attrs[1].val_ptr);
        else
@@ -476,15 +659,15 @@ static int exofs_statfs(struct dentry *dentry, struct kstatfs *buf)
        /* fill in the stats buffer */
        buf->f_type = EXOFS_SUPER_MAGIC;
        buf->f_bsize = EXOFS_BLKSIZE;
-       buf->f_blocks = (capacity >> EXOFS_BLKSHIFT);
-       buf->f_bfree = ((capacity - used) >> EXOFS_BLKSHIFT);
+       buf->f_blocks = capacity >> 9;
+       buf->f_bfree = (capacity - used) >> 9;
        buf->f_bavail = buf->f_bfree;
        buf->f_files = sbi->s_numfiles;
        buf->f_ffree = EXOFS_MAX_ID - sbi->s_numfiles;
        buf->f_namelen = EXOFS_NAME_LEN;
 
 out:
-       osd_end_request(or);
+       exofs_put_io_state(ios);
        return ret;
 }
 
index 354ed3b..2db9577 100644 (file)
@@ -2033,7 +2033,7 @@ static Indirect *ext3_find_shared(struct inode *inode, int depth,
        int k, err;
 
        *top = 0;
-       /* Make k index the deepest non-null offest + 1 */
+       /* Make k index the deepest non-null offset + 1 */
        for (k = depth; k > 1 && !offsets[k-1]; k--)
                ;
        partial = ext3_get_branch(inode, k, offsets, chain, &err);
index 9f2d45d..9acf7e8 100644 (file)
@@ -26,6 +26,16 @@ config EXT4_FS
 
          If unsure, say N.
 
+config EXT4_USE_FOR_EXT23
+       bool "Use ext4 for ext2/ext3 file systems"
+       depends on EXT3_FS=n || EXT2_FS=n
+       default y
+       help
+         Allow the ext4 file system driver code to be used for ext2 or
+         ext3 file system mounts.  This allows users to reduce their
+         compiled kernel size by using one file system driver for
+         ext2, ext3, and ext4 file systems.
+
 config EXT4_FS_XATTR
        bool "Ext4 extended attributes"
        depends on EXT4_FS
index 1d04189..22bc743 100644 (file)
@@ -498,44 +498,6 @@ error_return:
        return;
 }
 
-/**
- * ext4_free_blocks() -- Free given blocks and update quota
- * @handle:            handle for this transaction
- * @inode:             inode
- * @block:             start physical block to free
- * @count:             number of blocks to count
- * @metadata:          Are these metadata blocks
- */
-void ext4_free_blocks(handle_t *handle, struct inode *inode,
-                       ext4_fsblk_t block, unsigned long count,
-                       int metadata)
-{
-       struct super_block *sb;
-       unsigned long dquot_freed_blocks;
-
-       /* this isn't the right place to decide whether block is metadata
-        * inode.c/extents.c knows better, but for safety ... */
-       if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-               metadata = 1;
-
-       /* We need to make sure we don't reuse
-        * block released untill the transaction commit.
-        * writeback mode have weak data consistency so
-        * don't force data as metadata when freeing block
-        * for writeback mode.
-        */
-       if (metadata == 0 && !ext4_should_writeback_data(inode))
-               metadata = 1;
-
-       sb = inode->i_sb;
-
-       ext4_mb_free_blocks(handle, inode, block, count,
-                           metadata, &dquot_freed_blocks);
-       if (dquot_freed_blocks)
-               vfs_dq_free_block(inode, dquot_freed_blocks);
-       return;
-}
-
 /**
  * ext4_has_free_blocks()
  * @sbi:       in-core super block structure.
@@ -761,7 +723,13 @@ static unsigned long ext4_bg_num_gdb_meta(struct super_block *sb,
 static unsigned long ext4_bg_num_gdb_nometa(struct super_block *sb,
                                        ext4_group_t group)
 {
-       return ext4_bg_has_super(sb, group) ? EXT4_SB(sb)->s_gdb_count : 0;
+       if (!ext4_bg_has_super(sb, group))
+               return 0;
+
+       if (EXT4_HAS_INCOMPAT_FEATURE(sb,EXT4_FEATURE_INCOMPAT_META_BG))
+               return le32_to_cpu(EXT4_SB(sb)->s_es->s_first_meta_bg);
+       else
+               return EXT4_SB(sb)->s_gdb_count;
 }
 
 /**
index 50784ef..4df8621 100644 (file)
@@ -160,7 +160,7 @@ int ext4_setup_system_zone(struct super_block *sb)
                if (ext4_bg_has_super(sb, i) &&
                    ((i < 5) || ((i % flex_size) == 0)))
                        add_system_zone(sbi, ext4_group_first_block_no(sb, i),
-                                       sbi->s_gdb_count + 1);
+                                       ext4_bg_num_gdb(sb, i) + 1);
                gdp = ext4_get_group_desc(sb, i, NULL);
                ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1);
                if (ret)
@@ -228,6 +228,7 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk,
        struct rb_node *n = sbi->system_blks.rb_node;
 
        if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
+           (start_blk + count < start_blk) ||
            (start_blk + count > ext4_blocks_count(sbi->s_es)))
                return 0;
        while (n) {
index 8825515..ab31e65 100644 (file)
@@ -375,6 +375,12 @@ struct ext4_new_group_data {
 #define EXT4_GET_BLOCKS_DIO_CONVERT_EXT                (EXT4_GET_BLOCKS_CONVERT|\
                                         EXT4_GET_BLOCKS_DIO_CREATE_EXT)
 
+/*
+ * Flags used by ext4_free_blocks
+ */
+#define EXT4_FREE_BLOCKS_METADATA      0x0001
+#define EXT4_FREE_BLOCKS_FORGET                0x0002
+
 /*
  * ioctl commands
  */
@@ -703,6 +709,13 @@ struct ext4_inode_info {
        struct list_head i_aio_dio_complete_list;
        /* current io_end structure for async DIO write*/
        ext4_io_end_t *cur_aio_dio;
+
+       /*
+        * Transactions that contain inode's metadata needed to complete
+        * fsync and fdatasync, respectively.
+        */
+       tid_t i_sync_tid;
+       tid_t i_datasync_tid;
 };
 
 /*
@@ -750,6 +763,7 @@ struct ext4_inode_info {
 #define EXT4_MOUNT_DELALLOC            0x8000000 /* Delalloc support */
 #define EXT4_MOUNT_DATA_ERR_ABORT      0x10000000 /* Abort on file data write */
 #define EXT4_MOUNT_BLOCK_VALIDITY      0x20000000 /* Block validity checking */
+#define EXT4_MOUNT_DISCARD             0x40000000 /* Issue DISCARD requests */
 
 #define clear_opt(o, opt)              o &= ~EXT4_MOUNT_##opt
 #define set_opt(o, opt)                        o |= EXT4_MOUNT_##opt
@@ -1324,8 +1338,6 @@ extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
                        ext4_fsblk_t goal, unsigned long *count, int *errp);
 extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks);
 extern int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks);
-extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
-                       ext4_fsblk_t block, unsigned long count, int metadata);
 extern void ext4_add_groupblocks(handle_t *handle, struct super_block *sb,
                                ext4_fsblk_t block, unsigned long count);
 extern ext4_fsblk_t ext4_count_free_blocks(struct super_block *);
@@ -1384,16 +1396,15 @@ extern int ext4_mb_reserve_blocks(struct super_block *, int);
 extern void ext4_discard_preallocations(struct inode *);
 extern int __init init_ext4_mballoc(void);
 extern void exit_ext4_mballoc(void);
-extern void ext4_mb_free_blocks(handle_t *, struct inode *,
-               ext4_fsblk_t, unsigned long, int, unsigned long *);
+extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
+                            struct buffer_head *bh, ext4_fsblk_t block,
+                            unsigned long count, int flags);
 extern int ext4_mb_add_groupinfo(struct super_block *sb,
                ext4_group_t i, struct ext4_group_desc *desc);
 extern int ext4_mb_get_buddy_cache_lock(struct super_block *, ext4_group_t);
 extern void ext4_mb_put_buddy_cache_lock(struct super_block *,
                                                ext4_group_t, int);
 /* inode.c */
-int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode,
-               struct buffer_head *bh, ext4_fsblk_t blocknr);
 struct buffer_head *ext4_getblk(handle_t *, struct inode *,
                                                ext4_lblk_t, int, int *);
 struct buffer_head *ext4_bread(handle_t *, struct inode *,
index 6a94099..b57e5c7 100644 (file)
@@ -4,6 +4,8 @@
 
 #include "ext4_jbd2.h"
 
+#include <trace/events/ext4.h>
+
 int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
                                struct buffer_head *bh)
 {
@@ -32,35 +34,69 @@ int __ext4_journal_get_write_access(const char *where, handle_t *handle,
        return err;
 }
 
-int __ext4_journal_forget(const char *where, handle_t *handle,
-                               struct buffer_head *bh)
+/*
+ * The ext4 forget function must perform a revoke if we are freeing data
+ * which has been journaled.  Metadata (eg. indirect blocks) must be
+ * revoked in all cases.
+ *
+ * "bh" may be NULL: a metadata block may have been freed from memory
+ * but there may still be a record of it in the journal, and that record
+ * still needs to be revoked.
+ *
+ * If the handle isn't valid we're not journaling, but we still need to
+ * call into ext4_journal_revoke() to put the buffer head.
+ */
+int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
+                 struct inode *inode, struct buffer_head *bh,
+                 ext4_fsblk_t blocknr)
 {
-       int err = 0;
+       int err;
 
-       if (ext4_handle_valid(handle)) {
-               err = jbd2_journal_forget(handle, bh);
-               if (err)
-                       ext4_journal_abort_handle(where, __func__, bh,
-                                                 handle, err);
-       }
-       else
+       might_sleep();
+
+       trace_ext4_forget(inode, is_metadata, blocknr);
+       BUFFER_TRACE(bh, "enter");
+
+       jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
+                 "data mode %x\n",
+                 bh, is_metadata, inode->i_mode,
+                 test_opt(inode->i_sb, DATA_FLAGS));
+
+       /* In the no journal case, we can just do a bforget and return */
+       if (!ext4_handle_valid(handle)) {
                bforget(bh);
-       return err;
-}
+               return 0;
+       }
 
-int __ext4_journal_revoke(const char *where, handle_t *handle,
-                               ext4_fsblk_t blocknr, struct buffer_head *bh)
-{
-       int err = 0;
+       /* Never use the revoke function if we are doing full data
+        * journaling: there is no need to, and a V1 superblock won't
+        * support it.  Otherwise, only skip the revoke on un-journaled
+        * data blocks. */
 
-       if (ext4_handle_valid(handle)) {
-               err = jbd2_journal_revoke(handle, blocknr, bh);
-               if (err)
-                       ext4_journal_abort_handle(where, __func__, bh,
-                                                 handle, err);
+       if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
+           (!is_metadata && !ext4_should_journal_data(inode))) {
+               if (bh) {
+                       BUFFER_TRACE(bh, "call jbd2_journal_forget");
+                       err = jbd2_journal_forget(handle, bh);
+                       if (err)
+                               ext4_journal_abort_handle(where, __func__, bh,
+                                                         handle, err);
+                       return err;
+               }
+               return 0;
        }
-       else
-               bforget(bh);
+
+       /*
+        * data!=journal && (is_metadata || should_journal_data(inode))
+        */
+       BUFFER_TRACE(bh, "call jbd2_journal_revoke");
+       err = jbd2_journal_revoke(handle, blocknr, bh);
+       if (err) {
+               ext4_journal_abort_handle(where, __func__, bh, handle, err);
+               ext4_abort(inode->i_sb, __func__,
+                          "error %d when attempting revoke", err);
+       }
+       BUFFER_TRACE(bh, "exit");
        return err;
 }
 
index a286598..05eca81 100644 (file)
@@ -49,7 +49,7 @@
 
 #define EXT4_DATA_TRANS_BLOCKS(sb)     (EXT4_SINGLEDATA_TRANS_BLOCKS(sb) + \
                                         EXT4_XATTR_TRANS_BLOCKS - 2 + \
-                                        2*EXT4_QUOTA_TRANS_BLOCKS(sb))
+                                        EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))
 
 /*
  * Define the number of metadata blocks we need to account to modify data.
@@ -57,7 +57,7 @@
  * This include super block, inode block, quota blocks and xattr blocks
  */
 #define EXT4_META_TRANS_BLOCKS(sb)     (EXT4_XATTR_TRANS_BLOCKS + \
-                                       2*EXT4_QUOTA_TRANS_BLOCKS(sb))
+                                       EXT4_MAXQUOTAS_TRANS_BLOCKS(sb))
 
 /* Delete operations potentially hit one directory's namespace plus an
  * entire inode, plus arbitrary amounts of bitmap/indirection data.  Be
@@ -92,6 +92,7 @@
  * but inode, sb and group updates are done only once */
 #define EXT4_QUOTA_INIT_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_INIT_ALLOC*\
                (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_INIT_REWRITE) : 0)
+
 #define EXT4_QUOTA_DEL_BLOCKS(sb) (test_opt(sb, QUOTA) ? (DQUOT_DEL_ALLOC*\
                (EXT4_SINGLEDATA_TRANS_BLOCKS(sb)-3)+3+DQUOT_DEL_REWRITE) : 0)
 #else
 #define EXT4_QUOTA_INIT_BLOCKS(sb) 0
 #define EXT4_QUOTA_DEL_BLOCKS(sb) 0
 #endif
+#define EXT4_MAXQUOTAS_TRANS_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_TRANS_BLOCKS(sb))
+#define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb))
+#define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb))
 
 int
 ext4_mark_iloc_dirty(handle_t *handle,
@@ -116,12 +120,8 @@ int ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
 int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode);
 
 /*
- * Wrapper functions with which ext4 calls into JBD.  The intent here is
- * to allow these to be turned into appropriate stubs so ext4 can control
- * ext2 filesystems, so ext2+ext4 systems only nee one fs.  This work hasn't
- * been done yet.
+ * Wrapper functions with which ext4 calls into JBD.
  */
-
 void ext4_journal_abort_handle(const char *caller, const char *err_fn,
                struct buffer_head *bh, handle_t *handle, int err);
 
@@ -131,13 +131,9 @@ int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
 int __ext4_journal_get_write_access(const char *where, handle_t *handle,
                                struct buffer_head *bh);
 
-/* When called with an invalid handle, this will still do a put on the BH */
-int __ext4_journal_forget(const char *where, handle_t *handle,
-                               struct buffer_head *bh);
-
-/* When called with an invalid handle, this will still do a put on the BH */
-int __ext4_journal_revoke(const char *where, handle_t *handle,
-                               ext4_fsblk_t blocknr, struct buffer_head *bh);
+int __ext4_forget(const char *where, handle_t *handle, int is_metadata,
+                 struct inode *inode, struct buffer_head *bh,
+                 ext4_fsblk_t blocknr);
 
 int __ext4_journal_get_create_access(const char *where,
                                handle_t *handle, struct buffer_head *bh);
@@ -149,12 +145,11 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle,
        __ext4_journal_get_undo_access(__func__, (handle), (bh))
 #define ext4_journal_get_write_access(handle, bh) \
        __ext4_journal_get_write_access(__func__, (handle), (bh))
-#define ext4_journal_revoke(handle, blocknr, bh) \
-       __ext4_journal_revoke(__func__, (handle), (blocknr), (bh))
+#define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
+       __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh),\
+                     (block_nr))
 #define ext4_journal_get_create_access(handle, bh) \
        __ext4_journal_get_create_access(__func__, (handle), (bh))
-#define ext4_journal_forget(handle, bh) \
-       __ext4_journal_forget(__func__, (handle), (bh))
 #define ext4_handle_dirty_metadata(handle, inode, bh) \
        __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh))
 
@@ -254,6 +249,19 @@ static inline int ext4_jbd2_file_inode(handle_t *handle, struct inode *inode)
        return 0;
 }
 
+static inline void ext4_update_inode_fsync_trans(handle_t *handle,
+                                                struct inode *inode,
+                                                int datasync)
+{
+       struct ext4_inode_info *ei = EXT4_I(inode);
+
+       if (ext4_handle_valid(handle)) {
+               ei->i_sync_tid = handle->h_transaction->t_tid;
+               if (datasync)
+                       ei->i_datasync_tid = handle->h_transaction->t_tid;
+       }
+}
+
 /* super.c */
 int ext4_force_commit(struct super_block *sb);
 
index 715264b..3a7928f 100644 (file)
@@ -1007,7 +1007,8 @@ cleanup:
                for (i = 0; i < depth; i++) {
                        if (!ablocks[i])
                                continue;
-                       ext4_free_blocks(handle, inode, ablocks[i], 1, 1);
+                       ext4_free_blocks(handle, inode, 0, ablocks[i], 1,
+                                        EXT4_FREE_BLOCKS_METADATA);
                }
        }
        kfree(ablocks);
@@ -1761,7 +1762,9 @@ int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
        while (block < last && block != EXT_MAX_BLOCK) {
                num = last - block;
                /* find extent for this block */
+               down_read(&EXT4_I(inode)->i_data_sem);
                path = ext4_ext_find_extent(inode, block, path);
+               up_read(&EXT4_I(inode)->i_data_sem);
                if (IS_ERR(path)) {
                        err = PTR_ERR(path);
                        path = NULL;
@@ -1957,7 +1960,6 @@ errout:
 static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
                        struct ext4_ext_path *path)
 {
-       struct buffer_head *bh;
        int err;
        ext4_fsblk_t leaf;
 
@@ -1973,9 +1975,8 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
        if (err)
                return err;
        ext_debug("index is empty, remove it, free block %llu\n", leaf);
-       bh = sb_find_get_block(inode->i_sb, leaf);
-       ext4_forget(handle, 1, inode, bh, leaf);
-       ext4_free_blocks(handle, inode, leaf, 1, 1);
+       ext4_free_blocks(handle, inode, 0, leaf, 1,
+                        EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
        return err;
 }
 
@@ -2042,12 +2043,11 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
                                struct ext4_extent *ex,
                                ext4_lblk_t from, ext4_lblk_t to)
 {
-       struct buffer_head *bh;
        unsigned short ee_len =  ext4_ext_get_actual_len(ex);
-       int i, metadata = 0;
+       int flags = EXT4_FREE_BLOCKS_FORGET;
 
        if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-               metadata = 1;
+               flags |= EXT4_FREE_BLOCKS_METADATA;
 #ifdef EXTENTS_STATS
        {
                struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -2072,11 +2072,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
                num = le32_to_cpu(ex->ee_block) + ee_len - from;
                start = ext_pblock(ex) + ee_len - num;
                ext_debug("free last %u blocks starting %llu\n", num, start);
-               for (i = 0; i < num; i++) {
-                       bh = sb_find_get_block(inode->i_sb, start + i);
-                       ext4_forget(handle, 0, inode, bh, start + i);
-               }
-               ext4_free_blocks(handle, inode, start, num, metadata);
+               ext4_free_blocks(handle, inode, 0, start, num, flags);
        } else if (from == le32_to_cpu(ex->ee_block)
                   && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
                printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
@@ -2167,7 +2163,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
                        correct_index = 1;
                        credits += (ext_depth(inode)) + 1;
                }
-               credits += 2 * EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb);
+               credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
 
                err = ext4_ext_truncate_extend_restart(handle, inode, credits);
                if (err)
@@ -3064,6 +3060,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
        if (flags == EXT4_GET_BLOCKS_DIO_CONVERT_EXT) {
                ret = ext4_convert_unwritten_extents_dio(handle, inode,
                                                        path);
+               if (ret >= 0)
+                       ext4_update_inode_fsync_trans(handle, inode, 1);
                goto out2;
        }
        /* buffered IO case */
@@ -3091,6 +3089,8 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
        ret = ext4_ext_convert_to_initialized(handle, inode,
                                                path, iblock,
                                                max_blocks);
+       if (ret >= 0)
+               ext4_update_inode_fsync_trans(handle, inode, 1);
 out:
        if (ret <= 0) {
                err = ret;
@@ -3319,8 +3319,8 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
                /* not a good idea to call discard here directly,
                 * but otherwise we'd need to call it every free() */
                ext4_discard_preallocations(inode);
-               ext4_free_blocks(handle, inode, ext_pblock(&newex),
-                                       ext4_ext_get_actual_len(&newex), 0);
+               ext4_free_blocks(handle, inode, 0, ext_pblock(&newex),
+                                ext4_ext_get_actual_len(&newex), 0);
                goto out2;
        }
 
@@ -3329,10 +3329,16 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
        allocated = ext4_ext_get_actual_len(&newex);
        set_buffer_new(bh_result);
 
-       /* Cache only when it is _not_ an uninitialized extent */
-       if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0)
+       /*
+        * Cache the extent and update transaction to commit on fdatasync only
+        * when it is _not_ an uninitialized extent.
+        */
+       if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0) {
                ext4_ext_put_in_cache(inode, iblock, allocated, newblock,
                                                EXT4_EXT_CACHE_EXTENT);
+               ext4_update_inode_fsync_trans(handle, inode, 1);
+       } else
+               ext4_update_inode_fsync_trans(handle, inode, 0);
 out:
        if (allocated > max_blocks)
                allocated = max_blocks;
@@ -3720,10 +3726,8 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
                 * Walk the extent tree gathering extent information.
                 * ext4_ext_fiemap_cb will push extents back to user.
                 */
-               down_read(&EXT4_I(inode)->i_data_sem);
                error = ext4_ext_walk_space(inode, start_blk, len_blks,
                                          ext4_ext_fiemap_cb, fieinfo);
-               up_read(&EXT4_I(inode)->i_data_sem);
        }
 
        return error;
index 2b15312..0b22497 100644 (file)
 int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
 {
        struct inode *inode = dentry->d_inode;
+       struct ext4_inode_info *ei = EXT4_I(inode);
        journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
-       int err, ret = 0;
+       int ret;
+       tid_t commit_tid;
 
        J_ASSERT(ext4_journal_current_handle() == NULL);
 
        trace_ext4_sync_file(file, dentry, datasync);
 
+       if (inode->i_sb->s_flags & MS_RDONLY)
+               return 0;
+
        ret = flush_aio_dio_completed_IO(inode);
        if (ret < 0)
-               goto out;
+               return ret;
+       
+       if (!journal)
+               return simple_fsync(file, dentry, datasync);
+
        /*
-        * data=writeback:
+        * data=writeback,ordered:
         *  The caller's filemap_fdatawrite()/wait will sync the data.
-        *  sync_inode() will sync the metadata
-        *
-        * data=ordered:
-        *  The caller's filemap_fdatawrite() will write the data and
-        *  sync_inode() will write the inode if it is dirty.  Then the caller's
-        *  filemap_fdatawait() will wait on the pages.
+        *  Metadata is in the journal, we wait for proper transaction to
+        *  commit here.
         *
         * data=journal:
         *  filemap_fdatawrite won't do anything (the buffers are clean).
@@ -79,32 +84,13 @@ int ext4_sync_file(struct file *file, struct dentry *dentry, int datasync)
         *  (they were dirtied by commit).  But that's OK - the blocks are
         *  safe in-journal, which is all fsync() needs to ensure.
         */
-       if (ext4_should_journal_data(inode)) {
-               ret = ext4_force_commit(inode->i_sb);
-               goto out;
-       }
+       if (ext4_should_journal_data(inode))
+               return ext4_force_commit(inode->i_sb);
 
-       if (!journal)
-               ret = sync_mapping_buffers(inode->i_mapping);
-
-       if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
-               goto out;
-
-       /*
-        * The VFS has written the file data.  If the inode is unaltered
-        * then we need not start a commit.
-        */
-       if (inode->i_state & (I_DIRTY_SYNC|I_DIRTY_DATASYNC)) {
-               struct writeback_control wbc = {
-                       .sync_mode = WB_SYNC_ALL,
-                       .nr_to_write = 0, /* sys_fsync did this */
-               };
-               err = sync_inode(inode, &wbc);
-               if (ret == 0)
-                       ret = err;
-       }
-out:
-       if (journal && (journal->j_flags & JBD2_BARRIER))
+       commit_tid = datasync ? ei->i_datasync_tid : ei->i_sync_tid;
+       if (jbd2_log_start_commit(journal, commit_tid))
+               jbd2_log_wait_commit(journal, commit_tid);
+       else if (journal->j_flags & JBD2_BARRIER)
                blkdev_issue_flush(inode->i_sb->s_bdev, NULL);
        return ret;
 }
index 2c8caa5..5352db1 100644 (file)
@@ -70,58 +70,6 @@ static int ext4_inode_is_fast_symlink(struct inode *inode)
        return (S_ISLNK(inode->i_mode) && inode->i_blocks - ea_blocks == 0);
 }
 
-/*
- * The ext4 forget function must perform a revoke if we are freeing data
- * which has been journaled.  Metadata (eg. indirect blocks) must be
- * revoked in all cases.
- *
- * "bh" may be NULL: a metadata block may have been freed from memory
- * but there may still be a record of it in the journal, and that record
- * still needs to be revoked.
- *
- * If the handle isn't valid we're not journaling, but we still need to
- * call into ext4_journal_revoke() to put the buffer head.
- */
-int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode,
-               struct buffer_head *bh, ext4_fsblk_t blocknr)
-{
-       int err;
-
-       might_sleep();
-
-       BUFFER_TRACE(bh, "enter");
-
-       jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
-                 "data mode %x\n",
-                 bh, is_metadata, inode->i_mode,
-                 test_opt(inode->i_sb, DATA_FLAGS));
-
-       /* Never use the revoke function if we are doing full data
-        * journaling: there is no need to, and a V1 superblock won't
-        * support it.  Otherwise, only skip the revoke on un-journaled
-        * data blocks. */
-
-       if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
-           (!is_metadata && !ext4_should_journal_data(inode))) {
-               if (bh) {
-                       BUFFER_TRACE(bh, "call jbd2_journal_forget");
-                       return ext4_journal_forget(handle, bh);
-               }
-               return 0;
-       }
-
-       /*
-        * data!=journal && (is_metadata || should_journal_data(inode))
-        */
-       BUFFER_TRACE(bh, "call ext4_journal_revoke");
-       err = ext4_journal_revoke(handle, blocknr, bh);
-       if (err)
-               ext4_abort(inode->i_sb, __func__,
-                          "error %d when attempting revoke", err);
-       BUFFER_TRACE(bh, "exit");
-       return err;
-}
-
 /*
  * Work out how many blocks we need to proceed with the next chunk of a
  * truncate transaction.
@@ -721,7 +669,7 @@ allocated:
        return ret;
 failed_out:
        for (i = 0; i < index; i++)
-               ext4_free_blocks(handle, inode, new_blocks[i], 1, 0);
+               ext4_free_blocks(handle, inode, 0, new_blocks[i], 1, 0);
        return ret;
 }
 
@@ -817,14 +765,20 @@ static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
        return err;
 failed:
        /* Allocation failed, free what we already allocated */
+       ext4_free_blocks(handle, inode, 0, new_blocks[0], 1, 0);
        for (i = 1; i <= n ; i++) {
-               BUFFER_TRACE(branch[i].bh, "call jbd2_journal_forget");
-               ext4_journal_forget(handle, branch[i].bh);
+               /* 
+                * branch[i].bh is newly allocated, so there is no
+                * need to revoke the block, which is why we don't
+                * need to set EXT4_FREE_BLOCKS_METADATA.
+                */
+               ext4_free_blocks(handle, inode, 0, new_blocks[i], 1,
+                                EXT4_FREE_BLOCKS_FORGET);
        }
-       for (i = 0; i < indirect_blks; i++)
-               ext4_free_blocks(handle, inode, new_blocks[i], 1, 0);
+       for (i = n+1; i < indirect_blks; i++)
+               ext4_free_blocks(handle, inode, 0, new_blocks[i], 1, 0);
 
-       ext4_free_blocks(handle, inode, new_blocks[i], num, 0);
+       ext4_free_blocks(handle, inode, 0, new_blocks[i], num, 0);
 
        return err;
 }
@@ -903,12 +857,16 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode,
 
 err_out:
        for (i = 1; i <= num; i++) {
-               BUFFER_TRACE(where[i].bh, "call jbd2_journal_forget");
-               ext4_journal_forget(handle, where[i].bh);
-               ext4_free_blocks(handle, inode,
-                                       le32_to_cpu(where[i-1].key), 1, 0);
+               /* 
+                * branch[i].bh is newly allocated, so there is no
+                * need to revoke the block, which is why we don't
+                * need to set EXT4_FREE_BLOCKS_METADATA.
+                */
+               ext4_free_blocks(handle, inode, where[i].bh, 0, 1,
+                                EXT4_FREE_BLOCKS_FORGET);
        }
-       ext4_free_blocks(handle, inode, le32_to_cpu(where[num].key), blks, 0);
+       ext4_free_blocks(handle, inode, 0, le32_to_cpu(where[num].key),
+                        blks, 0);
 
        return err;
 }
@@ -1021,10 +979,12 @@ static int ext4_ind_get_blocks(handle_t *handle, struct inode *inode,
        if (!err)
                err = ext4_splice_branch(handle, inode, iblock,
                                         partial, indirect_blks, count);
-       else
+       if (err)
                goto cleanup;
 
        set_buffer_new(bh_result);
+
+       ext4_update_inode_fsync_trans(handle, inode, 1);
 got_it:
        map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));
        if (count > blocks_to_boundary)
@@ -1052,7 +1012,7 @@ qsize_t ext4_get_reserved_space(struct inode *inode)
                EXT4_I(inode)->i_reserved_meta_blocks;
        spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
 
-       return total;
+       return (total << inode->i_blkbits);
 }
 /*
  * Calculate the number of metadata blocks need to reserve
@@ -1534,6 +1494,16 @@ static int do_journal_get_write_access(handle_t *handle,
        return ext4_journal_get_write_access(handle, bh);
 }
 
+/*
+ * Truncate blocks that were not used by write. We have to truncate the
+ * pagecache as well so that corresponding buffers get properly unmapped.
+ */
+static void ext4_truncate_failed_write(struct inode *inode)
+{
+       truncate_inode_pages(inode->i_mapping, inode->i_size);
+       ext4_truncate(inode);
+}
+
 static int ext4_write_begin(struct file *file, struct address_space *mapping,
                            loff_t pos, unsigned len, unsigned flags,
                            struct page **pagep, void **fsdata)
@@ -1599,7 +1569,7 @@ retry:
 
                ext4_journal_stop(handle);
                if (pos + len > inode->i_size) {
-                       ext4_truncate(inode);
+                       ext4_truncate_failed_write(inode);
                        /*
                         * If truncate failed early the inode might
                         * still be on the orphan list; we need to
@@ -1709,7 +1679,7 @@ static int ext4_ordered_write_end(struct file *file,
                ret = ret2;
 
        if (pos + len > inode->i_size) {
-               ext4_truncate(inode);
+               ext4_truncate_failed_write(inode);
                /*
                 * If truncate failed early the inode might still be
                 * on the orphan list; we need to make sure the inode
@@ -1751,7 +1721,7 @@ static int ext4_writeback_write_end(struct file *file,
                ret = ret2;
 
        if (pos + len > inode->i_size) {
-               ext4_truncate(inode);
+               ext4_truncate_failed_write(inode);
                /*
                 * If truncate failed early the inode might still be
                 * on the orphan list; we need to make sure the inode
@@ -1814,7 +1784,7 @@ static int ext4_journalled_write_end(struct file *file,
        if (!ret)
                ret = ret2;
        if (pos + len > inode->i_size) {
-               ext4_truncate(inode);
+               ext4_truncate_failed_write(inode);
                /*
                 * If truncate failed early the inode might still be
                 * on the orphan list; we need to make sure the inode
@@ -2600,7 +2570,6 @@ static int bput_one(handle_t *handle, struct buffer_head *bh)
 }
 
 static int __ext4_journalled_writepage(struct page *page,
-                                      struct writeback_control *wbc,
                                       unsigned int len)
 {
        struct address_space *mapping = page->mapping;
@@ -2758,7 +2727,7 @@ static int ext4_writepage(struct page *page,
                 * doesn't seem much point in redirtying the page here.
                 */
                ClearPageChecked(page);
-               return __ext4_journalled_writepage(page, wbc, len);
+               return __ext4_journalled_writepage(page, len);
        }
 
        if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
@@ -2788,7 +2757,7 @@ static int ext4_da_writepages_trans_blocks(struct inode *inode)
         * number of contiguous block. So we will limit
         * number of contiguous block to a sane value
         */
-       if (!(inode->i_flags & EXT4_EXTENTS_FL) &&
+       if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) &&
            (max_blocks > EXT4_MAX_TRANS_DATA))
                max_blocks = EXT4_MAX_TRANS_DATA;
 
@@ -2933,7 +2902,7 @@ retry:
                ret = write_cache_pages(mapping, wbc, __mpage_da_writepage,
                                        &mpd);
                /*
-                * If we have a contigous extent of pages and we
+                * If we have a contiguous extent of pages and we
                 * haven't done the I/O yet, map the blocks and submit
                 * them for I/O.
                 */
@@ -3091,7 +3060,7 @@ retry:
                 * i_size_read because we hold i_mutex.
                 */
                if (pos + len > inode->i_size)
-                       ext4_truncate(inode);
+                       ext4_truncate_failed_write(inode);
        }
 
        if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
@@ -4064,7 +4033,7 @@ static Indirect *ext4_find_shared(struct inode *inode, int depth,
        int k, err;
 
        *top = 0;
-       /* Make k index the deepest non-null offest + 1 */
+       /* Make k index the deepest non-null offset + 1 */
        for (k = depth; k > 1 && !offsets[k-1]; k--)
                ;
        partial = ext4_get_branch(inode, k, offsets, chain, &err);
@@ -4120,6 +4089,11 @@ static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
                              __le32 *last)
 {
        __le32 *p;
+       int     flags = EXT4_FREE_BLOCKS_FORGET;
+
+       if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+               flags |= EXT4_FREE_BLOCKS_METADATA;
+
        if (try_to_extend_transaction(handle, inode)) {
                if (bh) {
                        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
@@ -4134,27 +4108,10 @@ static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
                }
        }
 
-       /*
-        * Any buffers which are on the journal will be in memory. We
-        * find them on the hash table so jbd2_journal_revoke() will
-        * run jbd2_journal_forget() on them.  We've already detached
-        * each block from the file, so bforget() in
-        * jbd2_journal_forget() should be safe.
-        *
-        * AKPM: turn on bforget in jbd2_journal_forget()!!!
-        */
-       for (p = first; p < last; p++) {
-               u32 nr = le32_to_cpu(*p);
-               if (nr) {
-                       struct buffer_head *tbh;
-
-                       *p = 0;
-                       tbh = sb_find_get_block(inode->i_sb, nr);
-                       ext4_forget(handle, 0, inode, tbh, nr);
-               }
-       }
+       for (p = first; p < last; p++)
+               *p = 0;
 
-       ext4_free_blocks(handle, inode, block_to_free, count, 0);
+       ext4_free_blocks(handle, inode, 0, block_to_free, count, flags);
 }
 
 /**
@@ -4342,7 +4299,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
                                            blocks_for_truncate(inode));
                        }
 
-                       ext4_free_blocks(handle, inode, nr, 1, 1);
+                       ext4_free_blocks(handle, inode, 0, nr, 1,
+                                        EXT4_FREE_BLOCKS_METADATA);
 
                        if (parent_bh) {
                                /*
@@ -4781,8 +4739,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        struct ext4_iloc iloc;
        struct ext4_inode *raw_inode;
        struct ext4_inode_info *ei;
-       struct buffer_head *bh;
        struct inode *inode;
+       journal_t *journal = EXT4_SB(sb)->s_journal;
        long ret;
        int block;
 
@@ -4793,11 +4751,11 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                return inode;
 
        ei = EXT4_I(inode);
+       iloc.bh = 0;
 
        ret = __ext4_get_inode_loc(inode, &iloc, 0);
        if (ret < 0)
                goto bad_inode;
-       bh = iloc.bh;
        raw_inode = ext4_raw_inode(&iloc);
        inode->i_mode = le16_to_cpu(raw_inode->i_mode);
        inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
@@ -4820,7 +4778,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                if (inode->i_mode == 0 ||
                    !(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ORPHAN_FS)) {
                        /* this inode is deleted */
-                       brelse(bh);
                        ret = -ESTALE;
                        goto bad_inode;
                }
@@ -4848,11 +4805,35 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                ei->i_data[block] = raw_inode->i_block[block];
        INIT_LIST_HEAD(&ei->i_orphan);
 
+       /*
+        * Set transaction id's of transactions that have to be committed
+        * to finish f[data]sync. We set them to currently running transaction
+        * as we cannot be sure that the inode or some of its metadata isn't
+        * part of the transaction - the inode could have been reclaimed and
+        * now it is reread from disk.
+        */
+       if (journal) {
+               transaction_t *transaction;
+               tid_t tid;
+
+               spin_lock(&journal->j_state_lock);
+               if (journal->j_running_transaction)
+                       transaction = journal->j_running_transaction;
+               else
+                       transaction = journal->j_committing_transaction;
+               if (transaction)
+                       tid = transaction->t_tid;
+               else
+                       tid = journal->j_commit_sequence;
+               spin_unlock(&journal->j_state_lock);
+               ei->i_sync_tid = tid;
+               ei->i_datasync_tid = tid;
+       }
+
        if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) {
                ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
                if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize >
                    EXT4_INODE_SIZE(inode->i_sb)) {
-                       brelse(bh);
                        ret = -EIO;
                        goto bad_inode;
                }
@@ -4884,10 +4865,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
 
        ret = 0;
        if (ei->i_file_acl &&
-           ((ei->i_file_acl <
-             (le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block) +
-              EXT4_SB(sb)->s_gdb_count)) ||
-            (ei->i_file_acl >= ext4_blocks_count(EXT4_SB(sb)->s_es)))) {
+           !ext4_data_block_valid(EXT4_SB(sb), ei->i_file_acl, 1)) {
                ext4_error(sb, __func__,
                           "bad extended attribute block %llu in inode #%lu",
                           ei->i_file_acl, inode->i_ino);
@@ -4905,10 +4883,8 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                /* Validate block references which are part of inode */
                ret = ext4_check_inode_blockref(inode);
        }
-       if (ret) {
-               brelse(bh);
+       if (ret)
                goto bad_inode;
-       }
 
        if (S_ISREG(inode->i_mode)) {
                inode->i_op = &ext4_file_inode_operations;
@@ -4936,7 +4912,6 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
                        init_special_inode(inode, inode->i_mode,
                           new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
        } else {
-               brelse(bh);
                ret = -EIO;
                ext4_error(inode->i_sb, __func__,
                           "bogus i_mode (%o) for inode=%lu",
@@ -4949,6 +4924,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        return inode;
 
 bad_inode:
+       brelse(iloc.bh);
        iget_failed(inode);
        return ERR_PTR(ret);
 }
@@ -5108,6 +5084,7 @@ static int ext4_do_update_inode(handle_t *handle,
                err = rc;
        ei->i_state &= ~EXT4_STATE_NEW;
 
+       ext4_update_inode_fsync_trans(handle, inode, 0);
 out_brelse:
        brelse(bh);
        ext4_std_error(inode->i_sb, err);
@@ -5227,8 +5204,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 
                /* (user+group)*(old+new) structure, inode write (sb,
                 * inode block, ? - but truncate inode update has it) */
-               handle = ext4_journal_start(inode, 2*(EXT4_QUOTA_INIT_BLOCKS(inode->i_sb)+
-                                       EXT4_QUOTA_DEL_BLOCKS(inode->i_sb))+3);
+               handle = ext4_journal_start(inode, (EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb)+
+                                       EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb))+3);
                if (IS_ERR(handle)) {
                        error = PTR_ERR(handle);
                        goto err_out;
@@ -5376,7 +5353,7 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
  * worse case, the indexs blocks spread over different block groups
  *
  * If datablocks are discontiguous, they are possible to spread over
- * different block groups too. If they are contiugous, with flexbg,
+ * different block groups too. If they are contiuguous, with flexbg,
  * they could still across block group boundary.
  *
  * Also account for superblock, inode, quota and xattr blocks
@@ -5452,7 +5429,7 @@ int ext4_writepage_trans_blocks(struct inode *inode)
  * Calculate the journal credits for a chunk of data modification.
  *
  * This is called from DIO, fallocate or whoever calling
- * ext4_get_blocks() to map/allocate a chunk of contigous disk blocks.
+ * ext4_get_blocks() to map/allocate a chunk of contiguous disk blocks.
  *
  * journal buffers for data blocks are not included here, as DIO
  * and fallocate do no need to journal data buffers.
index c1cdf61..b63d193 100644 (file)
@@ -221,31 +221,38 @@ setversion_out:
                struct file *donor_filp;
                int err;
 
+               if (!(filp->f_mode & FMODE_READ) ||
+                   !(filp->f_mode & FMODE_WRITE))
+                       return -EBADF;
+
                if (copy_from_user(&me,
                        (struct move_extent __user *)arg, sizeof(me)))
                        return -EFAULT;
+               me.moved_len = 0;
 
                donor_filp = fget(me.donor_fd);
                if (!donor_filp)
                        return -EBADF;
 
-               if (!capable(CAP_DAC_OVERRIDE)) {
-                       if ((current->real_cred->fsuid != inode->i_uid) ||
-                               !(inode->i_mode & S_IRUSR) ||
-                               !(donor_filp->f_dentry->d_inode->i_mode &
-                               S_IRUSR)) {
-                               fput(donor_filp);
-                               return -EACCES;
-                       }
+               if (!(donor_filp->f_mode & FMODE_WRITE)) {
+                       err = -EBADF;
+                       goto mext_out;
                }
 
+               err = mnt_want_write(filp->f_path.mnt);
+               if (err)
+                       goto mext_out;
+
                err = ext4_move_extents(filp, donor_filp, me.orig_start,
                                        me.donor_start, me.len, &me.moved_len);
-               fput(donor_filp);
+               mnt_drop_write(filp->f_path.mnt);
+               if (me.moved_len > 0)
+                       file_remove_suid(donor_filp);
 
                if (copy_to_user((struct move_extent *)arg, &me, sizeof(me)))
-                       return -EFAULT;
-
+                       err = -EFAULT;
+mext_out:
+               fput(donor_filp);
                return err;
        }
 
index bba1282..c1e19d5 100644 (file)
  * 2 blocks and the order of allocation is >= sbi->s_mb_order2_reqs. The
  * value of s_mb_order2_reqs can be tuned via
  * /sys/fs/ext4/<partition>/mb_order2_req.  If the request len is equal to
- * stripe size (sbi->s_stripe), we try to search for contigous block in
+ * stripe size (sbi->s_stripe), we try to search for contiguous block in
  * stripe size. This should result in better allocation on RAID setups. If
  * not, we search in the specific group using bitmap for best extents. The
  * tunable min_to_scan and max_to_scan control the behaviour here.
@@ -2529,7 +2529,6 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
        struct ext4_group_info *db;
        int err, count = 0, count2 = 0;
        struct ext4_free_data *entry;
-       ext4_fsblk_t discard_block;
        struct list_head *l, *ltmp;
 
        list_for_each_safe(l, ltmp, &txn->t_private_list) {
@@ -2559,13 +2558,19 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
                        page_cache_release(e4b.bd_bitmap_page);
                }
                ext4_unlock_group(sb, entry->group);
-               discard_block = (ext4_fsblk_t) entry->group * EXT4_BLOCKS_PER_GROUP(sb)
-                       + entry->start_blk
-                       + le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
-               trace_ext4_discard_blocks(sb, (unsigned long long)discard_block,
-                                         entry->count);
-               sb_issue_discard(sb, discard_block, entry->count);
-
+               if (test_opt(sb, DISCARD)) {
+                       ext4_fsblk_t discard_block;
+                       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+
+                       discard_block = (ext4_fsblk_t)entry->group *
+                                               EXT4_BLOCKS_PER_GROUP(sb)
+                                       + entry->start_blk
+                                       + le32_to_cpu(es->s_first_data_block);
+                       trace_ext4_discard_blocks(sb,
+                                       (unsigned long long)discard_block,
+                                       entry->count);
+                       sb_issue_discard(sb, discard_block, entry->count);
+               }
                kmem_cache_free(ext4_free_ext_cachep, entry);
                ext4_mb_release_desc(&e4b);
        }
@@ -3005,6 +3010,24 @@ static void ext4_mb_collect_stats(struct ext4_allocation_context *ac)
                trace_ext4_mballoc_prealloc(ac);
 }
 
+/*
+ * Called on failure; free up any blocks from the inode PA for this
+ * context.  We don't need this for MB_GROUP_PA because we only change
+ * pa_free in ext4_mb_release_context(), but on failure, we've already
+ * zeroed out ac->ac_b_ex.fe_len, so group_pa->pa_free is not changed.
+ */
+static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac)
+{
+       struct ext4_prealloc_space *pa = ac->ac_pa;
+       int len;
+
+       if (pa && pa->pa_type == MB_INODE_PA) {
+               len = ac->ac_b_ex.fe_len;
+               pa->pa_free += len;
+       }
+
+}
+
 /*
  * use blocks preallocated to inode
  */
@@ -4290,6 +4313,7 @@ repeat:
                        ac->ac_status = AC_STATUS_CONTINUE;
                        goto repeat;
                } else if (*errp) {
+                       ext4_discard_allocated_blocks(ac);
                        ac->ac_b_ex.fe_len = 0;
                        ar->len = 0;
                        ext4_mb_show_ac(ac);
@@ -4422,18 +4446,24 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
        return 0;
 }
 
-/*
- * Main entry point into mballoc to free blocks
+/**
+ * ext4_free_blocks() -- Free given blocks and update quota
+ * @handle:            handle for this transaction
+ * @inode:             inode
+ * @block:             start physical block to free
+ * @count:             number of blocks to count
+ * @metadata:          Are these metadata blocks
  */
-void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
-                       ext4_fsblk_t block, unsigned long count,
-                       int metadata, unsigned long *freed)
+void ext4_free_blocks(handle_t *handle, struct inode *inode,
+                     struct buffer_head *bh, ext4_fsblk_t block,
+                     unsigned long count, int flags)
 {
        struct buffer_head *bitmap_bh = NULL;
        struct super_block *sb = inode->i_sb;
        struct ext4_allocation_context *ac = NULL;
        struct ext4_group_desc *gdp;
        struct ext4_super_block *es;
+       unsigned long freed = 0;
        unsigned int overflow;
        ext4_grpblk_t bit;
        struct buffer_head *gd_bh;
@@ -4443,13 +4473,16 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
        int err = 0;
        int ret;
 
-       *freed = 0;
+       if (bh) {
+               if (block)
+                       BUG_ON(block != bh->b_blocknr);
+               else
+                       block = bh->b_blocknr;
+       }
 
        sbi = EXT4_SB(sb);
        es = EXT4_SB(sb)->s_es;
-       if (block < le32_to_cpu(es->s_first_data_block) ||
-           block + count < block ||
-           block + count > ext4_blocks_count(es)) {
+       if (!ext4_data_block_valid(sbi, block, count)) {
                ext4_error(sb, __func__,
                            "Freeing blocks not in datazone - "
                            "block = %llu, count = %lu", block, count);
@@ -4457,7 +4490,32 @@ void ext4_mb_free_blocks(handle_t *handle, struct inode *inode,
        }
 
        ext4_debug("freeing block %llu\n", block);
-       trace_ext4_free_blocks(inode, block, count, metadata);
+       trace_ext4_free_blocks(inode, block, count, flags);
+
+       if (flags & EXT4_FREE_BLOCKS_FORGET) {
+               struct buffer_head *tbh = bh;
+               int i;
+
+               BUG_ON(bh && (count > 1));
+
+               for (i = 0; i < count; i++) {
+                       if (!bh)
+                               tbh = sb_find_get_block(inode->i_sb,
+                                                       block + i);
+                       ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA, 
+                                   inode, tbh, block + i);
+               }
+       }
+
+       /* 
+        * We need to make sure we don't reuse the freed block until
+        * after the transaction is committed, which we can do by
+        * treating the block as metadata, below.  We make an
+        * exception if the inode is to be written in writeback mode
+        * since writeback mode has weak data consistency guarantees.
+        */
+       if (!ext4_should_writeback_data(inode))
+               flags |= EXT4_FREE_BLOCKS_METADATA;
 
        ac = kmem_cache_alloc(ext4_ac_cachep, GFP_NOFS);
        if (ac) {
@@ -4533,7 +4591,8 @@ do_more:
        err = ext4_mb_load_buddy(sb, block_group, &e4b);
        if (err)
                goto error_return;
-       if (metadata && ext4_handle_valid(handle)) {
+
+       if ((flags & EXT4_FREE_BLOCKS_METADATA) && ext4_handle_valid(handle)) {
                struct ext4_free_data *new_entry;
                /*
                 * blocks being freed are metadata. these blocks shouldn't
@@ -4572,7 +4631,7 @@ do_more:
 
        ext4_mb_release_desc(&e4b);
 
-       *freed += count;
+       freed += count;
 
        /* We dirtied the bitmap block */
        BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
@@ -4592,6 +4651,8 @@ do_more:
        }
        sb->s_dirt = 1;
 error_return:
+       if (freed)
+               vfs_dq_free_block(inode, freed);
        brelse(bitmap_bh);
        ext4_std_error(sb, err);
        if (ac)
index a93d5b8..8141581 100644 (file)
@@ -238,7 +238,7 @@ static int extend_credit_for_blkdel(handle_t *handle, struct inode *inode)
         * So allocate a credit of 3. We may update
         * quota (user and group).
         */
-       needed = 3 + 2*EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb);
+       needed = 3 + EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
 
        if (ext4_journal_extend(handle, needed) != 0)
                retval = ext4_journal_restart(handle, needed);
@@ -262,13 +262,17 @@ static int free_dind_blocks(handle_t *handle,
        for (i = 0; i < max_entries; i++) {
                if (tmp_idata[i]) {
                        extend_credit_for_blkdel(handle, inode);
-                       ext4_free_blocks(handle, inode,
-                                       le32_to_cpu(tmp_idata[i]), 1, 1);
+                       ext4_free_blocks(handle, inode, 0,
+                                        le32_to_cpu(tmp_idata[i]), 1,
+                                        EXT4_FREE_BLOCKS_METADATA |
+                                        EXT4_FREE_BLOCKS_FORGET);
                }
        }
        put_bh(bh);
        extend_credit_for_blkdel(handle, inode);
-       ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1, 1);
+       ext4_free_blocks(handle, inode, 0, le32_to_cpu(i_data), 1,
+                        EXT4_FREE_BLOCKS_METADATA |
+                        EXT4_FREE_BLOCKS_FORGET);
        return 0;
 }
 
@@ -297,7 +301,9 @@ static int free_tind_blocks(handle_t *handle,
        }
        put_bh(bh);
        extend_credit_for_blkdel(handle, inode);
-       ext4_free_blocks(handle, inode, le32_to_cpu(i_data), 1, 1);
+       ext4_free_blocks(handle, inode, 0, le32_to_cpu(i_data), 1,
+                        EXT4_FREE_BLOCKS_METADATA |
+                        EXT4_FREE_BLOCKS_FORGET);
        return 0;
 }
 
@@ -308,8 +314,10 @@ static int free_ind_block(handle_t *handle, struct inode *inode, __le32 *i_data)
        /* ei->i_data[EXT4_IND_BLOCK] */
        if (i_data[0]) {
                extend_credit_for_blkdel(handle, inode);
-               ext4_free_blocks(handle, inode,
-                               le32_to_cpu(i_data[0]), 1, 1);
+               ext4_free_blocks(handle, inode, 0,
+                               le32_to_cpu(i_data[0]), 1,
+                                EXT4_FREE_BLOCKS_METADATA |
+                                EXT4_FREE_BLOCKS_FORGET);
        }
 
        /* ei->i_data[EXT4_DIND_BLOCK] */
@@ -419,7 +427,8 @@ static int free_ext_idx(handle_t *handle, struct inode *inode,
        }
        put_bh(bh);
        extend_credit_for_blkdel(handle, inode);
-       ext4_free_blocks(handle, inode, block, 1, 1);
+       ext4_free_blocks(handle, inode, 0, block, 1,
+                        EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
        return retval;
 }
 
@@ -477,7 +486,7 @@ int ext4_ext_migrate(struct inode *inode)
        handle = ext4_journal_start(inode,
                                        EXT4_DATA_TRANS_BLOCKS(inode->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2 * EXT4_QUOTA_INIT_BLOCKS(inode->i_sb)
+                                       EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb)
                                        + 1);
        if (IS_ERR(handle)) {
                retval = PTR_ERR(handle);
index 25b6b14..82c415b 100644 (file)
@@ -77,12 +77,14 @@ static int
 mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
                      struct ext4_extent **extent)
 {
+       struct ext4_extent_header *eh;
        int ppos, leaf_ppos = path->p_depth;
 
        ppos = leaf_ppos;
        if (EXT_LAST_EXTENT(path[ppos].p_hdr) > path[ppos].p_ext) {
                /* leaf block */
                *extent = ++path[ppos].p_ext;
+               path[ppos].p_block = ext_pblock(path[ppos].p_ext);
                return 0;
        }
 
@@ -119,9 +121,18 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path,
                                        ext_block_hdr(path[cur_ppos+1].p_bh);
                        }
 
+                       path[leaf_ppos].p_ext = *extent = NULL;
+
+                       eh = path[leaf_ppos].p_hdr;
+                       if (le16_to_cpu(eh->eh_entries) == 0)
+                               /* empty leaf is found */
+                               return -ENODATA;
+
                        /* leaf block */
                        path[leaf_ppos].p_ext = *extent =
                                EXT_FIRST_EXTENT(path[leaf_ppos].p_hdr);
+                       path[leaf_ppos].p_block =
+                                       ext_pblock(path[leaf_ppos].p_ext);
                        return 0;
                }
        }
@@ -155,40 +166,15 @@ mext_check_null_inode(struct inode *inode1, struct inode *inode2,
 }
 
 /**
- * mext_double_down_read - Acquire two inodes' read semaphore
- *
- * @orig_inode:                original inode structure
- * @donor_inode:       donor inode structure
- * Acquire read semaphore of the two inodes (orig and donor) by i_ino order.
- */
-static void
-mext_double_down_read(struct inode *orig_inode, struct inode *donor_inode)
-{
-       struct inode *first = orig_inode, *second = donor_inode;
-
-       /*
-        * Use the inode number to provide the stable locking order instead
-        * of its address, because the C language doesn't guarantee you can
-        * compare pointers that don't come from the same array.
-        */
-       if (donor_inode->i_ino < orig_inode->i_ino) {
-               first = donor_inode;
-               second = orig_inode;
-       }
-
-       down_read(&EXT4_I(first)->i_data_sem);
-       down_read(&EXT4_I(second)->i_data_sem);
-}
-
-/**
- * mext_double_down_write - Acquire two inodes' write semaphore
+ * double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem
  *
  * @orig_inode:                original inode structure
  * @donor_inode:       donor inode structure
- * Acquire write semaphore of the two inodes (orig and donor) by i_ino order.
+ * Acquire write lock of i_data_sem of the two inodes (orig and donor) by
+ * i_ino order.
  */
 static void
-mext_double_down_write(struct inode *orig_inode, struct inode *donor_inode)
+double_down_write_data_sem(struct inode *orig_inode, struct inode *donor_inode)
 {
        struct inode *first = orig_inode, *second = donor_inode;
 
@@ -203,32 +189,18 @@ mext_double_down_write(struct inode *orig_inode, struct inode *donor_inode)
        }
 
        down_write(&EXT4_I(first)->i_data_sem);
-       down_write(&EXT4_I(second)->i_data_sem);
+       down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING);
 }
 
 /**
- * mext_double_up_read - Release two inodes' read semaphore
+ * double_up_write_data_sem - Release two inodes' write lock of i_data_sem
  *
  * @orig_inode:                original inode structure to be released its lock first
  * @donor_inode:       donor inode structure to be released its lock second
- * Release read semaphore of two inodes (orig and donor).
+ * Release write lock of i_data_sem of two inodes (orig and donor).
  */
 static void
-mext_double_up_read(struct inode *orig_inode, struct inode *donor_inode)
-{
-       up_read(&EXT4_I(orig_inode)->i_data_sem);
-       up_read(&EXT4_I(donor_inode)->i_data_sem);
-}
-
-/**
- * mext_double_up_write - Release two inodes' write semaphore
- *
- * @orig_inode:                original inode structure to be released its lock first
- * @donor_inode:       donor inode structure to be released its lock second
- * Release write semaphore of two inodes (orig and donor).
- */
-static void
-mext_double_up_write(struct inode *orig_inode, struct inode *donor_inode)
+double_up_write_data_sem(struct inode *orig_inode, struct inode *donor_inode)
 {
        up_write(&EXT4_I(orig_inode)->i_data_sem);
        up_write(&EXT4_I(donor_inode)->i_data_sem);
@@ -596,7 +568,7 @@ out:
  * @tmp_oext:          the extent that will belong to the donor inode
  * @orig_off:          block offset of original inode
  * @donor_off:         block offset of donor inode
- * @max_count:         the maximun length of extents
+ * @max_count:         the maximum length of extents
  *
  * Return 0 on success, or a negative error value on failure.
  */
@@ -661,6 +633,7 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
  * @donor_inode:       donor inode
  * @from:              block offset of orig_inode
  * @count:             block count to be replaced
+ * @err:               pointer to save return value
  *
  * Replace original inode extents and donor inode extents page by page.
  * We implement this replacement in the following three steps:
@@ -671,33 +644,33 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
  * 3. Change the block information of donor inode to point at the saved
  *    original inode blocks in the dummy extents.
  *
- * Return 0 on success, or a negative error value on failure.
+ * Return replaced block count.
  */
 static int
 mext_replace_branches(handle_t *handle, struct inode *orig_inode,
                           struct inode *donor_inode, ext4_lblk_t from,
-                          ext4_lblk_t count)
+                          ext4_lblk_t count, int *err)
 {
        struct ext4_ext_path *orig_path = NULL;
        struct ext4_ext_path *donor_path = NULL;
        struct ext4_extent *oext, *dext;
        struct ext4_extent tmp_dext, tmp_oext;
        ext4_lblk_t orig_off = from, donor_off = from;
-       int err = 0;
        int depth;
        int replaced_count = 0;
        int dext_alen;
 
-       mext_double_down_write(orig_inode, donor_inode);
+       /* Protect extent trees against block allocations via delalloc */
+       double_down_write_data_sem(orig_inode, donor_inode);
 
        /* Get the original extent for the block "orig_off" */
-       err = get_ext_path(orig_inode, orig_off, &orig_path);
-       if (err)
+       *err = get_ext_path(orig_inode, orig_off, &orig_path);
+       if (*err)
                goto out;
 
        /* Get the donor extent for the head */
-       err = get_ext_path(donor_inode, donor_off, &donor_path);
-       if (err)
+       *err = get_ext_path(donor_inode, donor_off, &donor_path);
+       if (*err)
                goto out;
        depth = ext_depth(orig_inode);
        oext = orig_path[depth].p_ext;
@@ -707,9 +680,9 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
        dext = donor_path[depth].p_ext;
        tmp_dext = *dext;
 
-       err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
+       *err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
                                      donor_off, count);
-       if (err)
+       if (*err)
                goto out;
 
        /* Loop for the donor extents */
@@ -718,7 +691,7 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
                if (!dext) {
                        ext4_error(donor_inode->i_sb, __func__,
                                   "The extent for donor must be found");
-                       err = -EIO;
+                       *err = -EIO;
                        goto out;
                } else if (donor_off != le32_to_cpu(tmp_dext.ee_block)) {
                        ext4_error(donor_inode->i_sb, __func__,
@@ -726,20 +699,20 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
                                "extent(%u) should be equal",
                                donor_off,
                                le32_to_cpu(tmp_dext.ee_block));
-                       err = -EIO;
+                       *err = -EIO;
                        goto out;
                }
 
                /* Set donor extent to orig extent */
-               err = mext_leaf_block(handle, orig_inode,
+               *err = mext_leaf_block(handle, orig_inode,
                                           orig_path, &tmp_dext, &orig_off);
-               if (err < 0)
+               if (*err)
                        goto out;
 
                /* Set orig extent to donor extent */
-               err = mext_leaf_block(handle, donor_inode,
+               *err = mext_leaf_block(handle, donor_inode,
                                           donor_path, &tmp_oext, &donor_off);
-               if (err < 0)
+               if (*err)
                        goto out;
 
                dext_alen = ext4_ext_get_actual_len(&tmp_dext);
@@ -753,35 +726,25 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
 
                if (orig_path)
                        ext4_ext_drop_refs(orig_path);
-               err = get_ext_path(orig_inode, orig_off, &orig_path);
-               if (err)
+               *err = get_ext_path(orig_inode, orig_off, &orig_path);
+               if (*err)
                        goto out;
                depth = ext_depth(orig_inode);
                oext = orig_path[depth].p_ext;
-               if (le32_to_cpu(oext->ee_block) +
-                               ext4_ext_get_actual_len(oext) <= orig_off) {
-                       err = 0;
-                       goto out;
-               }
                tmp_oext = *oext;
 
                if (donor_path)
                        ext4_ext_drop_refs(donor_path);
-               err = get_ext_path(donor_inode, donor_off, &donor_path);
-               if (err)
+               *err = get_ext_path(donor_inode, donor_off, &donor_path);
+               if (*err)
                        goto out;
                depth = ext_depth(donor_inode);
                dext = donor_path[depth].p_ext;
-               if (le32_to_cpu(dext->ee_block) +
-                               ext4_ext_get_actual_len(dext) <= donor_off) {
-                       err = 0;
-                       goto out;
-               }
                tmp_dext = *dext;
 
-               err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
+               *err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
                                           donor_off, count - replaced_count);
-               if (err)
+               if (*err)
                        goto out;
        }
 
@@ -795,8 +758,12 @@ out:
                kfree(donor_path);
        }
 
-       mext_double_up_write(orig_inode, donor_inode);
-       return err;
+       ext4_ext_invalidate_cache(orig_inode);
+       ext4_ext_invalidate_cache(donor_inode);
+
+       double_up_write_data_sem(orig_inode, donor_inode);
+
+       return replaced_count;
 }
 
 /**
@@ -808,16 +775,17 @@ out:
  * @data_offset_in_page:       block index where data swapping starts
  * @block_len_in_page:         the number of blocks to be swapped
  * @uninit:                    orig extent is uninitialized or not
+ * @err:                       pointer to save return value
  *
  * Save the data in original inode blocks and replace original inode extents
  * with donor inode extents by calling mext_replace_branches().
- * Finally, write out the saved data in new original inode blocks. Return 0
- * on success, or a negative error value on failure.
+ * Finally, write out the saved data in new original inode blocks. Return
+ * replaced block count.
  */
 static int
 move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
                  pgoff_t orig_page_offset, int data_offset_in_page,
-                 int block_len_in_page, int uninit)
+                 int block_len_in_page, int uninit, int *err)
 {
        struct inode *orig_inode = o_filp->f_dentry->d_inode;
        struct address_space *mapping = orig_inode->i_mapping;
@@ -829,9 +797,11 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
        long long offs = orig_page_offset << PAGE_CACHE_SHIFT;
        unsigned long blocksize = orig_inode->i_sb->s_blocksize;
        unsigned int w_flags = 0;
-       unsigned int tmp_data_len, data_len;
+       unsigned int tmp_data_size, data_size, replaced_size;
        void *fsdata;
-       int ret, i, jblocks;
+       int i, jblocks;
+       int err2 = 0;
+       int replaced_count = 0;
        int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits;
 
        /*
@@ -841,8 +811,8 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
        jblocks = ext4_writepage_trans_blocks(orig_inode) * 2;
        handle = ext4_journal_start(orig_inode, jblocks);
        if (IS_ERR(handle)) {
-               ret = PTR_ERR(handle);
-               return ret;
+               *err = PTR_ERR(handle);
+               return 0;
        }
 
        if (segment_eq(get_fs(), KERNEL_DS))
@@ -858,39 +828,36 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
         * Just swap data blocks between orig and donor.
         */
        if (uninit) {
-               ret = mext_replace_branches(handle, orig_inode,
-                                                donor_inode, orig_blk_offset,
-                                                block_len_in_page);
-
-               /* Clear the inode cache not to refer to the old data */
-               ext4_ext_invalidate_cache(orig_inode);
-               ext4_ext_invalidate_cache(donor_inode);
+               replaced_count = mext_replace_branches(handle, orig_inode,
+                                               donor_inode, orig_blk_offset,
+                                               block_len_in_page, err);
                goto out2;
        }
 
        offs = (long long)orig_blk_offset << orig_inode->i_blkbits;
 
-       /* Calculate data_len */
+       /* Calculate data_size */
        if ((orig_blk_offset + block_len_in_page - 1) ==
            ((orig_inode->i_size - 1) >> orig_inode->i_blkbits)) {
                /* Replace the last block */
-               tmp_data_len = orig_inode->i_size & (blocksize - 1);
+               tmp_data_size = orig_inode->i_size & (blocksize - 1);
                /*
-                * If data_len equal zero, it shows data_len is multiples of
+                * If data_size equal zero, it shows data_size is multiples of
                 * blocksize. So we set appropriate value.
                 */
-               if (tmp_data_len == 0)
-                       tmp_data_len = blocksize;
+               if (tmp_data_size == 0)
+                       tmp_data_size = blocksize;
 
-               data_len = tmp_data_len +
+               data_size = tmp_data_size +
                        ((block_len_in_page - 1) << orig_inode->i_blkbits);
-       } else {
-               data_len = block_len_in_page << orig_inode->i_blkbits;
-       }
+       } else
+               data_size = block_len_in_page << orig_inode->i_blkbits;
+
+       replaced_size = data_size;
 
-       ret = a_ops->write_begin(o_filp, mapping, offs, data_len, w_flags,
+       *err = a_ops->write_begin(o_filp, mapping, offs, data_size, w_flags,
                                 &page, &fsdata);
-       if (unlikely(ret < 0))
+       if (unlikely(*err < 0))
                goto out;
 
        if (!PageUptodate(page)) {
@@ -911,14 +878,17 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
        /* Release old bh and drop refs */
        try_to_release_page(page, 0);
 
-       ret = mext_replace_branches(handle, orig_inode, donor_inode,
-                                        orig_blk_offset, block_len_in_page);
-       if (ret < 0)
-               goto out;
-
-       /* Clear the inode cache not to refer to the old data */
-       ext4_ext_invalidate_cache(orig_inode);
-       ext4_ext_invalidate_cache(donor_inode);
+       replaced_count = mext_replace_branches(handle, orig_inode, donor_inode,
+                                       orig_blk_offset, block_len_in_page,
+                                       &err2);
+       if (err2) {
+               if (replaced_count) {
+                       block_len_in_page = replaced_count;
+                       replaced_size =
+                               block_len_in_page << orig_inode->i_blkbits;
+               } else
+                       goto out;
+       }
 
        if (!page_has_buffers(page))
                create_empty_buffers(page, 1 << orig_inode->i_blkbits, 0);
@@ -928,16 +898,16 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
                bh = bh->b_this_page;
 
        for (i = 0; i < block_len_in_page; i++) {
-               ret = ext4_get_block(orig_inode,
+               *err = ext4_get_block(orig_inode,
                                (sector_t)(orig_blk_offset + i), bh, 0);
-               if (ret < 0)
+               if (*err < 0)
                        goto out;
 
                if (bh->b_this_page != NULL)
                        bh = bh->b_this_page;
        }
 
-       ret = a_ops->write_end(o_filp, mapping, offs, data_len, data_len,
+       *err = a_ops->write_end(o_filp, mapping, offs, data_size, replaced_size,
                               page, fsdata);
        page = NULL;
 
@@ -951,7 +921,10 @@ out:
 out2:
        ext4_journal_stop(handle);
 
-       return ret < 0 ? ret : 0;
+       if (err2)
+               *err = err2;
+
+       return replaced_count;
 }
 
 /**
@@ -962,7 +935,6 @@ out2:
  * @orig_start:                logical start offset in block for orig
  * @donor_start:       logical start offset in block for donor
  * @len:               the number of blocks to be moved
- * @moved_len:         moved block length
  *
  * Check the arguments of ext4_move_extents() whether the files can be
  * exchanged with each other.
@@ -970,8 +942,8 @@ out2:
  */
 static int
 mext_check_arguments(struct inode *orig_inode,
-                         struct inode *donor_inode, __u64 orig_start,
-                         __u64 donor_start, __u64 *len, __u64 moved_len)
+                    struct inode *donor_inode, __u64 orig_start,
+                    __u64 donor_start, __u64 *len)
 {
        ext4_lblk_t orig_blocks, donor_blocks;
        unsigned int blkbits = orig_inode->i_blkbits;
@@ -985,6 +957,13 @@ mext_check_arguments(struct inode *orig_inode,
                return -EINVAL;
        }
 
+       if (donor_inode->i_mode & (S_ISUID|S_ISGID)) {
+               ext4_debug("ext4 move extent: suid or sgid is set"
+                          " to donor file [ino:orig %lu, donor %lu]\n",
+                          orig_inode->i_ino, donor_inode->i_ino);
+               return -EINVAL;
+       }
+
        /* Ext4 move extent does not support swapfile */
        if (IS_SWAPFILE(orig_inode) || IS_SWAPFILE(donor_inode)) {
                ext4_debug("ext4 move extent: The argument files should "
@@ -1025,13 +1004,6 @@ mext_check_arguments(struct inode *orig_inode,
                return -EINVAL;
        }
 
-       if (moved_len) {
-               ext4_debug("ext4 move extent: moved_len should be 0 "
-                       "[ino:orig %lu, donor %lu]\n", orig_inode->i_ino,
-                       donor_inode->i_ino);
-               return -EINVAL;
-       }
-
        if ((orig_start > EXT_MAX_BLOCK) ||
            (donor_start > EXT_MAX_BLOCK) ||
            (*len > EXT_MAX_BLOCK) ||
@@ -1088,7 +1060,7 @@ mext_check_arguments(struct inode *orig_inode,
        }
 
        if (!*len) {
-               ext4_debug("ext4 move extent: len shoudld not be 0 "
+               ext4_debug("ext4 move extent: len should not be 0 "
                        "[ino:orig %lu, donor %lu]\n", orig_inode->i_ino,
                        donor_inode->i_ino);
                return -EINVAL;
@@ -1232,16 +1204,16 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                return -EINVAL;
        }
 
-       /* protect orig and donor against a truncate */
+       /* Protect orig and donor inodes against a truncate */
        ret1 = mext_inode_double_lock(orig_inode, donor_inode);
        if (ret1 < 0)
                return ret1;
 
-       mext_double_down_read(orig_inode, donor_inode);
+       /* Protect extent tree against block allocations via delalloc */
+       double_down_write_data_sem(orig_inode, donor_inode);
        /* Check the filesystem environment whether move_extent can be done */
        ret1 = mext_check_arguments(orig_inode, donor_inode, orig_start,
-                                       donor_start, &len, *moved_len);
-       mext_double_up_read(orig_inode, donor_inode);
+                                   donor_start, &len);
        if (ret1)
                goto out;
 
@@ -1355,36 +1327,39 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                seq_start = le32_to_cpu(ext_cur->ee_block);
                rest_blocks = seq_blocks;
 
-               /* Discard preallocations of two inodes */
-               down_write(&EXT4_I(orig_inode)->i_data_sem);
-               ext4_discard_preallocations(orig_inode);
-               up_write(&EXT4_I(orig_inode)->i_data_sem);
-
-               down_write(&EXT4_I(donor_inode)->i_data_sem);
-               ext4_discard_preallocations(donor_inode);
-               up_write(&EXT4_I(donor_inode)->i_data_sem);
+               /*
+                * Up semaphore to avoid following problems:
+                * a. transaction deadlock among ext4_journal_start,
+                *    ->write_begin via pagefault, and jbd2_journal_commit
+                * b. racing with ->readpage, ->write_begin, and ext4_get_block
+                *    in move_extent_per_page
+                */
+               double_up_write_data_sem(orig_inode, donor_inode);
 
                while (orig_page_offset <= seq_end_page) {
 
                        /* Swap original branches with new branches */
-                       ret1 = move_extent_per_page(o_filp, donor_inode,
+                       block_len_in_page = move_extent_per_page(
+                                               o_filp, donor_inode,
                                                orig_page_offset,
                                                data_offset_in_page,
-                                               block_len_in_page, uninit);
-                       if (ret1 < 0)
-                               goto out;
-                       orig_page_offset++;
+                                               block_len_in_page, uninit,
+                                               &ret1);
+
                        /* Count how many blocks we have exchanged */
                        *moved_len += block_len_in_page;
+                       if (ret1 < 0)
+                               break;
                        if (*moved_len > len) {
                                ext4_error(orig_inode->i_sb, __func__,
                                        "We replaced blocks too much! "
                                        "sum of replaced: %llu requested: %llu",
                                        *moved_len, len);
                                ret1 = -EIO;
-                               goto out;
+                               break;
                        }
 
+                       orig_page_offset++;
                        data_offset_in_page = 0;
                        rest_blocks -= block_len_in_page;
                        if (rest_blocks > blocks_per_page)
@@ -1393,6 +1368,10 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
                                block_len_in_page = rest_blocks;
                }
 
+               double_down_write_data_sem(orig_inode, donor_inode);
+               if (ret1 < 0)
+                       break;
+
                /* Decrease buffer counter */
                if (holecheck_path)
                        ext4_ext_drop_refs(holecheck_path);
@@ -1414,6 +1393,11 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
 
        }
 out:
+       if (*moved_len) {
+               ext4_discard_preallocations(orig_inode);
+               ext4_discard_preallocations(donor_inode);
+       }
+
        if (orig_path) {
                ext4_ext_drop_refs(orig_path);
                kfree(orig_path);
@@ -1422,7 +1406,7 @@ out:
                ext4_ext_drop_refs(holecheck_path);
                kfree(holecheck_path);
        }
-
+       double_up_write_data_sem(orig_inode, donor_inode);
        ret2 = mext_inode_double_unlock(orig_inode, donor_inode);
 
        if (ret1)
index 6d2c1b8..17a17e1 100644 (file)
@@ -1292,9 +1292,6 @@ errout:
  * add_dirent_to_buf will attempt search the directory block for
  * space.  It will return -ENOSPC if no space is available, and -EIO
  * and -EEXIST if directory entry already exists.
- *
- * NOTE!  bh is NOT released in the case where ENOSPC is returned.  In
- * all other cases bh is released.
  */
 static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
                             struct inode *inode, struct ext4_dir_entry_2 *de,
@@ -1315,14 +1312,10 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
                top = bh->b_data + blocksize - reclen;
                while ((char *) de <= top) {
                        if (!ext4_check_dir_entry("ext4_add_entry", dir, de,
-                                                 bh, offset)) {
-                               brelse(bh);
+                                                 bh, offset))
                                return -EIO;
-                       }
-                       if (ext4_match(namelen, name, de)) {
-                               brelse(bh);
+                       if (ext4_match(namelen, name, de))
                                return -EEXIST;
-                       }
                        nlen = EXT4_DIR_REC_LEN(de->name_len);
                        rlen = ext4_rec_len_from_disk(de->rec_len, blocksize);
                        if ((de->inode? rlen - nlen: rlen) >= reclen)
@@ -1337,7 +1330,6 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
        err = ext4_journal_get_write_access(handle, bh);
        if (err) {
                ext4_std_error(dir->i_sb, err);
-               brelse(bh);
                return err;
        }
 
@@ -1377,7 +1369,6 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
        err = ext4_handle_dirty_metadata(handle, dir, bh);
        if (err)
                ext4_std_error(dir->i_sb, err);
-       brelse(bh);
        return 0;
 }
 
@@ -1471,7 +1462,9 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
        if (!(de))
                return retval;
 
-       return add_dirent_to_buf(handle, dentry, inode, de, bh);
+       retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
+       brelse(bh);
+       return retval;
 }
 
 /*
@@ -1514,8 +1507,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
                if(!bh)
                        return retval;
                retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
-               if (retval != -ENOSPC)
+               if (retval != -ENOSPC) {
+                       brelse(bh);
                        return retval;
+               }
 
                if (blocks == 1 && !dx_fallback &&
                    EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX))
@@ -1528,7 +1523,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        de->inode = 0;
        de->rec_len = ext4_rec_len_to_disk(blocksize, blocksize);
-       return add_dirent_to_buf(handle, dentry, inode, de, bh);
+       retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
+       brelse(bh);
+       return retval;
 }
 
 /*
@@ -1561,10 +1558,8 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
                goto journal_error;
 
        err = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
-       if (err != -ENOSPC) {
-               bh = NULL;
+       if (err != -ENOSPC)
                goto cleanup;
-       }
 
        /* Block full, should compress but for now just split */
        dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n",
@@ -1657,7 +1652,6 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
        if (!de)
                goto cleanup;
        err = add_dirent_to_buf(handle, dentry, inode, de, bh);
-       bh = NULL;
        goto cleanup;
 
 journal_error:
@@ -1775,7 +1769,7 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, int mode,
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb));
+                                       EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -1809,7 +1803,7 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry,
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb));
+                                       EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -1846,7 +1840,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 +
-                                       2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb));
+                                       EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -2259,7 +2253,7 @@ static int ext4_symlink(struct inode *dir,
 retry:
        handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) +
                                        EXT4_INDEX_EXTRA_TRANS_BLOCKS + 5 +
-                                       2*EXT4_QUOTA_INIT_BLOCKS(dir->i_sb));
+                                       EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
index 3cfc343..3b2c554 100644 (file)
@@ -247,7 +247,7 @@ static int setup_new_group_blocks(struct super_block *sb,
                        goto exit_bh;
 
                if (IS_ERR(gdb = bclean(handle, sb, block))) {
-                       err = PTR_ERR(bh);
+                       err = PTR_ERR(gdb);
                        goto exit_bh;
                }
                ext4_handle_dirty_metadata(handle, NULL, gdb);
index d4ca92a..8b58a14 100644 (file)
@@ -603,10 +603,6 @@ static void ext4_put_super(struct super_block *sb)
        if (sb->s_dirt)
                ext4_commit_super(sb, 1);
 
-       ext4_release_system_zone(sb);
-       ext4_mb_release(sb);
-       ext4_ext_release(sb);
-       ext4_xattr_put_super(sb);
        if (sbi->s_journal) {
                err = jbd2_journal_destroy(sbi->s_journal);
                sbi->s_journal = NULL;
@@ -614,6 +610,12 @@ static void ext4_put_super(struct super_block *sb)
                        ext4_abort(sb, __func__,
                                   "Couldn't clean up the journal");
        }
+
+       ext4_release_system_zone(sb);
+       ext4_mb_release(sb);
+       ext4_ext_release(sb);
+       ext4_xattr_put_super(sb);
+
        if (!(sb->s_flags & MS_RDONLY)) {
                EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
                es->s_state = cpu_to_le16(sbi->s_mount_state);
@@ -704,6 +706,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        spin_lock_init(&(ei->i_block_reservation_lock));
        INIT_LIST_HEAD(&ei->i_aio_dio_complete_list);
        ei->cur_aio_dio = NULL;
+       ei->i_sync_tid = 0;
+       ei->i_datasync_tid = 0;
 
        return &ei->vfs_inode;
 }
@@ -899,6 +903,12 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
        if (test_opt(sb, NO_AUTO_DA_ALLOC))
                seq_puts(seq, ",noauto_da_alloc");
 
+       if (test_opt(sb, DISCARD))
+               seq_puts(seq, ",discard");
+
+       if (test_opt(sb, NOLOAD))
+               seq_puts(seq, ",norecovery");
+
        ext4_show_quota_options(seq, sb);
 
        return 0;
@@ -1079,7 +1089,8 @@ enum {
        Opt_usrquota, Opt_grpquota, Opt_i_version,
        Opt_stripe, Opt_delalloc, Opt_nodelalloc,
        Opt_block_validity, Opt_noblock_validity,
-       Opt_inode_readahead_blks, Opt_journal_ioprio
+       Opt_inode_readahead_blks, Opt_journal_ioprio,
+       Opt_discard, Opt_nodiscard,
 };
 
 static const match_table_t tokens = {
@@ -1104,6 +1115,7 @@ static const match_table_t tokens = {
        {Opt_acl, "acl"},
        {Opt_noacl, "noacl"},
        {Opt_noload, "noload"},
+       {Opt_noload, "norecovery"},
        {Opt_nobh, "nobh"},
        {Opt_bh, "bh"},
        {Opt_commit, "commit=%u"},
@@ -1144,6 +1156,8 @@ static const match_table_t tokens = {
        {Opt_auto_da_alloc, "auto_da_alloc=%u"},
        {Opt_auto_da_alloc, "auto_da_alloc"},
        {Opt_noauto_da_alloc, "noauto_da_alloc"},
+       {Opt_discard, "discard"},
+       {Opt_nodiscard, "nodiscard"},
        {Opt_err, NULL},
 };
 
@@ -1565,6 +1579,12 @@ set_qf_format:
                        else
                                set_opt(sbi->s_mount_opt,NO_AUTO_DA_ALLOC);
                        break;
+               case Opt_discard:
+                       set_opt(sbi->s_mount_opt, DISCARD);
+                       break;
+               case Opt_nodiscard:
+                       clear_opt(sbi->s_mount_opt, DISCARD);
+                       break;
                default:
                        ext4_msg(sb, KERN_ERR,
                               "Unrecognized mount option \"%s\" "
@@ -1673,14 +1693,14 @@ static int ext4_fill_flex_info(struct super_block *sb)
        size_t size;
        int i;
 
-       if (!sbi->s_es->s_log_groups_per_flex) {
+       sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
+       groups_per_flex = 1 << sbi->s_log_groups_per_flex;
+
+       if (groups_per_flex < 2) {
                sbi->s_log_groups_per_flex = 0;
                return 1;
        }
 
-       sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
-       groups_per_flex = 1 << sbi->s_log_groups_per_flex;
-
        /* We allocate both existing and potentially added groups */
        flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) +
                        ((le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) + 1) <<
@@ -2721,26 +2741,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
            EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_HAS_JOURNAL)) {
                if (ext4_load_journal(sb, es, journal_devnum))
                        goto failed_mount3;
-               if (!(sb->s_flags & MS_RDONLY) &&
-                   EXT4_SB(sb)->s_journal->j_failed_commit) {
-                       ext4_msg(sb, KERN_CRIT, "error: "
-                              "ext4_fill_super: Journal transaction "
-                              "%u is corrupt",
-                              EXT4_SB(sb)->s_journal->j_failed_commit);
-                       if (test_opt(sb, ERRORS_RO)) {
-                               ext4_msg(sb, KERN_CRIT,
-                                      "Mounting filesystem read-only");
-                               sb->s_flags |= MS_RDONLY;
-                               EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-                               es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
-                       }
-                       if (test_opt(sb, ERRORS_PANIC)) {
-                               EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-                               es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
-                               ext4_commit_super(sb, 1);
-                               goto failed_mount4;
-                       }
-               }
        } else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) &&
              EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) {
                ext4_msg(sb, KERN_ERR, "required journal recovery "
@@ -3668,13 +3668,11 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last;
        buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
                       percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter);
-       ext4_free_blocks_count_set(es, buf->f_bfree);
        buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);
        if (buf->f_bfree < ext4_r_blocks_count(es))
                buf->f_bavail = 0;
        buf->f_files = le32_to_cpu(es->s_inodes_count);
        buf->f_ffree = percpu_counter_sum_positive(&sbi->s_freeinodes_counter);
-       es->s_free_inodes_count = cpu_to_le32(buf->f_ffree);
        buf->f_namelen = EXT4_NAME_LEN;
        fsid = le64_to_cpup((void *)es->s_uuid) ^
               le64_to_cpup((void *)es->s_uuid + sizeof(u64));
@@ -3966,6 +3964,58 @@ static int ext4_get_sb(struct file_system_type *fs_type, int flags,
        return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super,mnt);
 }
 
+#if !defined(CONTIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
+static struct file_system_type ext2_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "ext2",
+       .get_sb         = ext4_get_sb,
+       .kill_sb        = kill_block_super,
+       .fs_flags       = FS_REQUIRES_DEV,
+};
+
+static inline void register_as_ext2(void)
+{
+       int err = register_filesystem(&ext2_fs_type);
+       if (err)
+               printk(KERN_WARNING
+                      "EXT4-fs: Unable to register as ext2 (%d)\n", err);
+}
+
+static inline void unregister_as_ext2(void)
+{
+       unregister_filesystem(&ext2_fs_type);
+}
+#else
+static inline void register_as_ext2(void) { }
+static inline void unregister_as_ext2(void) { }
+#endif
+
+#if !defined(CONTIG_EXT3_FS) && !defined(CONFIG_EXT3_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT23)
+static struct file_system_type ext3_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "ext3",
+       .get_sb         = ext4_get_sb,
+       .kill_sb        = kill_block_super,
+       .fs_flags       = FS_REQUIRES_DEV,
+};
+
+static inline void register_as_ext3(void)
+{
+       int err = register_filesystem(&ext3_fs_type);
+       if (err)
+               printk(KERN_WARNING
+                      "EXT4-fs: Unable to register as ext3 (%d)\n", err);
+}
+
+static inline void unregister_as_ext3(void)
+{
+       unregister_filesystem(&ext3_fs_type);
+}
+#else
+static inline void register_as_ext3(void) { }
+static inline void unregister_as_ext3(void) { }
+#endif
+
 static struct file_system_type ext4_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "ext4",
@@ -3995,11 +4045,15 @@ static int __init init_ext4_fs(void)
        err = init_inodecache();
        if (err)
                goto out1;
+       register_as_ext2();
+       register_as_ext3();
        err = register_filesystem(&ext4_fs_type);
        if (err)
                goto out;
        return 0;
 out:
+       unregister_as_ext2();
+       unregister_as_ext3();
        destroy_inodecache();
 out1:
        exit_ext4_xattr();
@@ -4015,6 +4069,8 @@ out4:
 
 static void __exit exit_ext4_fs(void)
 {
+       unregister_as_ext2();
+       unregister_as_ext3();
        unregister_filesystem(&ext4_fs_type);
        destroy_inodecache();
        exit_ext4_xattr();
index fed5b01..910bf9a 100644 (file)
@@ -482,9 +482,10 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
                ea_bdebug(bh, "refcount now=0; freeing");
                if (ce)
                        mb_cache_entry_free(ce);
-               ext4_free_blocks(handle, inode, bh->b_blocknr, 1, 1);
                get_bh(bh);
-               ext4_forget(handle, 1, inode, bh, bh->b_blocknr);
+               ext4_free_blocks(handle, inode, bh, 0, 1,
+                                EXT4_FREE_BLOCKS_METADATA |
+                                EXT4_FREE_BLOCKS_FORGET);
        } else {
                le32_add_cpu(&BHDR(bh)->h_refcount, -1);
                error = ext4_handle_dirty_metadata(handle, inode, bh);
@@ -832,7 +833,8 @@ inserted:
                        new_bh = sb_getblk(sb, block);
                        if (!new_bh) {
 getblk_failed:
-                               ext4_free_blocks(handle, inode, block, 1, 1);
+                               ext4_free_blocks(handle, inode, 0, block, 1,
+                                                EXT4_FREE_BLOCKS_METADATA);
                                error = -EIO;
                                goto cleanup;
                        }
@@ -988,6 +990,10 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
        if (error)
                goto cleanup;
 
+       error = ext4_journal_get_write_access(handle, is.iloc.bh);
+       if (error)
+               goto cleanup;
+
        if (EXT4_I(inode)->i_state & EXT4_STATE_NEW) {
                struct ext4_inode *raw_inode = ext4_raw_inode(&is.iloc);
                memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size);
@@ -1013,9 +1019,6 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
                if (flags & XATTR_CREATE)
                        goto cleanup;
        }
-       error = ext4_journal_get_write_access(handle, is.iloc.bh);
-       if (error)
-               goto cleanup;
        if (!value) {
                if (!is.s.not_found)
                        error = ext4_xattr_ibody_set(handle, inode, &i, &is);
index d4cfd6d..8896c1d 100644 (file)
@@ -636,6 +636,10 @@ void jbd2_journal_commit_transaction(journal_t *journal)
                JBUFFER_TRACE(jh, "ph3: write metadata");
                flags = jbd2_journal_write_metadata_buffer(commit_transaction,
                                                      jh, &new_jh, blocknr);
+               if (flags < 0) {
+                       jbd2_journal_abort(journal, flags);
+                       continue;
+               }
                set_bit(BH_JWrite, &jh2bh(new_jh)->b_state);
                wbuf[bufs++] = jh2bh(new_jh);
 
index fed8538..b7ca3a9 100644 (file)
@@ -78,6 +78,7 @@ EXPORT_SYMBOL(jbd2_journal_errno);
 EXPORT_SYMBOL(jbd2_journal_ack_err);
 EXPORT_SYMBOL(jbd2_journal_clear_err);
 EXPORT_SYMBOL(jbd2_log_wait_commit);
+EXPORT_SYMBOL(jbd2_log_start_commit);
 EXPORT_SYMBOL(jbd2_journal_start_commit);
 EXPORT_SYMBOL(jbd2_journal_force_commit_nested);
 EXPORT_SYMBOL(jbd2_journal_wipe);
@@ -358,6 +359,10 @@ repeat:
 
                jbd_unlock_bh_state(bh_in);
                tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS);
+               if (!tmp) {
+                       jbd2_journal_put_journal_head(new_jh);
+                       return -ENOMEM;
+               }
                jbd_lock_bh_state(bh_in);
                if (jh_in->b_frozen_data) {
                        jbd2_free(tmp, bh_in->b_size);
@@ -1248,6 +1253,13 @@ int jbd2_journal_load(journal_t *journal)
        if (jbd2_journal_recover(journal))
                goto recovery_error;
 
+       if (journal->j_failed_commit) {
+               printk(KERN_ERR "JBD2: journal transaction %u on %s "
+                      "is corrupt.\n", journal->j_failed_commit,
+                      journal->j_devname);
+               return -EIO;
+       }
+
        /* OK, we've finished with the dynamic journal bits:
         * reinitialise the dynamic contents of the superblock in memory
         * and reset them on disk. */
index f25e70c..f029441 100644 (file)
@@ -177,7 +177,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
                spin_unlock(&jffs2_compressor_list_lock);
                break;
        default:
-               printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+               printk(KERN_ERR "JFFS2: unknown compression mode.\n");
        }
  out:
        if (ret == JFFS2_COMPR_NONE) {
index 1a80301..378991c 100644 (file)
@@ -931,7 +931,7 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
  * Helper function for jffs2_get_inode_nodes().
  * The function detects whether more data should be read and reads it if yes.
  *
- * Returns: 0 on succes;
+ * Returns: 0 on success;
  *         negative error code on failure.
  */
 static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
index 082e844..4b10788 100644 (file)
@@ -31,7 +31,7 @@
  *   is used to release xattr name/value pair and detach from c->xattrindex.
  * reclaim_xattr_datum(c)
  *   is used to reclaim xattr name/value pairs on the xattr name/value pair cache when
- *   memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold 
+ *   memory usage by cache is over c->xdatum_mem_threshold. Currently, this threshold 
  *   is hard coded as 32KiB.
  * do_verify_xattr_datum(c, xd)
  *   is used to load the xdatum informations without name/value pair from the medium.
index 2bc7d8a..d9b031c 100644 (file)
@@ -755,7 +755,7 @@ int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results)
         * allocation group.
         */
        if ((blkno & (bmp->db_agsize - 1)) == 0)
-               /* check if the AG is currenly being written to.
+               /* check if the AG is currently being written to.
                 * if so, call dbNextAG() to find a non-busy
                 * AG with sufficient free space.
                 */
@@ -3337,7 +3337,7 @@ int dbExtendFS(struct inode *ipbmap, s64 blkno,   s64 nblocks)
        for (i = 0, n = 0; i < agno; n++) {
                bmp->db_agfree[n] = 0;  /* init collection point */
 
-               /* coalesce cotiguous k AGs; */
+               /* coalesce contiguous k AGs; */
                for (j = 0; j < k && i < agno; j++, i++) {
                        /* merge AGi to AGn */
                        bmp->db_agfree[n] += bmp->db_agfree[i];
index 0d58caf..ec8f45f 100644 (file)
@@ -835,7 +835,7 @@ static int ncp_ioctl_need_write(unsigned int cmd)
        case NCP_IOC_SETROOT:
                return 0;
        default:
-               /* unkown IOCTL command, assume write */
+               /* unknown IOCTL command, assume write */
                return 1;
        }
 }
index 1d1d1a2..5ef5f36 100644 (file)
@@ -741,10 +741,6 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
 
        /* create/update an inode mark */
        ret = inotify_update_watch(group, inode, mask);
-       if (unlikely(ret))
-               goto path_put_and_out;
-
-path_put_and_out:
        path_put(&path);
 fput_and_out:
        fput_light(filp, fput_needed);
index 9669541..08f7530 100644 (file)
@@ -927,7 +927,7 @@ lock_retry_remap:
                return 0;
 
        ntfs_debug("Failed. Returning error code %s.", err == -EOVERFLOW ?
-                       "EOVERFLOW" : (!err ? "EIO" : "unkown error"));
+                       "EOVERFLOW" : (!err ? "EIO" : "unknown error"));
        return err < 0 ? err : -EIO;
 
 read_err:
index 663c0e3..43179dd 100644 (file)
@@ -399,7 +399,7 @@ static inline void ntfs_fault_in_pages_readable_iovec(const struct iovec *iov,
  * @cached_page: allocated but as yet unused page
  * @lru_pvec:  lru-buffering pagevec of caller
  *
- * Obtain @nr_pages locked page cache pages from the mapping @maping and
+ * Obtain @nr_pages locked page cache pages from the mapping @mapping and
  * starting at index @index.
  *
  * If a page is newly created, increment its refcount and add it to the
@@ -1281,7 +1281,7 @@ rl_not_mapped_enoent:
 
 /*
  * Copy as much as we can into the pages and return the number of bytes which
- * were sucessfully copied.  If a fault is encountered then clear the pages
+ * were successfully copied.  If a fault is encountered then clear the pages
  * out to (ofs + bytes) and return the number of bytes which were copied.
  */
 static inline size_t ntfs_copy_from_user(struct page **pages,
index 89b0298..4dadcdf 100644 (file)
@@ -338,7 +338,7 @@ err_out:
  * copy of the complete multi sector transfer deprotected page.  On failure,
  * *@wrp is undefined.
  *
- * Simillarly, if @lsn is not NULL, on succes *@lsn will be set to the current
+ * Simillarly, if @lsn is not NULL, on success *@lsn will be set to the current
  * logfile lsn according to this restart page.  On failure, *@lsn is undefined.
  *
  * The following error codes are defined:
index 38a42f5..7c7198a 100644 (file)
@@ -2398,7 +2398,7 @@ static int ocfs2_leftmost_rec_contains(struct ocfs2_extent_list *el, u32 cpos)
  *
  * The array is assumed to be large enough to hold an entire path (tree depth).
  *
- * Upon succesful return from this function:
+ * Upon successful return from this function:
  *
  * - The 'right_path' array will contain a path to the leaf block
  *   whose range contains e_cpos.
index a1163b8..b7428c5 100644 (file)
@@ -47,7 +47,7 @@
  * Calculate the bit offset in the hamming code buffer based on the bit's
  * offset in the data buffer.  Since the hamming code reserves all
  * power-of-two bits for parity, the data bit number and the code bit
- * number are offest by all the parity bits beforehand.
+ * number are offset by all the parity bits beforehand.
  *
  * Recall that bit numbers in hamming code are 1-based.  This function
  * takes the 0-based data bit from the caller.
index 83bcaf2..03ccf9a 100644 (file)
@@ -2586,7 +2586,7 @@ fail:
         * is complete everywhere.  if the target dies while this is
         * going on, some nodes could potentially see the target as the
         * master, so it is important that my recovery finds the migration
-        * mle and sets the master to UNKNONWN. */
+        * mle and sets the master to UNKNOWN. */
 
 
        /* wait for new node to assert master */
index 0d38d67..c5e4a49 100644 (file)
@@ -1855,7 +1855,7 @@ int ocfs2_file_lock(struct file *file, int ex, int trylock)
                 * outstanding lock request, so a cancel convert is
                 * required. We intentionally overwrite 'ret' - if the
                 * cancel fails and the lock was granted, it's easier
-                * to just bubble sucess back up to the user.
+                * to just bubble success back up to the user.
                 */
                ret = ocfs2_flock_handle_signal(lockres, level);
        } else if (!ret && (level > lockres->l_level)) {
index 54c16b6..bf34c49 100644 (file)
@@ -659,7 +659,7 @@ static int __ocfs2_journal_access(handle_t *handle,
 
        default:
                status = -EINVAL;
-               mlog(ML_ERROR, "Uknown access type!\n");
+               mlog(ML_ERROR, "Unknown access type!\n");
        }
        if (!status && ocfs2_meta_ecc(osb) && triggers)
                jbd2_journal_set_triggers(bh, &triggers->ot_triggers);
index 3a0df7a..30967e3 100644 (file)
@@ -2431,7 +2431,7 @@ out:
  * we gonna touch and whether we need to create new blocks.
  *
  * Normally the refcount blocks store these refcount should be
- * continguous also, so that we can get the number easily.
+ * contiguous also, so that we can get the number easily.
  * As for meta_ac, we will at most add split 2 refcount record and
  * 2 more refcount block, so just check it in a rough way.
  *
index e1c0ec0..0822345 100644 (file)
@@ -85,7 +85,7 @@ out:
 }
 
 /*
- * Tries to allocate exactly one block.  Returns true if sucessful.
+ * Tries to allocate exactly one block.  Returns true if successful.
  */
 int omfs_allocate_block(struct super_block *sb, u64 block)
 {
index 0afba06..32f5d13 100644 (file)
@@ -67,7 +67,7 @@ unsigned long qnx4_count_free_blocks(struct super_block *sb)
 
        while (total < size) {
                if ((bh = sb_bread(sb, start + offset)) == NULL) {
-                       printk("qnx4: I/O error in counting free blocks\n");
+                       printk(KERN_ERR "qnx4: I/O error in counting free blocks\n");
                        break;
                }
                count_bits(bh->b_data, size - total, &total_free);
index 86cc39c..6f30c3d 100644 (file)
@@ -26,8 +26,8 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir)
        int ix, ino;
        int size;
 
-       QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size));
-       QNX4DEBUG(("filp->f_pos         = %ld\n", (long) filp->f_pos));
+       QNX4DEBUG((KERN_INFO "qnx4_readdir:i_size = %ld\n", (long) inode->i_size));
+       QNX4DEBUG((KERN_INFO "filp->f_pos         = %ld\n", (long) filp->f_pos));
 
        lock_kernel();
 
@@ -50,7 +50,7 @@ static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir)
                                        size = QNX4_NAME_MAX;
 
                                if ( ( de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK) ) != 0 ) {
-                                       QNX4DEBUG(("qnx4_readdir:%.*s\n", size, de->di_fname));
+                                       QNX4DEBUG((KERN_INFO "qnx4_readdir:%.*s\n", size, de->di_fname));
                                        if ( ( de->di_status & QNX4_FILE_LINK ) == 0 )
                                                ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1;
                                        else {
index d2cd179..449f5a6 100644 (file)
@@ -107,7 +107,7 @@ static int qnx4_get_block( struct inode *inode, sector_t iblock, struct buffer_h
 {
        unsigned long phys;
 
-       QNX4DEBUG(("qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock));
+       QNX4DEBUG((KERN_INFO "qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock));
 
        phys = qnx4_block_map( inode, iblock );
        if ( phys ) {
@@ -142,12 +142,12 @@ unsigned long qnx4_block_map( struct inode *inode, long iblock )
                                // read next xtnt block.
                                bh = sb_bread(inode->i_sb, i_xblk - 1);
                                if ( !bh ) {
-                                       QNX4DEBUG(("qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1));
+                                       QNX4DEBUG((KERN_ERR "qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1));
                                        return -EIO;
                                }
                                xblk = (struct qnx4_xblk*)bh->b_data;
                                if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) {
-                                       QNX4DEBUG(("qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk));
+                                       QNX4DEBUG((KERN_ERR "qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk));
                                        return -EIO;
                                }
                        }
@@ -168,7 +168,7 @@ unsigned long qnx4_block_map( struct inode *inode, long iblock )
                        brelse( bh );
        }
 
-       QNX4DEBUG(("qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block));
+       QNX4DEBUG((KERN_INFO "qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block));
        return block;
 }
 
@@ -209,7 +209,7 @@ static const char *qnx4_checkroot(struct super_block *sb)
        if (*(qnx4_sb(sb)->sb->RootDir.di_fname) != '/') {
                return "no qnx4 filesystem (no root dir).";
        } else {
-               QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", sb->s_id));
+               QNX4DEBUG((KERN_NOTICE "QNX4 filesystem found on dev %s.\n", sb->s_id));
                rd = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_blk) - 1;
                rl = le32_to_cpu(qnx4_sb(sb)->sb->RootDir.di_first_xtnt.xtnt_size);
                for (j = 0; j < rl; j++) {
@@ -220,7 +220,7 @@ static const char *qnx4_checkroot(struct super_block *sb)
                        for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) {
                                rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE);
                                if (rootdir->di_fname != NULL) {
-                                       QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname));
+                                       QNX4DEBUG((KERN_INFO "rootdir entry found : [%s]\n", rootdir->di_fname));
                                        if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) {
                                                found = 1;
                                                qnx4_sb(sb)->BitMap = kmalloc( sizeof( struct qnx4_inode_entry ), GFP_KERNEL );
@@ -265,12 +265,12 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent)
           if we don't belong here... */
        bh = sb_bread(s, 1);
        if (!bh) {
-               printk("qnx4: unable to read the superblock\n");
+               printk(KERN_ERR "qnx4: unable to read the superblock\n");
                goto outnobh;
        }
        if ( le32_to_cpup((__le32*) bh->b_data) != QNX4_SUPER_MAGIC ) {
                if (!silent)
-                       printk("qnx4: wrong fsid in superblock.\n");
+                       printk(KERN_ERR "qnx4: wrong fsid in superblock.\n");
                goto out;
        }
        s->s_op = &qnx4_sops;
@@ -284,14 +284,14 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent)
        errmsg = qnx4_checkroot(s);
        if (errmsg != NULL) {
                if (!silent)
-                       printk("qnx4: %s\n", errmsg);
+                       printk(KERN_ERR "qnx4: %s\n", errmsg);
                goto out;
        }
 
        /* does root not have inode number QNX4_ROOT_INO ?? */
        root = qnx4_iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK);
        if (IS_ERR(root)) {
-               printk("qnx4: get inode failed\n");
+               printk(KERN_ERR "qnx4: get inode failed\n");
                ret = PTR_ERR(root);
                goto out;
        }
@@ -374,7 +374,7 @@ struct inode *qnx4_iget(struct super_block *sb, unsigned long ino)
        qnx4_inode = qnx4_raw_inode(inode);
        inode->i_mode = 0;
 
-       QNX4DEBUG(("Reading inode : [%d]\n", ino));
+       QNX4DEBUG((KERN_INFO "reading inode : [%d]\n", ino));
        if (!ino) {
                printk(KERN_ERR "qnx4: bad inode number on dev %s: %lu is "
                                "out of range\n",
@@ -385,7 +385,7 @@ struct inode *qnx4_iget(struct super_block *sb, unsigned long ino)
        block = ino / QNX4_INODES_PER_BLOCK;
 
        if (!(bh = sb_bread(sb, block))) {
-               printk("qnx4: major problem: unable to read inode from dev "
+               printk(KERN_ERR "qnx4: major problem: unable to read inode from dev "
                       "%s\n", sb->s_id);
                iget_failed(inode);
                return ERR_PTR(-EIO);
@@ -499,7 +499,7 @@ static int __init init_qnx4_fs(void)
                return err;
        }
 
-       printk("QNX4 filesystem 0.2.3 registered.\n");
+       printk(KERN_INFO "QNX4 filesystem 0.2.3 registered.\n");
        return 0;
 }
 
index ae1e7ed..58703eb 100644 (file)
@@ -30,7 +30,7 @@ static int qnx4_match(int len, const char *name,
        int namelen, thislen;
 
        if (bh == NULL) {
-               printk("qnx4: matching unassigned buffer !\n");
+               printk(KERN_WARNING "qnx4: matching unassigned buffer !\n");
                return 0;
        }
        de = (struct qnx4_inode_entry *) (bh->b_data + *offset);
@@ -66,7 +66,7 @@ static struct buffer_head *qnx4_find_entry(int len, struct inode *dir,
 
        *res_dir = NULL;
        if (!dir->i_sb) {
-               printk("qnx4: no superblock on dir.\n");
+               printk(KERN_WARNING "qnx4: no superblock on dir.\n");
                return NULL;
        }
        bh = NULL;
@@ -124,7 +124,7 @@ struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nam
        foundinode = qnx4_iget(dir->i_sb, ino);
        if (IS_ERR(foundinode)) {
                unlock_kernel();
-               QNX4DEBUG(("qnx4: lookup->iget -> error %ld\n",
+               QNX4DEBUG((KERN_ERR "qnx4: lookup->iget -> error %ld\n",
                           PTR_ERR(foundinode)));
                return ERR_CAST(foundinode);
        }
index d2f3133..6591cb2 100644 (file)
@@ -831,7 +831,7 @@ static int get_empty_nodes(struct tree_balance *tb, int h)
                RFALSE(buffer_dirty(new_bh) ||
                       buffer_journaled(new_bh) ||
                       buffer_journal_dirty(new_bh),
-                      "PAP-8140: journlaled or dirty buffer %b for the new block",
+                      "PAP-8140: journaled or dirty buffer %b for the new block",
                       new_bh);
 
                /* Put empty buffers into the array. */
index dbc093a..8a771c5 100644 (file)
@@ -2014,7 +2014,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr,
                inum = key_inum_flash(c, &dent->key);
                fscki1 = read_add_inode(c, priv, inum);
                if (IS_ERR(fscki1)) {
-                       err = PTR_ERR(fscki);
+                       err = PTR_ERR(fscki1);
                        ubifs_err("error %d while processing entry node and "
                                  "trying to find parent inode node %lu",
                                  err, (unsigned long)inum);
index 1009adc..39849f8 100644 (file)
@@ -1389,7 +1389,6 @@ static ssize_t ubifs_aio_write(struct kiocb *iocb, const struct iovec *iov,
                               unsigned long nr_segs, loff_t pos)
 {
        int err;
-       ssize_t ret;
        struct inode *inode = iocb->ki_filp->f_mapping->host;
        struct ubifs_info *c = inode->i_sb->s_fs_info;
 
@@ -1397,17 +1396,7 @@ static ssize_t ubifs_aio_write(struct kiocb *iocb, const struct iovec *iov,
        if (err)
                return err;
 
-       ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
-       if (ret < 0)
-               return ret;
-
-       if (ret > 0 && (IS_SYNC(inode) || iocb->ki_filp->f_flags & O_SYNC)) {
-               err = ubifs_sync_wbufs_by_inode(c, inode);
-               if (err)
-                       return err;
-       }
-
-       return ret;
+       return generic_file_aio_write(iocb, iov, nr_segs, pos);
 }
 
 static int ubifs_set_page_dirty(struct page *page)
index f94ddf7..868a55e 100644 (file)
@@ -23,7 +23,7 @@
 /*
  * This file implements functions needed to recover from unclean un-mounts.
  * When UBIFS is mounted, it checks a flag on the master node to determine if
- * an un-mount was completed sucessfully. If not, the process of mounting
+ * an un-mount was completed successfully. If not, the process of mounting
  * incorparates additional checking and fixing of on-flash data structures.
  * UBIFS always cleans away all remnants of an unclean un-mount, so that
  * errors do not accumulate. However UBIFS defers recovery if it is mounted
index 333e181..943ad56 100644 (file)
@@ -1842,22 +1842,32 @@ const struct super_operations ubifs_super_operations = {
  * @name: UBI volume name
  * @mode: UBI volume open mode
  *
- * There are several ways to specify UBI volumes when mounting UBIFS:
- * o ubiX_Y    - UBI device number X, volume Y;
- * o ubiY      - UBI device number 0, volume Y;
+ * The primary method of mounting UBIFS is by specifying the UBI volume
+ * character device node path. However, UBIFS may also be mounted withoug any
+ * character device node using one of the following methods:
+ *
+ * o ubiX_Y    - mount UBI device number X, volume Y;
+ * o ubiY      - mount UBI device number 0, volume Y;
  * o ubiX:NAME - mount UBI device X, volume with name NAME;
  * o ubi:NAME  - mount UBI device 0, volume with name NAME.
  *
  * Alternative '!' separator may be used instead of ':' (because some shells
  * like busybox may interpret ':' as an NFS host name separator). This function
- * returns ubi volume object in case of success and a negative error code in
- * case of failure.
+ * returns UBI volume description object in case of success and a negative
+ * error code in case of failure.
  */
 static struct ubi_volume_desc *open_ubi(const char *name, int mode)
 {
+       struct ubi_volume_desc *ubi;
        int dev, vol;
        char *endptr;
 
+       /* First, try to open using the device node path method */
+       ubi = ubi_open_volume_path(name, mode);
+       if (!IS_ERR(ubi))
+               return ubi;
+
+       /* Try the "nodev" method */
        if (name[0] != 'u' || name[1] != 'b' || name[2] != 'i')
                return ERR_PTR(-EINVAL);
 
index 6533ead..a2c16bc 100644 (file)
@@ -98,7 +98,7 @@ typedef struct xfs_dquot {
 #define dq_flags       q_lists.dqm_flags
 
 /*
- * Lock hierachy for q_qlock:
+ * Lock hierarchy for q_qlock:
  *     XFS_QLOCK_NORMAL is the implicit default,
  *     XFS_QLOCK_NESTED is the dquot with the higher id in xfs_dqlock2
  */
index e723b0f..5e1ad3c 100644 (file)
@@ -47,7 +47,7 @@
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
-#define ACPI_CA_VERSION                 0x20090903
+#define ACPI_CA_VERSION                 0x20091112
 
 #include "actypes.h"
 #include "actbl.h"
@@ -154,7 +154,8 @@ acpi_status
 acpi_walk_namespace(acpi_object_type type,
                    acpi_handle start_object,
                    u32 max_depth,
-                   acpi_walk_callback user_function,
+                   acpi_walk_callback pre_order_visit,
+                   acpi_walk_callback post_order_visit,
                    void *context, void **return_value);
 
 acpi_status
index 4c8d0af..fb2d63f 100644 (file)
@@ -47,7 +47,7 @@
 
 #elif defined(CONFIG_SPARSEMEM_VMEMMAP)
 
-/* memmap is virtually contigious.  */
+/* memmap is virtually contiguous.  */
 #define __pfn_to_page(pfn)     (vmemmap + (pfn))
 #define __page_to_pfn(page)    (unsigned long)((page) - vmemmap)
 
index d76b66a..7c38c14 100644 (file)
@@ -631,7 +631,7 @@ __SYSCALL(__NR_perf_event_open, sys_perf_event_open)
  * 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
+ * Last chance: are any of these important enough to
  * enable by default?
  */
 #ifdef __ARCH_WANT_SYSCALL_NO_AT
index 7cb50bd..e3f46e0 100644 (file)
 #ifndef _DRM_H_
 #define _DRM_H_
 
+#if defined(__linux__)
+
 #include <linux/types.h>
-#include <asm/ioctl.h>         /* For _IO* macros */
-#define DRM_IOCTL_NR(n)                _IOC_NR(n)
-#define DRM_IOC_VOID           _IOC_NONE
-#define DRM_IOC_READ           _IOC_READ
-#define DRM_IOC_WRITE          _IOC_WRITE
-#define DRM_IOC_READWRITE      _IOC_READ|_IOC_WRITE
-#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size)
+#include <asm/ioctl.h>
+typedef unsigned int drm_handle_t;
 
-#define DRM_MAJOR       226
-#define DRM_MAX_MINOR   15
+#else /* One of the BSDs */
+
+#include <sys/ioccom.h>
+#include <sys/types.h>
+typedef int8_t   __s8;
+typedef uint8_t  __u8;
+typedef int16_t  __s16;
+typedef uint16_t __u16;
+typedef int32_t  __s32;
+typedef uint32_t __u32;
+typedef int64_t  __s64;
+typedef uint64_t __u64;
+typedef unsigned long drm_handle_t;
+
+#endif
 
 #define DRM_NAME       "drm"     /**< Name in kernel, /dev, and /proc */
 #define DRM_MIN_ORDER  5         /**< At least 2^5 bytes = 32 bytes */
@@ -59,7 +69,6 @@
 #define _DRM_LOCK_IS_CONT(lock)           ((lock) & _DRM_LOCK_CONT)
 #define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
 
-typedef unsigned int drm_handle_t;
 typedef unsigned int drm_context_t;
 typedef unsigned int drm_drawable_t;
 typedef unsigned int drm_magic_t;
@@ -454,6 +463,7 @@ struct drm_irq_busid {
 enum drm_vblank_seq_type {
        _DRM_VBLANK_ABSOLUTE = 0x0,     /**< Wait for specific vblank sequence number */
        _DRM_VBLANK_RELATIVE = 0x1,     /**< Wait for given number of vblanks */
+       _DRM_VBLANK_EVENT = 0x4000000,   /**< Send event instead of blocking */
        _DRM_VBLANK_FLIP = 0x8000000,   /**< Scheduled buffer swap should flip */
        _DRM_VBLANK_NEXTONMISS = 0x10000000,    /**< If missed, wait for next vblank */
        _DRM_VBLANK_SECONDARY = 0x20000000,     /**< Secondary display controller */
@@ -461,8 +471,8 @@ enum drm_vblank_seq_type {
 };
 
 #define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE)
-#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \
-                               _DRM_VBLANK_NEXTONMISS)
+#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \
+                               _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)
 
 struct drm_wait_vblank_request {
        enum drm_vblank_seq_type type;
@@ -686,6 +696,8 @@ struct drm_gem_open {
 #define DRM_IOCTL_MODE_GETFB           DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_ADDFB           DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_RMFB            DRM_IOWR(0xAF, unsigned int)
+#define DRM_IOCTL_MODE_PAGE_FLIP       DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip)
+#define DRM_IOCTL_MODE_DIRTYFB         DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd)
 
 /**
  * Device specific ioctls should only be in their respective headers
@@ -698,6 +710,35 @@ struct drm_gem_open {
 #define DRM_COMMAND_BASE                0x40
 #define DRM_COMMAND_END                        0xA0
 
+/**
+ * Header for events written back to userspace on the drm fd.  The
+ * type defines the type of event, the length specifies the total
+ * length of the event (including the header), and user_data is
+ * typically a 64 bit value passed with the ioctl that triggered the
+ * event.  A read on the drm fd will always only return complete
+ * events, that is, if for example the read buffer is 100 bytes, and
+ * there are two 64 byte events pending, only one will be returned.
+ *
+ * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and
+ * up are chipset specific.
+ */
+struct drm_event {
+       __u32 type;
+       __u32 length;
+};
+
+#define DRM_EVENT_VBLANK 0x01
+#define DRM_EVENT_FLIP_COMPLETE 0x02
+
+struct drm_event_vblank {
+       struct drm_event base;
+       __u64 user_data;
+       __u32 tv_sec;
+       __u32 tv_usec;
+       __u32 sequence;
+       __u32 reserved;
+};
+
 /* typedef area */
 #ifndef __KERNEL__
 typedef struct drm_clip_rect drm_clip_rect_t;
index c8e64bb..19ef8eb 100644 (file)
@@ -245,16 +245,6 @@ extern void drm_ut_debug_printk(unsigned int request_level,
 
 #endif
 
-#define DRM_PROC_LIMIT (PAGE_SIZE-80)
-
-#define DRM_PROC_PRINT(fmt, arg...)                                    \
-   len += sprintf(&buf[len], fmt , ##arg);                             \
-   if (len > DRM_PROC_LIMIT) { *eof = 1; return len - offset; }
-
-#define DRM_PROC_PRINT_RET(ret, fmt, arg...)                           \
-   len += sprintf(&buf[len], fmt , ##arg);                             \
-   if (len > DRM_PROC_LIMIT) { ret; *eof = 1; return len - offset; }
-
 /*@}*/
 
 /***********************************************************************/
@@ -265,19 +255,8 @@ extern void drm_ut_debug_printk(unsigned int request_level,
 
 #define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
 #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
-#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist)
 
 #define DRM_IF_VERSION(maj, min) (maj << 16 | min)
-/**
- * Get the private SAREA mapping.
- *
- * \param _dev DRM device.
- * \param _ctx context number.
- * \param _map output mapping.
- */
-#define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do {      \
-       (_map) = (_dev)->context_sareas[_ctx];          \
-} while(0)
 
 /**
  * Test that the hardware lock is held by the caller, returning otherwise.
@@ -296,18 +275,6 @@ do {                                                                               \
        }                                                                       \
 } while (0)
 
-/**
- * Copy and IOCTL return string to user space
- */
-#define DRM_COPY( name, value )                                                \
-       len = strlen( value );                                          \
-       if ( len > name##_len ) len = name##_len;                       \
-       name##_len = strlen( value );                                   \
-       if ( len && name ) {                                            \
-               if ( copy_to_user( name, value, len ) )                 \
-                       return -EFAULT;                                 \
-       }
-
 /**
  * Ioctl function type.
  *
@@ -322,6 +289,9 @@ typedef int drm_ioctl_t(struct drm_device *dev, void *data,
 typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
                               unsigned long arg);
 
+#define DRM_IOCTL_NR(n)                _IOC_NR(n)
+#define DRM_MAJOR       226
+
 #define DRM_AUTH       0x1
 #define        DRM_MASTER      0x2
 #define DRM_ROOT_ONLY  0x4
@@ -426,6 +396,14 @@ struct drm_buf_entry {
        struct drm_freelist freelist;
 };
 
+/* Event queued up for userspace to read */
+struct drm_pending_event {
+       struct drm_event *event;
+       struct list_head link;
+       struct drm_file *file_priv;
+       void (*destroy)(struct drm_pending_event *event);
+};
+
 /** File private data */
 struct drm_file {
        int authenticated;
@@ -449,6 +427,10 @@ struct drm_file {
        struct drm_master *master; /* master this node is currently associated with
                                      N.B. not always minor->master */
        struct list_head fbs;
+
+       wait_queue_head_t event_wait;
+       struct list_head event_list;
+       int event_space;
 };
 
 /** Wait queue */
@@ -795,6 +777,15 @@ struct drm_driver {
        /* Master routines */
        int (*master_create)(struct drm_device *dev, struct drm_master *master);
        void (*master_destroy)(struct drm_device *dev, struct drm_master *master);
+       /**
+        * master_set is called whenever the minor master is set.
+        * master_drop is called whenever the minor master is dropped.
+        */
+
+       int (*master_set)(struct drm_device *dev, struct drm_file *file_priv,
+                         bool from_open);
+       void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv,
+                           bool from_release);
 
        int (*proc_init)(struct drm_minor *minor);
        void (*proc_cleanup)(struct drm_minor *minor);
@@ -900,6 +891,12 @@ struct drm_minor {
        struct drm_mode_group mode_group;
 };
 
+struct drm_pending_vblank_event {
+       struct drm_pending_event base;
+       int pipe;
+       struct drm_event_vblank event;
+};
+
 /**
  * DRM device structure. This structure represent a complete card that
  * may contain multiple heads.
@@ -999,6 +996,12 @@ struct drm_device {
 
        u32 max_vblank_count;           /**< size of vblank counter register */
 
+       /**
+        * List of events
+        */
+       struct list_head vblank_event_list;
+       spinlock_t event_lock;
+
        /*@} */
        cycles_t ctx_start;
        cycles_t lck_start;
@@ -1135,6 +1138,8 @@ extern int drm_lastclose(struct drm_device *dev);
 extern int drm_open(struct inode *inode, struct file *filp);
 extern int drm_stub_open(struct inode *inode, struct file *filp);
 extern int drm_fasync(int fd, struct file *filp, int on);
+extern ssize_t drm_read(struct file *filp, char __user *buffer,
+                       size_t count, loff_t *offset);
 extern int drm_release(struct inode *inode, struct file *filp);
 
                                /* Mapping support (drm_vm.h) */
@@ -1295,6 +1300,7 @@ extern u32 drm_vblank_count(struct drm_device *dev, int crtc);
 extern void drm_handle_vblank(struct drm_device *dev, int crtc);
 extern int drm_vblank_get(struct drm_device *dev, int crtc);
 extern void drm_vblank_put(struct drm_device *dev, int crtc);
+extern void drm_vblank_off(struct drm_device *dev, int crtc);
 extern void drm_vblank_cleanup(struct drm_device *dev);
 /* Modesetting support */
 extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
@@ -1519,14 +1525,27 @@ static __inline__ void drm_core_dropmap(struct drm_local_map *map)
 
 static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
 {
+       if (size != 0 && nmemb > ULONG_MAX / size)
+               return NULL;
+
        if (size * nmemb <= PAGE_SIZE)
            return kcalloc(nmemb, size, GFP_KERNEL);
 
+       return __vmalloc(size * nmemb,
+                        GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
+}
+
+/* Modeled after cairo's malloc_ab, it's like calloc but without the zeroing. */
+static __inline__ void *drm_malloc_ab(size_t nmemb, size_t size)
+{
        if (size != 0 && nmemb > ULONG_MAX / size)
                return NULL;
 
+       if (size * nmemb <= PAGE_SIZE)
+           return kmalloc(nmemb * size, GFP_KERNEL);
+
        return __vmalloc(size * nmemb,
-                        GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
+                        GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
 }
 
 static __inline void drm_free_large(void *ptr)
index b69347b..fdf43ab 100644 (file)
@@ -123,7 +123,7 @@ struct drm_display_mode {
        int type;
 
        /* Proposed mode values */
-       int clock;
+       int clock;              /* in kHz */
        int hdisplay;
        int hsync_start;
        int hsync_end;
@@ -164,8 +164,8 @@ struct drm_display_mode {
        int *private;
        int private_flags;
 
-       int vrefresh;
-       float hsync;
+       int vrefresh;           /* in Hz */
+       int hsync;              /* in kHz */
 };
 
 enum drm_connector_status {
@@ -242,6 +242,21 @@ struct drm_framebuffer_funcs {
        int (*create_handle)(struct drm_framebuffer *fb,
                             struct drm_file *file_priv,
                             unsigned int *handle);
+       /**
+        * Optinal callback for the dirty fb ioctl.
+        *
+        * Userspace can notify the driver via this callback
+        * that a area of the framebuffer has changed and should
+        * be flushed to the display hardware.
+        *
+        * See documentation in drm_mode.h for the struct
+        * drm_mode_fb_dirty_cmd for more information as all
+        * the semantics and arguments have a one to one mapping
+        * on this function.
+        */
+       int (*dirty)(struct drm_framebuffer *framebuffer, unsigned flags,
+                    unsigned color, struct drm_clip_rect *clips,
+                    unsigned num_clips);
 };
 
 struct drm_framebuffer {
@@ -256,7 +271,7 @@ struct drm_framebuffer {
        unsigned int depth;
        int bits_per_pixel;
        int flags;
-       void *fbdev;
+       struct fb_info *fbdev;
        u32 pseudo_palette[17];
        struct list_head filp_head;
        /* if you are using the helper */
@@ -290,6 +305,7 @@ struct drm_property {
 struct drm_crtc;
 struct drm_connector;
 struct drm_encoder;
+struct drm_pending_vblank_event;
 
 /**
  * drm_crtc_funcs - control CRTCs for a given device
@@ -333,6 +349,19 @@ struct drm_crtc_funcs {
        void (*destroy)(struct drm_crtc *crtc);
 
        int (*set_config)(struct drm_mode_set *set);
+
+       /*
+        * Flip to the given framebuffer.  This implements the page
+        * flip ioctl descibed in drm_mode.h, specifically, the
+        * implementation must return immediately and block all
+        * rendering to the current fb until the flip has completed.
+        * If userspace set the event flag in the ioctl, the event
+        * argument will point to an event to send back when the flip
+        * completes, otherwise it will be NULL.
+        */
+       int (*page_flip)(struct drm_crtc *crtc,
+                        struct drm_framebuffer *fb,
+                        struct drm_pending_vblank_event *event);
 };
 
 /**
@@ -596,6 +625,7 @@ struct drm_mode_config {
        /* Optional properties */
        struct drm_property *scaling_mode_property;
        struct drm_property *dithering_mode_property;
+       struct drm_property *dirty_info_property;
 };
 
 #define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
@@ -667,6 +697,7 @@ extern void drm_mode_validate_size(struct drm_device *dev,
 extern void drm_mode_prune_invalid(struct drm_device *dev,
                                   struct list_head *mode_list, bool verbose);
 extern void drm_mode_sort(struct list_head *mode_list);
+extern int drm_mode_hsync(struct drm_display_mode *mode);
 extern int drm_mode_vrefresh(struct drm_display_mode *mode);
 extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
                                  int adjust_flags);
@@ -703,6 +734,7 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats
                                     char *formats[]);
 extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 extern int drm_mode_create_dithering_property(struct drm_device *dev);
+extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
 extern char *drm_get_encoder_name(struct drm_encoder *encoder);
 
 extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
@@ -711,7 +743,8 @@ extern void drm_mode_connector_detach_encoder(struct drm_connector *connector,
                                           struct drm_encoder *encoder);
 extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
                                         int gamma_size);
-extern void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
+extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
+               uint32_t id, uint32_t type);
 /* IOCTLs */
 extern int drm_mode_getresources(struct drm_device *dev,
                                 void *data, struct drm_file *file_priv);
@@ -730,6 +763,8 @@ extern int drm_mode_rmfb(struct drm_device *dev,
                         void *data, struct drm_file *file_priv);
 extern int drm_mode_getfb(struct drm_device *dev,
                          void *data, struct drm_file *file_priv);
+extern int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+                                 void *data, struct drm_file *file_priv);
 extern int drm_mode_addmode_ioctl(struct drm_device *dev,
                                  void *data, struct drm_file *file_priv);
 extern int drm_mode_rmmode_ioctl(struct drm_device *dev,
@@ -756,6 +791,8 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                                    void *data, struct drm_file *file_priv);
 extern bool drm_detect_hdmi_monitor(struct edid *edid);
+extern int drm_mode_page_flip_ioctl(struct drm_device *dev,
+                                   void *data, struct drm_file *file_priv);
 extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
                                int hdisplay, int vdisplay, int vrefresh,
                                bool reduced, bool interlaced, bool margins);
similarity index 70%
rename from drivers/gpu/drm/i915/intel_dp.h
rename to include/drm/drm_dp_helper.h
index 2b38054..a49e791 100644 (file)
@@ -20,8 +20,8 @@
  * OF THIS SOFTWARE.
  */
 
-#ifndef _INTEL_DP_H_
-#define _INTEL_DP_H_
+#ifndef _DRM_DP_HELPER_H_
+#define _DRM_DP_HELPER_H_
 
 /* From the VESA DisplayPort spec */
 
 #define AUX_I2C_REPLY_MASK     (0x3 << 6)
 
 /* AUX CH addresses */
-#define        DP_LINK_BW_SET          0x100
+/* DPCD */
+#define DP_DPCD_REV                         0x000
+
+#define DP_MAX_LINK_RATE                    0x001
+
+#define DP_MAX_LANE_COUNT                   0x002
+# define DP_MAX_LANE_COUNT_MASK                    0x1f
+# define DP_ENHANCED_FRAME_CAP             (1 << 7)
+
+#define DP_MAX_DOWNSPREAD                   0x003
+# define DP_NO_AUX_HANDSHAKE_LINK_TRAINING  (1 << 6)
+
+#define DP_NORP                             0x004
+
+#define DP_DOWNSTREAMPORT_PRESENT           0x005
+# define DP_DWN_STRM_PORT_PRESENT           (1 << 0)
+# define DP_DWN_STRM_PORT_TYPE_MASK         0x06
+/* 00b = DisplayPort */
+/* 01b = Analog */
+/* 10b = TMDS or HDMI */
+/* 11b = Other */
+# define DP_FORMAT_CONVERSION               (1 << 3)
+
+#define DP_MAIN_LINK_CHANNEL_CODING         0x006
+
+/* link configuration */
+#define        DP_LINK_BW_SET                      0x100
 # define DP_LINK_BW_1_62                   0x06
 # define DP_LINK_BW_2_7                            0x0a
 
-#define DP_LANE_COUNT_SET      0x101
+#define DP_LANE_COUNT_SET                  0x101
 # define DP_LANE_COUNT_MASK                0x0f
 # define DP_LANE_COUNT_ENHANCED_FRAME_EN    (1 << 7)
 
-#define DP_TRAINING_PATTERN_SET        0x102
-
+#define DP_TRAINING_PATTERN_SET                    0x102
 # define DP_TRAINING_PATTERN_DISABLE       0
 # define DP_TRAINING_PATTERN_1             1
 # define DP_TRAINING_PATTERN_2             2
 
 #define DP_LANE0_1_STATUS                  0x202
 #define DP_LANE2_3_STATUS                  0x203
-
 # define DP_LANE_CR_DONE                   (1 << 0)
 # define DP_LANE_CHANNEL_EQ_DONE           (1 << 1)
 # define DP_LANE_SYMBOL_LOCKED             (1 << 2)
 
+#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE |          \
+                           DP_LANE_CHANNEL_EQ_DONE |   \
+                           DP_LANE_SYMBOL_LOCKED)
+
 #define DP_LANE_ALIGN_STATUS_UPDATED       0x204
 
 #define DP_INTERLANE_ALIGN_DONE                    (1 << 0)
 
 #define DP_ADJUST_REQUEST_LANE0_1          0x206
 #define DP_ADJUST_REQUEST_LANE2_3          0x207
-
-#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK  0x03
-#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
-#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK   0x0c
-#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT  2
-#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK  0x30
-#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
-#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK   0xc0
-#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT  6
+# define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK  0x03
+# define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
+# define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK   0x0c
+# define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT  2
+# define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK  0x30
+# define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
+# define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK   0xc0
+# define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT  6
+
+#define DP_SET_POWER                        0x600
+# define DP_SET_POWER_D0                    0x1
+# define DP_SET_POWER_D3                    0x2
+
+#define MODE_I2C_START 1
+#define MODE_I2C_WRITE 2
+#define MODE_I2C_READ  4
+#define MODE_I2C_STOP  8
 
 struct i2c_algo_dp_aux_data {
        bool running;
        u16 address;
        int (*aux_ch) (struct i2c_adapter *adapter,
-                      uint8_t *send, int send_bytes,
-                      uint8_t *recv, int recv_bytes);
+                      int mode, uint8_t write_byte,
+                      uint8_t *read_byte);
 };
 
 int
 i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
 
-#endif /* _INTEL_DP_H_ */
+#endif /* _DRM_DP_HELPER_H_ */
index 7d6c9a2..d33c3e0 100644 (file)
@@ -106,6 +106,10 @@ struct detailed_data_color_point {
        u8 wpindex2[3];
 } __attribute__((packed));
 
+struct cvt_timing {
+       u8 code[3];
+} __attribute__((packed));
+
 struct detailed_non_pixel {
        u8 pad1;
        u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
@@ -117,9 +121,13 @@ struct detailed_non_pixel {
                struct detailed_data_monitor_range range;
                struct detailed_data_wpindex color;
                struct std_timing timings[5];
+               struct cvt_timing cvt[4];
        } data;
 } __attribute__((packed));
 
+#define EDID_DETAIL_EST_TIMINGS 0xf7
+#define EDID_DETAIL_CVT_3BYTE 0xf8
+#define EDID_DETAIL_COLOR_MGMT_DATA 0xf9
 #define EDID_DETAIL_STD_MODES 0xfa
 #define EDID_DETAIL_MONITOR_CPDATA 0xfb
 #define EDID_DETAIL_MONITOR_NAME 0xfc
index 62329f9..4c10be3 100644 (file)
@@ -66,6 +66,13 @@ extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
                                                    unsigned long size,
                                                    unsigned alignment,
                                                    int atomic);
+extern struct drm_mm_node *drm_mm_get_block_range_generic(
+                                               struct drm_mm_node *node,
+                                               unsigned long size,
+                                               unsigned alignment,
+                                               unsigned long start,
+                                               unsigned long end,
+                                               int atomic);
 static inline struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent,
                                                   unsigned long size,
                                                   unsigned alignment)
@@ -78,11 +85,38 @@ static inline struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *pa
 {
        return drm_mm_get_block_generic(parent, size, alignment, 1);
 }
+static inline struct drm_mm_node *drm_mm_get_block_range(
+                                               struct drm_mm_node *parent,
+                                               unsigned long size,
+                                               unsigned alignment,
+                                               unsigned long start,
+                                               unsigned long end)
+{
+       return drm_mm_get_block_range_generic(parent, size, alignment,
+                                               start, end, 0);
+}
+static inline struct drm_mm_node *drm_mm_get_block_atomic_range(
+                                               struct drm_mm_node *parent,
+                                               unsigned long size,
+                                               unsigned alignment,
+                                               unsigned long start,
+                                               unsigned long end)
+{
+       return drm_mm_get_block_range_generic(parent, size, alignment,
+                                               start, end, 1);
+}
 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 struct drm_mm_node *drm_mm_search_free_in_range(
+                                               const struct drm_mm *mm,
+                                               unsigned long size,
+                                               unsigned alignment,
+                                               unsigned long start,
+                                               unsigned long end,
+                                               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);
@@ -99,6 +133,7 @@ static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block)
        return block->mm;
 }
 
+extern void drm_mm_debug_table(struct drm_mm *mm, const char *prefix);
 #ifdef CONFIG_DEBUG_FS
 int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm);
 #endif
index 1f90841..43009bc 100644 (file)
@@ -27,9 +27,6 @@
 #ifndef _DRM_MODE_H
 #define _DRM_MODE_H
 
-#include <linux/kernel.h>
-#include <linux/types.h>
-
 #define DRM_DISPLAY_INFO_LEN   32
 #define DRM_CONNECTOR_NAME_LEN 32
 #define DRM_DISPLAY_MODE_LEN   32
 #define DRM_MODE_DITHERING_OFF 0
 #define DRM_MODE_DITHERING_ON  1
 
+/* Dirty info options */
+#define DRM_MODE_DIRTY_OFF      0
+#define DRM_MODE_DIRTY_ON       1
+#define DRM_MODE_DIRTY_ANNOTATE 2
+
 struct drm_mode_modeinfo {
        __u32 clock;
        __u16 hdisplay, hsync_start, hsync_end, htotal, hskew;
@@ -225,6 +227,45 @@ struct drm_mode_fb_cmd {
        __u32 handle;
 };
 
+#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01
+#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02
+#define DRM_MODE_FB_DIRTY_FLAGS         0x03
+
+/*
+ * Mark a region of a framebuffer as dirty.
+ *
+ * Some hardware does not automatically update display contents
+ * as a hardware or software draw to a framebuffer. This ioctl
+ * allows userspace to tell the kernel and the hardware what
+ * regions of the framebuffer have changed.
+ *
+ * The kernel or hardware is free to update more then just the
+ * region specified by the clip rects. The kernel or hardware
+ * may also delay and/or coalesce several calls to dirty into a
+ * single update.
+ *
+ * Userspace may annotate the updates, the annotates are a
+ * promise made by the caller that the change is either a copy
+ * of pixels or a fill of a single color in the region specified.
+ *
+ * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then
+ * the number of updated regions are half of num_clips given,
+ * where the clip rects are paired in src and dst. The width and
+ * height of each one of the pairs must match.
+ *
+ * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller
+ * promises that the region specified of the clip rects is filled
+ * completely with a single color as given in the color argument.
+ */
+
+struct drm_mode_fb_dirty_cmd {
+       __u32 fb_id;
+       __u32 flags;
+       __u32 color;
+       __u32 num_clips;
+       __u64 clips_ptr;
+};
+
 struct drm_mode_mode_cmd {
        __u32 connector_id;
        struct drm_mode_modeinfo mode;
@@ -268,4 +309,37 @@ struct drm_mode_crtc_lut {
        __u64 blue;
 };
 
+#define DRM_MODE_PAGE_FLIP_EVENT 0x01
+#define DRM_MODE_PAGE_FLIP_FLAGS DRM_MODE_PAGE_FLIP_EVENT
+
+/*
+ * Request a page flip on the specified crtc.
+ *
+ * This ioctl will ask KMS to schedule a page flip for the specified
+ * crtc.  Once any pending rendering targeting the specified fb (as of
+ * ioctl time) has completed, the crtc will be reprogrammed to display
+ * that fb after the next vertical refresh.  The ioctl returns
+ * immediately, but subsequent rendering to the current fb will block
+ * in the execbuffer ioctl until the page flip happens.  If a page
+ * flip is already pending as the ioctl is called, EBUSY will be
+ * returned.
+ *
+ * The ioctl supports one flag, DRM_MODE_PAGE_FLIP_EVENT, which will
+ * request that drm sends back a vblank event (see drm.h: struct
+ * drm_event_vblank) when the page flip is done.  The user_data field
+ * passed in with this ioctl will be returned as the user_data field
+ * in the vblank event struct.
+ *
+ * The reserved field must be zero until we figure out something
+ * clever to use it for.
+ */
+
+struct drm_mode_crtc_page_flip {
+       __u32 crtc_id;
+       __u32 fb_id;
+       __u32 flags;
+       __u32 reserved;
+       __u64 user_data;
+};
+
 #endif
index 26641e9..3933691 100644 (file)
@@ -123,5 +123,5 @@ do {                                                                \
        remove_wait_queue(&(queue), &entry);                    \
 } while (0)
 
-#define DRM_WAKEUP( queue ) wake_up_interruptible( queue )
+#define DRM_WAKEUP( queue ) wake_up( queue )
 #define DRM_INIT_WAITQUEUE( queue ) init_waitqueue_head( queue )
index 7e0cb1d..ec3f5e8 100644 (file)
 #ifndef _I915_DRM_H_
 #define _I915_DRM_H_
 
+#include "drm.h"
+
 /* Please note that modifications to all structs defined here are
  * subject to backwards-compatibility constraints.
  */
-#include <linux/types.h>
-#include "drm.h"
 
 /* Each region is a minimum of 16k, and there are at most 255 of them.
  */
@@ -186,6 +186,8 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_MMAP_GTT  0x24
 #define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25
 #define DRM_I915_GEM_MADVISE   0x26
+#define DRM_I915_OVERLAY_PUT_IMAGE     0x27
+#define DRM_I915_OVERLAY_ATTRS 0x28
 
 #define DRM_IOCTL_I915_INIT            DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH           DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -221,8 +223,10 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_SET_TILING  DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
 #define DRM_IOCTL_I915_GEM_GET_TILING  DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
 #define DRM_IOCTL_I915_GEM_GET_APERTURE        DRM_IOR  (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
-#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_intel_get_pipe_from_crtc_id)
+#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
 #define DRM_IOCTL_I915_GEM_MADVISE     DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
+#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE       DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image)
+#define DRM_IOCTL_I915_OVERLAY_ATTRS   DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -266,6 +270,8 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_CHIPSET_ID            4
 #define I915_PARAM_HAS_GEM               5
 #define I915_PARAM_NUM_FENCES_AVAIL      6
+#define I915_PARAM_HAS_OVERLAY           7
+#define I915_PARAM_HAS_PAGEFLIPPING     8
 
 typedef struct drm_i915_getparam {
        int param;
@@ -686,4 +692,70 @@ struct drm_i915_gem_madvise {
        __u32 retained;
 };
 
+/* flags */
+#define I915_OVERLAY_TYPE_MASK                 0xff
+#define I915_OVERLAY_YUV_PLANAR        0x01
+#define I915_OVERLAY_YUV_PACKED        0x02
+#define I915_OVERLAY_RGB               0x03
+
+#define I915_OVERLAY_DEPTH_MASK                0xff00
+#define I915_OVERLAY_RGB24             0x1000
+#define I915_OVERLAY_RGB16             0x2000
+#define I915_OVERLAY_RGB15             0x3000
+#define I915_OVERLAY_YUV422            0x0100
+#define I915_OVERLAY_YUV411            0x0200
+#define I915_OVERLAY_YUV420            0x0300
+#define I915_OVERLAY_YUV410            0x0400
+
+#define I915_OVERLAY_SWAP_MASK         0xff0000
+#define I915_OVERLAY_NO_SWAP           0x000000
+#define I915_OVERLAY_UV_SWAP           0x010000
+#define I915_OVERLAY_Y_SWAP            0x020000
+#define I915_OVERLAY_Y_AND_UV_SWAP     0x030000
+
+#define I915_OVERLAY_FLAGS_MASK                0xff000000
+#define I915_OVERLAY_ENABLE            0x01000000
+
+struct drm_intel_overlay_put_image {
+       /* various flags and src format description */
+       __u32 flags;
+       /* source picture description */
+       __u32 bo_handle;
+       /* stride values and offsets are in bytes, buffer relative */
+       __u16 stride_Y; /* stride for packed formats */
+       __u16 stride_UV;
+       __u32 offset_Y; /* offset for packet formats */
+       __u32 offset_U;
+       __u32 offset_V;
+       /* in pixels */
+       __u16 src_width;
+       __u16 src_height;
+       /* to compensate the scaling factors for partially covered surfaces */
+       __u16 src_scan_width;
+       __u16 src_scan_height;
+       /* output crtc description */
+       __u32 crtc_id;
+       __u16 dst_x;
+       __u16 dst_y;
+       __u16 dst_width;
+       __u16 dst_height;
+};
+
+/* flags */
+#define I915_OVERLAY_UPDATE_ATTRS      (1<<0)
+#define I915_OVERLAY_UPDATE_GAMMA      (1<<1)
+struct drm_intel_overlay_attrs {
+       __u32 flags;
+       __u32 color_key;
+       __s32 brightness;
+       __u32 contrast;
+       __u32 saturation;
+       __u32 gamma0;
+       __u32 gamma1;
+       __u32 gamma2;
+       __u32 gamma3;
+       __u32 gamma4;
+       __u32 gamma5;
+};
+
 #endif                         /* _I915_DRM_H_ */
index 325fd6f..3ffbc47 100644 (file)
@@ -35,7 +35,7 @@
 #ifndef __MGA_DRM_H__
 #define __MGA_DRM_H__
 
-#include <linux/types.h>
+#include "drm.h"
 
 /* WARNING: If you change any of these defines, make sure to change the
  * defines in the Xserver file (mga_sarea.h)
index 3b9932a..39537f3 100644 (file)
@@ -33,7 +33,7 @@
 #ifndef __RADEON_DRM_H__
 #define __RADEON_DRM_H__
 
-#include <linux/types.h>
+#include "drm.h"
 
 /* WARNING: If you change any of these defines, make sure to change the
  * defines in the X server file (radeon_sarea.h)
index 4911461..4fd4985 100644 (file)
@@ -44,6 +44,29 @@ struct ttm_bo_device;
 
 struct drm_mm_node;
 
+
+/**
+ * struct ttm_placement
+ *
+ * @fpfn:              first valid page frame number to put the object
+ * @lpfn:              last valid page frame number to put the object
+ * @num_placement:     number of prefered placements
+ * @placement:         prefered placements
+ * @num_busy_placement:        number of prefered placements when need to evict buffer
+ * @busy_placement:    prefered placements when need to evict buffer
+ *
+ * Structure indicating the placement you request for an object.
+ */
+struct ttm_placement {
+       unsigned        fpfn;
+       unsigned        lpfn;
+       unsigned        num_placement;
+       const uint32_t  *placement;
+       unsigned        num_busy_placement;
+       const uint32_t  *busy_placement;
+};
+
+
 /**
  * struct ttm_mem_reg
  *
@@ -109,10 +132,6 @@ struct ttm_tt;
  * the object is destroyed.
  * @event_queue: Queue for processes waiting on buffer object status change.
  * @lock: spinlock protecting mostly synchronization members.
- * @proposed_placement: Proposed placement for the buffer. Changed only by the
- * creator prior to validation as opposed to bo->mem.proposed_flags which is
- * changed by the implementation prior to a buffer move if it wants to outsmart
- * the buffer creator / user. This latter happens, for example, at eviction.
  * @mem: structure describing current placement.
  * @persistant_swap_storage: Usually the swap storage is deleted for buffers
  * pinned in physical memory. If this behaviour is not desired, this member
@@ -177,7 +196,6 @@ struct ttm_buffer_object {
         * Members protected by the bo::reserved lock.
         */
 
-       uint32_t proposed_placement;
        struct ttm_mem_reg mem;
        struct file *persistant_swap_storage;
        struct ttm_tt *ttm;
@@ -285,7 +303,7 @@ ttm_bo_reference(struct ttm_buffer_object *bo)
  * Note: It might be necessary to block validations before the
  * wait by reserving the buffer.
  * Returns -EBUSY if no_wait is true and the buffer is busy.
- * Returns -ERESTART if interrupted by a signal.
+ * Returns -ERESTARTSYS if interrupted by a signal.
  */
 extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
                       bool interruptible, bool no_wait);
@@ -293,21 +311,22 @@ extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
  * ttm_buffer_object_validate
  *
  * @bo: The buffer object.
- * @proposed_placement: Proposed_placement for the buffer object.
+ * @placement: Proposed placement for the buffer object.
  * @interruptible: Sleep interruptible if sleeping.
  * @no_wait: Return immediately if the buffer is busy.
  *
  * Changes placement and caching policy of the buffer object
- * according to bo::proposed_flags.
+ * according proposed placement.
  * Returns
- * -EINVAL on invalid proposed_flags.
+ * -EINVAL on invalid proposed placement.
  * -ENOMEM on out-of-memory condition.
  * -EBUSY if no_wait is true and buffer busy.
- * -ERESTART if interrupted by a signal.
+ * -ERESTARTSYS if interrupted by a signal.
  */
 extern int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
-                                     uint32_t proposed_placement,
-                                     bool interruptible, bool no_wait);
+                                       struct ttm_placement *placement,
+                                       bool interruptible, bool no_wait);
+
 /**
  * ttm_bo_unref
  *
@@ -328,7 +347,7 @@ extern void ttm_bo_unref(struct ttm_buffer_object **bo);
  * waiting for buffer idle. This lock is recursive.
  * Returns
  * -EBUSY if the buffer is busy and no_wait is true.
- * -ERESTART if interrupted by a signal.
+ * -ERESTARTSYS if interrupted by a signal.
  */
 
 extern int
@@ -371,7 +390,7 @@ extern void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo);
  * Returns
  * -ENOMEM: Out of memory.
  * -EINVAL: Invalid placement flags.
- * -ERESTART: Interrupted by signal while sleeping waiting for resources.
+ * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources.
  */
 
 extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
@@ -411,7 +430,7 @@ extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
  * Returns
  * -ENOMEM: Out of memory.
  * -EINVAL: Invalid placement flags.
- * -ERESTART: Interrupted by signal while waiting for resources.
+ * -ERESTARTSYS: Interrupted by signal while waiting for resources.
  */
 
 extern int ttm_buffer_object_create(struct ttm_bo_device *bdev,
@@ -445,7 +464,6 @@ extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
  *
  * @bdev: Pointer to a ttm_bo_device struct.
  * @mem_type: The memory type.
- * @p_offset: offset for managed area in pages.
  * @p_size: size managed area in pages.
  *
  * Initialize a manager for a given memory type.
@@ -458,7 +476,7 @@ extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
  */
 
 extern int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
-                         unsigned long p_offset, unsigned long p_size);
+                               unsigned long p_size);
 /**
  * ttm_bo_clean_mm
  *
@@ -503,7 +521,7 @@ extern int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type);
  *
  * Returns:
  * -EINVAL: Invalid or uninitialized memory type.
- * -ERESTART: The call was interrupted by a signal while waiting to
+ * -ERESTARTSYS: The call was interrupted by a signal while waiting to
  * evict a buffer.
  */
 
@@ -606,7 +624,7 @@ extern int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
  * be called from the fops::read and fops::write method.
  * Returns:
  * See man (2) write, man(2) read. In particular,
- * the function may return -EINTR if
+ * the function may return -ERESTARTSYS if
  * interrupted by a signal.
  */
 
index e8cd6d2..ff7664e 100644 (file)
@@ -242,12 +242,6 @@ struct ttm_mem_type_manager {
 /**
  * struct ttm_bo_driver
  *
- * @mem_type_prio: Priority array of memory types to place a buffer object in
- * if it fits without evicting buffers from any of these memory types.
- * @mem_busy_prio: Priority array of memory types to place a buffer object in
- * if it needs to evict buffers to make room.
- * @num_mem_type_prio: Number of elements in the @mem_type_prio array.
- * @num_mem_busy_prio: Number of elements in the @num_mem_busy_prio array.
  * @create_ttm_backend_entry: Callback to create a struct ttm_backend.
  * @invalidate_caches: Callback to invalidate read caches when a buffer object
  * has been evicted.
@@ -265,11 +259,6 @@ struct ttm_mem_type_manager {
  */
 
 struct ttm_bo_driver {
-       const uint32_t *mem_type_prio;
-       const uint32_t *mem_busy_prio;
-       uint32_t num_mem_type_prio;
-       uint32_t num_mem_busy_prio;
-
        /**
         * struct ttm_bo_driver member create_ttm_backend_entry
         *
@@ -306,7 +295,8 @@ struct ttm_bo_driver {
         * finished, they'll end up in bo->mem.flags
         */
 
-        uint32_t(*evict_flags) (struct ttm_buffer_object *bo);
+        void(*evict_flags) (struct ttm_buffer_object *bo,
+                               struct ttm_placement *placement);
        /**
         * struct ttm_bo_driver member move:
         *
@@ -544,6 +534,15 @@ extern int ttm_tt_set_user(struct ttm_tt *ttm,
  */
 extern int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem);
 
+/**
+ * ttm_tt_populate:
+ *
+ * @ttm: The struct ttm_tt to contain the backing pages.
+ *
+ * Add backing pages to all of @ttm
+ */
+extern int ttm_tt_populate(struct ttm_tt *ttm);
+
 /**
  * ttm_ttm_destroy:
  *
@@ -639,12 +638,12 @@ extern bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev,
  * -EBUSY: No space available (only if no_wait == 1).
  * -ENOMEM: Could not allocate memory for the buffer object, either due to
  * fragmentation or concurrent allocators.
- * -ERESTART: An interruptible sleep was interrupted by a signal.
+ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
  */
 extern int ttm_bo_mem_space(struct ttm_buffer_object *bo,
-                           uint32_t proposed_placement,
-                           struct ttm_mem_reg *mem,
-                           bool interruptible, bool no_wait);
+                               struct ttm_placement *placement,
+                               struct ttm_mem_reg *mem,
+                               bool interruptible, bool no_wait);
 /**
  * ttm_bo_wait_for_cpu
  *
@@ -654,7 +653,7 @@ extern int ttm_bo_mem_space(struct ttm_buffer_object *bo,
  * Wait until a buffer object is no longer sync'ed for CPU access.
  * Returns:
  * -EBUSY: Buffer object was sync'ed for CPU access. (only if no_wait == 1).
- * -ERESTART: An interruptible sleep was interrupted by a signal.
+ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
  */
 
 extern int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait);
@@ -758,7 +757,7 @@ extern void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo);
  * -EAGAIN: The reservation may cause a deadlock.
  * Release all buffer reservations, wait for @bo to become unreserved and
  * try again. (only if use_sequence == 1).
- * -ERESTART: A wait for the buffer to become unreserved was interrupted by
+ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
  * a signal. Release all buffer reservations and return to user-space.
  */
 extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
@@ -799,7 +798,7 @@ extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo,
  *
  * Returns:
  * -EBUSY: If no_wait == 1 and the buffer is already reserved.
- * -ERESTART: If interruptible == 1 and the process received a signal
+ * -ERESTARTSYS: If interruptible == 1 and the process received a signal
  * while sleeping.
  */
 extern int ttm_bo_block_reservation(struct ttm_buffer_object *bo,
diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h
new file mode 100644 (file)
index 0000000..cd2c475
--- /dev/null
@@ -0,0 +1,107 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., 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 <thellstrom-at-vmware-dot-com>
+ */
+
+#ifndef _TTM_EXECBUF_UTIL_H_
+#define _TTM_EXECBUF_UTIL_H_
+
+#include "ttm/ttm_bo_api.h"
+#include <linux/list.h>
+
+/**
+ * struct ttm_validate_buffer
+ *
+ * @head:           list head for thread-private list.
+ * @bo:             refcounted buffer object pointer.
+ * @new_sync_obj_arg: New sync_obj_arg for @bo, to be used once
+ * adding a new sync object.
+ * @reservied:      Indicates whether @bo has been reserved for validation.
+ */
+
+struct ttm_validate_buffer {
+       struct list_head head;
+       struct ttm_buffer_object *bo;
+       void *new_sync_obj_arg;
+       bool reserved;
+};
+
+/**
+ * function ttm_eu_backoff_reservation
+ *
+ * @list:     thread private list of ttm_validate_buffer structs.
+ *
+ * Undoes all buffer validation reservations for bos pointed to by
+ * the list entries.
+ */
+
+extern void ttm_eu_backoff_reservation(struct list_head *list);
+
+/**
+ * function ttm_eu_reserve_buffers
+ *
+ * @list:    thread private list of ttm_validate_buffer structs.
+ * @val_seq: A unique sequence number.
+ *
+ * Tries to reserve bos pointed to by the list entries for validation.
+ * If the function returns 0, all buffers are marked as "unfenced",
+ * taken off the lru lists and are not synced for write CPU usage.
+ *
+ * If the function detects a deadlock due to multiple threads trying to
+ * reserve the same buffers in reverse order, all threads except one will
+ * back off and retry. This function may sleep while waiting for
+ * CPU write reservations to be cleared, and for other threads to
+ * unreserve their buffers.
+ *
+ * This function may return -ERESTART or -EAGAIN if the calling process
+ * receives a signal while waiting. In that case, no buffers on the list
+ * will be reserved upon return.
+ *
+ * Buffers reserved by this function should be unreserved by
+ * a call to either ttm_eu_backoff_reservation() or
+ * ttm_eu_fence_buffer_objects() when command submission is complete or
+ * has failed.
+ */
+
+extern int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq);
+
+/**
+ * function ttm_eu_fence_buffer_objects.
+ *
+ * @list:        thread private list of ttm_validate_buffer structs.
+ * @sync_obj:    The new sync object for the buffers.
+ *
+ * This function should be called when command submission is complete, and
+ * it will add a new sync object to bos pointed to by entries on @list.
+ * It also unreserves all buffers, putting them on lru lists.
+ *
+ */
+
+extern void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj);
+
+#endif
diff --git a/include/drm/ttm/ttm_lock.h b/include/drm/ttm/ttm_lock.h
new file mode 100644 (file)
index 0000000..81ba0b0
--- /dev/null
@@ -0,0 +1,247 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., 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 <thellstrom-at-vmware-dot-com>
+ */
+
+/** @file ttm_lock.h
+ * This file implements a simple replacement for the buffer manager use
+ * of the DRM heavyweight hardware lock.
+ * The lock is a read-write lock. Taking it in read mode and write mode
+ * is relatively fast, and intended for in-kernel use only.
+ *
+ * The vt mode is used only when there is a need to block all
+ * user-space processes from validating buffers.
+ * It's allowed to leave kernel space with the vt lock held.
+ * If a user-space process dies while having the vt-lock,
+ * it will be released during the file descriptor release. The vt lock
+ * excludes write lock and read lock.
+ *
+ * The suspend mode is used to lock out all TTM users when preparing for
+ * and executing suspend operations.
+ *
+ */
+
+#ifndef _TTM_LOCK_H_
+#define _TTM_LOCK_H_
+
+#include "ttm/ttm_object.h"
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+/**
+ * struct ttm_lock
+ *
+ * @base: ttm base object used solely to release the lock if the client
+ * holding the lock dies.
+ * @queue: Queue for processes waiting for lock change-of-status.
+ * @lock: Spinlock protecting some lock members.
+ * @rw: Read-write lock counter. Protected by @lock.
+ * @flags: Lock state. Protected by @lock.
+ * @kill_takers: Boolean whether to kill takers of the lock.
+ * @signal: Signal to send when kill_takers is true.
+ */
+
+struct ttm_lock {
+       struct ttm_base_object base;
+       wait_queue_head_t queue;
+       spinlock_t lock;
+       int32_t rw;
+       uint32_t flags;
+       bool kill_takers;
+       int signal;
+       struct ttm_object_file *vt_holder;
+};
+
+
+/**
+ * ttm_lock_init
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * Initializes the lock.
+ */
+extern void ttm_lock_init(struct ttm_lock *lock);
+
+/**
+ * ttm_read_unlock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Releases a read lock.
+ */
+extern void ttm_read_unlock(struct ttm_lock *lock);
+
+/**
+ * ttm_read_lock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * @interruptible: Interruptible sleeping while waiting for a lock.
+ *
+ * Takes the lock in read mode.
+ * Returns:
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
+ */
+extern int ttm_read_lock(struct ttm_lock *lock, bool interruptible);
+
+/**
+ * ttm_read_trylock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * @interruptible: Interruptible sleeping while waiting for a lock.
+ *
+ * Tries to take the lock in read mode. If the lock is already held
+ * in write mode, the function will return -EBUSY. If the lock is held
+ * in vt or suspend mode, the function will sleep until these modes
+ * are unlocked.
+ *
+ * Returns:
+ * -EBUSY The lock was already held in write mode.
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
+ */
+extern int ttm_read_trylock(struct ttm_lock *lock, bool interruptible);
+
+/**
+ * ttm_write_unlock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Releases a write lock.
+ */
+extern void ttm_write_unlock(struct ttm_lock *lock);
+
+/**
+ * ttm_write_lock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * @interruptible: Interruptible sleeping while waiting for a lock.
+ *
+ * Takes the lock in write mode.
+ * Returns:
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
+ */
+extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
+
+/**
+ * ttm_lock_downgrade
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Downgrades a write lock to a read lock.
+ */
+extern void ttm_lock_downgrade(struct ttm_lock *lock);
+
+/**
+ * ttm_suspend_lock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Takes the lock in suspend mode. Excludes read and write mode.
+ */
+extern void ttm_suspend_lock(struct ttm_lock *lock);
+
+/**
+ * ttm_suspend_unlock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Releases a suspend lock
+ */
+extern void ttm_suspend_unlock(struct ttm_lock *lock);
+
+/**
+ * ttm_vt_lock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * @interruptible: Interruptible sleeping while waiting for a lock.
+ * @tfile: Pointer to a struct ttm_object_file to register the lock with.
+ *
+ * Takes the lock in vt mode.
+ * Returns:
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
+ * -ENOMEM: Out of memory when locking.
+ */
+extern int ttm_vt_lock(struct ttm_lock *lock, bool interruptible,
+                      struct ttm_object_file *tfile);
+
+/**
+ * ttm_vt_unlock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Releases a vt lock.
+ * Returns:
+ * -EINVAL If the lock was not held.
+ */
+extern int ttm_vt_unlock(struct ttm_lock *lock);
+
+/**
+ * ttm_write_unlock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ *
+ * Releases a write lock.
+ */
+extern void ttm_write_unlock(struct ttm_lock *lock);
+
+/**
+ * ttm_write_lock
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * @interruptible: Interruptible sleeping while waiting for a lock.
+ *
+ * Takes the lock in write mode.
+ * Returns:
+ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
+ */
+extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
+
+/**
+ * ttm_lock_set_kill
+ *
+ * @lock: Pointer to a struct ttm_lock
+ * @val: Boolean whether to kill processes taking the lock.
+ * @signal: Signal to send to the process taking the lock.
+ *
+ * The kill-when-taking-lock functionality is used to kill processes that keep
+ * on using the TTM functionality when its resources has been taken down, for
+ * example when the X server exits. A typical sequence would look like this:
+ * - X server takes lock in write mode.
+ * - ttm_lock_set_kill() is called with @val set to true.
+ * - As part of X server exit, TTM resources are taken down.
+ * - X server releases the lock on file release.
+ * - Another dri client wants to render, takes the lock and is killed.
+ *
+ */
+static inline void ttm_lock_set_kill(struct ttm_lock *lock, bool val,
+                                    int signal)
+{
+       lock->kill_takers = val;
+       if (val)
+               lock->signal = signal;
+}
+
+#endif
index 6983a7c..b199170 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/wait.h>
 #include <linux/errno.h>
 #include <linux/kobject.h>
+#include <linux/mm.h>
 
 /**
  * struct ttm_mem_shrink - callback to shrink TTM memory usage.
diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h
new file mode 100644 (file)
index 0000000..703ca4d
--- /dev/null
@@ -0,0 +1,267 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., 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 <thellstrom-at-vmware-dot-com>
+ */
+/** @file ttm_object.h
+ *
+ * Base- and reference object implementation for the various
+ * ttm objects. Implements reference counting, minimal security checks
+ * and release on file close.
+ */
+
+#ifndef _TTM_OBJECT_H_
+#define _TTM_OBJECT_H_
+
+#include <linux/list.h>
+#include "drm_hashtab.h"
+#include <linux/kref.h>
+#include <ttm/ttm_memory.h>
+
+/**
+ * enum ttm_ref_type
+ *
+ * Describes what type of reference a ref object holds.
+ *
+ * TTM_REF_USAGE is a simple refcount on a base object.
+ *
+ * TTM_REF_SYNCCPU_READ is a SYNCCPU_READ reference on a
+ * buffer object.
+ *
+ * TTM_REF_SYNCCPU_WRITE is a SYNCCPU_WRITE reference on a
+ * buffer object.
+ *
+ */
+
+enum ttm_ref_type {
+       TTM_REF_USAGE,
+       TTM_REF_SYNCCPU_READ,
+       TTM_REF_SYNCCPU_WRITE,
+       TTM_REF_NUM
+};
+
+/**
+ * enum ttm_object_type
+ *
+ * One entry per ttm object type.
+ * Device-specific types should use the
+ * ttm_driver_typex types.
+ */
+
+enum ttm_object_type {
+       ttm_fence_type,
+       ttm_buffer_type,
+       ttm_lock_type,
+       ttm_driver_type0 = 256,
+       ttm_driver_type1
+};
+
+struct ttm_object_file;
+struct ttm_object_device;
+
+/**
+ * struct ttm_base_object
+ *
+ * @hash: hash entry for the per-device object hash.
+ * @type: derived type this object is base class for.
+ * @shareable: Other ttm_object_files can access this object.
+ *
+ * @tfile: Pointer to ttm_object_file of the creator.
+ * NULL if the object was not created by a user request.
+ * (kernel object).
+ *
+ * @refcount: Number of references to this object, not
+ * including the hash entry. A reference to a base object can
+ * only be held by a ref object.
+ *
+ * @refcount_release: A function to be called when there are
+ * no more references to this object. This function should
+ * destroy the object (or make sure destruction eventually happens),
+ * and when it is called, the object has
+ * already been taken out of the per-device hash. The parameter
+ * "base" should be set to NULL by the function.
+ *
+ * @ref_obj_release: A function to be called when a reference object
+ * with another ttm_ref_type than TTM_REF_USAGE is deleted.
+ * this function may, for example, release a lock held by a user-space
+ * process.
+ *
+ * This struct is intended to be used as a base struct for objects that
+ * are visible to user-space. It provides a global name, race-safe
+ * access and refcounting, minimal access contol and hooks for unref actions.
+ */
+
+struct ttm_base_object {
+       struct drm_hash_item hash;
+       enum ttm_object_type object_type;
+       bool shareable;
+       struct ttm_object_file *tfile;
+       struct kref refcount;
+       void (*refcount_release) (struct ttm_base_object **base);
+       void (*ref_obj_release) (struct ttm_base_object *base,
+                                enum ttm_ref_type ref_type);
+};
+
+/**
+ * ttm_base_object_init
+ *
+ * @tfile: Pointer to a struct ttm_object_file.
+ * @base: The struct ttm_base_object to initialize.
+ * @shareable: This object is shareable with other applcations.
+ * (different @tfile pointers.)
+ * @type: The object type.
+ * @refcount_release: See the struct ttm_base_object description.
+ * @ref_obj_release: See the struct ttm_base_object description.
+ *
+ * Initializes a struct ttm_base_object.
+ */
+
+extern int ttm_base_object_init(struct ttm_object_file *tfile,
+                               struct ttm_base_object *base,
+                               bool shareable,
+                               enum ttm_object_type type,
+                               void (*refcount_release) (struct ttm_base_object
+                                                         **),
+                               void (*ref_obj_release) (struct ttm_base_object
+                                                        *,
+                                                        enum ttm_ref_type
+                                                        ref_type));
+
+/**
+ * ttm_base_object_lookup
+ *
+ * @tfile: Pointer to a struct ttm_object_file.
+ * @key: Hash key
+ *
+ * Looks up a struct ttm_base_object with the key @key.
+ * Also verifies that the object is visible to the application, by
+ * comparing the @tfile argument and checking the object shareable flag.
+ */
+
+extern struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file
+                                                     *tfile, uint32_t key);
+
+/**
+ * ttm_base_object_unref
+ *
+ * @p_base: Pointer to a pointer referncing a struct ttm_base_object.
+ *
+ * Decrements the base object refcount and clears the pointer pointed to by
+ * p_base.
+ */
+
+extern void ttm_base_object_unref(struct ttm_base_object **p_base);
+
+/**
+ * ttm_ref_object_add.
+ *
+ * @tfile: A struct ttm_object_file representing the application owning the
+ * ref_object.
+ * @base: The base object to reference.
+ * @ref_type: The type of reference.
+ * @existed: Upon completion, indicates that an identical reference object
+ * already existed, and the refcount was upped on that object instead.
+ *
+ * Adding a ref object to a base object is basically like referencing the
+ * base object, but a user-space application holds the reference. When the
+ * file corresponding to @tfile is closed, all its reference objects are
+ * deleted. A reference object can have different types depending on what
+ * it's intended for. It can be refcounting to prevent object destruction,
+ * When user-space takes a lock, it can add a ref object to that lock to
+ * make sure the lock is released if the application dies. A ref object
+ * will hold a single reference on a base object.
+ */
+extern int ttm_ref_object_add(struct ttm_object_file *tfile,
+                             struct ttm_base_object *base,
+                             enum ttm_ref_type ref_type, bool *existed);
+/**
+ * ttm_ref_object_base_unref
+ *
+ * @key: Key representing the base object.
+ * @ref_type: Ref type of the ref object to be dereferenced.
+ *
+ * Unreference a ref object with type @ref_type
+ * on the base object identified by @key. If there are no duplicate
+ * references, the ref object will be destroyed and the base object
+ * will be unreferenced.
+ */
+extern int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
+                                    unsigned long key,
+                                    enum ttm_ref_type ref_type);
+
+/**
+ * ttm_object_file_init - initialize a struct ttm_object file
+ *
+ * @tdev: A struct ttm_object device this file is initialized on.
+ * @hash_order: Order of the hash table used to hold the reference objects.
+ *
+ * This is typically called by the file_ops::open function.
+ */
+
+extern struct ttm_object_file *ttm_object_file_init(struct ttm_object_device
+                                                   *tdev,
+                                                   unsigned int hash_order);
+
+/**
+ * ttm_object_file_release - release data held by a ttm_object_file
+ *
+ * @p_tfile: Pointer to pointer to the ttm_object_file object to release.
+ * *p_tfile will be set to NULL by this function.
+ *
+ * Releases all data associated by a ttm_object_file.
+ * Typically called from file_ops::release. The caller must
+ * ensure that there are no concurrent users of tfile.
+ */
+
+extern void ttm_object_file_release(struct ttm_object_file **p_tfile);
+
+/**
+ * ttm_object device init - initialize a struct ttm_object_device
+ *
+ * @hash_order: Order of hash table used to hash the base objects.
+ *
+ * This function is typically called on device initialization to prepare
+ * data structures needed for ttm base and ref objects.
+ */
+
+extern struct ttm_object_device *ttm_object_device_init
+    (struct ttm_mem_global *mem_glob, unsigned int hash_order);
+
+/**
+ * ttm_object_device_release - release data held by a ttm_object_device
+ *
+ * @p_tdev: Pointer to pointer to the ttm_object_device object to release.
+ * *p_tdev will be set to NULL by this function.
+ *
+ * Releases all data associated by a ttm_object_device.
+ * Typically called from driver::unload before the destruction of the
+ * device private data structure.
+ */
+
+extern void ttm_object_device_release(struct ttm_object_device **p_tdev);
+
+#endif
index 170786e..fd11a5b 100644 (file)
@@ -24,7 +24,7 @@
 #ifndef _VIA_DRM_H_
 #define _VIA_DRM_H_
 
-#include <linux/types.h>
+#include "drm.h"
 
 /* WARNING: These defines must be the same as what the Xserver uses.
  * if you change them, you must change the defines in the Xserver.
index cb57c30..eb130b4 100644 (file)
@@ -39,7 +39,7 @@ typedef __u32 DriverVer_type;
 #ifndef CCISS_CMD_H
 // This defines are duplicated in cciss_cmd.h in the driver directory 
 
-//general boundary defintions
+//general boundary definitions
 #define SENSEINFOBYTES          32//note that this value may vary between host implementations
 
 //Command Status value
index 519248d..d9bac7f 100644 (file)
@@ -21,7 +21,7 @@
  *    query vendor-specific element types
  *
  *    accessing elements works by specifing type and unit of the element.
- *    for eample, storage elements are addressed with type = CHET_ST and
+ *    for example, storage elements are addressed with type = CHET_ST and
  *    unit = 0 .. cp_nslots-1
  *
  */
index 8a1f972..0c3c3a2 100644 (file)
  * various request types above.  The remaining 24-bits are currently
  * set to zero and are reserved for future use and compatibility concerns.
  *
- * User-space should always use DM_ULOG_REQUEST_TYPE to aquire the
+ * User-space should always use DM_ULOG_REQUEST_TYPE to acquire the
  * request type from the 'request_type' field to maintain forward compatibility.
  */
 #define DM_ULOG_REQUEST_MASK 0xFF
index de9c722..369767b 100644 (file)
@@ -763,6 +763,7 @@ struct fb_tile_ops {
         *  takes over; acceleration engine should be in a quiescent state */
 
 /* hints */
+#define FBINFO_VIRTFB          0x0004 /* FB is System RAM, not device. */
 #define FBINFO_PARTIAL_PAN_OK  0x0040 /* otw use pan only for double-buffering */
 #define FBINFO_READS_FAST      0x0080 /* soft-copy faster than rendering */
 
index 10f6284..8709365 100644 (file)
@@ -312,6 +312,7 @@ struct hid_item {
 #define HID_QUIRK_MULTI_INPUT                  0x00000040
 #define HID_QUIRK_SKIP_OUTPUT_REPORTS          0x00010000
 #define HID_QUIRK_FULLSPEED_INTERVAL           0x10000000
+#define HID_QUIRK_NO_INIT_REPORTS              0x20000000
 
 /*
  * This is the global environment of the parser. This information is
index e4135d6..0ec6129 100644 (file)
@@ -125,8 +125,8 @@ struct ide_io_ports {
  * Timeouts for various operations:
  */
 enum {
-       /* spec allows up to 20ms */
-       WAIT_DRQ        = HZ / 10,      /* 100ms */
+       /* spec allows up to 20ms, but CF cards and SSD drives need more */
+       WAIT_DRQ        = 1 * HZ,       /* 1s */
        /* some laptops are very slow */
        WAIT_READY      = 5 * HZ,       /* 5s */
        /* should be less than 3ms (?), if all ATAPI CD is closed at boot */
index dfa2916..bd55c6e 100644 (file)
@@ -110,7 +110,7 @@ struct in6_flowlabel_req {
 #define IPV6_FLOWINFO_FLOWLABEL                0x000fffff
 #define IPV6_FLOWINFO_PRIORITY         0x0ff00000
 
-/* These defintions are obsolete */
+/* These definitions are obsolete */
 #define IPV6_PRIORITY_UNCHARACTERIZED  0x0000
 #define IPV6_PRIORITY_FILLER           0x0100
 #define IPV6_PRIORITY_UNATTENDED       0x0200
index 597a007..5e3dddf 100644 (file)
 
 /**
  * struct input_polled_dev - simple polled input device
- * @private: private driver data
- * @flush: driver-supplied method that flushes device's state upon
- *     opening (optional)
+ * @private: private driver data.
+ * @open: driver-supplied method that prepares device for polling
+ *     (enabled the device and maybe flushes device state).
+ * @close: driver-supplied method that is called when device is no
+ *     longer being polled. Used to put device into low power mode.
  * @poll: driver-supplied method that polls the device and posts
  *     input events (mandatory).
- * @poll_interval: specifies how often the poll() method shoudl be called.
+ * @poll_interval: specifies how often the poll() method should be called.
+ *     Defaults to 500 msec unless overriden when registering the device.
+ * @poll_interval_max: specifies upper bound for the poll interval.
+ *     Defaults to the initial value of @poll_interval.
+ * @poll_interval_min: specifies lower bound for the poll interval.
+ *     Defaults to 0.
  * @input: input device structire associated with the polled device.
  *     Must be properly initialized by the driver (id, name, phys, bits).
  *
 struct input_polled_dev {
        void *private;
 
-       void (*flush)(struct input_polled_dev *dev);
+       void (*open)(struct input_polled_dev *dev);
+       void (*close)(struct input_polled_dev *dev);
        void (*poll)(struct input_polled_dev *dev);
        unsigned int poll_interval; /* msec */
+       unsigned int poll_interval_max; /* msec */
+       unsigned int poll_interval_min; /* msec */
 
        struct input_dev *input;
+
+/* private: */
        struct delayed_work work;
 };
 
index 84b501a..7be8a65 100644 (file)
@@ -895,7 +895,7 @@ struct ff_periodic_effect {
        struct ff_envelope envelope;
 
        __u32 custom_len;
-       __s16 *custom_data;
+       __s16 __user *custom_data;
 };
 
 /**
@@ -1021,9 +1021,12 @@ struct ff_effect {
  * @keycodesize: size of elements in keycode table
  * @keycode: map of scancodes to keycodes for this device
  * @setkeycode: optional method to alter current keymap, used to implement
- *     sparse keymaps. If not supplied default mechanism will be used
+ *     sparse keymaps. If not supplied default mechanism will be used.
+ *     The method is being called while holding event_lock and thus must
+ *     not sleep
  * @getkeycode: optional method to retrieve current keymap. If not supplied
- *     default mechanism will be used
+ *     default mechanism will be used. The method is being called while
+ *     holding event_lock and thus must not sleep
  * @ff: force feedback structure associated with the device if device
  *     supports force feedback effects
  * @repeat_key: stores key code of the last key pressed; used to implement
@@ -1040,6 +1043,7 @@ struct ff_effect {
  * @absmin: minimum values for events coming from absolute axes
  * @absfuzz: describes noisiness for axes
  * @absflat: size of the center flat position (used by joydev)
+ * @absres: resolution used for events coming form absolute axes
  * @open: this method is called when the very first user calls
  *     input_open_device(). The driver must prepare the device
  *     to start generating events (start polling thread,
@@ -1294,6 +1298,9 @@ void input_unregister_device(struct input_dev *);
 int __must_check input_register_handler(struct input_handler *);
 void input_unregister_handler(struct input_handler *);
 
+int input_handler_for_each_handle(struct input_handler *, void *data,
+                                 int (*fn)(struct input_handle *, void *));
+
 int input_register_handle(struct input_handle *);
 void input_unregister_handle(struct input_handle *);
 
index b3cd42d..3bd018b 100644 (file)
@@ -41,6 +41,9 @@ struct matrix_keymap_data {
  * @col_scan_delay_us: delay, measured in microseconds, that is
  *     needed before we can keypad after activating column gpio
  * @debounce_ms: debounce interval in milliseconds
+ * @active_low: gpio polarity
+ * @wakeup: controls whether the device should be set up as wakeup
+ *     source
  *
  * This structure represents platform-specific data that use used by
  * matrix_keypad driver to perform proper initialization.
similarity index 75%
rename from arch/sh/include/asm/sh_keysc.h
rename to include/linux/input/sh_keysc.h
index 4a65b1e..c211b5c 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __ASM_KEYSC_H__
-#define __ASM_KEYSC_H__
+#ifndef __SH_KEYSC_H__
+#define __SH_KEYSC_H__
 
 #define SH_KEYSC_MAXKEYS 30
 
@@ -11,4 +11,4 @@ struct sh_keysc_info {
        int keycodes[SH_KEYSC_MAXKEYS];
 };
 
-#endif /* __ASM_KEYSC_H__ */
+#endif /* __SH_KEYSC_H__ */
diff --git a/include/linux/input/sparse-keymap.h b/include/linux/input/sparse-keymap.h
new file mode 100644 (file)
index 0000000..52db620
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef _SPARSE_KEYMAP_H
+#define _SPARSE_KEYMAP_H
+
+/*
+ * Copyright (c) 2009 Dmitry Torokhov
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#define KE_END         0       /* Indicates end of keymap */
+#define KE_KEY         1       /* Ordinary key/button */
+#define KE_SW          2       /* Switch (predetermined value) */
+#define KE_VSW         3       /* Switch (value supplied at runtime) */
+#define KE_IGNORE      4       /* Known entry that should be ignored */
+#define KE_LAST                KE_IGNORE
+
+/**
+ * struct key_entry - keymap entry for use in sparse keymap
+ * @type: Type of the key entry (KE_KEY, KE_SW, KE_VSW, KE_END);
+ *     drivers are allowed to extend the list with their own
+ *     private definitions.
+ * @code: Device-specific data identifying the button/switch
+ * @keycode: KEY_* code assigned to a key/button
+ * @sw.code: SW_* code assigned to a switch
+ * @sw.value: Value that should be sent in an input even when KE_SW
+ *     switch is toggled. KE_VSW switches ignore this field and
+ *     expect driver to supply value for the event.
+ *
+ * This structure defines an entry in a sparse keymap used by some
+ * input devices for which traditional table-based approach is not
+ * suitable.
+ */
+struct key_entry {
+       int type;               /* See KE_* above */
+       u32 code;
+       union {
+               u16 keycode;            /* For KE_KEY */
+               struct {                /* For KE_SW, KE_VSW */
+                       u8 code;
+                       u8 value;       /* For KE_SW, ignored by KE_VSW */
+               } sw;
+       };
+};
+
+struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
+                                                   unsigned int code);
+struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
+                                                  unsigned int code);
+int sparse_keymap_setup(struct input_dev *dev,
+                       const struct key_entry *keymap,
+                       int (*setup)(struct input_dev *, struct key_entry *));
+void sparse_keymap_free(struct input_dev *dev);
+
+void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
+                               unsigned int value, bool autorelease);
+
+bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
+                               unsigned int value, bool autorelease);
+
+#endif /* _SPARSE_KEYMAP_H */
index ae9653d..a287cfc 100644 (file)
@@ -282,7 +282,7 @@ extern irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action);
 
 /*
  * Built-in IRQ handlers for various IRQ types,
- * callable via desc->chip->handle_irq()
+ * callable via desc->handle_irq()
  */
 extern void handle_level_irq(unsigned int irq, struct irq_desc *desc);
 extern void handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc);
index a5b3dc7..6a9c4dd 100644 (file)
@@ -1024,7 +1024,7 @@ extern int ata_std_bios_param(struct scsi_device *sdev,
 extern int ata_scsi_slave_config(struct scsi_device *sdev);
 extern void ata_scsi_slave_destroy(struct scsi_device *sdev);
 extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
-                                      int queue_depth);
+                                      int queue_depth, int reason);
 extern struct ata_device *ata_dev_pair(struct ata_device *adev);
 extern int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev);
 
index e5124ce..3402042 100644 (file)
@@ -45,7 +45,7 @@ void pcap_set_ts_bits(struct pcap_chip *, u32);
 #define PCAP_CLEAR_INTERRUPT_REGISTER  0x01ffffff
 #define PCAP_MASK_ALL_INTERRUPT                0x01ffffff
 
-/* registers acessible by both pcap ports */
+/* registers accessible by both pcap ports */
 #define PCAP_REG_ISR           0x0     /* Interrupt Status */
 #define PCAP_REG_MSR           0x1     /* Interrupt Mask */
 #define PCAP_REG_PSTAT         0x2     /* Processor Status */
@@ -67,7 +67,7 @@ void pcap_set_ts_bits(struct pcap_chip *, u32);
 #define PCAP_REG_VENDOR_TEST1  0x1e
 #define PCAP_REG_VENDOR_TEST2  0x1f
 
-/* registers acessible by pcap port 1 only (a1200, e2 & e6) */
+/* registers accessible by pcap port 1 only (a1200, e2 & e6) */
 #define PCAP_REG_INT_SEL       0x3     /* Interrupt Select */
 #define PCAP_REG_SWCTRL                0x4     /* Switching Regulator Control */
 #define PCAP_REG_VREG1         0x5     /* Regulator Bank 1 Control */
diff --git a/include/linux/mfd/sh_mobile_sdhi.h b/include/linux/mfd/sh_mobile_sdhi.h
new file mode 100644 (file)
index 0000000..3bcd716
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __SH_MOBILE_SDHI_H__
+#define __SH_MOBILE_SDHI_H__
+
+struct sh_mobile_sdhi_info {
+       void (*set_pwr)(struct platform_device *pdev, int state);
+};
+
+#endif /* __SH_MOBILE_SDHI_H__ */
index 6913b71..b31bd9e 100644 (file)
@@ -174,6 +174,8 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc,
 struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode);
 struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name,
                                           int mode);
+struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode);
+
 int ubi_register_volume_notifier(struct notifier_block *nb,
                                 int ignore_existing);
 int ubi_unregister_volume_notifier(struct notifier_block *nb);
diff --git a/include/linux/omapfb.h b/include/linux/omapfb.h
new file mode 100644 (file)
index 0000000..f46c40a
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * File: include/linux/omapfb.h
+ *
+ * Framebuffer driver for TI OMAP boards
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __LINUX_OMAPFB_H__
+#define __LINUX_OMAPFB_H__
+
+#include <linux/fb.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* IOCTL commands. */
+
+#define OMAP_IOW(num, dtype)   _IOW('O', num, dtype)
+#define OMAP_IOR(num, dtype)   _IOR('O', num, dtype)
+#define OMAP_IOWR(num, dtype)  _IOWR('O', num, dtype)
+#define OMAP_IO(num)           _IO('O', num)
+
+#define OMAPFB_MIRROR          OMAP_IOW(31, int)
+#define OMAPFB_SYNC_GFX                OMAP_IO(37)
+#define OMAPFB_VSYNC           OMAP_IO(38)
+#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int)
+#define OMAPFB_GET_CAPS                OMAP_IOR(42, struct omapfb_caps)
+#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int)
+#define OMAPFB_LCD_TEST                OMAP_IOW(45, int)
+#define OMAPFB_CTRL_TEST       OMAP_IOW(46, int)
+#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old)
+#define OMAPFB_SET_COLOR_KEY   OMAP_IOW(50, struct omapfb_color_key)
+#define OMAPFB_GET_COLOR_KEY   OMAP_IOW(51, struct omapfb_color_key)
+#define OMAPFB_SETUP_PLANE     OMAP_IOW(52, struct omapfb_plane_info)
+#define OMAPFB_QUERY_PLANE     OMAP_IOW(53, struct omapfb_plane_info)
+#define OMAPFB_UPDATE_WINDOW   OMAP_IOW(54, struct omapfb_update_window)
+#define OMAPFB_SETUP_MEM       OMAP_IOW(55, struct omapfb_mem_info)
+#define OMAPFB_QUERY_MEM       OMAP_IOW(56, struct omapfb_mem_info)
+#define OMAPFB_WAITFORVSYNC    OMAP_IO(57)
+#define OMAPFB_MEMORY_READ     OMAP_IOR(58, struct omapfb_memory_read)
+#define OMAPFB_GET_OVERLAY_COLORMODE OMAP_IOR(59, struct omapfb_ovl_colormode)
+#define OMAPFB_WAITFORGO       OMAP_IO(60)
+#define OMAPFB_GET_VRAM_INFO   OMAP_IOR(61, struct omapfb_vram_info)
+#define OMAPFB_SET_TEARSYNC    OMAP_IOW(62, struct omapfb_tearsync_info)
+
+#define OMAPFB_CAPS_GENERIC_MASK       0x00000fff
+#define OMAPFB_CAPS_LCDC_MASK          0x00fff000
+#define OMAPFB_CAPS_PANEL_MASK         0xff000000
+
+#define OMAPFB_CAPS_MANUAL_UPDATE      0x00001000
+#define OMAPFB_CAPS_TEARSYNC           0x00002000
+#define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000
+#define OMAPFB_CAPS_PLANE_SCALE                0x00008000
+#define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE        0x00010000
+#define OMAPFB_CAPS_WINDOW_SCALE       0x00020000
+#define OMAPFB_CAPS_WINDOW_OVERLAY     0x00040000
+#define OMAPFB_CAPS_WINDOW_ROTATE      0x00080000
+#define OMAPFB_CAPS_SET_BACKLIGHT      0x01000000
+
+/* Values from DSP must map to lower 16-bits */
+#define OMAPFB_FORMAT_MASK             0x00ff
+#define OMAPFB_FORMAT_FLAG_DOUBLE      0x0100
+#define OMAPFB_FORMAT_FLAG_TEARSYNC    0x0200
+#define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400
+#define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY      0x0800
+#define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY     0x1000
+
+#define OMAPFB_MEMTYPE_SDRAM           0
+#define OMAPFB_MEMTYPE_SRAM            1
+#define OMAPFB_MEMTYPE_MAX             1
+
+enum omapfb_color_format {
+       OMAPFB_COLOR_RGB565 = 0,
+       OMAPFB_COLOR_YUV422,
+       OMAPFB_COLOR_YUV420,
+       OMAPFB_COLOR_CLUT_8BPP,
+       OMAPFB_COLOR_CLUT_4BPP,
+       OMAPFB_COLOR_CLUT_2BPP,
+       OMAPFB_COLOR_CLUT_1BPP,
+       OMAPFB_COLOR_RGB444,
+       OMAPFB_COLOR_YUY422,
+
+       OMAPFB_COLOR_ARGB16,
+       OMAPFB_COLOR_RGB24U,    /* RGB24, 32-bit container */
+       OMAPFB_COLOR_RGB24P,    /* RGB24, 24-bit container */
+       OMAPFB_COLOR_ARGB32,
+       OMAPFB_COLOR_RGBA32,
+       OMAPFB_COLOR_RGBX32,
+};
+
+struct omapfb_update_window {
+       __u32 x, y;
+       __u32 width, height;
+       __u32 format;
+       __u32 out_x, out_y;
+       __u32 out_width, out_height;
+       __u32 reserved[8];
+};
+
+struct omapfb_update_window_old {
+       __u32 x, y;
+       __u32 width, height;
+       __u32 format;
+};
+
+enum omapfb_plane {
+       OMAPFB_PLANE_GFX = 0,
+       OMAPFB_PLANE_VID1,
+       OMAPFB_PLANE_VID2,
+};
+
+enum omapfb_channel_out {
+       OMAPFB_CHANNEL_OUT_LCD = 0,
+       OMAPFB_CHANNEL_OUT_DIGIT,
+};
+
+struct omapfb_plane_info {
+       __u32 pos_x;
+       __u32 pos_y;
+       __u8  enabled;
+       __u8  channel_out;
+       __u8  mirror;
+       __u8  reserved1;
+       __u32 out_width;
+       __u32 out_height;
+       __u32 reserved2[12];
+};
+
+struct omapfb_mem_info {
+       __u32 size;
+       __u8  type;
+       __u8  reserved[3];
+};
+
+struct omapfb_caps {
+       __u32 ctrl;
+       __u32 plane_color;
+       __u32 wnd_color;
+};
+
+enum omapfb_color_key_type {
+       OMAPFB_COLOR_KEY_DISABLED = 0,
+       OMAPFB_COLOR_KEY_GFX_DST,
+       OMAPFB_COLOR_KEY_VID_SRC,
+};
+
+struct omapfb_color_key {
+       __u8  channel_out;
+       __u32 background;
+       __u32 trans_key;
+       __u8  key_type;
+};
+
+enum omapfb_update_mode {
+       OMAPFB_UPDATE_DISABLED = 0,
+       OMAPFB_AUTO_UPDATE,
+       OMAPFB_MANUAL_UPDATE
+};
+
+struct omapfb_memory_read {
+       __u16 x;
+       __u16 y;
+       __u16 w;
+       __u16 h;
+       size_t buffer_size;
+       void __user *buffer;
+};
+
+struct omapfb_ovl_colormode {
+       __u8 overlay_idx;
+       __u8 mode_idx;
+       __u32 bits_per_pixel;
+       __u32 nonstd;
+       struct fb_bitfield red;
+       struct fb_bitfield green;
+       struct fb_bitfield blue;
+       struct fb_bitfield transp;
+};
+
+struct omapfb_vram_info {
+       __u32 total;
+       __u32 free;
+       __u32 largest_free_block;
+       __u32 reserved[5];
+};
+
+struct omapfb_tearsync_info {
+       __u8 enabled;
+       __u8 reserved1[3];
+       __u16 line;
+       __u16 reserved2;
+};
+
+#ifdef __KERNEL__
+
+#include <plat/board.h>
+
+#ifdef CONFIG_ARCH_OMAP1
+#define OMAPFB_PLANE_NUM               1
+#else
+#define OMAPFB_PLANE_NUM               3
+#endif
+
+struct omapfb_mem_region {
+       u32             paddr;
+       void __iomem    *vaddr;
+       unsigned long   size;
+       u8              type;           /* OMAPFB_PLANE_MEM_* */
+       enum omapfb_color_format format;/* OMAPFB_COLOR_* */
+       unsigned        format_used:1;  /* Must be set when format is set.
+                                        * Needed b/c of the badly chosen 0
+                                        * base for OMAPFB_COLOR_* values
+                                        */
+       unsigned        alloc:1;        /* allocated by the driver */
+       unsigned        map:1;          /* kernel mapped by the driver */
+};
+
+struct omapfb_mem_desc {
+       int                             region_cnt;
+       struct omapfb_mem_region        region[OMAPFB_PLANE_NUM];
+};
+
+struct omapfb_platform_data {
+       struct omap_lcd_config          lcd;
+       struct omapfb_mem_desc          mem_desc;
+       void                            *ctrl_platform_data;
+};
+
+/* in arch/arm/plat-omap/fb.c */
+extern void omapfb_set_platform_data(struct omapfb_platform_data *data);
+extern void omapfb_set_ctrl_platform_data(void *pdata);
+extern void omapfb_reserve_sdram(void);
+
+#endif
+
+#endif /* __OMAPFB_H */
index cabf074..eae1f86 100644 (file)
 #define PCI_VENDOR_ID_COMPEX           0x11f6
 #define PCI_DEVICE_ID_COMPEX_ENET100VG4        0x0112
 
+#define PCI_VENDOR_ID_PMC_Sierra       0x11f8
+
 #define PCI_VENDOR_ID_RP               0x11fe
 #define PCI_DEVICE_ID_RP32INTF         0x0001
 #define PCI_DEVICE_ID_RP8INTF          0x0002
index d745f5b..76e5053 100644 (file)
@@ -30,7 +30,7 @@
 
 /*
  * use drive write caching -- we need deferred error handling to be
- * able to sucessfully recover with this option (drive will return good
+ * able to successfully recover with this option (drive will return good
  * status as soon as the cdb is validated).
  */
 #if defined(CONFIG_CDROM_PKTCDVD_WCACHE)
index 850db2e..cf9327c 100644 (file)
 
 #define UART_IIR_TOD   0x08    /* Character Timeout Indication Detected */
 
-#define UART_FCR_PXAR1 0x00    /* receive FIFO treshold = 1 */
-#define UART_FCR_PXAR8 0x40    /* receive FIFO treshold = 8 */
-#define UART_FCR_PXAR16        0x80    /* receive FIFO treshold = 16 */
-#define UART_FCR_PXAR32        0xc0    /* receive FIFO treshold = 32 */
+#define UART_FCR_PXAR1 0x00    /* receive FIFO threshold = 1 */
+#define UART_FCR_PXAR8 0x40    /* receive FIFO threshold = 8 */
+#define UART_FCR_PXAR16        0x80    /* receive FIFO threshold = 16 */
+#define UART_FCR_PXAR32        0xc0    /* receive FIFO threshold = 32 */
 
 
 
index a640bc2..e2f3044 100644 (file)
@@ -215,5 +215,6 @@ static inline void serio_unpin_driver(struct serio *serio)
 #define SERIO_INEXIO   0x37
 #define SERIO_TOUCHIT213       0x38
 #define SERIO_W8001    0x39
+#define SERIO_DYNAPRO  0x3a
 
 #endif
index 68e212f..4ef246f 100644 (file)
@@ -57,10 +57,8 @@ struct intc_desc {
        struct intc_sense_reg *sense_regs;
        unsigned int nr_sense_regs;
        char *name;
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
        struct intc_mask_reg *ack_regs;
        unsigned int nr_ack_regs;
-#endif
 };
 
 #define _INTC_ARRAY(a) a, sizeof(a)/sizeof(*a)
@@ -73,7 +71,6 @@ struct intc_desc symbol __initdata = {                                        \
        chipname,                                                       \
 }
 
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
 #define DECLARE_INTC_DESC_ACK(symbol, chipname, vectors, groups,       \
        mask_regs, prio_regs, sense_regs, ack_regs)                     \
 struct intc_desc symbol __initdata = {                                 \
@@ -83,9 +80,11 @@ struct intc_desc symbol __initdata = {                                       \
        chipname,                                                       \
        _INTC_ARRAY(ack_regs),                                          \
 }
-#endif
 
 void __init register_intc_controller(struct intc_desc *desc);
 int intc_set_priority(unsigned int irq, unsigned int prio);
 
+int reserve_irq_vector(unsigned int irq);
+void reserve_irq_legacy(void);
+
 #endif /* __SH_INTC_H */
diff --git a/include/linux/sh_pfc.h b/include/linux/sh_pfc.h
new file mode 100644 (file)
index 0000000..07c08af
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * SuperH Pin Function Controller Support
+ *
+ * Copyright (c) 2008 Magnus Damm
+ *
+ * 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 __SH_PFC_H
+#define __SH_PFC_H
+
+#include <asm-generic/gpio.h>
+
+typedef unsigned short pinmux_enum_t;
+typedef unsigned short pinmux_flag_t;
+
+#define PINMUX_TYPE_NONE            0
+#define PINMUX_TYPE_FUNCTION        1
+#define PINMUX_TYPE_GPIO            2
+#define PINMUX_TYPE_OUTPUT          3
+#define PINMUX_TYPE_INPUT           4
+#define PINMUX_TYPE_INPUT_PULLUP    5
+#define PINMUX_TYPE_INPUT_PULLDOWN  6
+
+#define PINMUX_FLAG_TYPE            (0x7)
+#define PINMUX_FLAG_WANT_PULLUP     (1 << 3)
+#define PINMUX_FLAG_WANT_PULLDOWN   (1 << 4)
+
+#define PINMUX_FLAG_DBIT_SHIFT      5
+#define PINMUX_FLAG_DBIT            (0x1f << PINMUX_FLAG_DBIT_SHIFT)
+#define PINMUX_FLAG_DREG_SHIFT      10
+#define PINMUX_FLAG_DREG            (0x3f << PINMUX_FLAG_DREG_SHIFT)
+
+struct pinmux_gpio {
+       pinmux_enum_t enum_id;
+       pinmux_flag_t flags;
+};
+
+#define PINMUX_GPIO(gpio, data_or_mark) [gpio] = { data_or_mark }
+#define PINMUX_DATA(data_or_mark, ids...) data_or_mark, ids, 0
+
+struct pinmux_cfg_reg {
+       unsigned long reg, reg_width, field_width;
+       unsigned long *cnt;
+       pinmux_enum_t *enum_ids;
+};
+
+#define PINMUX_CFG_REG(name, r, r_width, f_width) \
+       .reg = r, .reg_width = r_width, .field_width = f_width,         \
+       .cnt = (unsigned long [r_width / f_width]) {}, \
+       .enum_ids = (pinmux_enum_t [(r_width / f_width) * (1 << f_width)]) \
+
+struct pinmux_data_reg {
+       unsigned long reg, reg_width, reg_shadow;
+       pinmux_enum_t *enum_ids;
+};
+
+#define PINMUX_DATA_REG(name, r, r_width) \
+       .reg = r, .reg_width = r_width, \
+       .enum_ids = (pinmux_enum_t [r_width]) \
+
+struct pinmux_range {
+       pinmux_enum_t begin;
+       pinmux_enum_t end;
+       pinmux_enum_t force;
+};
+
+struct pinmux_info {
+       char *name;
+       pinmux_enum_t reserved_id;
+       struct pinmux_range data;
+       struct pinmux_range input;
+       struct pinmux_range input_pd;
+       struct pinmux_range input_pu;
+       struct pinmux_range output;
+       struct pinmux_range mark;
+       struct pinmux_range function;
+
+       unsigned first_gpio, last_gpio;
+
+       struct pinmux_gpio *gpios;
+       struct pinmux_cfg_reg *cfg_regs;
+       struct pinmux_data_reg *data_regs;
+
+       pinmux_enum_t *gpio_data;
+       unsigned int gpio_data_size;
+
+       unsigned long *gpio_in_use;
+       struct gpio_chip chip;
+};
+
+int register_pinmux(struct pinmux_info *pip);
+
+#endif /* __SH_PFC_H */
index eed4254..3274c50 100644 (file)
@@ -15,7 +15,7 @@
  * Some hardware works well with requests at spi_transfer scope:
  *
  *   - Drivers leveraging smarter hardware, with fifos or DMA; or for half
- *     duplex (MicroWire) controllers.  Provide chipslect() and txrx_bufs(),
+ *     duplex (MicroWire) controllers.  Provide chipselect() and txrx_bufs(),
  *     and custom setup()/cleanup() methods.
  */
 
index 56af3ca..877ba03 100644 (file)
@@ -89,8 +89,8 @@ enum
        KERN_VERSION=4,         /* string: compile time info */
        KERN_SECUREMASK=5,      /* struct: maximum rights mask */
        KERN_PROF=6,            /* table: profiling information */
-       KERN_NODENAME=7,
-       KERN_DOMAINNAME=8,
+       KERN_NODENAME=7,        /* string: hostname */
+       KERN_DOMAINNAME=8,      /* string: domainname */
 
        KERN_PANIC=15,          /* int: panic timeout */
        KERN_REALROOTDEV=16,    /* real root device to mount after initrd */
@@ -102,8 +102,8 @@ enum
        KERN_PPC_HTABRECLAIM=25, /* turn htab reclaimation on/off on PPC */
        KERN_PPC_ZEROPAGED=26,  /* turn idle page zeroing on/off on PPC */
        KERN_PPC_POWERSAVE_NAP=27, /* use nap mode for power saving */
-       KERN_MODPROBE=28,
-       KERN_SG_BIG_BUFF=29,
+       KERN_MODPROBE=28,       /* string: modprobe path */
+       KERN_SG_BIG_BUFF=29,    /* int: sg driver reserved buffer size */
        KERN_ACCT=30,           /* BSD process accounting parameters */
        KERN_PPC_L2CR=31,       /* l2cr register on PPC */
 
@@ -150,7 +150,7 @@ enum
        KERN_ACPI_VIDEO_FLAGS=71, /* int: flags for setting up video after ACPI sleep */
        KERN_IA64_UNALIGNED=72, /* int: ia64 unaligned userland trap enable */
        KERN_COMPAT_LOG=73,     /* int: print compat layer  messages */
-       KERN_MAX_LOCK_DEPTH=74,
+       KERN_MAX_LOCK_DEPTH=74, /* int: rtmutex's maximum lock depth */
        KERN_NMI_WATCHDOG=75, /* int: enable/disable nmi watchdog */
        KERN_PANIC_ON_NMI=76, /* int: whether we will panic on an unrecovered */
 };
index adb4406..1b47909 100644 (file)
@@ -110,6 +110,10 @@ struct ucb1400 {
        struct platform_device  *ucb1400_gpio;
 };
 
+struct ucb1400_pdata {
+       int     irq;
+};
+
 static inline u16 ucb1400_reg_read(struct snd_ac97 *ac97, u16 reg)
 {
        return ac97->bus->ops->read(ac97, reg);
index 429c631..63ebdcc 100644 (file)
@@ -74,7 +74,7 @@ enum {
  * WUSB defines that CHIDs, CDIDs and CKs are a 16 byte string of
  * data. In order to avoid confusion and enforce types, we wrap it.
  *
- * Make it packed, as we use it in some hw defintions.
+ * Make it packed, as we use it in some hw definitions.
  */
 struct wusb_ckhdid {
        u8 data[16];
index b59e78c..32b9229 100644 (file)
@@ -361,6 +361,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_PJPG     v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
 #define V4L2_PIX_FMT_OV511    v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
 #define V4L2_PIX_FMT_OV518    v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
+#define V4L2_PIX_FMT_STV0680  v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
 
 /*
  *     F O R M A T   E N U M E R A T I O N
@@ -490,7 +491,7 @@ struct v4l2_jpegcompression {
                                 * you do, leave them untouched.
                                 * Inluding less markers will make the
                                 * resulting code smaller, but there will
-                                * be fewer aplications which can read it.
+                                * be fewer applications which can read it.
                                 * The presence of the APP and COM marker
                                 * is influenced by APP_len and COM_len
                                 * ONLY, not by this property! */
@@ -563,6 +564,7 @@ struct v4l2_framebuffer {
 #define V4L2_FBUF_CAP_LOCAL_ALPHA      0x0010
 #define V4L2_FBUF_CAP_GLOBAL_ALPHA     0x0020
 #define V4L2_FBUF_CAP_LOCAL_INV_ALPHA  0x0040
+#define V4L2_FBUF_CAP_SRC_CHROMAKEY    0x0080
 /*  Flags for the 'flags' field. */
 #define V4L2_FBUF_FLAG_PRIMARY         0x0001
 #define V4L2_FBUF_FLAG_OVERLAY         0x0002
@@ -570,6 +572,7 @@ struct v4l2_framebuffer {
 #define V4L2_FBUF_FLAG_LOCAL_ALPHA     0x0008
 #define V4L2_FBUF_FLAG_GLOBAL_ALPHA    0x0010
 #define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA 0x0020
+#define V4L2_FBUF_FLAG_SRC_CHROMAKEY   0x0040
 
 struct v4l2_clip {
        struct v4l2_rect        c;
@@ -912,8 +915,10 @@ enum v4l2_colorfx {
 #define V4L2_CID_AUTOBRIGHTNESS                        (V4L2_CID_BASE+32)
 #define V4L2_CID_BAND_STOP_FILTER              (V4L2_CID_BASE+33)
 
+#define V4L2_CID_ROTATE                                (V4L2_CID_BASE+34)
+#define V4L2_CID_BG_COLOR                      (V4L2_CID_BASE+35)
 /* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+34)
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+36)
 
 /*  MPEG-class control IDs defined by V4L2 */
 #define V4L2_CID_MPEG_BASE                     (V4L2_CTRL_CLASS_MPEG | 0x900)
index cf24c20..9466e86 100644 (file)
@@ -25,6 +25,7 @@ typedef void (*work_func_t)(struct work_struct *work);
 struct work_struct {
        atomic_long_t data;
 #define WORK_STRUCT_PENDING 0          /* T if work item pending execution */
+#define WORK_STRUCT_STATIC  1          /* static initializer (debugobjects) */
 #define WORK_STRUCT_FLAG_MASK (3UL)
 #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
        struct list_head entry;
@@ -35,6 +36,7 @@ struct work_struct {
 };
 
 #define WORK_DATA_INIT()       ATOMIC_LONG_INIT(0)
+#define WORK_DATA_STATIC_INIT()        ATOMIC_LONG_INIT(2)
 
 struct delayed_work {
        struct work_struct work;
@@ -63,7 +65,7 @@ struct execute_work {
 #endif
 
 #define __WORK_INITIALIZER(n, f) {                             \
-       .data = WORK_DATA_INIT(),                               \
+       .data = WORK_DATA_STATIC_INIT(),                        \
        .entry  = { &(n).entry, &(n).entry },                   \
        .func = (f),                                            \
        __WORK_INIT_LOCKDEP_MAP(#n, &(n))                       \
@@ -91,6 +93,14 @@ struct execute_work {
 #define PREPARE_DELAYED_WORK(_work, _func)                     \
        PREPARE_WORK(&(_work)->work, (_func))
 
+#ifdef CONFIG_DEBUG_OBJECTS_WORK
+extern void __init_work(struct work_struct *work, int onstack);
+extern void destroy_work_on_stack(struct work_struct *work);
+#else
+static inline void __init_work(struct work_struct *work, int onstack) { }
+static inline void destroy_work_on_stack(struct work_struct *work) { }
+#endif
+
 /*
  * initialize all of a work item in one go
  *
@@ -99,24 +109,36 @@ struct execute_work {
  * to generate better code.
  */
 #ifdef CONFIG_LOCKDEP
-#define INIT_WORK(_work, _func)                                                \
+#define __INIT_WORK(_work, _func, _onstack)                            \
        do {                                                            \
                static struct lock_class_key __key;                     \
                                                                        \
+               __init_work((_work), _onstack);                         \
                (_work)->data = (atomic_long_t) WORK_DATA_INIT();       \
                lockdep_init_map(&(_work)->lockdep_map, #_work, &__key, 0);\
                INIT_LIST_HEAD(&(_work)->entry);                        \
                PREPARE_WORK((_work), (_func));                         \
        } while (0)
 #else
-#define INIT_WORK(_work, _func)                                                \
+#define __INIT_WORK(_work, _func, _onstack)                            \
        do {                                                            \
+               __init_work((_work), _onstack);                         \
                (_work)->data = (atomic_long_t) WORK_DATA_INIT();       \
                INIT_LIST_HEAD(&(_work)->entry);                        \
                PREPARE_WORK((_work), (_func));                         \
        } while (0)
 #endif
 
+#define INIT_WORK(_work, _func)                                        \
+       do {                                                    \
+               __INIT_WORK((_work), (_func), 0);               \
+       } while (0)
+
+#define INIT_WORK_ON_STACK(_work, _func)                       \
+       do {                                                    \
+               __INIT_WORK((_work), (_func), 1);               \
+       } while (0)
+
 #define INIT_DELAYED_WORK(_work, _func)                                \
        do {                                                    \
                INIT_WORK(&(_work)->work, (_func));             \
@@ -125,22 +147,16 @@ struct execute_work {
 
 #define INIT_DELAYED_WORK_ON_STACK(_work, _func)               \
        do {                                                    \
-               INIT_WORK(&(_work)->work, (_func));             \
+               INIT_WORK_ON_STACK(&(_work)->work, (_func));    \
                init_timer_on_stack(&(_work)->timer);           \
        } while (0)
 
-#define INIT_DELAYED_WORK_DEFERRABLE(_work, _func)                     \
+#define INIT_DELAYED_WORK_DEFERRABLE(_work, _func)             \
        do {                                                    \
                INIT_WORK(&(_work)->work, (_func));             \
                init_timer_deferrable(&(_work)->timer);         \
        } while (0)
 
-#define INIT_DELAYED_WORK_ON_STACK(_work, _func)               \
-       do {                                                    \
-               INIT_WORK(&(_work)->work, (_func));             \
-               init_timer_on_stack(&(_work)->timer);           \
-       } while (0)
-
 /**
  * work_pending - Find out whether a work item is currently pending
  * @work: The work item in question
index 2c3fbaa..0b0cb17 100644 (file)
@@ -84,6 +84,7 @@ enum cx25840_video_input {
        CX25840_NONE0_CH3 = 0x80000080,
        CX25840_NONE1_CH3 = 0x800000c0,
        CX25840_SVIDEO_ON = 0x80000100,
+       CX25840_COMPONENT_ON = 0x80000200,
 };
 
 enum cx25840_audio_input {
index 71d8982..d863e5e 100644 (file)
@@ -83,6 +83,8 @@ struct vpfe_subdev_info {
 struct vpfe_config {
        /* Number of sub devices connected to vpfe */
        int num_subdevs;
+       /* i2c bus adapter no */
+       int i2c_adapter_id;
        /* information about each subdev */
        struct vpfe_subdev_info *sub_devs;
        /* evm card info */
index 29f0e53..e41a99e 100644 (file)
 #include <linux/input.h>
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+extern int media_ir_debug;    /* media_ir_debug level (0,1,2) */
+#define IR_dprintk(level, fmt, arg...) if (media_ir_debug >= level) \
+       printk(KERN_DEBUG "%s: " fmt , __func__, ## arg)
 
 #define IR_TYPE_RC5     1
 #define IR_TYPE_PD      2 /* Pulse distance encoded IR */
 #define IR_TYPE_OTHER  99
 
-#define IR_KEYTAB_TYPE u32
-#define IR_KEYTAB_SIZE 128  /* enougth for rc5, probably need more some day */
-
 struct ir_scancode {
        u16     scancode;
        u32     keycode;
@@ -42,11 +44,9 @@ struct ir_scancode {
 struct ir_scancode_table {
        struct ir_scancode *scan;
        int size;
+       spinlock_t lock;
 };
 
-#define IR_KEYCODE(tab,code)   (((unsigned)code < IR_KEYTAB_SIZE) \
-                                ? tab[code] : KEY_RESERVED)
-
 #define RC5_START(x)   (((x)>>12)&3)
 #define RC5_TOGGLE(x)  (((x)>>11)&1)
 #define RC5_ADDR(x)    (((x)>>6)&31)
@@ -55,11 +55,11 @@ struct ir_scancode_table {
 struct ir_input_state {
        /* configuration */
        int                ir_type;
-       IR_KEYTAB_TYPE     ir_codes[IR_KEYTAB_SIZE];
+
+       struct ir_scancode_table keytable;
 
        /* key info */
-       u32                ir_raw;      /* raw data */
-       u32                ir_key;      /* ir key code */
+       u32                ir_key;      /* ir scancode */
        u32                keycode;     /* linux key code */
        int                keypressed;  /* current state */
 };
@@ -102,20 +102,36 @@ struct card_ir {
        struct tasklet_struct   tlet;
 };
 
-void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
+/* Routines from ir-functions.c */
+
+int ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
                   int ir_type, struct ir_scancode_table *ir_codes);
 void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir);
 void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
-                     u32 ir_key, u32 ir_raw);
+                     u32 ir_key);
 u32  ir_extract_bits(u32 data, u32 mask);
 int  ir_dump_samples(u32 *samples, int count);
 int  ir_decode_biphase(u32 *samples, int count, int low, int high);
 int  ir_decode_pulsedistance(u32 *samples, int count, int low, int high);
+u32  ir_rc5_decode(unsigned int code);
 
 void ir_rc5_timer_end(unsigned long data);
 void ir_rc5_timer_keyup(unsigned long data);
 
-/* Keymaps to be used by other modules */
+/* Routines from ir-keytable.c */
+
+u32 ir_g_keycode_from_table(struct input_dev *input_dev,
+                           u32 scancode);
+
+int ir_set_keycode_table(struct input_dev *input_dev,
+                        struct ir_scancode_table *rc_tab);
+
+int ir_roundup_tablesize(int n_elems);
+int ir_copy_table(struct ir_scancode_table *destin,
+                const struct ir_scancode_table *origin);
+void ir_input_free(struct input_dev *input_dev);
+
+/* scancode->keycode map tables from ir-keymaps.c */
 
 extern struct ir_scancode_table ir_codes_empty_table;
 extern struct ir_scancode_table ir_codes_avermedia_table;
@@ -150,6 +166,7 @@ extern struct ir_scancode_table ir_codes_rc5_tv_table;
 extern struct ir_scancode_table ir_codes_winfast_table;
 extern struct ir_scancode_table ir_codes_pinnacle_color_table;
 extern struct ir_scancode_table ir_codes_hauppauge_new_table;
+extern struct ir_scancode_table ir_codes_rc5_hauppauge_new_table;
 extern struct ir_scancode_table ir_codes_npgtech_table;
 extern struct ir_scancode_table ir_codes_norwood_table;
 extern struct ir_scancode_table ir_codes_proteus_2309_table;
@@ -172,6 +189,8 @@ extern struct ir_scancode_table ir_codes_ati_tv_wonder_hd_600_table;
 extern struct ir_scancode_table ir_codes_kworld_plus_tv_analog_table;
 extern struct ir_scancode_table ir_codes_kaiomy_table;
 extern struct ir_scancode_table ir_codes_dm1105_nec_table;
+extern struct ir_scancode_table ir_codes_tevii_nec_table;
+extern struct ir_scancode_table ir_codes_tbs_nec_table;
 extern struct ir_scancode_table ir_codes_evga_indtube_table;
 extern struct ir_scancode_table ir_codes_terratec_cinergy_xs_table;
 extern struct ir_scancode_table ir_codes_videomate_s350_table;
index cf16689..91942db 100644 (file)
@@ -64,6 +64,7 @@ enum {
        V4L2_IDENT_OV9650 = 254,
        V4L2_IDENT_OV9655 = 255,
        V4L2_IDENT_SOI968 = 256,
+       V4L2_IDENT_OV9640 = 257,
 
        /* module saa7146: reserved range 300-309 */
        V4L2_IDENT_SAA7146 = 300,
@@ -72,6 +73,7 @@ enum {
        V4L2_IDENT_CX23418_843 = 403, /* Integrated A/V Decoder on the '418 */
        V4L2_IDENT_CX23415 = 415,
        V4L2_IDENT_CX23416 = 416,
+       V4L2_IDENT_CX23417 = 417,
        V4L2_IDENT_CX23418 = 418,
 
        /* module au0828 */
@@ -129,6 +131,9 @@ enum {
        V4L2_IDENT_SAA6752HS = 6752,
        V4L2_IDENT_SAA6752HS_AC3 = 6753,
 
+       /* modules tef6862: just ident 6862 */
+       V4L2_IDENT_TEF6862 = 6862,
+
        /* module adv7170: just ident 7170 */
        V4L2_IDENT_ADV7170 = 7170,
 
@@ -165,12 +170,27 @@ enum {
        /* module mt9v011, just ident 8243 */
        V4L2_IDENT_MT9V011 = 8243,
 
+       /* module cx23885 and cx25840 */
+       V4L2_IDENT_CX23885    = 8850,
+       V4L2_IDENT_CX23885_AV = 8851, /* Integrated A/V decoder */
+       V4L2_IDENT_CX23887    = 8870,
+       V4L2_IDENT_CX23887_AV = 8871, /* Integrated A/V decoder */
+       V4L2_IDENT_CX23888    = 8880,
+       V4L2_IDENT_CX23888_AV = 8881, /* Integrated A/V decoder */
+       V4L2_IDENT_CX23888_IR = 8882, /* Integrated infrared controller */
+
        /* module tw9910: just ident 9910 */
        V4L2_IDENT_TW9910 = 9910,
 
        /* module sn9c20x: just ident 10000 */
        V4L2_IDENT_SN9C20X = 10000,
 
+       /* module cx231xx and cx25840 */
+       V4L2_IDENT_CX2310X_AV = 23099, /* Integrated A/V decoder; not in '100 */
+       V4L2_IDENT_CX23100    = 23100,
+       V4L2_IDENT_CX23101    = 23101,
+       V4L2_IDENT_CX23102    = 23102,
+
        /* module msp3400: reserved range 34000-34999 and 44000-44999 */
        V4L2_IDENT_MSPX4XX  = 34000, /* generic MSPX4XX identifier, only
                                        use internally (tveeprom.c). */
@@ -264,6 +284,9 @@ enum {
 
        /* module m52790: just ident 52790 */
        V4L2_IDENT_M52790 = 52790,
+
+       /* Sharp RJ54N1CB0C, 0xCB0C = 51980 */
+       V4L2_IDENT_RJ54N1CB0C = 51980,
 };
 
 #endif
index d411345..00bf176 100644 (file)
 
 #include <media/v4l2-common.h>
 
+/* generic v4l2_device notify callback notification values */
+#define V4L2_SUBDEV_IR_RX_NOTIFY               _IOW('v', 0, u32)
+#define V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ     0x00000001
+#define V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED   0x00000002
+#define V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN      0x00000004
+#define V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN      0x00000008
+
+#define V4L2_SUBDEV_IR_TX_NOTIFY               _IOW('v', 1, u32)
+#define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ     0x00000001
+
 struct v4l2_device;
 struct v4l2_subdev;
 struct tuner_setup;
@@ -96,6 +106,9 @@ struct v4l2_decode_vbi_line {
 
    s_gpio: set GPIO pins. Very simple right now, might need to be extended with
        a direction argument if needed.
+
+   s_power: puts subdevice in power saving mode (on == 0) or normal operation
+       mode (on == 1).
  */
 struct v4l2_subdev_core_ops {
        int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
@@ -118,6 +131,7 @@ struct v4l2_subdev_core_ops {
        int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
        int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);
 #endif
+       int (*s_power)(struct v4l2_subdev *sd, int on);
 };
 
 /* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio.
@@ -127,8 +141,6 @@ struct v4l2_subdev_core_ops {
    s_type_addr: sets tuner type and its I2C addr.
 
    s_config: sets tda9887 specific stuff, like port1, port2 and qss
-
-   s_standby: puts tuner on powersaving state, disabling it, except for i2c.
  */
 struct v4l2_subdev_tuner_ops {
        int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type);
@@ -141,7 +153,6 @@ struct v4l2_subdev_tuner_ops {
        int (*s_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
        int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
        int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
-       int (*s_standby)(struct v4l2_subdev *sd);
 };
 
 /* s_clock_freq: set the frequency (in Hz) of the audio clock output.
@@ -231,11 +242,95 @@ struct v4l2_subdev_video_ops {
        int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);
 };
 
+/*
+   interrupt_service_routine: Called by the bridge chip's interrupt service
+       handler, when an IR interrupt status has be raised due to this subdev,
+       so that this subdev can handle the details.  It may schedule work to be
+       performed later.  It must not sleep.  *Called from an IRQ context*.
+
+   [rt]x_g_parameters: Get the current operating parameters and state of the
+       the IR receiver or transmitter.
+
+   [rt]x_s_parameters: Set the current operating parameters and state of the
+       the IR receiver or transmitter.  It is recommended to call
+       [rt]x_g_parameters first to fill out the current state, and only change
+       the fields that need to be changed.  Upon return, the actual device
+       operating parameters and state will be returned.  Note that hardware
+       limitations may prevent the actual settings from matching the requested
+       settings - e.g. an actual carrier setting of 35,904 Hz when 36,000 Hz
+       was requested.  An exception is when the shutdown parameter is true.
+       The last used operational parameters will be returned, but the actual
+       state of the hardware be different to minimize power consumption and
+       processing when shutdown is true.
+
+   rx_read: Reads received codes or pulse width data.
+       The semantics are similar to a non-blocking read() call.
+
+   tx_write: Writes codes or pulse width data for transmission.
+       The semantics are similar to a non-blocking write() call.
+ */
+
+enum v4l2_subdev_ir_mode {
+       V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, /* space & mark widths in nanosecs */
+};
+
+/* Data format of data read or written for V4L2_SUBDEV_IR_MODE_PULSE_WIDTH */
+#define V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS      0x7fffffff
+#define V4L2_SUBDEV_IR_PULSE_LEVEL_MASK                0x80000000
+#define V4L2_SUBDEV_IR_PULSE_RX_SEQ_END                0xffffffff
+
+struct v4l2_subdev_ir_parameters {
+       /* Either Rx or Tx */
+       unsigned int bytes_per_data_element; /* of data in read or write call */
+       enum v4l2_subdev_ir_mode mode;
+
+       bool enable;
+       bool interrupt_enable;
+       bool shutdown; /* true: set hardware to low/no power, false: normal */
+
+       bool modulation;           /* true: uses carrier, false: baseband */
+       u32 max_pulse_width;       /* ns,      valid only for baseband signal */
+       unsigned int carrier_freq; /* Hz,      valid only for modulated signal*/
+       unsigned int duty_cycle;   /* percent, valid only for modulated signal*/
+       bool invert;               /* logically invert sense of mark/space */
+
+       /* Rx only */
+       u32 noise_filter_min_width;       /* ns, min time of a valid pulse */
+       unsigned int carrier_range_lower; /* Hz, valid only for modulated sig */
+       unsigned int carrier_range_upper; /* Hz, valid only for modulated sig */
+       u32 resolution;                   /* ns */
+};
+
+struct v4l2_subdev_ir_ops {
+       /* Common to receiver and transmitter */
+       int (*interrupt_service_routine)(struct v4l2_subdev *sd,
+                                               u32 status, bool *handled);
+
+       /* Receiver */
+       int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count,
+                               ssize_t *num);
+
+       int (*rx_g_parameters)(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_ir_parameters *params);
+       int (*rx_s_parameters)(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_ir_parameters *params);
+
+       /* Transmitter */
+       int (*tx_write)(struct v4l2_subdev *sd, u8 *buf, size_t count,
+                               ssize_t *num);
+
+       int (*tx_g_parameters)(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_ir_parameters *params);
+       int (*tx_s_parameters)(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_ir_parameters *params);
+};
+
 struct v4l2_subdev_ops {
        const struct v4l2_subdev_core_ops  *core;
        const struct v4l2_subdev_tuner_ops *tuner;
        const struct v4l2_subdev_audio_ops *audio;
        const struct v4l2_subdev_video_ops *video;
+       const struct v4l2_subdev_ir_ops    *ir;
 };
 
 #define V4L2_SUBDEV_NAME_SIZE 32
@@ -290,7 +385,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
    Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
  */
 #define v4l2_subdev_call(sd, o, f, args...)                            \
-       (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \
+       (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \
                (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))
 
 /* Send a notification to v4l2_device. */
index 1c5946c..316fdcc 100644 (file)
@@ -166,7 +166,7 @@ struct videobuf_queue {
        enum v4l2_field            field;
        enum v4l2_field            last;   /* for field=V4L2_FIELD_ALTERNATE */
        struct videobuf_buffer     *bufs[VIDEO_MAX_FRAME];
-       struct videobuf_queue_ops  *ops;
+       const struct videobuf_queue_ops  *ops;
        struct videobuf_qtype_ops  *int_ops;
 
        unsigned int               streaming:1;
@@ -195,7 +195,7 @@ void *videobuf_queue_to_vmalloc (struct videobuf_queue* q,
                                 struct videobuf_buffer *buf);
 
 void videobuf_queue_core_init(struct videobuf_queue *q,
-                        struct videobuf_queue_ops *ops,
+                        const struct videobuf_queue_ops *ops,
                         struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
index 5493866..ebaa9bc 100644 (file)
@@ -17,7 +17,7 @@
 #include <media/videobuf-core.h>
 
 void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
-                                   struct videobuf_queue_ops *ops,
+                                   const struct videobuf_queue_ops *ops,
                                    struct device *dev,
                                    spinlock_t *irqlock,
                                    enum v4l2_buf_type type,
index dda47f0..53e72f7 100644 (file)
@@ -103,7 +103,7 @@ struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf);
 void *videobuf_sg_alloc(size_t size);
 
 void videobuf_queue_sg_init(struct videobuf_queue* q,
-                        struct videobuf_queue_ops *ops,
+                        const struct videobuf_queue_ops *ops,
                         struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
index 6ba4f12..07cf4b9 100644 (file)
@@ -42,7 +42,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
                          void *adapter_priv,
                          struct device *device,
                          short *adapter_nr,
-                         int mfe_shared);
+                         int mfe_shared,
+                         int (*fe_ioctl_override)(struct dvb_frontend *,
+                                       unsigned int, void *, unsigned int));
 
 void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f);
 
index e87222c..4b419a2 100644 (file)
@@ -30,8 +30,8 @@ struct videobuf_vmalloc_memory
 };
 
 void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
-                        struct videobuf_queue_ops *ops,
-                        void *dev,
+                        const struct videobuf_queue_ops *ops,
+                        struct device *dev,
                         spinlock_t *irqlock,
                         enum v4l2_buf_type type,
                         enum v4l2_field field,
index 7571e32..ff30177 100644 (file)
@@ -989,7 +989,7 @@ struct sctp_transport {
        int init_sent_count;
 
        /* state       : The current state of this destination,
-        *             : i.e. SCTP_ACTIVE, SCTP_INACTIVE, SCTP_UNKOWN.
+        *             : i.e. SCTP_ACTIVE, SCTP_INACTIVE, SCTP_UNKNOWN.
         */
        int state;
 
index e2d2ca2..5740b85 100644 (file)
@@ -1262,7 +1262,7 @@ static inline struct sk_buff *tcp_write_queue_prev(struct sock *sk, struct sk_bu
        skb_queue_walk_from_safe(&(sk)->sk_write_queue, skb, tmp)
 
 /* This function calculates a "timeout" which is equivalent to the timeout of a
- * TCP connection after "boundary" unsucessful, exponentially backed-off
+ * TCP connection after "boundary" unsuccessful, exponentially backed-off
  * retransmissions with an initial RTO of TCP_RTO_MIN.
  */
 static inline bool retransmits_timed_out(const struct sock *sk,
index d69c4a7..3461aa1 100644 (file)
@@ -79,7 +79,7 @@
  * drivers have to only report state changes due to external
  * conditions.
  *
- * All API operations are 'atomic', serialized thorough a mutex in the
+ * All API operations are 'atomic', serialized through a mutex in the
  * `struct wimax_dev`.
  *
  * EXPORTING TO USER SPACE THROUGH GENERIC NETLINK
index 33b2750..b3a0ee6 100644 (file)
@@ -2,3 +2,4 @@ header-y += scsi.h
 header-y += scsi_netlink.h
 header-y += scsi_netlink_fc.h
 header-y += scsi_bsg_fc.h
+header-y += fc/
diff --git a/include/scsi/fc/Kbuild b/include/scsi/fc/Kbuild
new file mode 100644 (file)
index 0000000..5660381
--- /dev/null
@@ -0,0 +1,4 @@
+header-y += fc_els.h
+header-y += fc_fs.h
+header-y += fc_gs.h
+header-y += fc_ns.h
index 195ca01..f943281 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef _FC_ELS_H_
 #define        _FC_ELS_H_
 
+#include <linux/types.h>
+
 /*
  * Fibre Channel Switch - Enhanced Link Services definitions.
  * From T11 FC-LS Rev 1.2 June 7, 2005.
@@ -248,10 +250,12 @@ struct fc_els_csp {
 /*
  * sp_features
  */
-#define        FC_SP_FT_CIRO   0x8000  /* continuously increasing rel. off. */
+#define        FC_SP_FT_NPIV   0x8000  /* multiple N_Port_ID support (FLOGI) */
+#define        FC_SP_FT_CIRO   0x8000  /* continuously increasing rel off (PLOGI) */
 #define        FC_SP_FT_CLAD   0x8000  /* clean address (in FLOGI LS_ACC) */
 #define        FC_SP_FT_RAND   0x4000  /* random relative offset */
 #define        FC_SP_FT_VAL    0x2000  /* valid vendor version level */
+#define        FC_SP_FT_NPIV_ACC       0x2000  /* NPIV assignment (FLOGI LS_ACC) */
 #define        FC_SP_FT_FPORT  0x1000  /* F port (1) vs. N port (0) */
 #define        FC_SP_FT_ABB    0x0800  /* alternate BB_credit management */
 #define        FC_SP_FT_EDTR   0x0400  /* E_D_TOV Resolution is nanoseconds */
index ccb3dbe..e6ad3d2 100644 (file)
@@ -85,6 +85,18 @@ struct fcoe_crc_eof {
  */
 #define FCOE_MIN_FRAME 46
 
+/*
+ * FCoE Link Error Status Block: T11 FC-BB-5 Rev2.0, Clause 7.10.
+ */
+struct fcoe_fc_els_lesb {
+       __be32          lesb_link_fail; /* link failure count */
+       __be32          lesb_vlink_fail; /* virtual link failure count */
+       __be32          lesb_miss_fka;  /* missing FIP keep-alive count */
+       __be32          lesb_symb_err;  /* symbol error during carrier count */
+       __be32          lesb_err_block; /* errored block count */
+       __be32          lesb_fcs_error; /* frame check sequence error count */
+};
+
 /*
  * fc_fcoe_set_mac - Store OUI + DID into MAC address field.
  * @mac: mac address to be set
index 5d38f19..747e2c7 100644 (file)
@@ -83,6 +83,8 @@ struct fcp_cmnd32 {
  * fc_tm_flags - task management flags field.
  */
 #define        FCP_TMF_CLR_ACA         0x40    /* clear ACA condition */
+#define        FCP_TMF_TGT_RESET       0x20    /* target reset task management,
+                                          deprecated as of FCP-3 */
 #define        FCP_TMF_LUN_RESET       0x10    /* logical unit reset task management */
 #define        FCP_TMF_CLR_TASK_SET    0x04    /* clear task set */
 #define        FCP_TMF_ABT_TASK_SET    0x02    /* abort task set */
@@ -196,4 +198,10 @@ struct fcp_srr {
        __u8            srr_resvd2[3];  /* reserved */
 };
 
+/*
+ * Feature bits in name server FC-4 Features object.
+ */
+#define        FCP_FEAT_TARG   (1 << 0)        /* target function supported */
+#define        FCP_FEAT_INIT   (1 << 1)        /* initiator function supported */
+
 #endif /* _FC_FCP_H_ */
index 3d138c1..17baa19 100644 (file)
@@ -214,10 +214,20 @@ struct fip_vn_desc {
  */
 struct fip_fka_desc {
        struct fip_desc fd_desc;
-       __u8            fd_resvd[2];
+       __u8            fd_resvd;
+       __u8            fd_flags;       /* bit0 is fka disable flag */
        __be32          fd_fka_period;  /* adv./keep-alive period in mS */
 } __attribute__((packed));
 
+/*
+ * flags for fip_fka_desc.fd_flags
+ */
+enum fip_fka_flags {
+       FIP_FKA_ADV_D = 0x01,           /* no need for FKA from ENode */
+};
+
+/* FIP_DT_FKA flags */
+
 /*
  * FIP_DT_VENDOR descriptor.
  */
index ac4cd38..50f28b1 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef _FC_FS_H_
 #define _FC_FS_H_
 
+#include <linux/types.h>
+
 /*
  * Fibre Channel Framing and Signalling definitions.
  * From T11 FC-FS-2 Rev 0.90 - 9 August 2005.
index 324dd0e..a37346d 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef _FC_GS_H_
 #define        _FC_GS_H_
 
+#include <linux/types.h>
+
 /*
  * Fibre Channel Services - Common Transport.
  * From T11.org FC-GS-2 Rev 5.3 November 1998.
index 790d7b9..e7d3ac4 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef _FC_NS_H_
 #define        _FC_NS_H_
 
+#include <linux/types.h>
+
 /*
  * Fibre Channel Services - Name Service (dNS)
  * From T11.org FC-GS-2 Rev 5.3 November 1998.
@@ -44,9 +46,12 @@ enum fc_ns_req {
        FC_NS_GID_FT =  0x0171,         /* get IDs by FC4 type */
        FC_NS_GPN_FT =  0x0172,         /* get port names by FC4 type */
        FC_NS_GID_PT =  0x01a1,         /* get IDs by port type */
-       FC_NS_RFT_ID =  0x0217,         /* reg FC4 type for ID */
        FC_NS_RPN_ID =  0x0212,         /* reg port name for ID */
        FC_NS_RNN_ID =  0x0213,         /* reg node name for ID */
+       FC_NS_RFT_ID =  0x0217,         /* reg FC4 type for ID */
+       FC_NS_RSPN_ID = 0x0218,         /* reg symbolic port name */
+       FC_NS_RFF_ID =  0x021f,         /* reg FC4 Features for ID */
+       FC_NS_RSNN_NN = 0x0239,         /* reg symbolic node name */
 };
 
 /*
@@ -156,4 +161,32 @@ struct fc_ns_rn_id {
        __be64          fr_wwn;         /* node name or port name */
 } __attribute__((__packed__));
 
+/*
+ * RSNN_NN request - register symbolic node name
+ */
+struct fc_ns_rsnn {
+       __be64          fr_wwn;         /* node name */
+       __u8            fr_name_len;
+       char            fr_name[];
+} __attribute__((__packed__));
+
+/*
+ * RSPN_ID request - register symbolic port name
+ */
+struct fc_ns_rspn {
+       struct fc_ns_fid fr_fid;        /* port ID object */
+       __u8            fr_name_len;
+       char            fr_name[];
+} __attribute__((__packed__));
+
+/*
+ * RFF_ID request - register FC-4 Features for ID.
+ */
+struct fc_ns_rff_id {
+       struct fc_ns_fid fr_fid;        /* port ID object */
+       __u8            fr_resvd[2];
+       __u8            fr_feat;        /* FC-4 Feature bits */
+       __u8            fr_type;        /* FC-4 type */
+} __attribute__((__packed__));
+
 #endif /* _FC_NS_H_ */
index 27dad70..8eb0a0f 100644 (file)
@@ -32,7 +32,10 @@ struct fc_ct_req {
                struct fc_ns_gid_ft gid;
                struct fc_ns_rn_id  rn;
                struct fc_ns_rft rft;
+               struct fc_ns_rff_id rff;
                struct fc_ns_fid fid;
+               struct fc_ns_rsnn snn;
+               struct fc_ns_rspn spn;
        } payload;
 };
 
@@ -109,6 +112,7 @@ static inline int fc_ct_fill(struct fc_lport *lport,
                      enum fc_fh_type *fh_type)
 {
        struct fc_ct_req *ct;
+       size_t len;
 
        switch (op) {
        case FC_NS_GPN_FT:
@@ -128,12 +132,41 @@ static inline int fc_ct_fill(struct fc_lport *lport,
                ct->payload.rft.fts = lport->fcts;
                break;
 
-       case FC_NS_RPN_ID:
+       case FC_NS_RFF_ID:
+               ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rff_id));
+               hton24(ct->payload.rff.fr_fid.fp_fid,
+                      fc_host_port_id(lport->host));
+               ct->payload.rff.fr_type = FC_TYPE_FCP;
+               if (lport->service_params & FCP_SPPF_INIT_FCN)
+                       ct->payload.rff.fr_feat = FCP_FEAT_INIT;
+               if (lport->service_params & FCP_SPPF_TARG_FCN)
+                       ct->payload.rff.fr_feat |= FCP_FEAT_TARG;
+               break;
+
+       case FC_NS_RNN_ID:
                ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id));
                hton24(ct->payload.rn.fr_fid.fp_fid,
                       fc_host_port_id(lport->host));
-               ct->payload.rft.fts = lport->fcts;
-               put_unaligned_be64(lport->wwpn, &ct->payload.rn.fr_wwn);
+               put_unaligned_be64(lport->wwnn, &ct->payload.rn.fr_wwn);
+               break;
+
+       case FC_NS_RSPN_ID:
+               len = strnlen(fc_host_symbolic_name(lport->host), 255);
+               ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rspn) + len);
+               hton24(ct->payload.spn.fr_fid.fp_fid,
+                      fc_host_port_id(lport->host));
+               strncpy(ct->payload.spn.fr_name,
+                       fc_host_symbolic_name(lport->host), len);
+               ct->payload.spn.fr_name_len = len;
+               break;
+
+       case FC_NS_RSNN_NN:
+               len = strnlen(fc_host_symbolic_name(lport->host), 255);
+               ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rsnn) + len);
+               put_unaligned_be64(lport->wwnn, &ct->payload.snn.fr_wwn);
+               strncpy(ct->payload.snn.fr_name,
+                       fc_host_symbolic_name(lport->host), len);
+               ct->payload.snn.fr_name_len = len;
                break;
 
        default:
@@ -198,6 +231,31 @@ static inline void fc_flogi_fill(struct fc_lport *lport, struct fc_frame *fp)
        sp->sp_bb_data = htons((u16) lport->mfs);
        cp = &flogi->fl_cssp[3 - 1];    /* class 3 parameters */
        cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
+       if (lport->does_npiv)
+               sp->sp_features = htons(FC_SP_FT_NPIV);
+}
+
+/**
+ * fc_fdisc_fill - Fill in a fdisc request frame.
+ */
+static inline void fc_fdisc_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+       struct fc_els_csp *sp;
+       struct fc_els_cssp *cp;
+       struct fc_els_flogi *fdisc;
+
+       fdisc = fc_frame_payload_get(fp, sizeof(*fdisc));
+       memset(fdisc, 0, sizeof(*fdisc));
+       fdisc->fl_cmd = (u8) ELS_FDISC;
+       put_unaligned_be64(lport->wwpn, &fdisc->fl_wwpn);
+       put_unaligned_be64(lport->wwnn, &fdisc->fl_wwnn);
+       sp = &fdisc->fl_csp;
+       sp->sp_hi_ver = 0x20;
+       sp->sp_lo_ver = 0x20;
+       sp->sp_bb_cred = htons(10);     /* this gets set by gateway */
+       sp->sp_bb_data = htons((u16) lport->mfs);
+       cp = &fdisc->fl_cssp[3 - 1];    /* class 3 parameters */
+       cp->cp_class = htons(FC_CPC_VALID | FC_CPC_SEQ);
 }
 
 /**
@@ -296,6 +354,10 @@ static inline int fc_els_fill(struct fc_lport *lport,
                fc_flogi_fill(lport, fp);
                break;
 
+       case ELS_FDISC:
+               fc_fdisc_fill(lport, fp);
+               break;
+
        case ELS_LOGO:
                fc_logo_fill(lport, fp);
                break;
index c35d238..4d3e9c7 100644 (file)
@@ -28,6 +28,8 @@
 #include <scsi/fc/fc_fcp.h>
 #include <scsi/fc/fc_encaps.h>
 
+#include <linux/if_ether.h>
+
 /*
  * The fc_frame interface is used to pass frame data between functions.
  * The frame includes the data buffer, length, and SOF / EOF delimiter types.
@@ -37,6 +39,9 @@
 #define        FC_FRAME_HEADROOM       32      /* headroom for VLAN + FCoE headers */
 #define        FC_FRAME_TAILROOM       8       /* trailer space for FCoE */
 
+/* Max number of skb frags allowed, reserving one for fcoe_crc_eof page */
+#define FC_FRAME_SG_LEN                (MAX_SKB_FRAGS - 1)
+
 #define fp_skb(fp)     (&((fp)->skb))
 #define fr_hdr(fp)     ((fp)->skb.data)
 #define fr_len(fp)     ((fp)->skb.len)
@@ -64,6 +69,7 @@ struct fcoe_rcv_info {
        enum fc_sof     fr_sof;         /* start of frame delimiter */
        enum fc_eof     fr_eof;         /* end of frame delimiter */
        u8              fr_flags;       /* flags - see below */
+       u8              granted_mac[ETH_ALEN]; /* FCoE MAC address */
 };
 
 
@@ -94,17 +100,7 @@ static inline void fc_frame_init(struct fc_frame *fp)
 }
 
 struct fc_frame *fc_frame_alloc_fill(struct fc_lport *, size_t payload_len);
-
-struct fc_frame *__fc_frame_alloc(size_t payload_len);
-
-/*
- * Get frame for sending via port.
- */
-static inline struct fc_frame *_fc_frame_alloc(struct fc_lport *dev,
-                                              size_t payload_len)
-{
-       return __fc_frame_alloc(payload_len);
-}
+struct fc_frame *_fc_frame_alloc(size_t payload_len);
 
 /*
  * Allocate fc_frame structure and buffer.  Set the initial length to
@@ -118,10 +114,10 @@ static inline struct fc_frame *fc_frame_alloc(struct fc_lport *dev, size_t len)
         * Note: Since len will often be a constant multiple of 4,
         * this check will usually be evaluated and eliminated at compile time.
         */
-       if ((len % 4) != 0)
+       if (len && len % 4)
                fp = fc_frame_alloc_fill(dev, len);
        else
-               fp = _fc_frame_alloc(dev, len);
+               fp = _fc_frame_alloc(len);
        return fp;
 }
 
index d67dda2..66d377b 100644 (file)
@@ -311,6 +311,8 @@ enum iscsi_param {
        ISCSI_PARAM_IFACE_NAME,
        ISCSI_PARAM_ISID,
        ISCSI_PARAM_INITIATOR_NAME,
+
+       ISCSI_PARAM_TGT_RESET_TMO,
        /* must always be last */
        ISCSI_PARAM_MAX,
 };
@@ -350,6 +352,7 @@ enum iscsi_param {
 #define ISCSI_IFACE_NAME               (1ULL << ISCSI_PARAM_IFACE_NAME)
 #define ISCSI_ISID                     (1ULL << ISCSI_PARAM_ISID)
 #define ISCSI_INITIATOR_NAME           (1ULL << ISCSI_PARAM_INITIATOR_NAME)
+#define ISCSI_TGT_RESET_TMO            (1ULL << ISCSI_PARAM_TGT_RESET_TMO)
 
 /* iSCSI HBA params */
 enum iscsi_host_param {
index f2a2c11..dd0a52c 100644 (file)
@@ -279,6 +279,8 @@ struct iscsi_tm {
 #define ISCSI_TM_FUNC_TARGET_COLD_RESET                7
 #define ISCSI_TM_FUNC_TASK_REASSIGN            8
 
+#define ISCSI_TM_FUNC_VALUE(hdr) ((hdr)->flags & ISCSI_FLAG_TM_FUNC_MASK)
+
 /* SCSI Task Management Response Header */
 struct iscsi_tm_rsp {
        uint8_t opcode;
index 65dc9aa..4b912ee 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
 
 #include <scsi/fc/fc_fcp.h>
 #include <scsi/fc/fc_ns.h>
 
 #include <scsi/fc_frame.h>
 
-#define FC_LIBFC_LOGGING 0x01 /* General logging, not categorized */
-#define FC_LPORT_LOGGING 0x02 /* lport layer logging */
-#define FC_DISC_LOGGING  0x04 /* discovery layer logging */
-#define FC_RPORT_LOGGING 0x08 /* rport layer logging */
-#define FC_FCP_LOGGING   0x10 /* I/O path logging */
-#define FC_EM_LOGGING    0x20 /* Exchange Manager logging */
-#define FC_EXCH_LOGGING  0x40 /* Exchange/Sequence logging */
-#define FC_SCSI_LOGGING  0x80 /* SCSI logging (mostly error handling) */
-
-extern unsigned int fc_debug_logging;
-
-#define FC_CHECK_LOGGING(LEVEL, CMD)                           \
-do {                                                           \
-       if (unlikely(fc_debug_logging & LEVEL))                 \
-               do {                                            \
-                       CMD;                                    \
-               } while (0);                                    \
-} while (0)
-
-#define FC_LIBFC_DBG(fmt, args...)                                     \
-       FC_CHECK_LOGGING(FC_LIBFC_LOGGING,                              \
-                        printk(KERN_INFO "libfc: " fmt, ##args))
-
-#define FC_LPORT_DBG(lport, fmt, args...)                              \
-       FC_CHECK_LOGGING(FC_LPORT_LOGGING,                              \
-                        printk(KERN_INFO "host%u: lport %6x: " fmt,    \
-                               (lport)->host->host_no,                 \
-                               fc_host_port_id((lport)->host), ##args))
-
-#define FC_DISC_DBG(disc, fmt, args...)                                        \
-       FC_CHECK_LOGGING(FC_DISC_LOGGING,                               \
-                        printk(KERN_INFO "host%u: disc: " fmt,         \
-                               (disc)->lport->host->host_no,           \
-                               ##args))
-
-#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...)                  \
-       FC_CHECK_LOGGING(FC_RPORT_LOGGING,                              \
-                        printk(KERN_INFO "host%u: rport %6x: " fmt,    \
-                               (lport)->host->host_no,                 \
-                               (port_id), ##args))
-
-#define FC_RPORT_DBG(rdata, fmt, args...)                              \
-       FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)
-
-#define FC_FCP_DBG(pkt, fmt, args...)                                  \
-       FC_CHECK_LOGGING(FC_FCP_LOGGING,                                \
-                        printk(KERN_INFO "host%u: fcp: %6x: " fmt,     \
-                               (pkt)->lp->host->host_no,               \
-                               pkt->rport->port_id, ##args))
-
-#define FC_EXCH_DBG(exch, fmt, args...)                                        \
-       FC_CHECK_LOGGING(FC_EXCH_LOGGING,                               \
-                        printk(KERN_INFO "host%u: xid %4x: " fmt,      \
-                               (exch)->lp->host->host_no,              \
-                               exch->xid, ##args))
-
-#define FC_SCSI_DBG(lport, fmt, args...)                               \
-       FC_CHECK_LOGGING(FC_SCSI_LOGGING,                               \
-                        printk(KERN_INFO "host%u: scsi: " fmt,         \
-                               (lport)->host->host_no, ##args))
-
 /*
  * libfc error codes
  */
@@ -115,15 +55,28 @@ do {                                                               \
                p[2] = ((v) & 0xFF);            \
        } while (0)
 
-/*
- * FC HBA status
+/**
+ * enum fc_lport_state - Local port states
+ * @LPORT_ST_DISABLED: Disabled
+ * @LPORT_ST_FLOGI:    Fabric login (FLOGI) sent
+ * @LPORT_ST_DNS:      Waiting for name server remote port to become ready
+ * @LPORT_ST_RPN_ID:   Register port name by ID (RPN_ID) sent
+ * @LPORT_ST_RFT_ID:   Register Fibre Channel types by ID (RFT_ID) sent
+ * @LPORT_ST_RFF_ID:   Register FC-4 Features by ID (RFF_ID) sent
+ * @LPORT_ST_SCR:      State Change Register (SCR) sent
+ * @LPORT_ST_READY:    Ready for use
+ * @LPORT_ST_LOGO:     Local port logout (LOGO) sent
+ * @LPORT_ST_RESET:    Local port reset
  */
 enum fc_lport_state {
        LPORT_ST_DISABLED = 0,
        LPORT_ST_FLOGI,
        LPORT_ST_DNS,
-       LPORT_ST_RPN_ID,
+       LPORT_ST_RNN_ID,
+       LPORT_ST_RSNN_NN,
+       LPORT_ST_RSPN_ID,
        LPORT_ST_RFT_ID,
+       LPORT_ST_RFF_ID,
        LPORT_ST_SCR,
        LPORT_ST_READY,
        LPORT_ST_LOGO,
@@ -136,15 +89,28 @@ enum fc_disc_event {
        DISC_EV_FAILED
 };
 
+/**
+ * enum fc_rport_state - Remote port states
+ * @RPORT_ST_INIT:    Initialized
+ * @RPORT_ST_PLOGI:   Waiting for PLOGI completion
+ * @RPORT_ST_PRLI:    Waiting for PRLI completion
+ * @RPORT_ST_RTV:     Waiting for RTV completion
+ * @RPORT_ST_READY:   Ready for use
+ * @RPORT_ST_LOGO:    Remote port logout (LOGO) sent
+ * @RPORT_ST_ADISC:   Discover Address sent
+ * @RPORT_ST_DELETE:  Remote port being deleted
+ * @RPORT_ST_RESTART: Remote port being deleted and will restart
+*/
 enum fc_rport_state {
-       RPORT_ST_INIT,          /* initialized */
-       RPORT_ST_PLOGI,         /* waiting for PLOGI completion */
-       RPORT_ST_PRLI,          /* waiting for PRLI completion */
-       RPORT_ST_RTV,           /* waiting for RTV completion */
-       RPORT_ST_READY,         /* ready for use */
-       RPORT_ST_LOGO,          /* port logout sent */
-       RPORT_ST_ADISC,         /* Discover Address sent */
-       RPORT_ST_DELETE,        /* port being deleted */
+       RPORT_ST_INIT,
+       RPORT_ST_PLOGI,
+       RPORT_ST_PRLI,
+       RPORT_ST_RTV,
+       RPORT_ST_READY,
+       RPORT_ST_LOGO,
+       RPORT_ST_ADISC,
+       RPORT_ST_DELETE,
+       RPORT_ST_RESTART,
 };
 
 /**
@@ -155,12 +121,20 @@ enum fc_rport_state {
  * @port_id:    Port ID of the discovered port
  */
 struct fc_disc_port {
-       struct fc_lport             *lp;
-       struct list_head            peers;
-       struct work_struct          rport_work;
-       u32                         port_id;
+       struct fc_lport    *lp;
+       struct list_head   peers;
+       struct work_struct rport_work;
+       u32                port_id;
 };
 
+/**
+ * enum fc_rport_event - Remote port events
+ * @RPORT_EV_NONE:   No event
+ * @RPORT_EV_READY:  Remote port is ready for use
+ * @RPORT_EV_FAILED: State machine failed, remote port is not ready
+ * @RPORT_EV_STOP:   Remote port has been stopped
+ * @RPORT_EV_LOGO:   Remote port logout (LOGO) sent
+ */
 enum fc_rport_event {
        RPORT_EV_NONE = 0,
        RPORT_EV_READY,
@@ -171,6 +145,10 @@ enum fc_rport_event {
 
 struct fc_rport_priv;
 
+/**
+ * struct fc_rport_operations - Operations for a remote port
+ * @event_callback: Function to be called for remote port events
+ */
 struct fc_rport_operations {
        void (*event_callback)(struct fc_lport *, struct fc_rport_priv *,
                               enum fc_rport_event);
@@ -178,11 +156,11 @@ struct fc_rport_operations {
 
 /**
  * struct fc_rport_libfc_priv - libfc internal information about a remote port
- * @local_port: Fibre Channel host port instance
- * @rp_state: indicates READY for I/O or DELETE when blocked.
- * @flags: REC and RETRY supported flags
- * @e_d_tov: error detect timeout value (in msec)
- * @r_a_tov: resource allocation timeout value (in msec)
+ * @local_port: The associated local port
+ * @rp_state:   Indicates READY for I/O or DELETE when blocked
+ * @flags:      REC and RETRY supported flags
+ * @e_d_tov:    Error detect timeout value (in msec)
+ * @r_a_tov:    Resource allocation timeout value (in msec)
  */
 struct fc_rport_libfc_priv {
        struct fc_lport            *local_port;
@@ -195,47 +173,66 @@ struct fc_rport_libfc_priv {
 };
 
 /**
- * struct fc_rport_priv - libfc rport and discovery info about a remote port
- * @local_port: Fibre Channel host port instance
- * @rport: transport remote port
- * @kref: reference counter
- * @rp_state: state tracks progress of PLOGI, PRLI, and RTV exchanges
- * @ids: remote port identifiers and roles
- * @flags: REC and RETRY supported flags
- * @max_seq: maximum number of concurrent sequences
- * @disc_id: discovery identifier
- * @maxframe_size: maximum frame size
- * @retries: retry count in current state
- * @e_d_tov: error detect timeout value (in msec)
- * @r_a_tov: resource allocation timeout value (in msec)
- * @rp_mutex: mutex protects rport
- * @retry_work:
- * @event_callback: Callback for rport READY, FAILED or LOGO
+ * struct fc_rport_priv - libfc remote port and discovery info
+ * @local_port:     The associated local port
+ * @rport:          The FC transport remote port
+ * @kref:           Reference counter
+ * @rp_state:       Enumeration that tracks progress of PLOGI, PRLI,
+ *                  and RTV exchanges
+ * @ids:            The remote port identifiers and roles
+ * @flags:          REC and RETRY supported flags
+ * @max_seq:        Maximum number of concurrent sequences
+ * @disc_id:        The discovery identifier
+ * @maxframe_size:  The maximum frame size
+ * @retries:        The retry count for the current state
+ * @e_d_tov:        Error detect timeout value (in msec)
+ * @r_a_tov:        Resource allocation timeout value (in msec)
+ * @rp_mutex:       The mutex that protects the remote port
+ * @retry_work:     Handle for retries
+ * @event_callback: Callback when READY, FAILED or LOGO states complete
  */
 struct fc_rport_priv {
-       struct fc_lport            *local_port;
-       struct fc_rport            *rport;
-       struct kref                kref;
-       enum fc_rport_state        rp_state;
+       struct fc_lport             *local_port;
+       struct fc_rport             *rport;
+       struct kref                 kref;
+       enum fc_rport_state         rp_state;
        struct fc_rport_identifiers ids;
-       u16                        flags;
-       u16                        max_seq;
-       u16                        disc_id;
-       u16                        maxframe_size;
-       unsigned int               retries;
-       unsigned int               e_d_tov;
-       unsigned int               r_a_tov;
-       struct mutex               rp_mutex;
-       struct delayed_work        retry_work;
-       enum fc_rport_event        event;
-       struct fc_rport_operations *ops;
-       struct list_head           peers;
-       struct work_struct         event_work;
-       u32                        supported_classes;
+       u16                         flags;
+       u16                         max_seq;
+       u16                         disc_id;
+       u16                         maxframe_size;
+       unsigned int                retries;
+       unsigned int                e_d_tov;
+       unsigned int                r_a_tov;
+       struct mutex                rp_mutex;
+       struct delayed_work         retry_work;
+       enum fc_rport_event         event;
+       struct fc_rport_operations  *ops;
+       struct list_head            peers;
+       struct work_struct          event_work;
+       u32                         supported_classes;
 };
 
-/*
- * fcoe stats structure
+/**
+ * struct fcoe_dev_stats - fcoe stats structure
+ * @SecondsSinceLastReset: Seconds since the last reset
+ * @TxFrames:              Number of transmitted frames
+ * @TxWords:               Number of transmitted words
+ * @RxFrames:              Number of received frames
+ * @RxWords:               Number of received words
+ * @ErrorFrames:           Number of received error frames
+ * @DumpedFrames:          Number of dumped frames
+ * @LinkFailureCount:      Number of link failures
+ * @LossOfSignalCount:     Number for signal losses
+ * @InvalidTxWordCount:    Number of invalid transmitted words
+ * @InvalidCRCCount:       Number of invalid CRCs
+ * @InputRequests:         Number of input requests
+ * @OutputRequests:        Number of output requests
+ * @ControlRequests:       Number of control requests
+ * @InputMegabytes:        Number of received megabytes
+ * @OutputMegabytes:       Number of transmitted megabytes
+ * @VLinkFailureCount:     Number of virtual link failures
+ * @MissDiscAdvCount:      Number of missing FIP discovery advertisement
  */
 struct fcoe_dev_stats {
        u64             SecondsSinceLastReset;
@@ -254,12 +251,17 @@ struct fcoe_dev_stats {
        u64             ControlRequests;
        u64             InputMegabytes;
        u64             OutputMegabytes;
+       u64             VLinkFailureCount;
+       u64             MissDiscAdvCount;
 };
 
-/*
- * els data is used for passing ELS respone specific
- * data to send ELS response mainly using infomation
- * in exchange and sequence in EM layer.
+/**
+ * struct fc_seq_els_data - ELS data used for passing ELS specific responses
+ * @fp:     The ELS frame
+ * @reason: The reason for rejection
+ * @explan: The explaination of the rejection
+ *
+ * Mainly used by the exchange manager layer.
  */
 struct fc_seq_els_data {
        struct fc_frame *fp;
@@ -267,77 +269,87 @@ struct fc_seq_els_data {
        enum fc_els_rjt_explan explan;
 };
 
-/*
- * FCP request structure, one for each scsi cmd request
+/**
+ * struct fc_fcp_pkt - FCP request structure (one for each scsi_cmnd request)
+ * @lp:              The associated local port
+ * @state:           The state of the I/O
+ * @tgt_flags:       Target's flags
+ * @ref_cnt:         Reference count
+ * @scsi_pkt_lock:   Lock to protect the SCSI packet (must be taken before the
+ *                   host_lock if both are to be held at the same time)
+ * @cmd:             The SCSI command (set and clear with the host_lock held)
+ * @list:            Tracks queued commands (accessed with the host_lock held)
+ * @timer:           The command timer
+ * @tm_done:         Completion indicator
+ * @wait_for_comp:   Indicator to wait for completion of the I/O (in jiffies)
+ * @start_time:      Timestamp indicating the start of the I/O (in jiffies)
+ * @end_time:        Timestamp indicating the end of the I/O (in jiffies)
+ * @last_pkt_time:   Timestamp of the last frame received (in jiffies)
+ * @data_len:        The length of the data
+ * @cdb_cmd:         The CDB command
+ * @xfer_len:        The transfer length
+ * @xfer_ddp:        Indicates if this transfer used DDP (XID of the exchange
+ *                   will be set here if DDP was setup)
+ * @xfer_contig_end: The offset into the buffer if the buffer is contiguous
+ *                   (Tx and Rx)
+ * @max_payload:     The maximum payload size (in bytes)
+ * @io_status:       SCSI result (upper 24 bits)
+ * @cdb_status:      CDB status
+ * @status_code:     FCP I/O status
+ * @scsi_comp_flags: Completion flags (bit 3 Underrun bit 2: overrun)
+ * @req_flags:       Request flags (bit 0: read bit:1 write)
+ * @scsi_resid:      SCSI residule length
+ * @rport:           The remote port that the SCSI command is targeted at
+ * @seq_ptr:         The sequence that will carry the SCSI command
+ * @recov_retry:     Number of recovery retries
+ * @recov_seq:       The sequence for REC or SRR
  */
 struct fc_fcp_pkt {
-       /*
-        * housekeeping stuff
-        */
-       struct fc_lport *lp;    /* handle to hba struct */
-       u16             state;          /* scsi_pkt state state */
-       u16             tgt_flags;      /* target flags  */
-       atomic_t        ref_cnt;        /* fcp pkt ref count */
-       spinlock_t      scsi_pkt_lock;  /* Must be taken before the host lock
-                                        * if both are held at the same time */
-       /*
-        * SCSI I/O related stuff
-        */
-       struct scsi_cmnd *cmd;          /* scsi command pointer. set/clear
-                                        * under host lock */
-       struct list_head list;          /* tracks queued commands. access under
-                                        * host lock */
-       /*
-        * timeout related stuff
-        */
-       struct timer_list timer;        /* command timer */
+       /* Housekeeping information */
+       struct fc_lport   *lp;
+       u16               state;
+       u16               tgt_flags;
+       atomic_t          ref_cnt;
+       spinlock_t        scsi_pkt_lock;
+
+       /* SCSI I/O related information */
+       struct scsi_cmnd  *cmd;
+       struct list_head  list;
+
+       /* Timeout related information */
+       struct timer_list timer;
        struct completion tm_done;
-       int     wait_for_comp;
-       unsigned long   start_time;     /* start jiffie */
-       unsigned long   end_time;       /* end jiffie */
-       unsigned long   last_pkt_time;   /* jiffies of last frame received */
-
-       /*
-        * scsi cmd and data transfer information
-        */
-       u32             data_len;
-       /*
-        * transport related veriables
-        */
-       struct fcp_cmnd cdb_cmd;
-       size_t          xfer_len;
-       u16             xfer_ddp;       /* this xfer is ddped */
-       u32             xfer_contig_end; /* offset of end of contiguous xfer */
-       u16             max_payload;    /* max payload size in bytes */
-
-       /*
-        * scsi/fcp return status
-        */
-       u32             io_status;      /* SCSI result upper 24 bits */
-       u8              cdb_status;
-       u8              status_code;    /* FCP I/O status */
-       /* bit 3 Underrun bit 2: overrun */
-       u8              scsi_comp_flags;
-       u32             req_flags;      /* bit 0: read bit:1 write */
-       u32             scsi_resid;     /* residule length */
-
-       struct fc_rport *rport;         /* remote port pointer */
-       struct fc_seq   *seq_ptr;       /* current sequence pointer */
-       /*
-        * Error Processing
-        */
-       u8              recov_retry;    /* count of recovery retries */
-       struct fc_seq   *recov_seq;     /* sequence for REC or SRR */
+       int               wait_for_comp;
+       unsigned long     start_time;
+       unsigned long     end_time;
+       unsigned long     last_pkt_time;
+
+       /* SCSI command and data transfer information */
+       u32               data_len;
+
+       /* Transport related veriables */
+       struct fcp_cmnd   cdb_cmd;
+       size_t            xfer_len;
+       u16               xfer_ddp;
+       u32               xfer_contig_end;
+       u16               max_payload;
+
+       /* SCSI/FCP return status */
+       u32               io_status;
+       u8                cdb_status;
+       u8                status_code;
+       u8                scsi_comp_flags;
+       u32               req_flags;
+       u32               scsi_resid;
+
+       /* Associated structures */
+       struct fc_rport   *rport;
+       struct fc_seq     *seq_ptr;
+
+       /* Error Processing information */
+       u8                recov_retry;
+       struct fc_seq     *recov_seq;
 };
-/*
- * FC_FCP HELPER FUNCTIONS
- *****************************/
-static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp)
-{
-       if (fsp && fsp->cmd)
-               return fsp->cmd->sc_data_direction == DMA_FROM_DEVICE;
-       return false;
-}
 
 /*
  * Structure and function definitions for managing Fibre Channel Exchanges
@@ -350,23 +362,51 @@ static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp)
 
 struct fc_exch_mgr;
 struct fc_exch_mgr_anchor;
-extern u16     fc_cpu_mask;    /* cpu mask for possible cpus */
+extern u16 fc_cpu_mask;        /* cpu mask for possible cpus */
 
-/*
- * Sequence.
+/**
+ * struct fc_seq - FC sequence
+ * @id:       The sequence ID
+ * @ssb_stat: Status flags for the sequence status block (SSB)
+ * @cnt:      Number of frames sent so far
+ * @rec_data: FC-4 value for REC
  */
 struct fc_seq {
-       u8      id;             /* seq ID */
-       u16     ssb_stat;       /* status flags for sequence status block */
-       u16     cnt;            /* frames sent so far on sequence */
-       u32     rec_data;       /* FC-4 value for REC */
+       u8  id;
+       u16 ssb_stat;
+       u16 cnt;
+       u32 rec_data;
 };
 
 #define FC_EX_DONE             (1 << 0) /* ep is completed */
 #define FC_EX_RST_CLEANUP      (1 << 1) /* reset is forcing completion */
 
-/*
- * Exchange.
+/**
+ * struct fc_exch - Fibre Channel Exchange
+ * @em:           Exchange manager
+ * @pool:         Exchange pool
+ * @state:        The exchange's state
+ * @xid:          The exchange ID
+ * @ex_list:      Handle used by the EM to track free exchanges
+ * @ex_lock:      Lock that protects the exchange
+ * @ex_refcnt:    Reference count
+ * @timeout_work: Handle for timeout handler
+ * @lp:           The local port that this exchange is on
+ * @oxid:         Originator's exchange ID
+ * @rxid:         Responder's exchange ID
+ * @oid:          Originator's FCID
+ * @sid:          Source FCID
+ * @did:          Destination FCID
+ * @esb_stat:     ESB exchange status
+ * @r_a_tov:      Resouce allocation time out value (in msecs)
+ * @seq_id:       The next sequence ID to use
+ * @f_ctl:        F_CTL flags for the sequence
+ * @fh_type:      The frame type
+ * @class:        The class of service
+ * @seq:          The sequence in use on this exchange
+ * @resp:         Callback for responses on this exchange
+ * @destructor:   Called when destroying the exchange
+ * @arg:          Passed as a void pointer to the resp() callback
  *
  * Locking notes: The ex_lock protects following items:
  *     state, esb_stat, f_ctl, seq.ssb_stat
@@ -374,76 +414,59 @@ struct fc_seq {
  *     sequence allocation
  */
 struct fc_exch {
-       struct fc_exch_mgr *em;         /* exchange manager */
-       struct fc_exch_pool *pool;      /* per cpu exches pool */
-       u32             state;          /* internal driver state */
-       u16             xid;            /* our exchange ID */
-       struct list_head        ex_list;        /* free or busy list linkage */
-       spinlock_t      ex_lock;        /* lock covering exchange state */
-       atomic_t        ex_refcnt;      /* reference counter */
-       struct delayed_work timeout_work; /* timer for upper level protocols */
-       struct fc_lport *lp;            /* fc device instance */
-       u16             oxid;           /* originator's exchange ID */
-       u16             rxid;           /* responder's exchange ID */
-       u32             oid;            /* originator's FCID */
-       u32             sid;            /* source FCID */
-       u32             did;            /* destination FCID */
-       u32             esb_stat;       /* exchange status for ESB */
-       u32             r_a_tov;        /* r_a_tov from rport (msec) */
-       u8              seq_id;         /* next sequence ID to use */
-       u32             f_ctl;          /* F_CTL flags for sequences */
-       u8              fh_type;        /* frame type */
-       enum fc_class   class;          /* class of service */
-       struct fc_seq   seq;            /* single sequence */
-       /*
-        * Handler for responses to this current exchange.
-        */
-       void            (*resp)(struct fc_seq *, struct fc_frame *, void *);
-       void            (*destructor)(struct fc_seq *, void *);
-       /*
-        * arg is passed as void pointer to exchange
-        * resp and destructor handlers
-        */
-       void            *arg;
+       struct fc_exch_mgr  *em;
+       struct fc_exch_pool *pool;
+       u32                 state;
+       u16                 xid;
+       struct list_head    ex_list;
+       spinlock_t          ex_lock;
+       atomic_t            ex_refcnt;
+       struct delayed_work timeout_work;
+       struct fc_lport     *lp;
+       u16                 oxid;
+       u16                 rxid;
+       u32                 oid;
+       u32                 sid;
+       u32                 did;
+       u32                 esb_stat;
+       u32                 r_a_tov;
+       u8                  seq_id;
+       u32                 f_ctl;
+       u8                  fh_type;
+       enum fc_class       class;
+       struct fc_seq       seq;
+
+       void                (*resp)(struct fc_seq *, struct fc_frame *, void *);
+       void                *arg;
+
+       void                (*destructor)(struct fc_seq *, void *);
+
 };
 #define        fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
 
-struct libfc_function_template {
 
+struct libfc_function_template {
        /*
         * Interface to send a FC frame
         *
         * STATUS: REQUIRED
         */
-       int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp);
+       int (*frame_send)(struct fc_lport *, struct fc_frame *);
 
        /*
         * Interface to send ELS/CT frames
         *
         * STATUS: OPTIONAL
         */
-       struct fc_seq *(*elsct_send)(struct fc_lport *lport,
-                                    u32 did,
-                                    struct fc_frame *fp,
-                                    unsigned int op,
+       struct fc_seq *(*elsct_send)(struct fc_lport *, u32 did,
+                                    struct fc_frame *, unsigned int op,
                                     void (*resp)(struct fc_seq *,
-                                            struct fc_frame *fp,
-                                            void *arg),
+                                            struct fc_frame *, void *arg),
                                     void *arg, u32 timer_msec);
 
        /*
         * Send the FC frame payload using a new exchange and sequence.
         *
-        * The frame pointer with some of the header's fields must be
-        * filled before calling exch_seq_send(), those fields are,
-        *
-        * - routing control
-        * - FC port did
-        * - FC port sid
-        * - FC header type
-        * - frame control
-        * - parameter or relative offset
-        *
         * The exchange response handler is set in this routine to resp()
         * function pointer. It can be called in two scenarios: if a timeout
         * occurs or if a response frame is received for the exchange. The
@@ -464,14 +487,13 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       struct fc_seq *(*exch_seq_send)(struct fc_lport *lp,
-                                       struct fc_frame *fp,
-                                       void (*resp)(struct fc_seq *sp,
-                                                    struct fc_frame *fp,
-                                                    void *arg),
-                                       void (*destructor)(struct fc_seq *sp,
-                                                          void *arg),
-                                       void *arg, unsigned int timer_msec);
+       struct fc_seq *(*exch_seq_send)(struct fc_lport *, struct fc_frame *,
+                                       void (*resp)(struct fc_seq *,
+                                                    struct fc_frame *,
+                                                    void *),
+                                       void (*destructor)(struct fc_seq *,
+                                                          void *),
+                                       void *, unsigned int timer_msec);
 
        /*
         * Sets up the DDP context for a given exchange id on the given
@@ -479,22 +501,28 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       int (*ddp_setup)(struct fc_lport *lp, u16 xid,
-                        struct scatterlist *sgl, unsigned int sgc);
+       int (*ddp_setup)(struct fc_lport *, u16, struct scatterlist *,
+                        unsigned int);
        /*
         * Completes the DDP transfer and returns the length of data DDPed
         * for the given exchange id.
         *
         * STATUS: OPTIONAL
         */
-       int (*ddp_done)(struct fc_lport *lp, u16 xid);
+       int (*ddp_done)(struct fc_lport *, u16);
+       /*
+        * Allow LLD to fill its own Link Error Status Block
+        *
+        * STATUS: OPTIONAL
+        */
+       void (*get_lesb)(struct fc_lport *, struct fc_els_lesb *lesb);
        /*
         * Send a frame using an existing sequence and exchange.
         *
         * STATUS: OPTIONAL
         */
-       int (*seq_send)(struct fc_lport *lp, struct fc_seq *sp,
-                       struct fc_frame *fp);
+       int (*seq_send)(struct fc_lport *, struct fc_seq *,
+                       struct fc_frame *);
 
        /*
         * Send an ELS response using infomation from a previous
@@ -502,8 +530,8 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       void (*seq_els_rsp_send)(struct fc_seq *sp, enum fc_els_cmd els_cmd,
-                                struct fc_seq_els_data *els_data);
+       void (*seq_els_rsp_send)(struct fc_seq *, enum fc_els_cmd,
+                                struct fc_seq_els_data *);
 
        /*
         * Abort an exchange and sequence. Generally called because of a
@@ -515,7 +543,7 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       int (*seq_exch_abort)(const struct fc_seq *req_sp,
+       int (*seq_exch_abort)(const struct fc_seq *,
                              unsigned int timer_msec);
 
        /*
@@ -524,14 +552,14 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       void (*exch_done)(struct fc_seq *sp);
+       void (*exch_done)(struct fc_seq *);
 
        /*
         * Start a new sequence on the same exchange/sequence tuple.
         *
         * STATUS: OPTIONAL
         */
-       struct fc_seq *(*seq_start_next)(struct fc_seq *sp);
+       struct fc_seq *(*seq_start_next)(struct fc_seq *);
 
        /*
         * Reset an exchange manager, completing all sequences and exchanges.
@@ -540,8 +568,7 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       void (*exch_mgr_reset)(struct fc_lport *,
-                              u32 s_id, u32 d_id);
+       void (*exch_mgr_reset)(struct fc_lport *, u32 s_id, u32 d_id);
 
        /*
         * Flush the rport work queue. Generally used before shutdown.
@@ -555,8 +582,8 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp,
-                          struct fc_frame *fp);
+       void (*lport_recv)(struct fc_lport *, struct fc_seq *,
+                          struct fc_frame *);
 
        /*
         * Reset the local port.
@@ -565,6 +592,26 @@ struct libfc_function_template {
         */
        int (*lport_reset)(struct fc_lport *);
 
+       /*
+        * Set the local port FC_ID.
+        *
+        * This may be provided by the LLD to allow it to be
+        * notified when the local port is assigned a FC-ID.
+        *
+        * The frame, if non-NULL, is the incoming frame with the
+        * FLOGI LS_ACC or FLOGI, and may contain the granted MAC
+        * address for the LLD.  The frame pointer may be NULL if
+        * no MAC is associated with this assignment (LOGO or PLOGI).
+        *
+        * If FC_ID is non-zero, r_a_tov and e_d_tov must be valid.
+        *
+        * Note: this is called with the local port mutex held.
+        *
+        * STATUS: OPTIONAL
+        */
+       void (*lport_set_port_id)(struct fc_lport *, u32 port_id,
+                                 struct fc_frame *);
+
        /*
         * Create a remote port with a given port ID
         *
@@ -622,31 +669,31 @@ struct libfc_function_template {
         *
         * STATUS: OPTIONAL
         */
-       int (*fcp_cmd_send)(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
-                           void (*resp)(struct fc_seq *, struct fc_frame *fp,
-                                        void *arg));
+       int (*fcp_cmd_send)(struct fc_lport *, struct fc_fcp_pkt *,
+                           void (*resp)(struct fc_seq *, struct fc_frame *,
+                                        void *));
 
        /*
         * Cleanup the FCP layer, used durring link down and reset
         *
         * STATUS: OPTIONAL
         */
-       void (*fcp_cleanup)(struct fc_lport *lp);
+       void (*fcp_cleanup)(struct fc_lport *);
 
        /*
         * Abort all I/O on a local port
         *
         * STATUS: OPTIONAL
         */
-       void (*fcp_abort_io)(struct fc_lport *lp);
+       void (*fcp_abort_io)(struct fc_lport *);
 
        /*
         * Receive a request for the discovery layer.
         *
         * STATUS: OPTIONAL
         */
-       void (*disc_recv_req)(struct fc_seq *,
-                             struct fc_frame *, struct fc_lport *);
+       void (*disc_recv_req)(struct fc_seq *, struct fc_frame *,
+                             struct fc_lport *);
 
        /*
         * Start discovery for a local port.
@@ -675,419 +722,340 @@ struct libfc_function_template {
        void (*disc_stop_final) (struct fc_lport *);
 };
 
-/* information used by the discovery layer */
+/**
+ * struct fc_disc - Discovery context
+ * @retry_count:   Number of retries
+ * @pending:       1 if discovery is pending, 0 if not
+ * @requesting:    1 if discovery has been requested, 0 if not
+ * @seq_count:     Number of sequences used for discovery
+ * @buf_len:       Length of the discovery buffer
+ * @disc_id:       Discovery ID
+ * @rports:        List of discovered remote ports
+ * @lport:         The local port that discovery is for
+ * @disc_mutex:    Mutex that protects the discovery context
+ * @partial_buf:   Partial name buffer (if names are returned
+ *                 in multiple frames)
+ * @disc_work:     handle for delayed work context
+ * @disc_callback: Callback routine called when discovery completes
+ */
 struct fc_disc {
-       unsigned char           retry_count;
-       unsigned char           pending;
-       unsigned char           requested;
-       unsigned short          seq_count;
-       unsigned char           buf_len;
-       u16                     disc_id;
+       unsigned char         retry_count;
+       unsigned char         pending;
+       unsigned char         requested;
+       unsigned short        seq_count;
+       unsigned char         buf_len;
+       u16                   disc_id;
+
+       struct list_head      rports;
+       struct fc_lport       *lport;
+       struct mutex          disc_mutex;
+       struct fc_gpn_ft_resp partial_buf;
+       struct delayed_work   disc_work;
 
        void (*disc_callback)(struct fc_lport *,
                              enum fc_disc_event);
-
-       struct list_head         rports;
-       struct fc_lport         *lport;
-       struct mutex            disc_mutex;
-       struct fc_gpn_ft_resp   partial_buf;    /* partial name buffer */
-       struct delayed_work     disc_work;
 };
 
+/**
+ * struct fc_lport - Local port
+ * @host:                  The SCSI host associated with a local port
+ * @ema_list:              Exchange manager anchor list
+ * @dns_rdata:             The directory server remote port
+ * @ptp_rdata:             Point to point remote port
+ * @scsi_priv:             FCP layer internal data
+ * @disc:                  Discovery context
+ * @vports:                Child vports if N_Port
+ * @vport:                 Parent vport if VN_Port
+ * @tt:                    Libfc function template
+ * @link_up:               Link state (1 = link up, 0 = link down)
+ * @qfull:                 Queue state (1 queue is full, 0 queue is not full)
+ * @state:                 Identifies the state
+ * @boot_time:             Timestamp indicating when the local port came online
+ * @host_stats:            SCSI host statistics
+ * @dev_stats:             FCoE device stats (TODO: libfc should not be
+ *                         FCoE aware)
+ * @retry_count:           Number of retries in the current state
+ * @wwpn:                  World Wide Port Name
+ * @wwnn:                  World Wide Node Name
+ * @service_params:        Common service parameters
+ * @e_d_tov:               Error detection timeout value
+ * @r_a_tov:               Resouce allocation timeout value
+ * @rnid_gen:              RNID information
+ * @sg_supp:               Indicates if scatter gather is supported
+ * @seq_offload:           Indicates if sequence offload is supported
+ * @crc_offload:           Indicates if CRC offload is supported
+ * @lro_enabled:           Indicates if large receive offload is supported
+ * @does_npiv:             Supports multiple vports
+ * @npiv_enabled:          Switch/fabric allows NPIV
+ * @mfs:                   The maximum Fibre Channel payload size
+ * @max_retry_count:       The maximum retry attempts
+ * @max_rport_retry_count: The maximum remote port retry attempts
+ * @lro_xid:               The maximum XID for LRO
+ * @lso_max:               The maximum large offload send size
+ * @fcts:                  FC-4 type mask
+ * @lp_mutex:              Mutex to protect the local port
+ * @list:                  Handle for list of local ports
+ * @retry_work:            Handle to local port for delayed retry context
+ */
 struct fc_lport {
-       struct list_head list;
-
        /* Associations */
-       struct Scsi_Host        *host;
-       struct list_head        ema_list;
-       struct fc_rport_priv    *dns_rp;
-       struct fc_rport_priv    *ptp_rp;
-       void                    *scsi_priv;
-       struct fc_disc          disc;
+       struct Scsi_Host               *host;
+       struct list_head               ema_list;
+       struct fc_rport_priv           *dns_rdata;
+       struct fc_rport_priv           *ptp_rdata;
+       void                           *scsi_priv;
+       struct fc_disc                 disc;
+
+       /* Virtual port information */
+       struct list_head               vports;
+       struct fc_vport                *vport;
 
        /* Operational Information */
        struct libfc_function_template tt;
-       u8                      link_up;
-       u8                      qfull;
-       enum fc_lport_state     state;
-       unsigned long           boot_time;
-
-       struct fc_host_statistics host_stats;
-       struct fcoe_dev_stats   *dev_stats;
-
-       u64                     wwpn;
-       u64                     wwnn;
-       u8                      retry_count;
+       u8                             link_up;
+       u8                             qfull;
+       enum fc_lport_state            state;
+       unsigned long                  boot_time;
+       struct fc_host_statistics      host_stats;
+       struct fcoe_dev_stats          *dev_stats;
+       u8                             retry_count;
+
+       /* Fabric information */
+       u64                            wwpn;
+       u64                            wwnn;
+       unsigned int                   service_params;
+       unsigned int                   e_d_tov;
+       unsigned int                   r_a_tov;
+       struct fc_els_rnid_gen         rnid_gen;
 
        /* Capabilities */
-       u32                     sg_supp:1;      /* scatter gather supported */
-       u32                     seq_offload:1;  /* seq offload supported */
-       u32                     crc_offload:1;  /* crc offload supported */
-       u32                     lro_enabled:1;  /* large receive offload */
-       u32                     mfs;            /* max FC payload size */
-       unsigned int            service_params;
-       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 */
-       unsigned int            lso_max;        /* max large send size */
-       struct fc_ns_fts        fcts;           /* FC-4 type masks */
-       struct fc_els_rnid_gen  rnid_gen;       /* RNID information */
-
-       /* Semaphores */
-       struct mutex lp_mutex;
+       u32                            sg_supp:1;
+       u32                            seq_offload:1;
+       u32                            crc_offload:1;
+       u32                            lro_enabled:1;
+       u32                            does_npiv:1;
+       u32                            npiv_enabled:1;
+       u32                            mfs;
+       u8                             max_retry_count;
+       u8                             max_rport_retry_count;
+       u16                            link_speed;
+       u16                            link_supported_speeds;
+       u16                            lro_xid;
+       unsigned int                   lso_max;
+       struct fc_ns_fts               fcts;
 
        /* Miscellaneous */
-       struct delayed_work     retry_work;
-       struct delayed_work     disc_work;
+       struct mutex                   lp_mutex;
+       struct list_head               list;
+       struct delayed_work            retry_work;
 };
 
 /*
  * FC_LPORT HELPER FUNCTIONS
  *****************************/
-static inline int fc_lport_test_ready(struct fc_lport *lp)
+
+/**
+ * fc_lport_test_ready() - Determine if a local port is in the READY state
+ * @lport: The local port to test
+ */
+static inline int fc_lport_test_ready(struct fc_lport *lport)
 {
-       return lp->state == LPORT_ST_READY;
+       return lport->state == LPORT_ST_READY;
 }
 
-static inline void fc_set_wwnn(struct fc_lport *lp, u64 wwnn)
+/**
+ * fc_set_wwnn() - Set the World Wide Node Name of a local port
+ * @lport: The local port whose WWNN is to be set
+ * @wwnn:  The new WWNN
+ */
+static inline void fc_set_wwnn(struct fc_lport *lport, u64 wwnn)
 {
-       lp->wwnn = wwnn;
+       lport->wwnn = wwnn;
 }
 
-static inline void fc_set_wwpn(struct fc_lport *lp, u64 wwnn)
+/**
+ * fc_set_wwpn() - Set the World Wide Port Name of a local port
+ * @lport: The local port whose WWPN is to be set
+ * @wwnn:  The new WWPN
+ */
+static inline void fc_set_wwpn(struct fc_lport *lport, u64 wwnn)
 {
-       lp->wwpn = wwnn;
+       lport->wwpn = wwnn;
 }
 
-static inline void fc_lport_state_enter(struct fc_lport *lp,
+/**
+ * fc_lport_state_enter() - Change a local port's state
+ * @lport: The local port whose state is to change
+ * @state: The new state
+ */
+static inline void fc_lport_state_enter(struct fc_lport *lport,
                                        enum fc_lport_state state)
 {
-       if (state != lp->state)
-               lp->retry_count = 0;
-       lp->state = state;
+       if (state != lport->state)
+               lport->retry_count = 0;
+       lport->state = state;
 }
 
-static inline int fc_lport_init_stats(struct fc_lport *lp)
+/**
+ * fc_lport_init_stats() - Allocate per-CPU statistics for a local port
+ * @lport: The local port whose statistics are to be initialized
+ */
+static inline int fc_lport_init_stats(struct fc_lport *lport)
 {
-       /* allocate per cpu stats block */
-       lp->dev_stats = alloc_percpu(struct fcoe_dev_stats);
-       if (!lp->dev_stats)
+       lport->dev_stats = alloc_percpu(struct fcoe_dev_stats);
+       if (!lport->dev_stats)
                return -ENOMEM;
        return 0;
 }
 
-static inline void fc_lport_free_stats(struct fc_lport *lp)
+/**
+ * fc_lport_free_stats() - Free memory for a local port's statistics
+ * @lport: The local port whose statistics are to be freed
+ */
+static inline void fc_lport_free_stats(struct fc_lport *lport)
 {
-       free_percpu(lp->dev_stats);
+       free_percpu(lport->dev_stats);
 }
 
-static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lp)
+/**
+ * fc_lport_get_stats() - Get a local port's statistics
+ * @lport: The local port whose statistics are to be retreived
+ */
+static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lport)
 {
-       return per_cpu_ptr(lp->dev_stats, smp_processor_id());
+       return per_cpu_ptr(lport->dev_stats, smp_processor_id());
 }
 
-static inline void *lport_priv(const struct fc_lport *lp)
+/**
+ * lport_priv() - Return the private data from a local port
+ * @lport: The local port whose private data is to be retreived
+ */
+static inline void *lport_priv(const struct fc_lport *lport)
 {
-       return (void *)(lp + 1);
+       return (void *)(lport + 1);
 }
 
 /**
- * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
- * @sht: ptr to the scsi host templ
- * @priv_size: size of private data after fc_lport
+ * libfc_host_alloc() - Allocate a Scsi_Host with room for a local port and
+ *                      LLD private data
+ * @sht:       The SCSI host template
+ * @priv_size: Size of private data
  *
- * Returns: ptr to Scsi_Host
+ * Returns: libfc lport
  */
-static inline struct Scsi_Host *
+static inline struct fc_lport *
 libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
 {
-       return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+       struct fc_lport *lport;
+       struct Scsi_Host *shost;
+
+       shost = scsi_host_alloc(sht, sizeof(*lport) + priv_size);
+       if (!shost)
+               return NULL;
+       lport = shost_priv(shost);
+       lport->host = shost;
+       INIT_LIST_HEAD(&lport->ema_list);
+       INIT_LIST_HEAD(&lport->vports);
+       return lport;
 }
 
 /*
- * LOCAL PORT LAYER
+ * FC_FCP HELPER FUNCTIONS
  *****************************/
-int fc_lport_init(struct fc_lport *lp);
-
-/*
- * Destroy the specified local port by finding and freeing all
- * fc_rports associated with it and then by freeing the fc_lport
- * itself.
- */
-int fc_lport_destroy(struct fc_lport *lp);
-
-/*
- * Logout the specified local port from the fabric
- */
-int fc_fabric_logoff(struct fc_lport *lp);
-
-/*
- * Initiate the LP state machine. This handler will use fc_host_attr
- * to store the FLOGI service parameters, so fc_host_attr must be
- * initialized before calling this handler.
- */
-int fc_fabric_login(struct fc_lport *lp);
+static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp)
+{
+       if (fsp && fsp->cmd)
+               return fsp->cmd->sc_data_direction == DMA_FROM_DEVICE;
+       return false;
+}
 
 /*
- * The link is up for the given local port.
- */
+ * LOCAL PORT LAYER
+ *****************************/
+int fc_lport_init(struct fc_lport *);
+int fc_lport_destroy(struct fc_lport *);
+int fc_fabric_logoff(struct fc_lport *);
+int fc_fabric_login(struct fc_lport *);
+void __fc_linkup(struct fc_lport *);
 void fc_linkup(struct fc_lport *);
-
-/*
- * Link is down for the given local port.
- */
+void __fc_linkdown(struct fc_lport *);
 void fc_linkdown(struct fc_lport *);
-
-/*
- * Configure the local port.
- */
+void fc_vport_setlink(struct fc_lport *);
+void fc_vports_linkchange(struct fc_lport *);
 int fc_lport_config(struct fc_lport *);
-
-/*
- * Reset the local port.
- */
 int fc_lport_reset(struct fc_lport *);
-
-/*
- * Set the mfs or reset
- */
-int fc_set_mfs(struct fc_lport *lp, u32 mfs);
-
+int fc_set_mfs(struct fc_lport *, u32 mfs);
+struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
+struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
+int fc_lport_bsg_request(struct fc_bsg_job *);
 
 /*
  * REMOTE PORT LAYER
  *****************************/
-int fc_rport_init(struct fc_lport *lp);
-void fc_rport_terminate_io(struct fc_rport *rp);
+int fc_rport_init(struct fc_lport *);
+void fc_rport_terminate_io(struct fc_rport *);
 
 /*
  * DISCOVERY LAYER
  *****************************/
-int fc_disc_init(struct fc_lport *lp);
-
+int fc_disc_init(struct fc_lport *);
 
 /*
- * SCSI LAYER
+ * FCP LAYER
  *****************************/
-/*
- * Initialize the SCSI block of libfc
- */
 int fc_fcp_init(struct fc_lport *);
-
-/*
- * This section provides an API which allows direct interaction
- * with the SCSI-ml. Each of these functions satisfies a function
- * pointer defined in Scsi_Host and therefore is always called
- * directly from the SCSI-ml.
- */
-int fc_queuecommand(struct scsi_cmnd *sc_cmd,
-                   void (*done)(struct scsi_cmnd *));
-
-/*
- * complete processing of a fcp packet
- *
- * This function may sleep if a fsp timer is pending.
- * The host lock must not be held by caller.
- */
-void fc_fcp_complete(struct fc_fcp_pkt *fsp);
-
-/*
- * Send an ABTS frame to the target device. The sc_cmd argument
- * is a pointer to the SCSI command to be aborted.
- */
-int fc_eh_abort(struct scsi_cmnd *sc_cmd);
-
-/*
- * Reset a LUN by sending send the tm cmd to the target.
- */
-int fc_eh_device_reset(struct scsi_cmnd *sc_cmd);
-
-/*
- * Reset the host adapter.
- */
-int fc_eh_host_reset(struct scsi_cmnd *sc_cmd);
-
-/*
- * Check rport status.
- */
-int fc_slave_alloc(struct scsi_device *sdev);
-
-/*
- * Adjust the queue depth.
- */
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth);
-
-/*
- * Change the tag type.
- */
-int fc_change_queue_type(struct scsi_device *sdev, int tag_type);
-
-/*
- * Free memory pools used by the FCP layer.
- */
 void fc_fcp_destroy(struct fc_lport *);
 
 /*
- * Set up direct-data placement for this I/O request
- */
-void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+ * SCSI INTERACTION LAYER
+ *****************************/
+int fc_queuecommand(struct scsi_cmnd *,
+                   void (*done)(struct scsi_cmnd *));
+int fc_eh_abort(struct scsi_cmnd *);
+int fc_eh_device_reset(struct scsi_cmnd *);
+int fc_eh_host_reset(struct scsi_cmnd *);
+int fc_slave_alloc(struct scsi_device *);
+int fc_change_queue_depth(struct scsi_device *, int qdepth, int reason);
+int fc_change_queue_type(struct scsi_device *, int tag_type);
 
 /*
  * ELS/CT interface
  *****************************/
-/*
- * Initializes ELS/CT interface
- */
-int fc_elsct_init(struct fc_lport *lp);
+int fc_elsct_init(struct fc_lport *);
+struct fc_seq *fc_elsct_send(struct fc_lport *, u32 did,
+                                   struct fc_frame *,
+                                   unsigned int op,
+                                   void (*resp)(struct fc_seq *,
+                                                struct fc_frame *,
+                                                void *arg),
+                                   void *arg, u32 timer_msec);
+void fc_lport_flogi_resp(struct fc_seq *, struct fc_frame *, void *);
+void fc_lport_logo_resp(struct fc_seq *, struct fc_frame *, void *);
 
 
 /*
  * EXCHANGE MANAGER LAYER
  *****************************/
-/*
- * Initializes Exchange Manager related
- * function pointers in struct libfc_function_template.
- */
-int fc_exch_init(struct fc_lport *lp);
-
-/*
- * Adds Exchange Manager (EM) mp to lport.
- *
- * Adds specified mp to lport using struct fc_exch_mgr_anchor,
- * the struct fc_exch_mgr_anchor allows same EM sharing by
- * more than one lport with their specified match function,
- * the match function is used in allocating exchange from
- * added mp.
- */
-struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
-                                          struct fc_exch_mgr *mp,
+int fc_exch_init(struct fc_lport *);
+struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *,
+                                          struct fc_exch_mgr *,
                                           bool (*match)(struct fc_frame *));
-
-/*
- * Deletes Exchange Manager (EM) from lport by removing
- * its anchor ema from lport.
- *
- * If removed anchor ema was the last user of its associated EM
- * then also destroys associated EM.
- */
-void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema);
-
-/*
- * Allocates an Exchange Manager (EM).
- *
- * The EM manages exchanges for their allocation and
- * free, also allows exchange lookup for received
- * frame.
- *
- * The class is used for initializing FC class of
- * allocated exchange from EM.
- *
- * The min_xid and max_xid will limit new
- * exchange ID (XID) within this range for
- * a new exchange.
- * The LLD may choose to have multiple EMs,
- * e.g. one EM instance per CPU receive thread in LLD.
- *
- * Specified match function is used in allocating exchanges
- * from newly allocated EM.
- */
-struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
-                                     enum fc_class class,
-                                     u16 min_xid,
-                                     u16 max_xid,
+void fc_exch_mgr_del(struct fc_exch_mgr_anchor *);
+int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst);
+struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *, enum fc_class class,
+                                     u16 min_xid, u16 max_xid,
                                      bool (*match)(struct fc_frame *));
-
-/*
- * Free all exchange managers of a lport.
- */
-void fc_exch_mgr_free(struct fc_lport *lport);
-
-/*
- * Receive a frame on specified local port and exchange manager.
- */
-void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp);
-
-/*
- * This function is for exch_seq_send function pointer in
- * struct libfc_function_template, see comment block on
- * exch_seq_send for description of this function.
- */
-struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
-                               struct fc_frame *fp,
-                               void (*resp)(struct fc_seq *sp,
-                                            struct fc_frame *fp,
-                                            void *arg),
-                               void (*destructor)(struct fc_seq *sp,
-                                                  void *arg),
-                               void *arg, u32 timer_msec);
-
-/*
- * send a frame using existing sequence and exchange.
- */
-int fc_seq_send(struct fc_lport *lp, struct fc_seq *sp, struct fc_frame *fp);
-
-/*
- * Send ELS response using mainly infomation
- * in exchange and sequence in EM layer.
- */
-void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
-                        struct fc_seq_els_data *els_data);
-
-/*
- * This function is for seq_exch_abort function pointer in
- * struct libfc_function_template, see comment block on
- * seq_exch_abort for description of this function.
- */
-int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec);
-
-/*
- * Indicate that an exchange/sequence tuple is complete and the memory
- * allocated for the related objects may be freed.
- */
-void fc_exch_done(struct fc_seq *sp);
-
-/*
- * Allocate a new exchange and sequence pair.
- */
-struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp);
-/*
- * Start a new sequence on the same exchange as the supplied sequence.
- */
-struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
-
-
-/*
- * Reset all EMs of a lport, releasing its all sequences and
- * exchanges. If sid is non-zero, then reset only exchanges
- * we sourced from that FID. If did is non-zero, reset only
- * exchanges destined to that FID.
- */
+void fc_exch_mgr_free(struct fc_lport *);
+void fc_exch_recv(struct fc_lport *, struct fc_frame *);
 void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id);
 
 /*
  * Functions for fc_functions_template
  */
-void fc_get_host_speed(struct Scsi_Host *shost);
-void fc_get_host_port_type(struct Scsi_Host *shost);
-void fc_get_host_port_state(struct Scsi_Host *shost);
-void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout);
+void fc_get_host_speed(struct Scsi_Host *);
+void fc_get_host_port_type(struct Scsi_Host *);
+void fc_get_host_port_state(struct Scsi_Host *);
+void fc_set_rport_loss_tmo(struct fc_rport *, u32 timeout);
 struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *);
 
-/*
- * module setup functions.
- */
-int fc_setup_exch_mgr(void);
-void fc_destroy_exch_mgr(void);
-int fc_setup_rport(void);
-void fc_destroy_rport(void);
-
-/*
- * Internal libfc functions.
- */
-const char *fc_els_resp_type(struct fc_frame *);
-
 #endif /* _LIBFC_H_ */
index b241060..c603f4a 100644 (file)
@@ -53,33 +53,36 @@ enum fip_state {
 };
 
 /**
- * struct fcoe_ctlr - FCoE Controller and FIP state.
- * @state:     internal FIP state for network link and FIP or non-FIP mode.
- * @lp:                &fc_lport: libfc local port.
- * @sel_fcf:   currently selected FCF, or NULL.
- * @fcfs:      list of discovered FCFs.
- * @fcf_count: number of discovered FCF entries.
- * @sol_time:  time when a multicast solicitation was last sent.
- * @sel_time:  time after which to select an FCF.
- * @port_ka_time: time of next port keep-alive.
- * @ctlr_ka_time: time of next controller keep-alive.
- * @timer:     timer struct used for all delayed events.
- * @link_work: &work_struct for doing FCF selection.
- * @recv_work: &work_struct for receiving FIP frames.
+ * struct fcoe_ctlr - FCoE Controller and FIP state
+ * @state:        internal FIP state for network link and FIP or non-FIP mode.
+ * @mode:         LLD-selected mode.
+ * @lp:                   &fc_lport: libfc local port.
+ * @sel_fcf:      currently selected FCF, or NULL.
+ * @fcfs:         list of discovered FCFs.
+ * @fcf_count:    number of discovered FCF entries.
+ * @sol_time:     time when a multicast solicitation was last sent.
+ * @sel_time:     time after which to select an FCF.
+ * @port_ka_time:  time of next port keep-alive.
+ * @ctlr_ka_time:  time of next controller keep-alive.
+ * @timer:        timer struct used for all delayed events.
+ * @link_work:    &work_struct for doing FCF selection.
+ * @recv_work:    &work_struct for receiving FIP frames.
  * @fip_recv_list: list of received FIP frames.
- * @user_mfs:  configured maximum FC frame size, including FC header.
- * @flogi_oxid: exchange ID of most recent fabric login.
- * @flogi_count: number of FLOGI attempts in AUTO mode.
- * @link:      current link status for libfc.
- * @last_link: last link state reported to libfc.
- * @map_dest:  use the FC_MAP mode for destination MAC addresses.
- * @spma:      supports SPMA server-provided MACs mode
- * @dest_addr: MAC address of the selected FC forwarder.
- * @ctl_src_addr: the native MAC address of our local port.
- * @data_src_addr: the assigned MAC address for the local port after FLOGI.
- * @send:      LLD-supplied function to handle sending of FIP Ethernet frames.
- * @update_mac: LLD-supplied function to handle changes to MAC addresses.
- * @lock:      lock protecting this structure.
+ * @user_mfs:     configured maximum FC frame size, including FC header.
+ * @flogi_oxid:    exchange ID of most recent fabric login.
+ * @flogi_count:   number of FLOGI attempts in AUTO mode.
+ * @link:         current link status for libfc.
+ * @last_link:    last link state reported to libfc.
+ * @map_dest:     use the FC_MAP mode for destination MAC addresses.
+ * @spma:         supports SPMA server-provided MACs mode
+ * @send_ctlr_ka:  need to send controller keep alive
+ * @send_port_ka:  need to send port keep alives
+ * @dest_addr:    MAC address of the selected FC forwarder.
+ * @ctl_src_addr:  the native MAC address of our local port.
+ * @send:         LLD-supplied function to handle sending FIP Ethernet frames
+ * @update_mac:    LLD-supplied function to handle changes to MAC addresses.
+ * @get_src_addr:  LLD-supplied function to supply a source MAC address.
+ * @lock:         lock protecting this structure.
  *
  * This structure is used by all FCoE drivers.  It contains information
  * needed by all FCoE low-level drivers (LLDs) as well as internal state
@@ -87,6 +90,7 @@ enum fip_state {
  */
 struct fcoe_ctlr {
        enum fip_state state;
+       enum fip_state mode;
        struct fc_lport *lp;
        struct fcoe_fcf *sel_fcf;
        struct list_head fcfs;
@@ -104,29 +108,32 @@ struct fcoe_ctlr {
        u8 flogi_count;
        u8 link;
        u8 last_link;
+       u8 reset_req;
        u8 map_dest;
        u8 spma;
+       u8 send_ctlr_ka;
+       u8 send_port_ka;
        u8 dest_addr[ETH_ALEN];
        u8 ctl_src_addr[ETH_ALEN];
-       u8 data_src_addr[ETH_ALEN];
 
        void (*send)(struct fcoe_ctlr *, struct sk_buff *);
-       void (*update_mac)(struct fcoe_ctlr *, u8 *old, u8 *new);
+       void (*update_mac)(struct fc_lport *, u8 *addr);
+       u8 * (*get_src_addr)(struct fc_lport *);
        spinlock_t lock;
 };
 
-/*
- * struct fcoe_fcf - Fibre-Channel Forwarder.
- * @list:      list linkage.
- * @time:      system time (jiffies) when an advertisement was last received.
- * @switch_name: WWN of switch from advertisement.
- * @fabric_name: WWN of fabric from advertisement.
- * @fc_map:    FC_MAP value from advertisement.
- * @fcf_mac:   Ethernet address of the FCF.
- * @vfid:      virtual fabric ID.
- * @pri:       seletion priority, smaller values are better.
- * @flags:     flags received from advertisement.
- * @fka_period:        keep-alive period, in jiffies.
+/**
+ * struct fcoe_fcf - Fibre-Channel Forwarder
+ * @list:       list linkage
+ * @time:       system time (jiffies) when an advertisement was last received
+ * @switch_name: WWN of switch from advertisement
+ * @fabric_name: WWN of fabric from advertisement
+ * @fc_map:     FC_MAP value from advertisement
+ * @fcf_mac:    Ethernet address of the FCF
+ * @vfid:       virtual fabric ID
+ * @pri:        selection priority, smaller values are better
+ * @flags:      flags received from advertisement
+ * @fka_period:         keep-alive period, in jiffies
  *
  * A Fibre-Channel Forwarder (FCF) is the entity on the Ethernet that
  * passes FCoE frames on to an FC fabric.  This structure represents
@@ -148,6 +155,7 @@ struct fcoe_fcf {
        u8 pri;
        u16 flags;
        u32 fka_period;
+       u8 fd_flags:1;
 };
 
 /* FIP API functions */
@@ -155,9 +163,10 @@ void fcoe_ctlr_init(struct fcoe_ctlr *);
 void fcoe_ctlr_destroy(struct fcoe_ctlr *);
 void fcoe_ctlr_link_up(struct fcoe_ctlr *);
 int fcoe_ctlr_link_down(struct fcoe_ctlr *);
-int fcoe_ctlr_els_send(struct fcoe_ctlr *, struct sk_buff *);
+int fcoe_ctlr_els_send(struct fcoe_ctlr *, struct fc_lport *, struct sk_buff *);
 void fcoe_ctlr_recv(struct fcoe_ctlr *, struct sk_buff *);
-int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_frame *fp, u8 *sa);
+int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *,
+                        struct fc_frame *);
 
 /* libfcoe funcs */
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
index a72edd4..7394e3b 100644 (file)
@@ -267,6 +267,7 @@ struct iscsi_session {
        /* configuration */
        int                     abort_timeout;
        int                     lu_reset_timeout;
+       int                     tgt_reset_timeout;
        int                     initial_r2t_en;
        unsigned                max_r2t;
        int                     imm_data_en;
@@ -333,7 +334,8 @@ struct iscsi_host {
 /*
  * scsi host template
  */
-extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth);
+extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth,
+                                   int reason);
 extern int iscsi_eh_abort(struct scsi_cmnd *sc);
 extern int iscsi_eh_target_reset(struct scsi_cmnd *sc);
 extern int iscsi_eh_device_reset(struct scsi_cmnd *sc);
index e78d3b6..9eaa3f0 100644 (file)
@@ -634,7 +634,8 @@ extern int sas_target_alloc(struct scsi_target *);
 extern int sas_slave_alloc(struct scsi_device *);
 extern int sas_slave_configure(struct scsi_device *);
 extern void sas_slave_destroy(struct scsi_device *);
-extern int sas_change_queue_depth(struct scsi_device *, int new_depth);
+extern int sas_change_queue_depth(struct scsi_device *, int new_depth,
+                                 int reason);
 extern int sas_change_queue_type(struct scsi_device *, int qt);
 extern int sas_bios_param(struct scsi_device *,
                          struct block_device *,
index 02bd9f7..39d6d10 100644 (file)
@@ -48,7 +48,6 @@ enum osd_std_version {
  */
 struct osd_dev {
        struct scsi_device *scsi_device;
-       struct file *file;
        unsigned def_timeout;
 
 #ifdef OSD_VER1_SUPPORT
@@ -56,10 +55,24 @@ struct osd_dev {
 #endif
 };
 
-/* Retrieve/return osd_dev(s) for use by Kernel clients */
-struct osd_dev *osduld_path_lookup(const char *dev_name); /*Use IS_ERR/ERR_PTR*/
+/* Unique Identification of an OSD device */
+struct osd_dev_info {
+       unsigned systemid_len;
+       u8 systemid[OSD_SYSTEMID_LEN];
+       unsigned osdname_len;
+       u8 *osdname;
+};
+
+/* Retrieve/return osd_dev(s) for use by Kernel clients
+ * Use IS_ERR/ERR_PTR on returned "osd_dev *".
+ */
+struct osd_dev *osduld_path_lookup(const char *dev_name);
+struct osd_dev *osduld_info_lookup(const struct osd_dev_info *odi);
 void osduld_put_device(struct osd_dev *od);
 
+const struct osd_dev_info *osduld_device_info(struct osd_dev *od);
+bool osduld_device_same(struct osd_dev *od, const struct osd_dev_info *odi);
+
 /* Add/remove test ioctls from external modules */
 typedef int (do_test_fn)(struct osd_dev *od, unsigned cmd, unsigned long arg);
 int osduld_register_test(unsigned ioctl, do_test_fn *do_test);
@@ -69,8 +82,24 @@ void osduld_unregister_test(unsigned ioctl);
 void osd_dev_init(struct osd_dev *od, struct scsi_device *scsi_device);
 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 */
+/**
+ * osd_auto_detect_ver - Detect the OSD version, return Unique Identification
+ *
+ * @od:     OSD target lun handle
+ * @caps:   Capabilities authorizing OSD root read attributes access
+ * @odi:    Retrieved information uniquely identifying the osd target lun
+ *          Note: odi->osdname must be kfreed by caller.
+ *
+ * Auto detects the OSD version of the OSD target and sets the @od
+ * accordingly. Meanwhile also returns the "system id" and "osd name" root
+ * attributes which uniquely identify the OSD target. This member is usually
+ * called by the ULD. ULD users should call osduld_device_info().
+ * This rutine allocates osd requests and memory at GFP_KERNEL level and might
+ * sleep.
+ */
+int osd_auto_detect_ver(struct osd_dev *od,
+       void *caps, struct osd_dev_info *odi);
+
 static inline struct request_queue *osd_request_queue(struct osd_dev *od)
 {
        return od->scsi_device->request_queue;
@@ -84,6 +113,15 @@ static inline void osd_dev_set_ver(struct osd_dev *od, enum osd_std_version v)
 #endif
 }
 
+static inline bool osd_dev_is_ver1(struct osd_dev *od)
+{
+#ifdef OSD_VER1_SUPPORT
+       return od->version == OSD_VER1;
+#else
+       return false;
+#endif
+}
+
 struct osd_request;
 typedef void (osd_req_done_fn)(struct osd_request *or, void *private);
 
@@ -120,14 +158,9 @@ struct osd_request {
        int async_error;
 };
 
-/* OSD Version control */
 static inline bool osd_req_is_ver1(struct osd_request *or)
 {
-#ifdef OSD_VER1_SUPPORT
-       return or->osd_dev->version == OSD_VER1;
-#else
-       return false;
-#endif
+       return osd_dev_is_ver1(or->osd_dev);
 }
 
 /*
@@ -234,7 +267,7 @@ int osd_execute_request_async(struct osd_request *or,
  * @bad_attr_list - List of failing attributes (optional)
  * @max_attr      - Size of @bad_attr_list.
  *
- * After execution, sense + return code can be analyzed using this function. The
+ * After execution, osd_request results are analyzed using this function. The
  * return code is the final disposition on the error. So it is possible that a
  * CHECK_CONDITION was returned from target but this will return NO_ERROR, for
  * example on recovered errors. All parameters are optional if caller does
@@ -243,7 +276,31 @@ int osd_execute_request_async(struct osd_request *or,
  * of the SCSI_OSD_DPRINT_SENSE Kconfig value. Set @silent if you know the
  * command would routinely fail, to not spam the dmsg file.
  */
+
+/**
+ * osd_err_priority - osd categorized return codes in ascending severity.
+ *
+ * The categories are borrowed from the pnfs_osd_errno enum.
+ * See comments for translated Linux codes returned by osd_req_decode_sense.
+ */
+enum osd_err_priority {
+       OSD_ERR_PRI_NO_ERROR    = 0,
+       /* Recoverable, caller should clear_highpage() all pages */
+       OSD_ERR_PRI_CLEAR_PAGES = 1, /* -EFAULT */
+       OSD_ERR_PRI_RESOURCE    = 2, /* -ENOMEM */
+       OSD_ERR_PRI_BAD_CRED    = 3, /* -EINVAL */
+       OSD_ERR_PRI_NO_ACCESS   = 4, /* -EACCES */
+       OSD_ERR_PRI_UNREACHABLE = 5, /* any other */
+       OSD_ERR_PRI_NOT_FOUND   = 6, /* -ENOENT */
+       OSD_ERR_PRI_NO_SPACE    = 7, /* -ENOSPC */
+       OSD_ERR_PRI_EIO         = 8, /* -EIO    */
+};
+
 struct osd_sense_info {
+       u64 out_resid;          /* Zero on success otherwise out residual */
+       u64 in_resid;           /* Zero on success otherwise in residual */
+       enum osd_err_priority osd_err_pri;
+
        int key;                /* one of enum scsi_sense_keys */
        int additional_code ;   /* enum osd_additional_sense_codes */
        union { /* Sense specific information */
index 2cc8e8b..6856612 100644 (file)
@@ -17,6 +17,7 @@
 #define __OSD_PROTOCOL_H__
 
 #include <linux/types.h>
+#include <linux/kernel.h>
 #include <asm/unaligned.h>
 #include <scsi/scsi.h>
 
index ff9b33c..91db543 100644 (file)
@@ -255,6 +255,9 @@ enum osdv2_cdb_field_offset {
        OSD_CFO_STARTING_BYTE   = OSD_CDB_OFFSET(v2.start_address),
        OSD_CFO_PARTITION_ID    = OSD_CDB_OFFSET(partition),
        OSD_CFO_OBJECT_ID       = OSD_CDB_OFFSET(object),
+       OSD_CFO_PERMISSIONS     = sizeof(struct osd_cdb_head) +
+                                       offsetof(struct osd_capability_head,
+                                                permissions_bit_mask),
 };
 
 #endif /* ndef __OSD_SENSE_H__ */
index 34c46ab..8b4deca 100644 (file)
@@ -94,6 +94,7 @@ struct scsi_cmnd;
 #define WRITE_LONG            0x3f
 #define CHANGE_DEFINITION     0x40
 #define WRITE_SAME            0x41
+#define UNMAP                0x42
 #define READ_TOC              0x43
 #define LOG_SELECT            0x4c
 #define LOG_SENSE             0x4d
@@ -122,9 +123,11 @@ struct scsi_cmnd;
 #define READ_16               0x88
 #define WRITE_16              0x8a
 #define VERIFY_16            0x8f
+#define WRITE_SAME_16        0x93
 #define SERVICE_ACTION_IN     0x9e
 /* values for service action in */
 #define        SAI_READ_CAPACITY_16  0x10
+#define SAI_GET_LBA_STATUS    0x12
 /* values for maintenance in */
 #define MI_REPORT_TARGET_PGS  0x0a
 /* values for maintenance out */
@@ -132,6 +135,7 @@ struct scsi_cmnd;
 /* values for variable length command */
 #define READ_32                      0x09
 #define WRITE_32             0x0b
+#define WRITE_SAME_32        0x0d
 
 /* Values for T10/04-262r7 */
 #define        ATA_16                0x85      /* 16-byte pass-thru */
index f097ae3..7c44499 100644 (file)
@@ -81,11 +81,14 @@ struct scsi_device {
        struct list_head starved_entry;
        struct scsi_cmnd *current_cmnd; /* currently active command */
        unsigned short queue_depth;     /* How deep of a queue we want */
+       unsigned short max_queue_depth; /* max queue depth */
        unsigned short last_queue_full_depth; /* These two are used by */
        unsigned short last_queue_full_count; /* scsi_track_queue_full() */
-       unsigned long last_queue_full_time;/* don't let QUEUE_FULLs on the same
-                                          jiffie count on our counter, they
-                                          could all be from the same event. */
+       unsigned long last_queue_full_time;     /* last queue full time */
+       unsigned long queue_ramp_up_period;     /* ramp up period in jiffies */
+#define SCSI_DEFAULT_RAMP_UP_PERIOD    (120 * HZ)
+
+       unsigned long last_queue_ramp_up;       /* last queue ramp up time */
 
        unsigned int id, lun, channel;
 
@@ -175,6 +178,7 @@ struct scsi_dh_devlist {
        char *model;
 };
 
+typedef void (*activate_complete)(void *, int);
 struct scsi_device_handler {
        /* Used by the infrastructure */
        struct list_head list; /* list of scsi_device_handlers */
@@ -186,7 +190,7 @@ struct scsi_device_handler {
        int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
        int (*attach)(struct scsi_device *);
        void (*detach)(struct scsi_device *);
-       int (*activate)(struct scsi_device *);
+       int (*activate)(struct scsi_device *, activate_complete, void *);
        int (*prep_fn)(struct scsi_device *, struct request *);
        int (*set_params)(struct scsi_device *, const char *);
 };
index ff24074..e3f2db2 100644 (file)
@@ -56,14 +56,16 @@ enum {
        SCSI_DH_DRIVER_MAX,
 };
 #if defined(CONFIG_SCSI_DH) || defined(CONFIG_SCSI_DH_MODULE)
-extern int scsi_dh_activate(struct request_queue *);
+extern int scsi_dh_activate(struct request_queue *, activate_complete, void *);
 extern int scsi_dh_handler_exist(const char *);
 extern int scsi_dh_attach(struct request_queue *, const char *);
 extern void scsi_dh_detach(struct request_queue *);
 extern int scsi_dh_set_params(struct request_queue *, const char *);
 #else
-static inline int scsi_dh_activate(struct request_queue *req)
+static inline int scsi_dh_activate(struct request_queue *req,
+                                       activate_complete fn, void *data)
 {
+       fn(data, 0);
        return 0;
 }
 static inline int scsi_dh_handler_exist(const char *name)
index 47941fc..c50a97f 100644 (file)
@@ -43,6 +43,12 @@ struct blk_queue_tags;
 #define DISABLE_CLUSTERING 0
 #define ENABLE_CLUSTERING 1
 
+enum {
+       SCSI_QDEPTH_DEFAULT,    /* default requested change, e.g. from sysfs */
+       SCSI_QDEPTH_QFULL,      /* scsi-ml requested due to queue full */
+       SCSI_QDEPTH_RAMP_UP,    /* scsi-ml requested due to threshhold event */
+};
+
 struct scsi_host_template {
        struct module *module;
        const char *name;
@@ -294,7 +300,7 @@ struct scsi_host_template {
         *
         * Status: OPTIONAL
         */
-       int (* change_queue_depth)(struct scsi_device *, int);
+       int (* change_queue_depth)(struct scsi_device *, int, int);
 
        /*
         * Fill in this function to allow the changing of tag types
@@ -676,6 +682,12 @@ struct Scsi_Host {
         */
        void *shost_data;
 
+       /*
+        * Points to the physical bus device we'd use to do DMA
+        * Needed just in case we have virtual hosts.
+        */
+       struct device *dma_dev;
+
        /*
         * We should ensure that this is aligned, both for better performance
         * and also because some compilers (m68k) don't automatically force
@@ -720,7 +732,9 @@ extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *);
 extern void scsi_flush_work(struct Scsi_Host *);
 
 extern struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *, int);
-extern int __must_check scsi_add_host(struct Scsi_Host *, struct device *);
+extern int __must_check scsi_add_host_with_dma(struct Scsi_Host *,
+                                              struct device *,
+                                              struct device *);
 extern void scsi_scan_host(struct Scsi_Host *);
 extern void scsi_rescan_device(struct device *);
 extern void scsi_remove_host(struct Scsi_Host *);
@@ -731,6 +745,12 @@ extern const char *scsi_host_state_name(enum scsi_host_state);
 
 extern u64 scsi_calculate_bounce_limit(struct Scsi_Host *);
 
+static inline int __must_check scsi_add_host(struct Scsi_Host *host,
+                                            struct device *dev)
+{
+       return scsi_add_host_with_dma(host, dev, dev);
+}
+
 static inline struct device *scsi_get_device(struct Scsi_Host *shost)
 {
         return shost->shost_gendev.parent;
index fc50bd6..8e86a94 100644 (file)
@@ -807,5 +807,6 @@ void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
 struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel,
                struct fc_vport_identifiers *);
 int fc_vport_terminate(struct fc_vport *vport);
+void fc_block_scsi_eh(struct scsi_cmnd *cmnd);
 
 #endif /* SCSI_TRANSPORT_FC_H */
index 3dae3f7..4940045 100644 (file)
@@ -593,7 +593,7 @@ enum {
 
 struct ac97_quirk {
        unsigned short subvendor; /* PCI subsystem vendor id */
-       unsigned short subdevice; /* PCI sybsystem device id */
+       unsigned short subdevice; /* PCI subsystem device id */
        unsigned short mask;    /* device id bit mask, 0 = accept all */
        unsigned int codec_id;  /* codec id (if any), 0 = accept all */
        const char *name;       /* name shown as info */
index 9c661f2..eee19f6 100644 (file)
@@ -36,7 +36,7 @@ struct wm8993_platform_data {
        unsigned int micbias1_lvl:1;
        unsigned int micbias2_lvl:1;
 
-       /* Jack detect threashold levels, see datasheet for values */
+       /* Jack detect threshold levels, see datasheet for values */
        unsigned int jd_scthr:2;
        unsigned int jd_thr:2;
 };
index 318f765..d0b6cd3 100644 (file)
@@ -38,7 +38,7 @@ TRACE_EVENT(ext4_free_inode,
                __entry->blocks = inode->i_blocks;
        ),
 
-       TP_printk("dev %s ino %lu mode %d uid %u gid %u blocks %llu",
+       TP_printk("dev %s ino %lu mode 0%o uid %u gid %u blocks %llu",
                  jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
                  __entry->mode, __entry->uid, __entry->gid,
                  (unsigned long long) __entry->blocks)
@@ -61,7 +61,7 @@ TRACE_EVENT(ext4_request_inode,
                __entry->mode   = mode;
        ),
 
-       TP_printk("dev %s dir %lu mode %d",
+       TP_printk("dev %s dir %lu mode 0%o",
                  jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->dir,
                  __entry->mode)
 );
@@ -85,7 +85,7 @@ TRACE_EVENT(ext4_allocate_inode,
                __entry->mode   = mode;
        ),
 
-       TP_printk("dev %s ino %lu dir %lu mode %d",
+       TP_printk("dev %s ino %lu dir %lu mode 0%o",
                  jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
                  (unsigned long) __entry->dir, __entry->mode)
 );
@@ -305,7 +305,6 @@ TRACE_EVENT(ext4_da_writepages_result,
                __field(        int,    ret                     )
                __field(        int,    pages_written           )
                __field(        long,   pages_skipped           )
-               __field(        char,   encountered_congestion  )
                __field(        char,   more_io                 )       
                __field(        char,   no_nrwrite_index_update )
                __field(       pgoff_t, writeback_index         )
@@ -317,17 +316,16 @@ TRACE_EVENT(ext4_da_writepages_result,
                __entry->ret            = ret;
                __entry->pages_written  = pages_written;
                __entry->pages_skipped  = wbc->pages_skipped;
-               __entry->encountered_congestion = wbc->encountered_congestion;
                __entry->more_io        = wbc->more_io;
                __entry->no_nrwrite_index_update = wbc->no_nrwrite_index_update;
                __entry->writeback_index = inode->i_mapping->writeback_index;
        ),
 
-       TP_printk("dev %s ino %lu ret %d pages_written %d pages_skipped %ld congestion %d more_io %d no_nrwrite_index_update %d writeback_index %lu",
+       TP_printk("dev %s ino %lu ret %d pages_written %d pages_skipped %ld more_io %d no_nrwrite_index_update %d writeback_index %lu",
                  jbd2_dev_to_name(__entry->dev),
                  (unsigned long) __entry->ino, __entry->ret,
                  __entry->pages_written, __entry->pages_skipped,
-                 __entry->encountered_congestion, __entry->more_io,
+                 __entry->more_io,
                  __entry->no_nrwrite_index_update,
                  (unsigned long) __entry->writeback_index)
 );
@@ -591,30 +589,32 @@ TRACE_EVENT(ext4_allocate_blocks,
 
 TRACE_EVENT(ext4_free_blocks,
        TP_PROTO(struct inode *inode, __u64 block, unsigned long count,
-                       int metadata),
+                int flags),
 
-       TP_ARGS(inode, block, count, metadata),
+       TP_ARGS(inode, block, count, flags),
 
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
                __field(        ino_t,  ino                     )
+               __field(      umode_t, mode                     )
                __field(        __u64,  block                   )
                __field(        unsigned long,  count           )
-               __field(        int,    metadata                )
-
+               __field(         int,   flags                   )
        ),
 
        TP_fast_assign(
                __entry->dev            = inode->i_sb->s_dev;
                __entry->ino            = inode->i_ino;
+               __entry->mode           = inode->i_mode;
                __entry->block          = block;
                __entry->count          = count;
-               __entry->metadata       = metadata;
+               __entry->flags          = flags;
        ),
 
-       TP_printk("dev %s ino %lu block %llu count %lu metadata %d",
+       TP_printk("dev %s ino %lu mode 0%o block %llu count %lu flags %d",
                  jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
-                 __entry->block, __entry->count, __entry->metadata)
+                 __entry->mode, __entry->block, __entry->count,
+                 __entry->flags)
 );
 
 TRACE_EVENT(ext4_sync_file,
@@ -848,6 +848,32 @@ TRACE_EVENT(ext4_mballoc_free,
                  __entry->result_len, __entry->result_logical)
 );
 
+TRACE_EVENT(ext4_forget,
+       TP_PROTO(struct inode *inode, int is_metadata, __u64 block),
+
+       TP_ARGS(inode, is_metadata, block),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev                     )
+               __field(        ino_t,  ino                     )
+               __field(        umode_t, mode                   )
+               __field(        int,    is_metadata             )
+               __field(        __u64,  block                   )
+       ),
+
+       TP_fast_assign(
+               __entry->dev    = inode->i_sb->s_dev;
+               __entry->ino    = inode->i_ino;
+               __entry->mode   = inode->i_mode;
+               __entry->is_metadata = is_metadata;
+               __entry->block  = block;
+       ),
+
+       TP_printk("dev %s ino %lu mode 0%o is_metadata %d block %llu",
+                 jbd2_dev_to_name(__entry->dev), (unsigned long) __entry->ino,
+                 __entry->mode, __entry->is_metadata, __entry->block)
+);
+
 #endif /* _TRACE_EXT4_H */
 
 /* This part must be outside protection */
index 55f2a7c..f6aa569 100644 (file)
@@ -351,7 +351,7 @@ typedef enum {
 struct dbe_timing_info
 {
   dbe_timing_t type;
-  int flags;                           
+  int flags;
   short width;             /* Monitor resolution               */
   short height;
   int fields_sec;          /* fields/sec  (Hz -3 dec. places */
@@ -389,11 +389,11 @@ struct dbe_timing_info dbeVTimings[] = {
   {
     DBE_VT_640_480_60,
     /* flags,  width,                  height,         fields_sec,             cfreq */
-    0,         640,                    480,            59940,                  25175,
+    0,         640,                    480,            59940,                  25175,
     /* htotal, hblank_start,   hblank_end,     hsync_start,    hsync_end */
-    800,       640,                800,                656,            752,
+    800,       640,                    800,            656,            752,
     /* vtotal, vblank_start,   vblank_end,     vsync_start,    vsync_end */
-    525,       480,                525,                490,                492,
+    525,       480,                    525,            490,                492,
     /* pll_m,  pll_n,                  pll_p */
     15,            2,                          3
   },
@@ -650,7 +650,7 @@ struct dbe_timing_info dbeVTimings[] = {
     /* pll_m,  pll_n,          pll_p */
     6,      1,              0
   },
-       
+
   {
     DBE_VT_1920_1200_60,
     /* flags,  width,          height,         fields_sec,     cfreq */
@@ -676,7 +676,6 @@ struct dbe_timing_info dbeVTimings[] = {
   }
 };
 
-#define DBE_VT_SIZE  (sizeof(dbeVTimings)/sizeof(dbeVTimings[0]))
 #endif // INCLUDE_TIMING_TABLE_DATA
 
 #endif // ! __SGIVWFB_H__
index 2ceab7f..085bd58 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -412,7 +412,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                       struct msqid_ds __user *buf, int version)
 {
        struct kern_ipc_perm *ipcp;
-       struct msqid64_ds msqid64;
+       struct msqid64_ds uninitialized_var(msqid64);
        struct msg_queue *msq;
        int err;
 
index 22b0a6e..e49ea1c 100644 (file)
@@ -220,7 +220,7 @@ void note_interrupt(unsigned int irq, struct irq_desc *desc,
                /*
                 * If we are seeing only the odd spurious IRQ caused by
                 * bus asynchronicity then don't eventually trigger an error,
-                * otherwise the couter becomes a doomsday timer for otherwise
+                * otherwise the counter becomes a doomsday timer for otherwise
                 * working systems
                 */
                if (time_after(jiffies, desc->last_unhandled + HZ/10))
index 6b7ddba..40a996e 100644 (file)
@@ -476,7 +476,7 @@ static void perf_event_remove_from_context(struct perf_event *event)
        if (!task) {
                /*
                 * Per cpu events are removed via an smp call and
-                * the removal is always sucessful.
+                * the removal is always successful.
                 */
                smp_call_function_single(event->cpu,
                                         __perf_event_remove_from_context,
@@ -845,7 +845,7 @@ perf_install_in_context(struct perf_event_context *ctx,
        if (!task) {
                /*
                 * Per cpu events are installed via an smp call and
-                * the install is always sucessful.
+                * the install is always successful.
                 */
                smp_call_function_single(cpu, __perf_install_in_context,
                                         event, 1);
index d422c7b..e85c234 100644 (file)
@@ -677,7 +677,7 @@ sysfs_show_current_clocksources(struct sys_device *dev,
  * @count:     length of buffer
  *
  * Takes input from sysfs interface for manually overriding the default
- * clocksource selction.
+ * clocksource selection.
  */
 static ssize_t sysfs_override_clocksource(struct sys_device *dev,
                                          struct sysdev_attribute *attr,
index 67e526b..dee4865 100644 (file)
@@ -68,6 +68,116 @@ struct workqueue_struct {
 #endif
 };
 
+#ifdef CONFIG_DEBUG_OBJECTS_WORK
+
+static struct debug_obj_descr work_debug_descr;
+
+/*
+ * fixup_init is called when:
+ * - an active object is initialized
+ */
+static int work_fixup_init(void *addr, enum debug_obj_state state)
+{
+       struct work_struct *work = addr;
+
+       switch (state) {
+       case ODEBUG_STATE_ACTIVE:
+               cancel_work_sync(work);
+               debug_object_init(work, &work_debug_descr);
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+/*
+ * fixup_activate is called when:
+ * - an active object is activated
+ * - an unknown object is activated (might be a statically initialized object)
+ */
+static int work_fixup_activate(void *addr, enum debug_obj_state state)
+{
+       struct work_struct *work = addr;
+
+       switch (state) {
+
+       case ODEBUG_STATE_NOTAVAILABLE:
+               /*
+                * This is not really a fixup. The work struct was
+                * statically initialized. We just make sure that it
+                * is tracked in the object tracker.
+                */
+               if (test_bit(WORK_STRUCT_STATIC, work_data_bits(work))) {
+                       debug_object_init(work, &work_debug_descr);
+                       debug_object_activate(work, &work_debug_descr);
+                       return 0;
+               }
+               WARN_ON_ONCE(1);
+               return 0;
+
+       case ODEBUG_STATE_ACTIVE:
+               WARN_ON(1);
+
+       default:
+               return 0;
+       }
+}
+
+/*
+ * fixup_free is called when:
+ * - an active object is freed
+ */
+static int work_fixup_free(void *addr, enum debug_obj_state state)
+{
+       struct work_struct *work = addr;
+
+       switch (state) {
+       case ODEBUG_STATE_ACTIVE:
+               cancel_work_sync(work);
+               debug_object_free(work, &work_debug_descr);
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static struct debug_obj_descr work_debug_descr = {
+       .name           = "work_struct",
+       .fixup_init     = work_fixup_init,
+       .fixup_activate = work_fixup_activate,
+       .fixup_free     = work_fixup_free,
+};
+
+static inline void debug_work_activate(struct work_struct *work)
+{
+       debug_object_activate(work, &work_debug_descr);
+}
+
+static inline void debug_work_deactivate(struct work_struct *work)
+{
+       debug_object_deactivate(work, &work_debug_descr);
+}
+
+void __init_work(struct work_struct *work, int onstack)
+{
+       if (onstack)
+               debug_object_init_on_stack(work, &work_debug_descr);
+       else
+               debug_object_init(work, &work_debug_descr);
+}
+EXPORT_SYMBOL_GPL(__init_work);
+
+void destroy_work_on_stack(struct work_struct *work)
+{
+       debug_object_free(work, &work_debug_descr);
+}
+EXPORT_SYMBOL_GPL(destroy_work_on_stack);
+
+#else
+static inline void debug_work_activate(struct work_struct *work) { }
+static inline void debug_work_deactivate(struct work_struct *work) { }
+#endif
+
 /* Serializes the accesses to the list of workqueues. */
 static DEFINE_SPINLOCK(workqueue_lock);
 static LIST_HEAD(workqueues);
@@ -145,6 +255,7 @@ static void __queue_work(struct cpu_workqueue_struct *cwq,
 {
        unsigned long flags;
 
+       debug_work_activate(work);
        spin_lock_irqsave(&cwq->lock, flags);
        insert_work(cwq, work, &cwq->worklist);
        spin_unlock_irqrestore(&cwq->lock, flags);
@@ -280,6 +391,7 @@ static void run_workqueue(struct cpu_workqueue_struct *cwq)
                struct lockdep_map lockdep_map = work->lockdep_map;
 #endif
                trace_workqueue_execution(cwq->thread, work);
+               debug_work_deactivate(work);
                cwq->current_work = work;
                list_del_init(cwq->worklist.next);
                spin_unlock_irq(&cwq->lock);
@@ -350,11 +462,18 @@ static void wq_barrier_func(struct work_struct *work)
 static void insert_wq_barrier(struct cpu_workqueue_struct *cwq,
                        struct wq_barrier *barr, struct list_head *head)
 {
-       INIT_WORK(&barr->work, wq_barrier_func);
+       /*
+        * debugobject calls are safe here even with cwq->lock locked
+        * as we know for sure that this will not trigger any of the
+        * checks and call back into the fixup functions where we
+        * might deadlock.
+        */
+       INIT_WORK_ON_STACK(&barr->work, wq_barrier_func);
        __set_bit(WORK_STRUCT_PENDING, work_data_bits(&barr->work));
 
        init_completion(&barr->done);
 
+       debug_work_activate(&barr->work);
        insert_work(cwq, &barr->work, head);
 }
 
@@ -372,8 +491,10 @@ static int flush_cpu_workqueue(struct cpu_workqueue_struct *cwq)
        }
        spin_unlock_irq(&cwq->lock);
 
-       if (active)
+       if (active) {
                wait_for_completion(&barr.done);
+               destroy_work_on_stack(&barr.work);
+       }
 
        return active;
 }
@@ -451,6 +572,7 @@ out:
                return 0;
 
        wait_for_completion(&barr.done);
+       destroy_work_on_stack(&barr.work);
        return 1;
 }
 EXPORT_SYMBOL_GPL(flush_work);
@@ -485,6 +607,7 @@ static int try_to_grab_pending(struct work_struct *work)
                 */
                smp_rmb();
                if (cwq == get_wq_data(work)) {
+                       debug_work_deactivate(work);
                        list_del_init(&work->entry);
                        ret = 1;
                }
@@ -507,8 +630,10 @@ static void wait_on_cpu_work(struct cpu_workqueue_struct *cwq,
        }
        spin_unlock_irq(&cwq->lock);
 
-       if (unlikely(running))
+       if (unlikely(running)) {
                wait_for_completion(&barr.done);
+               destroy_work_on_stack(&barr.work);
+       }
 }
 
 static void wait_on_work(struct work_struct *work)
index 0b8f359..2f22cf4 100644 (file)
@@ -105,7 +105,7 @@ config DEBUG_SECTION_MISMATCH
        bool "Enable full Section mismatch analysis"
        depends on UNDEFINED
        # This option is on purpose disabled for now.
-       # It will be enabled when we are down to a resonable number
+       # It will be enabled when we are down to a reasonable number
        # of section mismatch warnings (< 10 for an allyesconfig build)
        help
          The section mismatch analysis checks if there are illegal
@@ -298,6 +298,14 @@ config DEBUG_OBJECTS_TIMERS
          timer routines to track the life time of timer objects and
          validate the timer operations.
 
+config DEBUG_OBJECTS_WORK
+       bool "Debug work objects"
+       depends on DEBUG_OBJECTS
+       help
+         If you say Y here, additional code will be inserted into the
+         work queue routines to track the life time of work objects and
+         validate the work operations.
+
 config DEBUG_OBJECTS_ENABLE_DEFAULT
        int "debug_objects bootup default value (0-1)"
         range 0 1
index 600f473..7607420 100644 (file)
@@ -299,7 +299,7 @@ static int INIT get_next_block(struct bunzip_data *bd)
                   again when using them (during symbol decoding).*/
                base = hufGroup->base-1;
                limit = hufGroup->limit-1;
-               /* Calculate permute[].  Concurently, initialize
+               /* Calculate permute[].  Concurrently, initialize
                 * temp[] and limit[]. */
                pp = 0;
                for (i = minLen; i <= maxLen; i++) {
index ce6b7ea..d9b08e0 100644 (file)
@@ -259,7 +259,7 @@ static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket,
                 * times. Without a hardware IOMMU this results in the
                 * same device addresses being put into the dma-debug
                 * hash multiple times too. This can result in false
-                * positives being reported. Therfore we implement a
+                * positives being reported. Therefore we implement a
                 * best-fit algorithm here which returns the entry from
                 * the hash which fits best to the reference value
                 * instead of the first-fit.
index 80ca9ac..1cac726 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -281,7 +281,7 @@ static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id)
 /**
  * idr_get_new_above - allocate new idr entry above or equal to a start id
  * @idp: idr handle
- * @ptr: pointer you want associated with the ide
+ * @ptr: pointer you want associated with the id
  * @start_id: id to start search at
  * @id: pointer to the allocated handle
  *
@@ -313,7 +313,7 @@ EXPORT_SYMBOL(idr_get_new_above);
 /**
  * idr_get_new - allocate new idr entry
  * @idp: idr handle
- * @ptr: pointer you want associated with the ide
+ * @ptr: pointer you want associated with the id
  * @id: pointer to the allocated handle
  *
  * This is the allocate id function.  It should be called with any
index 795472d..5bc0180 100644 (file)
@@ -485,7 +485,7 @@ do_unmap_single(struct device *hwdev, char *dma_addr, size_t size, int dir)
 
        /*
         * Return the buffer to the free list by setting the corresponding
-        * entries to indicate the number of contigous entries available.
+        * entries to indicate the number of contiguous entries available.
         * While returning the entries to the free list, we merge the entries
         * with slots below and above the pool being returned.
         */
index ef169f3..c3d3506 100644 (file)
@@ -1844,7 +1844,7 @@ static size_t __iovec_copy_from_user_inatomic(char *vaddr,
 
 /*
  * Copy as much as we can into the page and return the number of bytes which
- * were sucessfully copied.  If a fault is encountered then return the number of
+ * were successfully copied.  If a fault is encountered then return the number of
  * bytes which were copied.
  */
 size_t iov_iter_copy_from_user_atomic(struct page *page,
index 8bf765c..13f33b3 100644 (file)
@@ -1050,8 +1050,8 @@ static void scan_object(struct kmemleak_object *object)
        unsigned long flags;
 
        /*
-        * Once the object->lock is aquired, the corresponding memory block
-        * cannot be freed (the same lock is aquired in delete_object).
+        * Once the object->lock is acquired, the corresponding memory block
+        * cannot be freed (the same lock is acquired in delete_object).
         */
        spin_lock_irqsave(&object->lock, flags);
        if (object->flags & OBJECT_NO_SCAN)
index f99f599..c31a310 100644 (file)
@@ -209,7 +209,7 @@ struct mem_cgroup {
        int     prev_priority;  /* for recording reclaim priority */
 
        /*
-        * While reclaiming in a hiearchy, we cache the last child we
+        * While reclaiming in a hierarchy, we cache the last child we
         * reclaimed from.
         */
        int last_scanned_child;
@@ -1720,7 +1720,7 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
 /*
  * While swap-in, try_charge -> commit or cancel, the page is locked.
  * And when try_charge() successfully returns, one refcnt to memcg without
- * struct page_cgroup is aquired. This refcnt will be cumsumed by
+ * struct page_cgroup is acquired. This refcnt will be consumed by
  * "commit()" or removed by "cancel()"
  */
 int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
@@ -2466,7 +2466,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
 
        cgroup_lock();
        /*
-        * If parent's use_hiearchy is set, we can't make any modifications
+        * If parent's use_hierarchy is set, we can't make any modifications
         * in the child subtrees. If it is unset, then the change can
         * occur, provided the current cgroup has no children.
         *
index dacc641..1ac49fe 100644 (file)
@@ -174,7 +174,7 @@ static void kill_procs_ao(struct list_head *to_kill, int doit, int trapno,
        list_for_each_entry_safe (tk, next, to_kill, nd) {
                if (doit) {
                        /*
-                        * In case something went wrong with munmaping
+                        * In case something went wrong with munmapping
                         * make sure the process doesn't catch the
                         * signal and then access the memory. Just kill it.
                         * the signal handlers
index 7dbcb22..0bc640f 100644 (file)
@@ -1044,7 +1044,7 @@ static int do_pages_stat(struct mm_struct *mm, unsigned long nr_pages,
        int err;
 
        for (i = 0; i < nr_pages; i += chunk_nr) {
-               if (chunk_nr + i > nr_pages)
+               if (chunk_nr > nr_pages - i)
                        chunk_nr = nr_pages - i;
 
                err = copy_from_user(chunk_pages, &pages[i],
index 450cebd..2c147a7 100644 (file)
@@ -490,7 +490,7 @@ EXPORT_SYMBOL_GPL(invalidate_inode_pages2_range);
  * Any pages which are found to be mapped into pagetables are unmapped prior to
  * invalidation.
  *
- * Returns -EIO if any pages could not be invalidated.
+ * Returns -EBUSY if any pages could not be invalidated.
  */
 int invalidate_inode_pages2(struct address_space *mapping)
 {
index 29b1b22..ef09c7b 100644 (file)
@@ -78,7 +78,7 @@ static struct bnep_session *__bnep_get_session(u8 *dst)
 static void __bnep_link_session(struct bnep_session *s)
 {
        /* It's safe to call __module_get() here because sessions are added
-          by the socket layer which has to hold the refference to this module.
+          by the socket layer which has to hold the reference to this module.
         */
        __module_get(THIS_MODULE);
        list_add(&s->list, &bnep_session_list);
@@ -632,7 +632,7 @@ int bnep_del_connection(struct bnep_conndel_req *req)
        s = __bnep_get_session(req->dst);
        if (s) {
                /* Wakeup user-space which is polling for socket errors.
-                * This is temporary hack untill we have shutdown in L2CAP */
+                * This is temporary hack until we have shutdown in L2CAP */
                s->sock->sk->sk_err = EUNATCH;
 
                /* Kill session thread */
index 549e206..ea5cea2 100644 (file)
@@ -50,7 +50,7 @@ set_ect_tcp(struct sk_buff *skb, const struct ipt_ECN_info *einfo)
        struct tcphdr _tcph, *tcph;
        __be16 oldval;
 
-       /* Not enought header? */
+       /* Not enough header? */
        tcph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
        if (!tcph)
                return false;
index 356e65b..783c5f3 100644 (file)
@@ -450,10 +450,10 @@ void irlap_disconnect_request(struct irlap_cb *self)
 
        /* Check if we are in the right state for disconnecting */
        switch (self->state) {
-       case LAP_XMIT_P:        /* FALLTROUGH */
-       case LAP_XMIT_S:        /* FALLTROUGH */
-       case LAP_CONN:          /* FALLTROUGH */
-       case LAP_RESET_WAIT:    /* FALLTROUGH */
+       case LAP_XMIT_P:        /* FALLTHROUGH */
+       case LAP_XMIT_S:        /* FALLTHROUGH */
+       case LAP_CONN:          /* FALLTHROUGH */
+       case LAP_RESET_WAIT:    /* FALLTHROUGH */
        case LAP_RESET_CHECK:
                irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL);
                break;
@@ -485,9 +485,9 @@ void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
                IRDA_DEBUG(1, "%s(), Sending reset request!\n", __func__);
                irlap_do_event(self, RESET_REQUEST, NULL, NULL);
                break;
-       case LAP_NO_RESPONSE:      /* FALLTROUGH */
-       case LAP_DISC_INDICATION:  /* FALLTROUGH */
-       case LAP_FOUND_NONE:       /* FALLTROUGH */
+       case LAP_NO_RESPONSE:      /* FALLTHROUGH */
+       case LAP_DISC_INDICATION:  /* FALLTHROUGH */
+       case LAP_FOUND_NONE:       /* FALLTHROUGH */
        case LAP_MEDIA_BUSY:
                irlmp_link_disconnect_indication(self->notify.instance, self,
                                                 reason, NULL);
index c5c5195..94a9884 100644 (file)
@@ -1741,7 +1741,7 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
  * Function irlap_state_xmit_s (event, skb, info)
  *
  *   XMIT_S, The secondary station has been given the right to transmit,
- *   and we therefor do not expect to receive any transmissions from other
+ *   and we therefore do not expect to receive any transmissions from other
  *   stations.
  */
 static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
index 7bf5b91..0e7d8bd 100644 (file)
@@ -105,7 +105,7 @@ int __init irlmp_init(void)
 
        init_timer(&irlmp->discovery_timer);
 
-       /* Do discovery every 3 seconds, conditionaly */
+       /* Do discovery every 3 seconds, conditionally */
        if (sysctl_discovery)
                irlmp_start_discovery_timer(irlmp,
                                            sysctl_discovery_timeout*HZ);
@@ -1842,7 +1842,7 @@ LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason)
                reason = LM_CONNECT_FAILURE;
                break;
        default:
-               IRDA_DEBUG(1, "%s(), Unknow IrLAP disconnect reason %d!\n",
+               IRDA_DEBUG(1, "%s(), Unknown IrLAP disconnect reason %d!\n",
                           __func__, lap_reason);
                reason = LM_LAP_DISCONNECT;
                break;
index a8da239..0192cfd 100644 (file)
@@ -244,7 +244,7 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data
  * @addr: destination address of the path (ETH_ALEN length)
  * @sdata: local subif
  *
- * Returns: 0 on sucess
+ * Returns: 0 on success
  *
  * State: the initial state of the new path is set to 0
  */
@@ -532,7 +532,7 @@ static void mesh_path_node_reclaim(struct rcu_head *rp)
  * @addr: dst address (ETH_ALEN length)
  * @sdata: local subif
  *
- * Returns: 0 if succesful
+ * Returns: 0 if successful
  */
 int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
 {
index 7a10bbe..c5d9f97 100644 (file)
@@ -682,7 +682,7 @@ struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain,
  * buckets and @skip_chain entries.  For each entry in the table call
  * @callback, if @callback returns a negative value stop 'walking' through the
  * table and return.  Updates the values in @skip_bkt and @skip_chain on
- * return.  Returns zero on succcess, negative values on failure.
+ * return.  Returns zero on success, negative values on failure.
  *
  */
 int netlbl_domhsh_walk(u32 *skip_bkt,
index 2a74003..64f5e32 100644 (file)
@@ -598,7 +598,7 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
                goto errout;
 
        /* compat_mode being true specifies a call that is supposed
-        * to add additional backward compatiblity statistic TLVs.
+        * to add additional backward compatibility statistic TLVs.
         */
        if (compat_mode) {
                if (a->type == TCA_OLD_COMPAT)
index d771cc1..4e4ca65 100644 (file)
@@ -717,7 +717,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
 
        if (sctp_style(sk, TCP)) {
                /* Change the sk->sk_state of a TCP-style socket that has
-                * sucessfully completed a connect() call.
+                * successfully completed a connect() call.
                 */
                if (sctp_state(asoc, ESTABLISHED) && sctp_sstate(sk, CLOSED))
                        sk->sk_state = SCTP_SS_ESTABLISHED;
index 1ef9de9..47bc20d 100644 (file)
@@ -3577,7 +3577,7 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep,
         * To do this properly, we'll set the destination address of the chunk
         * and at the transmit time, will try look up the transport to use.
         * Since ASCONFs may be bundled, the correct transport may not be
-        * created untill we process the entire packet, thus this workaround.
+        * created until we process the entire packet, thus this workaround.
         */
        asconf_ack->dest = chunk->source;
        sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(asconf_ack));
index f11be72..b15e1eb 100644 (file)
@@ -54,7 +54,7 @@
  * Assumptions:
  * - head[0] is physically contiguous.
  * - tail[0] is physically contiguous.
- * - pages[] is not physically or virtually contigous and consists of
+ * - pages[] is not physically or virtually contiguous and consists of
  *   PAGE_SIZE elements.
  *
  * Output:
index ca26917..35f3700 100644 (file)
@@ -62,7 +62,7 @@
  * Called when wanting to reset the device for any reason. Device is
  * taken back to power on status.
  *
- * This call blocks; on succesful return, the device has completed the
+ * This call blocks; on successful return, the device has completed the
  * reset process and is ready to operate.
  */
 int wimax_reset(struct wimax_dev *wimax_dev)
index e4be84a..8924f72 100644 (file)
@@ -1,5 +1,3 @@
-# samples/Kconfig
-
 menuconfig SAMPLES
        bool "Sample kernel code"
        help
@@ -47,4 +45,3 @@ config SAMPLE_HW_BREAKPOINT
          This builds kernel hardware breakpoint example modules.
 
 endif # SAMPLES
-
index d829535..8413cf3 100644 (file)
@@ -213,7 +213,7 @@ load_config_help[] = N_(
        "to modify that configuration.\n"
        "\n"
        "If you are uncertain, then you have probably never used alternate\n"
-       "configuration files.  You should therefor leave this blank to abort.\n"),
+       "configuration files. You should therefore leave this blank to abort.\n"),
 save_config_text[] = N_(
        "Enter a filename to which this configuration should be saved "
        "as an alternate.  Leave blank to abort."),
index e688237..2534400 100644 (file)
@@ -204,7 +204,7 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
  *
  * Description
  * Call the NetLabel mechanism to set the label of a packet using @sid.
- * Returns zero on auccess, negative values on failure.
+ * Returns zero on success, negative values on failure.
  *
  */
 int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
index d6bb20c..b3efae2 100644 (file)
@@ -836,7 +836,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
                goto out;
        }
 
-       /* type/domain unchaned */
+       /* type/domain unchanged */
        if (old_context->type == new_context->type) {
                rc = 0;
                goto out;
index b3e53e6..fcad760 100644 (file)
@@ -1,6 +1,3 @@
-# sound/Config.in
-#
-
 menuconfig SOUND
        tristate "Sound card support"
        depends on HAS_IOMEM
@@ -136,4 +133,3 @@ config AC97_BUS
          sound subsystem and other function drivers completely unrelated to
          sound although they're sharing the AC97 bus. Concerned drivers
          should "select" this.
-
index 93fa672..cc15d1d 100644 (file)
@@ -177,7 +177,7 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {
        { .id = "CSC0437", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
        /* Digital PC 5000 Onboard - CS4236B */
        { .id = "CSC0735", .devs = { { "CSC0000" }, { "CSC0010" } } },
-       /* some uknown CS4236B */
+       /* some unknown CS4236B */
        { .id = "CSC0b35", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
        /* Intel PR440FX Onboard sound */
        { .id = "CSC0b36", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },
index 6123c75..b865e45 100644 (file)
@@ -133,7 +133,7 @@ struct snd_miro {
 static struct snd_miro_aci aci_device;
 
 static char * snd_opti9xx_names[] = {
-       "unkown",
+       "unknown",
        "82C928", "82C929",
        "82C924", "82C925",
        "82C930", "82C931", "82C933"
index d08c389..106be6e 100644 (file)
@@ -135,6 +135,8 @@ struct snd_opti9xx {
        unsigned long mc_base_size;
 #ifdef OPTi93X
        unsigned long mc_indir_index;
+       unsigned long mc_indir_size;
+       struct resource *res_mc_indir;
        struct snd_wss *codec;
 #endif /* OPTi93X */
        unsigned long pwd_reg;
@@ -177,7 +179,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
 #endif
 
 static char * snd_opti9xx_names[] = {
-       "unkown",
+       "unknown",
        "82C928",       "82C929",
        "82C924",       "82C925",
        "82C930",       "82C931",       "82C933"
@@ -231,7 +233,10 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
        case OPTi9XX_HW_82C931:
        case OPTi9XX_HW_82C933:
                chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
-               chip->mc_indir_index = 0xe0e;
+               if (!chip->mc_indir_index) {
+                       chip->mc_indir_index = 0xe0e;
+                       chip->mc_indir_size = 2;
+               }
                chip->password = 0xe4;
                chip->pwd_reg = 0;
                break;
@@ -560,57 +565,69 @@ static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
 
 #endif /* OPTi93X */
 
-static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
-                                            struct snd_opti9xx *chip)
+static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip)
 {
-       int i, err;
+       unsigned char value;
+#ifdef OPTi93X
+       unsigned long flags;
+#endif
 
+       chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
+                                          "OPTi9xx MC");
+       if (chip->res_mc_base == NULL)
+               return -EBUSY;
 #ifndef OPTi93X
-       for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
-               unsigned char value;
+       value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
+       if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
+               if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
+                       return 0;
+#else  /* OPTi93X */
+       chip->res_mc_indir = request_region(chip->mc_indir_index,
+                                           chip->mc_indir_size,
+                                           "OPTi93x MC");
+       if (chip->res_mc_indir == NULL)
+               return -EBUSY;
 
-               if ((err = snd_opti9xx_init(chip, i)) < 0)
-                       return err;
+       spin_lock_irqsave(&chip->lock, flags);
+       outb(chip->password, chip->mc_base + chip->pwd_reg);
+       outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
+       spin_unlock_irqrestore(&chip->lock, flags);
 
-               if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
-                       continue;
+       value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
+       snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
+       if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
+               return 0;
 
-               value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
-               if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
-                       if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
-                               return 1;
+       release_and_free_resource(chip->res_mc_indir);
+       chip->res_mc_indir = NULL;
+#endif /* OPTi93X */
+       release_and_free_resource(chip->res_mc_base);
+       chip->res_mc_base = NULL;
 
-               release_and_free_resource(chip->res_mc_base);
-               chip->res_mc_base = NULL;
+       return -ENODEV;
+}
 
-       }
-#else  /* OPTi93X */
-       for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
-               unsigned long flags;
-               unsigned char value;
+static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
+                                            struct snd_opti9xx *chip)
+{
+       int i, err;
 
-               if ((err = snd_opti9xx_init(chip, i)) < 0)
+#ifndef OPTi93X
+       for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
+#else
+       for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
+#endif
+               err = snd_opti9xx_init(chip, i);
+               if (err < 0)
                        return err;
 
-               if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
-                       continue;
-
-               spin_lock_irqsave(&chip->lock, flags);
-               outb(chip->password, chip->mc_base + chip->pwd_reg);
-               outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
-                       ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
-               spin_unlock_irqrestore(&chip->lock, flags);
-
-               value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
-               snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
-               if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
+               err = snd_opti9xx_read_check(chip);
+               if (err == 0)
                        return 1;
-
-               release_and_free_resource(chip->res_mc_base);
-               chip->res_mc_base = NULL;
+#ifdef OPTi93X
+               chip->mc_indir_index = 0;
+#endif
        }
-#endif /* OPTi93X */
-
        return -ENODEV;
 }
 
@@ -639,6 +656,8 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
 #ifdef OPTi93X
        port = pnp_port_start(pdev, 0) - 4;
        fm_port = pnp_port_start(pdev, 1) + 8;
+       chip->mc_indir_index = pnp_port_start(pdev, 3) + 2;
+       chip->mc_indir_size = pnp_port_len(pdev, 3) - 2;
 #else
        if (pid->driver_data != 0x0924)
                port = pnp_port_start(pdev, 1);
@@ -669,7 +688,7 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
 static void snd_card_opti9xx_free(struct snd_card *card)
 {
        struct snd_opti9xx *chip = card->private_data;
-        
+
        if (chip) {
 #ifdef OPTi93X
                struct snd_wss *codec = chip->codec;
@@ -677,6 +696,7 @@ static void snd_card_opti9xx_free(struct snd_card *card)
                        disable_irq(codec->irq);
                        free_irq(codec->irq, codec);
                }
+               release_and_free_resource(chip->res_mc_indir);
 #endif
                release_and_free_resource(chip->res_mc_base);
        }
@@ -696,11 +716,6 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
        struct snd_rawmidi *rmidi;
        struct snd_hwdep *synth;
 
-       if (! chip->res_mc_base &&
-           (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
-                                               "OPTi9xx MC")) == NULL)
-               return -ENOMEM;
-
 #if defined(CS4231) || defined(OPTi93X)
        xdma2 = dma2;
 #else
@@ -954,6 +969,13 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
        }
        if (hw <= OPTi9XX_HW_82C930)
                chip->mc_base -= 0x80;
+
+       error = snd_opti9xx_read_check(chip);
+       if (error) {
+               snd_printk(KERN_ERR "OPTI chip not found\n");
+               snd_card_free(card);
+               return error;
+       }
        snd_card_set_dev(card, &pcard->card->dev);
        if ((error = snd_opti9xx_probe(card)) < 0) {
                snd_card_free(card);
index 135a2b7..a513651 100644 (file)
@@ -1,5 +1,3 @@
-# drivers/sound/Config.in
-#
 # 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net>
 # More hacking for modularisation.
 #
index 06e9e88..bb14e4c 100644 (file)
@@ -657,7 +657,7 @@ static int AmiStateInfo(char *buffer, size_t space)
        len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
                       dmasound.volume_right);
        if (len >= space) {
-               printk(KERN_ERR "dmasound_paula: overlowed state buffer alloc.\n") ;
+               printk(KERN_ERR "dmasound_paula: overflowed state buffer alloc.\n") ;
                len = space ;
        }
        return len;
index 15523e6..0470461 100644 (file)
@@ -233,7 +233,7 @@ static void snd_ca0106_proc_dump_iec958( struct snd_info_buffer *buffer, u32 val
                        snd_iprintf(buffer, "user-defined\n");
                        break;
                default:
-                       snd_iprintf(buffer, "unkown\n");
+                       snd_iprintf(buffer, "unknown\n");
                        break;
                }
                snd_iprintf(buffer, "Sample Bits: ");
index 09d24c7..a65e119 100644 (file)
 //
 //
 // The purpose of this code is very simple: make it possible to tranfser
-// the samples 'as they are' with no alteration from a PCMreader SCB (DMA from host)
-// to any other SCB. This is useful for AC3 throug SPDIF. SRC (source rate converters) 
-// task always alters the samples in some how, however it's from 48khz -> 48khz. The
-// alterations are not audible, but AC3 wont work. 
+// the samples 'as they are' with no alteration from a PCMreader
+// SCB (DMA from host) to any other SCB. This is useful for AC3 through SPDIF.
+// SRC (source rate converters) task always alters the samples in somehow,
+// however it's from 48khz -> 48khz.
+// The alterations are not audible, but AC3 wont work. 
 //
 //        ...
 //         |
index 6b8ae7b..1d369ff 100644 (file)
@@ -184,7 +184,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
  * The hardware has 3 channels for playback and 1 for capture.
  *  - channel 0 is the front channel
  *  - channel 1 is the rear channel
- *  - channel 2 is the center/lfe chanel
+ *  - channel 2 is the center/lfe channel
  * Volume is controlled by the AC97 for the front and rear channels by
  * the PCM Playback Volume, Sigmatel Surround Playback Volume and 
  * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
index d822bfc..efcc4f7 100644 (file)
@@ -2450,6 +2450,11 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
                }
        }
 
+       /* disable 64bit DMA address for Teradici */
+       /* it does not work with device 6549:1200 subsys e4a2:040b */
+       if (chip->driver_type == AZX_DRIVER_TERA)
+               gcap &= ~ICH6_GCAP_64OK;
+
        /* allow 64bit DMA address if supported by H/W */
        if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
                pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
index 455a049..447eda1 100644 (file)
@@ -72,7 +72,8 @@ struct ad198x_spec {
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
        unsigned int jack_present :1;
-       unsigned int inv_jack_detect:1;
+       unsigned int inv_jack_detect:1; /* inverted jack-detection */
+       unsigned int inv_eapd:1;        /* inverted EAPD implementation */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_loopback_check loopback;
@@ -458,7 +459,7 @@ static struct hda_codec_ops ad198x_patch_ops = {
 
 /*
  * EAPD control
- * the private value = nid | (invert << 8)
+ * the private value = nid
  */
 #define ad198x_eapd_info       snd_ctl_boolean_mono_info
 
@@ -467,8 +468,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
-       int invert = (kcontrol->private_value >> 8) & 1;
-       if (invert)
+       if (spec->inv_eapd)
                ucontrol->value.integer.value[0] = ! spec->cur_eapd;
        else
                ucontrol->value.integer.value[0] = spec->cur_eapd;
@@ -480,11 +480,10 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct ad198x_spec *spec = codec->spec;
-       int invert = (kcontrol->private_value >> 8) & 1;
        hda_nid_t nid = kcontrol->private_value & 0xff;
        unsigned int eapd;
        eapd = !!ucontrol->value.integer.value[0];
-       if (invert)
+       if (spec->inv_eapd)
                eapd = !eapd;
        if (eapd == spec->cur_eapd)
                return 0;
@@ -705,7 +704,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
-               .private_value = 0x1b | (1 << 8), /* port-D, inversed */
+               .private_value = 0x1b, /* port-D */
        },
        { } /* end */
 };
@@ -1074,6 +1073,7 @@ static int patch_ad1986a(struct hda_codec *codec)
        spec->loopback.amplist = ad1986a_loopbacks;
 #endif
        spec->vmaster_nid = 0x1b;
+       spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
 
        codec->patch_ops = ad198x_patch_ops;
 
@@ -2124,7 +2124,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
-               .private_value = 0x12 | (1 << 8), /* port-D, inversed */
+               .private_value = 0x12, /* port-D */
        },
 
        { } /* end */
@@ -3065,6 +3065,7 @@ static int patch_ad1988(struct hda_codec *codec)
                spec->input_mux = &ad1988_laptop_capture_source;
                spec->num_mixers = 1;
                spec->mixers[0] = ad1988_laptop_mixers;
+               spec->inv_eapd = 1; /* inverted EAPD */
                spec->num_init_verbs = 1;
                spec->init_verbs[0] = ad1988_laptop_init_verbs;
                if (board_config == AD1988_LAPTOP_DIG)
index 2439e84..4b200da 100644 (file)
@@ -938,7 +938,7 @@ static void init_input(struct hda_codec *codec)
                coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
        if (is_active_pin(codec, CS_DMIC1_PIN_NID))
                coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 
-                                * No effect if SPDIF_OUT2 is slected in 
+                                * No effect if SPDIF_OUT2 is selected in 
                                 * IDX_SPDIF_CTL.
                                  */
        cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
index 85c81fe..a45c116 100644 (file)
@@ -66,7 +66,7 @@ struct cmi_spec {
 
        struct hda_pcm pcm_rec[2];      /* PCM information */
 
-       /* pin deafault configuration */
+       /* pin default configuration */
        hda_nid_t pin_nid[NUM_PINS];
        unsigned int def_conf[NUM_PINS];
        unsigned int pin_def_confs;
index d967836..888b631 100644 (file)
@@ -208,6 +208,7 @@ enum {
        ALC885_MBP3,
        ALC885_MB5,
        ALC885_IMAC24,
+       ALC885_IMAC91,
        ALC883_3ST_2ch_DIG,
        ALC883_3ST_6ch_DIG,
        ALC883_3ST_6ch,
@@ -2400,6 +2401,8 @@ static const char *alc_slave_sws[] = {
        "Speaker Playback Switch",
        "Mono Playback Switch",
        "IEC958 Playback Switch",
+       "Line-Out Playback Switch",
+       "PCM Playback Switch",
        NULL,
 };
 
@@ -6618,7 +6621,7 @@ static struct hda_input_mux alc889A_mb31_capture_source = {
                /* Front Mic (0x01) unused */
                { "Line", 0x2 },
                /* Line 2 (0x03) unused */
-               /* CD (0x04) unsused? */
+               /* CD (0x04) unused? */
        },
 };
 
@@ -7050,6 +7053,20 @@ static struct snd_kcontrol_new alc885_mb5_mixer[] = {
        { } /* end */
 };
 
+static struct snd_kcontrol_new alc885_imac91_mixer[] = {
+       HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+       HDA_BIND_MUTE   ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE  ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+       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, 0x00, HDA_INPUT),
+       HDA_CODEC_MUTE  ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 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),
@@ -7505,6 +7522,66 @@ static struct hda_verb alc885_mbp3_init_verbs[] = {
        { }
 };
 
+/* iMac 9,1 */
+static struct hda_verb alc885_imac91_init_verbs[] = {
+       /* Line-Out mixer: unmute input/output amp left and right (volume = 0) */
+       {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)},
+       /* Rear 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)},
+       /* HP Pin: output 0 (0x0c) */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+       /* Internal Speakers: output 0 (0x0d) */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* Mic (rear) pin: input vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* 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: use output 1 when in LineOut mode */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+
+       /* FIXME: use matrix-type input source selection */
+       /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+       /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {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)},
+       /* Input mixer2 */
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* Input mixer3 */
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* ADC1: mute amp left and right */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* ADC2: mute amp left and right */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* ADC3: mute amp left and right */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+       { }
+};
+
 /* iMac 24 mixer. */
 static struct snd_kcontrol_new alc885_imac24_mixer[] = {
        HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
@@ -7551,6 +7628,26 @@ static void alc885_mbp3_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[0] = 0x14;
 }
 
+static void alc885_imac91_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, 0x15, 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);
+
+}
+
+static void alc885_imac91_unsol_event(struct hda_codec *codec,
+                                   unsigned int res)
+{
+       /* Headphone insertion or removal. */
+       if ((res >> 26) == ALC880_HP_EVENT)
+               alc885_imac91_automute(codec);
+}
 
 static struct hda_verb alc882_targa_verbs[] = {
        {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -8718,6 +8815,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
        [ALC885_MB5]            = "mb5",
        [ALC885_MBP3]           = "mbp3",
        [ALC885_IMAC24]         = "imac24",
+       [ALC885_IMAC91]         = "imac91",
        [ALC883_3ST_2ch_DIG]    = "3stack-2ch-dig",
        [ALC883_3ST_6ch_DIG]    = "3stack-6ch-dig",
        [ALC883_3ST_6ch]        = "3stack-6ch",
@@ -8891,6 +8989,7 @@ static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
        SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
        SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
        SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
+       SND_PCI_QUIRK(0x106b, 0x4900, "iMac 9,1 Aluminum", ALC885_IMAC91),
        SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
        /* FIXME: HP jack sense seems not working for MBP 5,1 or 5,2,
         * so apparently no perfect solution yet
@@ -9002,6 +9101,20 @@ static struct alc_config_preset alc882_presets[] = {
                .setup = alc885_imac24_setup,
                .init_hook = alc885_imac24_init_hook,
        },
+       [ALC885_IMAC91] = {
+               .mixers = { alc885_imac91_mixer, alc882_chmode_mixer },
+               .init_verbs = { alc885_imac91_init_verbs,
+                               alc880_gpio1_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc882_dac_nids),
+               .dac_nids = alc882_dac_nids,
+               .channel_mode = alc885_mbp_4ch_modes,
+               .num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
+               .input_mux = &alc882_capture_source,
+               .dig_out_nid = ALC882_DIGOUT_NID,
+               .dig_in_nid = ALC882_DIGIN_NID,
+               .unsol_event = alc885_imac91_unsol_event,
+               .init_hook = alc885_imac91_automute,
+       },
        [ALC882_TARGA] = {
                .mixers = { alc882_targa_mixer, alc882_chmode_mixer },
                .init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
@@ -9908,10 +10021,12 @@ static int patch_alc882(struct hda_codec *codec)
                spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
 
        if (!spec->adc_nids && spec->input_mux) {
-               int i;
+               int i, j;
                spec->num_adc_nids = 0;
                for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
+                       const struct hda_input_mux *imux = spec->input_mux;
                        hda_nid_t cap;
+                       hda_nid_t items[16];
                        hda_nid_t nid = alc882_adc_nids[i];
                        unsigned int wcap = get_wcaps(codec, nid);
                        /* get type */
@@ -9922,6 +10037,15 @@ static int patch_alc882(struct hda_codec *codec)
                        err = snd_hda_get_connections(codec, nid, &cap, 1);
                        if (err < 0)
                                continue;
+                       err = snd_hda_get_connections(codec, cap, items,
+                                                     ARRAY_SIZE(items));
+                       if (err < 0)
+                               continue;
+                       for (j = 0; j < imux->num_items; j++)
+                               if (imux->items[j].index >= err)
+                                       break;
+                       if (j < imux->num_items)
+                               continue;
                        spec->private_capsrc_nids[spec->num_adc_nids] = cap;
                        spec->num_adc_nids++;
                }
@@ -16846,6 +16970,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
                                        ALC662_3ST_6ch_DIG),
        SND_PCI_QUIRK_MASK(0x1854, 0xf000, 0x2000, "ASUS H13-200x",
                           ALC663_ASUS_H13),
+       SND_PCI_QUIRK(0x8086, 0xd604, "Intel mobo", ALC662_3ST_2ch_DIG),
        {}
 };
 
index 110d16e..765d7bd 100644 (file)
@@ -689,32 +689,14 @@ static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
        return change;
 }
 
-static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -10000, 100, 1);
 static const DECLARE_TLV_DB_SCALE(db_scale_wm_pcm, -6400, 50, 1);
 static const DECLARE_TLV_DB_SCALE(db_scale_wm_adc, -1200, 100, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_ac97_master, -4650, 150, 0);
 static const DECLARE_TLV_DB_SCALE(db_scale_ac97_gain, -3450, 150, 0);
 
-/*
- * Logarithmic volume values for WM8770
- * Computed as 20 * Log10(255 / x)
- */
-static const unsigned char wm_vol[256] = {
-       127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
-       23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
-       17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
-       13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
-       11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
-       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
-       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-       5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
-       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0
-};
-
-#define WM_VOL_MAX     (sizeof(wm_vol) - 1)
+#define WM_VOL_MAX     100
+#define WM_VOL_CNT     101     /* 0dB .. -100dB */
 #define WM_VOL_MUTE    0x8000
 
 static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)
@@ -724,7 +706,8 @@ static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned sho
        if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
                nvol = 0;
        else
-               nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
+               nvol = ((vol % WM_VOL_CNT) * (master % WM_VOL_CNT)) /
+                                                               WM_VOL_MAX;
 
        wm_put(ice, index, nvol);
        wm_put_nocache(ice, index, 0x180 | nvol);
@@ -820,7 +803,7 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = voices;
        uinfo->value.integer.min = 0;           /* mute (-101dB) */
-       uinfo->value.integer.max = 0x7F;        /* 0dB */
+       uinfo->value.integer.max = WM_VOL_MAX;  /* 0dB */
        return 0;
 }
 
@@ -850,7 +833,7 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *
        snd_ice1712_save_gpio_status(ice);
        for (i = 0; i < voices; i++) {
                unsigned int vol = ucontrol->value.integer.value[i];
-               if (vol > 0x7f)
+               if (vol > WM_VOL_MAX)
                        continue;
                vol |= spec->vol[ofs+i];
                if (vol != spec->vol[ofs+i]) {
index 0c9413d..98bc3b7 100644 (file)
@@ -380,7 +380,7 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
         * inputs) are fed from Xilinx.
         *
         * I even checked traces on board and coded a support in driver for
-        * an alternative possiblity - the unused I2S ICE output channels
+        * an alternative possibility - the unused I2S ICE output channels
         * switched to HW-IN/SPDIF-IN and providing the monitoring signal to
         * the DAC - to no avail. The I2S outputs seem to be unconnected.
         *
index 0dce331..a1b10d1 100644 (file)
@@ -3017,7 +3017,7 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
                insel = "Coaxial";
                break;
        default:
-               insel = "Unkown";
+               insel = "Unknown";
        }
 
        switch (hdspm->control_register & HDSPM_SyncRefMask) {
@@ -3028,7 +3028,7 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
                syncref = "MADI";
                break;
        default:
-               syncref = "Unkown";
+               syncref = "Unknown";
        }
        snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel,
                    syncref);
index aa40d98..3e99fe5 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: %u",
+               printk(KERN_ERR "%s unknown register: reg: %u",
                       __func__, reg);
                return -EINVAL;
        }
@@ -552,7 +552,7 @@ static int uda134x_soc_probe(struct platform_device *pdev)
                                        ARRAY_SIZE(uda1341_snd_controls));
        break;
        default:
-               printk(KERN_ERR "%s unkown codec type: %d",
+               printk(KERN_ERR "%s unknown codec type: %d",
                        __func__, pd->model);
        return -EINVAL;
        }
index b8cae17..ce5515e 100644 (file)
@@ -607,7 +607,7 @@ SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1,
 SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0),
 SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0),
 SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1),
-SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8903_DRC_3, 5, 124, 1,
+SOC_SINGLE_TLV("DRC Compressor Threshold Volume", WM8903_DRC_3, 5, 124, 1,
               drc_tlv_thresh),
 SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp),
 SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min),
@@ -617,11 +617,11 @@ SOC_ENUM("DRC Decay Rate", drc_decay),
 SOC_ENUM("DRC FF Delay", drc_ff_delay),
 SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0),
 SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0),
-SOC_SINGLE_TLV("DRC QR Threashold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max),
+SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max),
 SOC_ENUM("DRC QR Decay Rate", drc_qr_decay),
 SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0),
 SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0),
-SOC_ENUM("DRC Smoothing Threashold", drc_smoothing),
+SOC_ENUM("DRC Smoothing Threshold", drc_smoothing),
 SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup),
 
 SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT,
index 5e32f2e..2981afa 100644 (file)
@@ -689,7 +689,7 @@ SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE,
 
 SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0),
 SOC_ENUM("DRC Path", drc_path),
-SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8993_DRC_CONTROL_2,
+SOC_SINGLE_TLV("DRC Compressor Threshold Volume", WM8993_DRC_CONTROL_2,
               2, 60, 1, drc_comp_threash),
 SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3,
               11, 30, 1, drc_comp_amp),
@@ -709,7 +709,7 @@ SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0,
 SOC_ENUM("DRC Quick Release Rate", drc_qr_rate),
 SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0),
 SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0),
-SOC_ENUM("DRC Smoothing Hysteresis Threashold", drc_smooth),
+SOC_ENUM("DRC Smoothing Hysteresis Threshold", drc_smooth),
 SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0,
               drc_startup_tlv),
 
index b838665..bffffcd 100644 (file)
@@ -322,12 +322,12 @@ static int mx1_mx2_pcm_open(struct snd_pcm_substream *substream)
 
        pr_debug("%s: Requesting dma channel (%s)\n", __func__,
                                                prtd->dma_params->name);
-       prtd->dma_ch = imx_dma_request_by_prio(prtd->dma_params->name,
-                                               DMA_PRIO_HIGH);
-       if (prtd->dma_ch < 0) {
+       ret = imx_dma_request_by_prio(prtd->dma_params->name, DMA_PRIO_HIGH);
+       if (ret < 0) {
                printk(KERN_ERR "Error %d requesting dma channel\n", ret);
                return ret;
        }
+       prtd->dma_ch = ret;
        imx_dma_config_burstlen(prtd->dma_ch,
                                prtd->dma_params->watermark_level);
 
index 507b2ed..d441c3b 100644 (file)
@@ -270,7 +270,7 @@ static int attach_gpio_amp(struct device *dev,
                gpio_direction_output(pd->amp_gain[1], 0);
        }
 
-       /* note, curently we assume GPA0 isn't valid amp */
+       /* note, currently we assume GPA0 isn't valid amp */
        if (pdata->amp_gpio > 0) {
                ret = gpio_request(pd->amp_gpio, "gpio-amp");
                if (ret) {
index 0eb1722..1d61109 100644 (file)
@@ -196,7 +196,7 @@ static int s6000_pcm_start(struct snd_pcm_substream *substream)
                           0 /* destination skip after chunk (impossible) */,
                           4 /* 16 byte burst size */,
                           -1 /* don't conserve bandwidth */,
-                          0 /* low watermark irq descriptor theshold */,
+                          0 /* low watermark irq descriptor threshold */,
                           0 /* disable hardware timestamps */,
                           1 /* enable channel */);
 
index 49c9981..dbca7c9 100644 (file)
@@ -353,7 +353,7 @@ static struct sound_unit *chains[SOUND_STEP];
  *      @dev: device pointer
  *
  *     Allocate a special sound device by minor number from the sound
- *     subsystem. The allocated number is returned on succes. On failure
+ *     subsystem. The allocated number is returned on success. On failure
  *     a negative error code is returned.
  */
  
index 63c8f45..67c9123 100644 (file)
@@ -374,7 +374,7 @@ sf_zone_new(struct snd_sf_list *sflist, struct snd_soundfont *sf)
 
 
 /*
- * increment sample couter
+ * increment sample counter
  */
 static void
 set_sample_counter(struct snd_sf_list *sflist, struct snd_soundfont *sf,
index f71cd28..91bb296 100644 (file)
@@ -194,7 +194,8 @@ static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file)
        if (!us122l->first)
                us122l->first = file;
 
-       if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+       if (us122l->dev->descriptor.idProduct == USB_ID_US144 ||
+           us122l->dev->descriptor.idProduct == USB_ID_US144MKII) {
                iface = usb_ifnum_to_if(us122l->dev, 0);
                usb_autopm_get_interface(iface);
        }
@@ -209,7 +210,8 @@ static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file)
        struct usb_interface *iface;
        snd_printdd(KERN_DEBUG "%p %p\n", hw, file);
 
-       if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+       if (us122l->dev->descriptor.idProduct == USB_ID_US144 ||
+           us122l->dev->descriptor.idProduct == USB_ID_US144MKII) {
                iface = usb_ifnum_to_if(us122l->dev, 0);
                usb_autopm_put_interface(iface);
        }
@@ -476,7 +478,8 @@ static bool us122l_create_card(struct snd_card *card)
        int err;
        struct us122l *us122l = US122L(card);
 
-       if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+       if (us122l->dev->descriptor.idProduct == USB_ID_US144 ||
+           us122l->dev->descriptor.idProduct == USB_ID_US144MKII) {
                err = usb_set_interface(us122l->dev, 0, 1);
                if (err) {
                        snd_printk(KERN_ERR "usb_set_interface error \n");
@@ -495,7 +498,8 @@ static bool us122l_create_card(struct snd_card *card)
        if (!us122l_start(us122l, 44100, 256))
                return false;
 
-       if (us122l->dev->descriptor.idProduct == USB_ID_US144)
+       if (us122l->dev->descriptor.idProduct == USB_ID_US144 ||
+           us122l->dev->descriptor.idProduct == USB_ID_US144MKII)
                err = us144_create_usbmidi(card);
        else
                err = us122l_create_usbmidi(card);
@@ -597,7 +601,8 @@ static int snd_us122l_probe(struct usb_interface *intf,
        struct snd_card *card;
        int err;
 
-       if (device->descriptor.idProduct == USB_ID_US144
+       if ((device->descriptor.idProduct == USB_ID_US144 ||
+            device->descriptor.idProduct == USB_ID_US144MKII)
                && device->speed == USB_SPEED_HIGH) {
                snd_printk(KERN_ERR "disable ehci-hcd to run US-144 \n");
                return -ENODEV;
@@ -692,7 +697,8 @@ static int snd_us122l_resume(struct usb_interface *intf)
 
        mutex_lock(&us122l->mutex);
        /* needed, doesn't restart without: */
-       if (us122l->dev->descriptor.idProduct == USB_ID_US144) {
+       if (us122l->dev->descriptor.idProduct == USB_ID_US144 ||
+           us122l->dev->descriptor.idProduct == USB_ID_US144MKII) {
                err = usb_set_interface(us122l->dev, 0, 1);
                if (err) {
                        snd_printk(KERN_ERR "usb_set_interface error \n");
@@ -737,6 +743,16 @@ static struct usb_device_id snd_us122l_usb_id_table[] = {
                .idVendor =     0x0644,
                .idProduct =    USB_ID_US144
        },
+       {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     0x0644,
+               .idProduct =    USB_ID_US122MKII
+       },
+       {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     0x0644,
+               .idProduct =    USB_ID_US144MKII
+       },
        { /* terminator */ }
 };
 
index 4daf198..f263b3f 100644 (file)
@@ -25,5 +25,7 @@ struct us122l {
 
 #define USB_ID_US122L 0x800E
 #define USB_ID_US144 0x800F
+#define USB_ID_US122MKII 0x8021
+#define USB_ID_US144MKII 0x8020
 
 #endif